zoukankan      html  css  js  c++  java
  • 反射与代理

      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的速度不及标准输出,所以产生了延时,再跑一次就好可以看到日志打印的位置正常了。

  • 相关阅读:
    There is an overlap in the region chain修复
    There is an overlap in the region chain
    region xx not deployed on any region server
    python 中的re模块,正则表达式
    TCP粘包问题解析与解决
    yield from
    Git push提交时报错Permission denied(publickey)...Please make sure you have the correct access rights and the repository exists.
    mysql 中Varchar 与char的区别
    Mysql 字符集及排序规则
    请实现一个装饰器,限制该函数被调用的频率,如10秒一次
  • 原文地址:https://www.cnblogs.com/wuxun1997/p/6583244.html
Copyright © 2011-2022 走看看