zoukankan      html  css  js  c++  java
  • java动态代理实现与原理详细分析

     一、代理模式

       代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。

    二、静态代理

        1、静态代理

    静态代理:由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。

        2、静态代理简单实现

     假如一个班的同学要向老师交班费,但是都是通过班长把自己的钱转交给老师。这里,班长就是代理学生上交班费,班长就是学生的代理。

        首先,我们创建一个Person接口。这个接口就是学生(被代理类),和班长(代理类)的公共接口,他们都有上交班费的行为。这样,学生上交班费就可以让班长来代理执行。

    /**
     * 创建Person接口
     * @author Gonjan
     */
    public interface Person {
        //上交班费
        void giveMoney();
    }

    Student类实现Person接口。Student可以具体实施上交班费的动作。

    public class Student implements Person {
        private String name;
        public Student(String name) {
            this.name = name;
        }
        
        @Override
        public void giveMoney() {
           System.out.println(name + "上交班费50元");
        }
    }

    StudentsProxy类,这个类也实现了Person接口,但是还另外持有一个学生类对象,由于实现了Peson接口,同时持有一个学生对象,那么他可以代理学生类对象执行上交班费(执行giveMoney()方法)行为。

    /**
     * 学生代理类,也实现了Person接口,保存一个学生实体,这样既可以代理学生产生行为
     * @author Gonjan
     *
     */
    public class StudentsProxy implements Person{
        //被代理的学生
        Student stu;
        
        public StudentsProxy(Person stu) {
            // 只代理学生对象
            if(stu.getClass() == Student.class) {
                this.stu = (Student)stu;
            }
        }
        
        //代理上交班费,调用被代理学生的上交班费行为
        public void giveMoney() {
            stu.giveMoney();
        }
    }

    下面测试一下,看如何使用代理模式:

    public class StaticProxyTest {
        public static void main(String[] args) {
            //被代理的学生张三,他的班费上交有代理对象monitor(班长)完成
            Person zhangsan = new Student("张三");
            
            //生成代理对象,并将张三传给代理对象
            Person monitor = new StudentsProxy(zhangsan);
            
            //班长代理上交班费
            monitor.giveMoney();
        }
    }

    这里并没有直接通过张三(被代理对象)来执行上交班费的行为,而是通过班长(代理对象)来代理执行了。这就是代理模式。

    三、动态代理

    JDK动态代理

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.util.Arrays;
    
    /**
     * JDK动态代理
     * 一个类(Proxy)一个接口(InvocationHandler)
     * */
    public class JDKProxy implements InvocationHandler{
        //真实主题对象
        private Object target;
        
        /**
         * 给主题创建一个代理对象(基于接口实现)
         * */
        public Object proxy(Object target){
            this.target = target;
            /*
             * 1.类加载器
             * 2.接口数组
             * 3.InvocationHandler 对象
             * */
            return Proxy.newProxyInstance(
                    target.getClass().getClassLoader(), 
                    target.getClass().getInterfaces(), 
                    this);
        }
        /**
         * proxy 代理对象
         * method 当前执行的方法
         * args 方法的参数数组
         * */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //前置增强方法...
            System.out.println("前置增强方法");
            
            Object ret = method.invoke(target, args);
            
            //后置增强方法...
            System.out.println("后置增强方法");
            return ret;
        }

    优点

      基于接口实现-----获取真实主题对象的接口,通过反射机制创建一个实现该接口的匿名类,在调用要增强的方法前调用InvacationHandler接口里的invoke方法。因为是jdk自带的功能,所以不需要额外导入第三方jar包
    缺点

      不能对单个类(未实现接口)进行代理,因为jdk动态代理是基于接口实现

    cglib动态代理

    import java.lang.reflect.Method;
    import java.util.Arrays;
    import org.springframework.cglib.proxy.Enhancer;
    import org.springframework.cglib.proxy.MethodInterceptor;
    import org.springframework.cglib.proxy.MethodProxy;
    
    /**
     * Cglib动态代理    基于:1、继承    2、动态编译
     * 1、Enhancer类
     * 2、MethodInterceptor 方法拦截器  接口
     * */
    public class CglibProxy implements MethodInterceptor{
        //真实主题对象
        private Object target;
        /**
         * 给主题创建一个代理对象(基于继承实现)
         * obj        代理主题
         * method    调用方法    
         * args        参数数组
         * mp        代理方法
         * */
        public Object proxy(Object target){
            this.target = target;
            
            Enhancer en = new Enhancer();
            en.setSuperclass(target.getClass());
            en.setCallback(this);
            return en.create();
        }
        
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
            //前置增强方法...
            System.out.println("前置增强方法");
            
            Object ret = mp.invoke(target, args);
            
            //后置增强方法...
            System.out.println("后置增强方法");
            return ret;
        }
        
    }

    优点

      基于类和继承实现----利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。功能十分强大,被代理的类可以不用实现接口,并且运行速度十分快
    缺点

      在java中有一种类是无法被继承的,那就是用final关键字定义的类。而cglib动态代理是基于继承来实现的,所以它唯一的不足就在这里----无法对 final类 进行代理。此外,由于cglib是由第三方组织提供的,所以在使用的时候需要引入cglib的jar包(我这里是用的spring,它里面集成了cglib,若是单独使用cglib,可以直接去mvnrepository.com这个网站下载它的jar包)

  • 相关阅读:
    MySQL百万级数据量分页查询方法及其优化
    Windows10内置Linux子系统初体验
    谈谈区块链(18):以太坊的UTXO
    永久告别mac屏幕涂层脱落
    Cloud Foundry中DEA启动应用实例时环境变量的使用
    jQuery 事件方法大全-超全的总结
    UVA12304-2D Geometry 110 in 1!
    Hbase总结(五)-hbase常识及habse适合什么场景
    Android笔记之 网络http通信
    Mac下安装Redis
  • 原文地址:https://www.cnblogs.com/deityjian/p/11298774.html
Copyright © 2011-2022 走看看