• java动态代理入门


     

    java动态代理

    动态代理是java的一个重要功能,也是Spring AOP等的实现基础。

    代理是什么?它的作用是什么?

    代理是在已有类的基础上加了一个中间层,可以在方法调用的前后进行一些附加操作,如请求过滤、参数变化、日志记录等。这样就可以在不修改已有类的情况下,增加或改变一些功能。

    代理有几种? 包括静态代理和动态代理。

    静态代理

    
        package com.invocationHandler;
        public interface Subject {
            public void myFunc(String arg);
        }
    
    
    package com.invocationHandler;
    public class RealSubject implements Subject {
        @Override
        public void myFunc(String arg) {
            System.out.println("RealSubject arg: " + arg);
        }
    }
    
    
    package com.invocationHandler;
    /**
     * 静态代理
     */
    public class SubjectProxy implements Subject {
        private RealSubject realSubject = new RealSubject();
    
        @Override
        public void myFunc(String arg) {
            realSubject.myFunc(arg);
        }
    }
    
    
    package com.invocationHandler;
    public class TestProxy {
        public static void main(String[] args){
            SubjectProxy subjectProxy = new SubjectProxy();
            subjectProxy.myFunc("静态代理");
        }
    }
    

    为什么要用代理?直接创建RealSubject的对象并调用它的方法不好吗?

    1. 可以增强现有接口的功能,或进行请求过滤等。
    2. 有时候不想或不能直接访问某个接口,可以通过代理来中转。

    静态代理的特点是?

    1. 在运行前class文件就已经编译好了。
    2. 如果被代理的类新增了方法,静态代理类也要新增相应的方法。这样系统就会变得越来越臃肿,也不利于扩展。

    动态代理

    jdk动态代理

    package com.invocationHandler;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * 动态代理
     */
    public class ProxyHandler 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 {
            Object result = null;
            System.out.println("动态代理:ProxyHandler before~~");
            result = method.invoke(target, args);
            System.out.println("动态代理:ProxyHandler after~~");
            return result;
        }
    }
    
    package com.invocationHandler;
    
    public class TestProxy {
        public static void main(String[] args){
            ProxyHandler proxyHandler = new ProxyHandler();
            Subject subject = (Subject) proxyHandler.bind(new RealSubject());
            System.out.println(subject.getClass().getName());
            subject.myFunc("动态代理");
        }
    }
    

    输出内容:

    com.sun.proxy.$Proxy0
    动态代理:ProxyHandler before~~
    RealSubject arg: 动态代理
    动态代理:ProxyHandler after~~
    

    bind()中调用的Proxy.newProxyInstance(),代码如下:

    public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
        {
            if (h == null) {
                throw new NullPointerException();
            }
    
            final SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                checkProxyAccess(Reflection.getCallerClass(), loader, interfaces);
            }
    
            /*
             * Look up or generate the designated proxy class.
             */
            Class<?> cl = getProxyClass0(loader, interfaces);
    
            /*
             * Invoke its constructor with the designated invocation handler.
             */
            try {
                final Constructor<?> cons = cl.getConstructor(constructorParams);
                final InvocationHandler ih = h;
                if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                    // create proxy instance with doPrivilege as the proxy class may
                    // implement non-public interfaces that requires a special permission
                    return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                        public Object run() {
                            return newInstance(cons, ih);
                        }
                    });
                } else {
                    return newInstance(cons, ih);
                }
            } catch (NoSuchMethodException e) {
                throw new InternalError(e.toString());
            }
        }
    

    newProxyInstance()生成的对象的类型是$Proxy0,它既不是Subject,也不是ProxyHandler。但是生成对象可以转换为Subject subject,是因为它实现了Subject的各个接口。


    动态代理是如何实现的?包括哪几步?

    1.创建调用处理器,用来处理Proxy所有方法调用

    ProxyHandler proxyHandler = new ProxyHandler();

    2.根据RealSubject的类加载器和所有接口列表,获取动态代理类类对象

    根据要实现的接口信息,在代码中动态创建该Proxy类的字节码  
    Class<?> cl = getProxyClass0(loader, interfaces);

    3.通过反射机制获取动态代理类的构造函数

    final Constructor<?> cons = cl.getConstructor(InvocationHandler.class);

    4.传入构造方法和处理器对象,创建代理类实例,参数是处理器对象

    return cons.newInstance(new Object[] {h} );


    Proxy.newProxyInstance()方法封装了2~4. Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);

    生成的代理对象继承Proxy类,实现了Subject接口,形式如:

    class $Proxy0 extends Proxy implements Subject。

    实现的Subject的方法实际调用的是proxyHandler的invoke方法。而invoke方法中利用反射调用被代理对象的对应方法:result = method.invoke(target, args)。


    动态代理的特点和用处?

    特点:

    1. 动态代理是在运行时才生成class文件,相比于静态代理,执行效率会有所降低。
    2. 利用java jdk实现的动态代理只能针对接口实现代理,见target.getClass().getInterfaces() ,而不能针对类实现,这是它的一个缺憾。cglib(code generation library)补充了jdk仅能支持接口实现类的不足。
    3. 动态代理Proxy的代码量是固定的,不会因为业务的庞大而变庞大可以减少冗余代码量。
    4. 解耦,通过参数就可以判断真实的类,不需要事先实例化,可以更灵活地添加功能,在不改变源码的情况下,增强一些方法。

    用处:

    1. spring AOP
    2. 远程调用,java标准库的RMI、hessian等.从某处看到的,我也没看懂o(╯□╰)o


    JDK 和 CGLIB 实现的动态代理

    1. JDK动态代理只能适用于实现了接口的类,而CGLIB可以针对类实现;
    2. JDK的实现是,代理类和目标类实现了相同的接口,目标对象作为代理对象的一个属性;CGLIB是,代理类是目标类的一个子类,覆盖了目标类的所有方法,所以目标类和方法不能声明为final的。CGLIB利用开源字节码处理框架ASM动态修改子类的代码来实现。具体怎么弄的我也不清楚(⊙o⊙)哦~~
    3. 如果目标类实现了接口,spring默认会使用JDK动态代理实现AOP;目标类实现了接口的情况下,可以强制用CGLIB实现AOP;若目标类没有实现接口,必须使用CGLIB,spring动态代理会自动在JDK动态代理和CGLIB之前转换。具体咋转换的呢?

    待深入内容

    1. JDK动态代理的代理类字节码具体是如何一步步生成的?
    2. spring AOP如何根据配置生成代理类的?
  • 相关阅读:
    eaysui 引用 CSS和JS 文件
    在JSP 页面中 添加CSS 样式
    Spring Hello World
    jsp+jdbc
    eclipse插件开发:使用emf建模型时,需要注意模型的坐标和大小的保存
    eclipse插件开发:GEF 进阶: Viewer
    Asp.net之MsChart控件动态绑定温度曲线图
    根据图中的盲点坐标,弹出div层
    小结get和Post的区别
    使用JavaC编译一个Java文件
  • 原文地址:https://www.cnblogs.com/rainsbaby/p/5527543.html
Copyright © 2020-2023  润新知