• 反射与代理


      java中反射跟代理是有点不好理解,除非要自己真写过代码,要不然还真不知道怎么玩。其实说白了也就没啥神秘的,反射本质就运行时加载类编译后的class文件,然后根据java.lang.Class所提供的API进行操作,包括获取该类的包名、所实现的接口名、所继承的父类名,以及该类自己的类名、方法名、字段名、构造函数名,真正有用的是利用java.lang.reflect提供的API直接调用方法,修改字段值,用构造函数实例化对象,这就是反射为啥能动态的秘密。具体参见下面代码:

    package com.wulinfeng.io;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Map.Entry;
    
    public class Reflection {
    
        public static void main(String[] args) throws Exception {
            Class clz = Member.class;
            printClassInfo(Member.class);
    
            // 通过构造函数实例化对象
            Constructor c = clz.getConstructor(String.class, String.class, int.class, String.class);
            Member m = (Member) c.newInstance("9527", "liangchaowei", 53, "18912346987");
            System.out.println(m.toString());
    
            // 构造方法名调用java.lang.reflect.Method的invoke方法重置字段值
            Map<String, Object> hashMap = new HashMap<>();
            hashMap.put("number", "0001");
            hashMap.put("name", "wulinfeng");
            hashMap.put("age", 33);
            hashMap.put("phone", "13812345678");
            for (Entry<String, Object> entry : hashMap.entrySet()) {
                String key = entry.getKey();
                Method method = clz.getMethod("set" + key.substring(0, 1).toUpperCase() + key.substring(1),
                        entry.getValue().getClass());
                if (Modifier.isPublic(method.getModifiers())) {
                    method.invoke(m, entry.getValue());
                }
            }
            System.out.println(m.toString());
    
            // 私有字段需要通过设置Accessible为true才能重新设值
            Field name = clz.getDeclaredField("name");
            name.setAccessible(true);
            name.set(m, "zhangtianyou");
    
            System.out.println(m.toString());
        }
    
        private static void printClassInfo(Class clz) {
            System.out.printf("类名:%s,是否接口:%b,是否基本类型:%b,是否数组:%b,父类:%s
    ", clz.getName(), clz.isInterface(),
                    clz.isPrimitive(), clz.isArray(),
                    clz.getSuperclass() != null ? clz.getSuperclass().getName() : "父类不存在");
            System.out.printf("包名:%s,修饰符:%s
    ", clz.getPackage() != null ? clz.getPackage().getName() : "包不存在",
                    Modifier.toString(clz.getModifiers()));
    
            for (Constructor c : clz.getConstructors()) {
                System.out.printf("构造函数名:%s	", c.getName());
            }
            System.out.println();
    
            for (Field field : clz.getDeclaredFields()) {
                System.out.printf("字段名:%s	", field.getName());
            }
            System.out.println();
    
            for (Method m : clz.getMethods()) {
                System.out.printf("方法:%s	", m.getName());
            }
            System.out.println();
    
        }
    }
    package com.wulinfeng.io;
    
    import java.io.Serializable;
    public class Member implements Serializable {
    
        static {
            System.out.println("I'm wumanshu!");
        }
    
        private String number;
        private String name;
        private Integer age;
        private String phone;
    
        public Member(String number, String name, int age, String phone) {
            this.number = number;
            this.name = name;
            this.age = age;
            this.phone = phone;
        }
    
        public String getNumber() {
            return number;
        }
    
        public void setNumber(String number) {
            this.number = number;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public String getPhone() {
            return phone;
        }
    
        public void setPhone(String phone) {
            this.phone = phone;
        }
    
        @Override
        public String toString() {
            return String.format("(%s,%s,%d,%s)", number, name, age, phone);
        }
    
    }
    
    

      运行结果:

    类名:com.wulinfeng.io.Member,是否接口:false,是否基本类型:false,是否数组:false,父类:java.lang.Object
    包名:com.wulinfeng.io,修饰符:public
    构造函数名:com.wulinfeng.io.Member    
    字段名:number    字段名:name    字段名:age    字段名:phone    
    方法:getNumber    方法:toString    方法:getName    方法:setName    方法:getAge    方法:setNumber    方法:setAge    方法:getPhone    方法:setPhone    方法:wait    方法:wait    方法:wait    方法:equals    方法:hashCode    方法:getClass    方法:notify    方法:notifyAll    
    I'm wumanshu!
    (9527,liangchaowei,53,18912346987)
    (0001,wulinfeng,33,13812345678)
    (0001,zhangtianyou,33,13812345678)

      代理本身就是设计模式的一种,就是一种行为,可以有多种实现方式。从类的角度看就是一个接口中的方法,有多个不同的类来实现。代理从类角度分接口代理和类代理,接口代理jdk本身提供了对应的API实现,但必须要有接口的存在;类代理由cglib提供支持,可以没有接口。从实现角度分静态代理和动态代理,静态代理需要先定义好一个接口,多个实现类,其中一个实现类调用了另一个实现类的接口方法,在编译期就完成了代理,这里其实就是装饰模式的一种实现;动态代理用到了上面提及的反射,在运行时生成对象实例,并通过实现java.lang.reflect.InvocationHandler的invoke方法完成代理。具体实现参考下面:

       静态代理:

    package com.wulinfeng.io;
    
    public interface Log {
    
        void warn(String name);
    
    }
    package com.wulinfeng.io;
    
    public class HelloLog implements Log {
    
        @Override
        public void warn(String name) {
            System.out.printf("hello %s, you have be traced.
    ", name);
        }
    
    }
    package com.wulinfeng.io;
    
    import java.util.logging.Level;
    import java.util.logging.Logger;
    
    public class HelloLogProxy implements Log {
    
        private HelloLog helloLog;
    
        public HelloLogProxy(HelloLog helloLog) {
            this.helloLog = helloLog;
        }
    
        public void warn(String name) {
            log("方法开始****");
            helloLog.warn(name);
            log("方法结束****");
        }
    
        private void log(String msg) {
            Logger.getLogger(HelloLog.class.getName()).log(Level.WARNING, msg);
        }
        
        public static void main(String[] args) {;
            Log log = new HelloLogProxy(new HelloLog());
            log.warn("wumanshu");
            
        }
    }

      运行结果:

    三月 20, 2017 12:58:51 上午 com.wulinfeng.io.HelloLogProxy log
    警告: 方法开始****
    hello wumanshu, you have be traced.
    三月 20, 2017 12:58:51 上午 com.wulinfeng.io.HelloLogProxy log
    警告: 方法结束****

      动态代理:

    package com.wulinfeng.io;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    
    public class LogHander implements InvocationHandler {
        private Object target;
    
        // 生成目标类
        public Object bind(Object target) {
            this.target = target;
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            log("begin log: " + method.getName());
            System.out.println("begin print: " + method.getName());
            Object result = method.invoke(target, args);
            log("finish log: " + method.getName());
            System.out.println("finish print: " + method.getName());
            return result;
        }
    
        /**
         * 日志打印
         * 
         * @param msg
         */
        private void log(String msg) {
            Logger.getLogger(HelloLog.class.getName()).log(Level.WARNING, msg);
        }
    
        public static void main(String[] args) {
            LogHander lh = new LogHander();
            Log log = (Log) lh.bind(new HelloLog());
            log.warn("world");
        }
    
    }

      运行结果:

    begin print: warn
    hello world, you have be traced.
    finish print: warn
    三月 20, 2017 12:59:50 上午 com.wulinfeng.io.LogHander log
    警告: begin log: warn
    三月 20, 2017 12:59:50 上午 com.wulinfeng.io.LogHander log
    警告: finish log: warn

      这里从结果看日志打印并不符合我们预期,我们预期日志打印位置应该跟静态代理运行结果一致才对。从标准输出来看代理是没问题的,所以问题出现在日志打印滞后了,这跟虚拟机执行日志打印到控制台的速度有关,因为加载并打印log的速度不及标准输出,所以产生了延时,再跑一次就好可以看到日志打印的位置正常了。

  • 相关阅读:
    使用ExcelMapper制作用于打印的矩阵
    八皇后问题-回溯法解
    HashMap-1.8 你看得懂的原理分析
    一生之敌
    必学十大经典排序算法,看这篇就够了(附完整代码/动图/优质文章)
    事务的四种隔离级别
    数据库的三范式
    ConcurrentHashMap底层实现原理和源码分析
    leetcode-160-相交链表(simple)
    JAVA中priorityqueue详解
  • 原文地址:https://www.cnblogs.com/wuxun1997/p/6583244.html
Copyright © 2020-2023  润新知