代理

作者: 月寒兮夜凉 | 来源:发表于2016-11-11 13:39 被阅读57次
    优秀文章

    博客:
    ykzhen2015 > MyBATIS插件原理第一篇——技术基础(反射和JDK动态代理)
    ITEYE:
    layznet > java静态代理和动态代理
    zheng12tian > java 动态代理学习(Proxy,InvocationHandler)——自己的理解


    说明
    • 代理是对真实对象的代表;
    • 代理通常使用接口来规范(JDK动态代理必须满足该条件);

    PS:下文中RealSubject为实际类,RealProxy为代理类,首字母小写为各自实例对象。


    代理模式
    为对象提供一个专用于访该对象的代理对象,外部通过访问代理对象实现对该对象的访问。
    

    静态代理
    1. 设计接口:
      设计接口的目的是为RealSubject与RealProxy规范代理方法,并为两者提供共同的引用;
        public interface Subject {
            public void func1();
            public void func2(String str);
        }
    
    1. 编写RealSubject类:
      类需实现Subject接口;
        public class RealSubject implements Subject {
            
            public void func1() {
                System.out.println("RealSubject类的func1方法被调用了。");
            }
        
            public void func2(String str) {
                System.out.println("RealSubject类的func2方法被调用了,参数是"+str+"。");
            }
            
        }
    
    1. 编写Proxy类:
      实现Subject接口并设置被代理对象RealSubject的引用;
        public class RealProxy implements Subject {
            // 在代理对象内声明被代理对象的引用,此处使用接口则可以接收任何实现该接口的代理对象;
            private Subject subject;
            
            // 设置被代理对象实例;
            public void setSubject(Subject subject){
                this.subject = subject;
            }
            
            // 对代理对象的方法代理;
            public void func1() {
                System.out.println("RealProxy类的func1方法被调用了。");
                subject.func1();
            }
            
            public void func2(String str) {
                System.out.println("RealProxy类的func2方法被调用了,参数是"+str+"。");
                subject.func2(str);
            }
            
        }
    
    1. 实现:
      利用Proxy完成对RealSubject的代理;
        public static void main(String[] args) {
            RealProxy realProxy = new RealProxy();
            realProxy.setSubject(new RealSubject());
            realProxy.func1();
            realProxy.func2("demo");
        }
    

    结果

    RealProxy类的func1方法被调用了。
    RealSubject类的func1方法被调用了。
    RealProxy类的func2方法被调用了,参数是demo。
    RealSubject类的func2方法被调用了,参数是demo。


    动态代理

    动态代理与静态代理不同点在于静态代理需要在RealProxy类中手动对RealSubject类的代理方法进行设置,而动态代理则是通过Java的反射(此处链接我的另一篇文章:反射)机制来实现对RealSubject类的全部代理方法进行设置,这里所指的全部代理方法是通过RealSubject类实现的Subject接口来判定的,因此,要实现动态代理,就必须使用接口规范代理方法,且RealProxy类在动态代理中不需要实现Subject接口而必须实现InvocationHandler接口;

    • 关键接口:InvocationHandler
    • 所有实现动态代理的代理对象都必须实现这个接口;
    • 实现该接口后,所有代理方法都是在invoke()方法中实现,故可以在此方法内设置额外的代理内容;
    • 关键方法:
      Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),realSubject.getClass().getInterfaces(), realProxy);
    • 获取具备RealSubject信息的RealProxy对象;
    • 修改RealProxy类:
        public class RealProxy implements InvocationHandler {
            // 在代理对象内声明被代理对象的引用,此处使用接口则可以接收任何实现该接口的代理对象;
            private Subject subject;
            
            // 设置被代理对象实例;
            public void setSubject(Subject subject){
                this.subject = subject;
            }
            
            // 对代理对象的方法代理;
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object result = null;
                // 获取参数;
                String str = "";
                if(args!=null && args.length>0){
                    for(Object obj : args){
                        String s = (String) obj;
                        str = str + ",参数是" + s;
                    }
                }
                System.out.println("RealProxy类的"+method.getName()+"方法被调用了"+str+"。");
                result = method.invoke(subject, args);
                return result;
            }
    
        }
    
    • 实现
        public static void main(String[] args) {
            
            RealProxy realProxy = new RealProxy();
            RealSubject realSubject = new RealSubject();
            realProxy.setSubject(realSubject);
            // 获取动态代理对象;
            Subject subject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),realSubject.getClass().getInterfaces(), realProxy);
            subject.func1();
            subject.func2("demo");
        }
    
    • 结果

    RealProxy类的func1方法被调用了。
    RealSubject类的func1方法被调用了。
    RealProxy类的func2方法被调用了,参数是demo。
    RealSubject类的func2方法被调用了,参数是demo。


    CGLib

    CGLib(Code Generation Library)是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。——CGLib百度百科

    使用CGLib,可以对未实现接口的代理对象进行代理,其原理是根据代理对象生成新的子类对象,通过拦截子类对象的所有方法来实现动态的代理,使用者所得到的代理对象实质是原代理对象的子类对象。

    • CGLib的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。
    • 由于CGLib由于是采用动态创建子类的方法,对于final方法,无法进行代理。

    最后

    • 动态代理是很多框架的基础实现技术,常见的就有Spring的AOP和MaBatis的Mapper;

    相关文章

      网友评论

        本文标题:代理

        本文链接:https://www.haomeiwen.com/subject/wgqfpttx.html