Java学习之反射机制

前段时间在做项目的时候,由于是用的纯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中的高级应用范畴了,也是一种相当相当好的机制,我个人觉得如果理解了反射机制,也会帮助我们更好的去理解那些集成的框架,这些框架学习起来也会相对的更加的容易一些。嗯。。。慢慢进步吧!!!

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值