前段时间在做项目的时候,由于是用的纯Servlet基础框架进行开发的,没有用到那些集成的框架,后来在后台处理表单中的数据的时候,感觉有很多东西都是重复的,比较繁琐,例如获取到前台页面表单中的值之后,要在后台实例化一个对象并且调用定义的setter方法来给对象赋值,由于表单中的数据比较多,然后这个调用setter方法的代码就显得有些重复臃肿,后来网上查资料才了解到可以通过java中的反射机制简化这一操作,并且也知道了很多框架里面也都用到了反射。。。
一、什么是反射机制
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
简单的来说,反射机制指的是程序在运行时能够获取到自身的一些信息,在java中,只要给定类的名字,那么就可以通过反射机制来获取类的所有相关信息。
二、应用场合
其实在之前的时候,就已经接触过了反射机制,Java中使用JDBC操作数据库的时候,只是那个时候还不知道专业术语叫“反射机制”,例如:加载数据库的驱动类的代码。Class.forName(“com.mysql.jdbc.Driver”) ; 通过了解之后才知道原来这就是反射机制,并且也知道了很多集成的开发框架都有用到反射机制,像hibernate、struts等集成框架基本上都是通过反射机制去实现的。
三,示例
package com.wx.utils.tool;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import com.wx.pojo.repair.RepairProduct;
/**
* 类的反射机制
* 通过java中的反射机制,动态的给对象属性赋值,并且获取对象属性值
* @remark
* @author feizi
* @time 2015-2-5下午4:07:03
*/
public class ClassRefUtil {
public static void main(String[] args) {
//构建map集合,存放值
Map<String, String> fieldValueMap = new HashMap<String, String>();
fieldValueMap.put("id", "10010");
fieldValueMap.put("cust_tax_code", "440232323232323");
fieldValueMap.put("cust_name", "飞子科技");
fieldValueMap.put("contact", "飞子");
fieldValueMap.put("tel", "15623232323");
fieldValueMap.put("addr", "飞子广场");
fieldValueMap.put("product", "飞机");
fieldValueMap.put("notes", "飞不起来");
fieldValueMap.put("create_time", "2015-02-06 12:23:55");
//类的全路径(带包名)
String className = "com.wx.pojo.repair.RepairProduct";
Object obj = null;
try {
//根据类的路径(类名)来创建Class对象
Class<?> c = Class.forName(className);
//创建类的实例
obj = c.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
//通过反射赋值
setFieldValue(obj, fieldValueMap);
//通过反射取值
int i = 0;
Map<String, String> getFieldValueMap = getFieldValueMap(obj);
/**
* 1、这个对所有的对象都是通用的
*/
System.out.println("\n\n=========输出调用get方法后取到的值==========");
for (Entry<String, String> entry : getFieldValueMap.entrySet()) {
System.out.println("[字段 "+(++i)+"] ["+entry.getKey()+"] --- [" + entry.getValue()+"]");
}
/**
* 2、通过反射方式动态调用set方法赋值,通过原生get方式取值
* 也可以通过下面这种方式,直接调用类中定义的get方法进行取值
* 只是需要先把obj对象通过强制类型转换强转成我们需要操作的对象类型
*/
/*RepairProduct product = (RepairProduct) obj;
System.out.println("id:"+product.getId());
System.out.println("cust_tax_code:"+product.getCust_tax_code());
System.out.println("cust_name:"+product.getCust_name());
System.out.println("contact:"+product.getContact());
System.out.println("tel:"+product.getTel());
System.out.println("addr:"+product.getAddr());
System.out.println("product:"+product.getProduct());
System.out.println("notes:"+product.getNotes());
System.out.println("create_time:"+product.getCreate_time());
System.out.println("order:"+product.getOrder());*/
}
/**
* 通过反射调用get方法取值,并且以属性名称,和属性值以键值对的形式放于Map集合中
* @param Object obj
* @return
*/
public static Map<String, String> getFieldValueMap(Object obj){
try {
//通过getClass方法获得obj类的类类型(即Class类型)
Class<?> c = obj.getClass();
//定义一个Map集合,存放get方法获取的值
Map<String, String> getFieldValueMap = new HashMap<String, String>();
//获取obj对象中自身定义的所有方法,包括public,private,protected
//(也可以用c.getMethods(),只不过这个是获取obj中所有共有的方法,包括从父类继承的或从接口实现的所有public方法)
Method[] methods = c.getDeclaredMethods();
//获取obj对象中自身定义的所有属性变量
Field[] fields = c.getDeclaredFields();
if(null != fields && fields.length > 0){
//遍历fields数组
for (Field field : fields) {
//获取属性的定义名称
String fieldName = field.getName();
//获取属性的定义类型
String fieldType = field.getType().getSimpleName();
//拼接属性的get方法
String fieldGetMethodName = defineGetOrSetMethod(fieldName,"get");
//判断该属性是否定义了get方法
if (!checkGetOrSetMethod(methods, fieldGetMethodName)) {
continue;
}
//获取类中定义的get方法
Method fieldGetMethod = c.getMethod(fieldGetMethodName, new Class[]{});
//调用get方法获取值
Object fieldVal = fieldGetMethod.invoke(obj, new Object[]{});
String result = null;
if("Date".equals(fieldType)){
result = date2String((Date)fieldVal);
}else{
if(null != fieldVal){
result = String.valueOf(fieldVal);
}
}
getFieldValueMap.put(fieldName, result);
}
}
return getFieldValueMap;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 传递Map集合,通过反射调用set方法给属性动态赋值,
* @param Object obj
* @param fieldValueMap
*/
public static void setFieldValue(Object obj,Map<String, String> fieldValueMap){
try {
//通过getClass方法获得obj类的类类型(即Class类型)
Class<?> c = obj.getClass();
//获取对象中自身定义的所有方法
Method[] methods = c.getDeclaredMethods();
//获取对象中自身定义的所有属性
Field[] fields = c.getDeclaredFields();
//遍历fields属性数组
if(null != fields && fields.length > 0){
for (Field field : fields) {
//获取属性的定义名称
String fieldName = field.getName();
//拼接属性的set方法(自定义的set方法)
String fieldSetMethodName = defineGetOrSetMethod(fieldName,"set");
//判断该属性是否定义了set方法
if (!checkGetOrSetMethod(methods, fieldSetMethodName)) {
continue;
}
//获取类中定义的set方法
Method fieldSetMethod = c.getMethod(fieldSetMethodName, field.getType());
//取出map集合中需要操作的属性值
String fieldValue = fieldValueMap.get(fieldName);
if(!isBlank(fieldValue)){
//获取属性的定义类型
String fieldType = field.getType().getSimpleName();
//根据参数类型判断,调用不同的set方法
if("String".equals(fieldType)){
fieldSetMethod.invoke(obj, fieldValue);
}else if("Date".equals(fieldType)){
fieldSetMethod.invoke(obj, string2Date(fieldValue));
}else if(("Integer".equals(fieldType)) || ("int".equals(fieldType))){
fieldSetMethod.invoke(obj, Integer.parseInt(fieldValue));
}else if("Long".equalsIgnoreCase(fieldType)){
fieldSetMethod.invoke(obj, Long.parseLong(fieldValue));
}else if("Double".equalsIgnoreCase(fieldType)){
fieldSetMethod.invoke(obj, Double.parseDouble(fieldValue));
}else if("Boolean".equalsIgnoreCase(fieldType)){
fieldSetMethod.invoke(obj, Boolean.parseBoolean(fieldValue));
}else{
//没有合适的类型匹配
System.out.println("not suitable type" + fieldType);
}
/**
* 当然也可以通过下列这种方式来判断参数的类型,不过还是觉得上面那种方式判断简洁一些,下面要多写一些代码
*/
/*Class[] paramTypes = fieldSetMethod.getParameterTypes();
System.out.println("方法的参数类型-paramTypes:"+Arrays.toString(paramTypes));
if(paramTypes[0] == String.class){
fieldSetMethod.invoke(obj, fieldValue);
}else if(paramTypes[0] == Date.class){
fieldSetMethod.invoke(obj, string2Date(fieldValue));
}else if((paramTypes[0] == Integer.class) || (paramTypes[0] == int.class)){
fieldSetMethod.invoke(obj, Integer.parseInt(fieldValue));
}else if((paramTypes[0] == Long.class) || (paramTypes[0] == long.class)){
fieldSetMethod.invoke(obj, Long.parseLong(fieldValue));
}else if((paramTypes[0] == Double.class) || (paramTypes[0] == double.class)){
fieldSetMethod.invoke(obj, Double.parseDouble(fieldValue));
}else if((paramTypes[0] == Boolean.class) || (paramTypes[0] == boolean.class)){
fieldSetMethod.invoke(obj, Boolean.parseBoolean(fieldValue));
}else{
//没有合适的类型匹配
System.out.println("not suitable type" + fieldType);
}*/
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 拼接属性的 get 方法 或 set 方法
* 因为我们自身在定义getter方法的时候,都是将属性名的第一个字母转成大写了
* @return
*/
public static String defineGetOrSetMethod(String fieldName,String methodType){
if(isBlank(fieldName)){
return null;
}
//这里也可以直接用“get 或 set”+属性名称,(属性名称的首字母也可以不用转换成大写,上面判断的时候用equalsIgnoreCase忽略大小写就可以了)
return methodType + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
}
/**
* String字符串转Date日期格式
* @param dateStr
* @return
*/
public static Date string2Date(String dateStr){
if(isBlank(dateStr)){
return null;
}
try {
String fmtStr = null;
if(dateStr.indexOf(":") > 0){
fmtStr = "yyyy-MM-dd HH:mm:ss";
}else{
fmtStr = "yyyy-MM-dd";
}
DateFormat df = new SimpleDateFormat(fmtStr);
return df.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
/**
* 日期转字符串
* @param date
* @return
*/
public static String date2String(Date date){
if(null == date){
return null;
}
try {
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return df.format(date);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 判断某个属性是否定义了gettter和setter方法
* @param methods
* @param fieldMethodName
* @return
*/
public static boolean checkGetOrSetMethod(Method[] methods,String fieldMethodName){
boolean flag = false;
if (null != methods && methods.length > 0) {
for (Method method : methods) {
if(fieldMethodName.equals(method.getName())){
flag = true;
}
}
}
return flag;
}
/**
* 判空
* @param str
* @return
*/
public static boolean isBlank(String str){
if(null == str || str.trim().length() == 0){
return true;
}
return false;
}
}
运行main方法后的效果:
四、在程序中进行调用,这里是在Servlet中进行调用的,获取表单的值,并且给对象赋值。
/**
* 保存用户报修的产品信息
* @param request
* @param response
* @throws IOException
* @throws ServletException
*/
public void saveRepairInfo(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException{
log.info("======================进入到saveRepairInfo");
//获取参数map集合
Map<String, String> fieldValueMap = makeParamMap(request);
//需要操作的类的全路径(带包名)
String className = "com.wx.pojo.repair.RepairProduct";
Object obj = null;
try {
//通过类名(类的全路径)创建Class对象
Class<?> c = Class.forName(className);
//通过Class对象获取类的实例
obj = c.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
//创建时间
fieldValueMap.put("create_time", df.format(new Date()));
//通过类的反射机制调用set方法动态给对象赋值
ClassRefUtil.setFieldValue(obj, fieldValueMap);
//通过强制类型转换将obj对象强转成RepairProduct类型的对象
RepairProduct product = (RepairProduct) obj;
//调用保存方法保存保修的信息
boolean flag = productService.saveRepairInfo(product);
log.info("=============保存成功标志flag:"+flag);
if(flag){
//设备报修信息保存之后,主动向客户推送一条报修成功的消息
SendTemplateMsgService.sendRepairSuccessMsg(product);
//设置页面跳转后显示的内容
request.setAttribute("title", "报修成功!");
request.setAttribute("msg", "您的设备信息已经报修成功,请您耐心等待,稍后我们将会在第一时间处理您的问题!您可以在我的服务记录中查看相关记录,感谢合作!!!");
//使用ServletContext来进行页面重定向
ServletContext sc = getServletContext();
//保存成功之后,将跳转到成功界面
sc.getRequestDispatcher("/mobile/feedBack.jsp").forward(request, response);
}
}
五,通过request.getParameterNames()方法将页面form表单中的数据获取出来并且封装成一个map键值对集合返回给调用者。这里给抽象出来形成一个单独的方法,也可以便于程序中其他的地方要用到。
/**
* 将request.getParameterNames()方法取出的枚举参数列表转换成Map集合
* @param params
* @return
*/
public Map<String, String> makeParamMap(HttpServletRequest request){
Map<String, String> paramMap = new HashMap<String, String>();
//读取所有可用的表单参数,该方法可以取得所有变量的名称,该方法返回一个Emumeration(枚举),键值对
Enumeration paramNames = request.getParameterNames();
if(null != paramNames){
//遍历枚举
while(paramNames.hasMoreElements()){
String paramName = (String) paramNames.nextElement();
String paramValue = request.getParameter(paramName);
paramMap.put(paramName, paramValue);
}
}
return paramMap;
}
额,由于也是刚接触,所以理解的也比较浅,上面的这些只是我自己的一些理解,也不知道这样对不对,当然,反射机制属于Java中的高级应用范畴了,也是一种相当相当好的机制,我个人觉得如果理解了反射机制,也会帮助我们更好的去理解那些集成的框架,这些框架学习起来也会相对的更加的容易一些。嗯。。。慢慢进步吧!!!