美文网首页
代理模式

代理模式

作者: 贪挽懒月 | 来源:发表于2020-08-23 18:30 被阅读0次

    代理模式,大家应该都不陌生,很多框架底层都用了代理模式,像spring、mybatis等。虽然大家都听说过代理模式,但是可能也并不是那么地了解,本文将说一下常用的代理模式。

    一、代理模式介绍

    代理模式其实就是找替身,要去办一件事儿,自己不去,找人代替你去,这就是代理模式。在程序中就是,为对象提供一个替身,控制替身去访问目标对象,这样做的好处是,除了目标对象能提供的功能外,还可以让替身多做一些活,即可以扩展目标对象的功能。被代理的可以是远程对象、创建时开销很大的对象或者需要安全控制的对象。

    代理模式主要分为以下三种:

    • 静态代理
    • 动态代理(又叫JDK代理、接口代理)
    • cglib代理(也属于动态代理的范畴)

    欢迎大家关注我的公众号 javawebkf,目前正在慢慢地将简书文章搬到公众号,以后简书和公众号文章将同步更新,且简书上的付费文章在公众号上将免费。


    二、静态代理

    1、静态代理介绍:

    使用静态代理的时候,需要定义接口或者父类,被代理的对象和代理对象需要一起实现相同的接口或者继承相同的父类。

    2、应用实例:

    • 定义一个接口:TeacherDao
    • 定义被代理的对象:TeacherDaoImpl,需要实现TeacherDao
    • 定义代理对象:TeacherDaoProxy,也需要实现TeacherDao
    • 要调用TeacherDaoImpl方法时,需要先创建TeacherDaoProxy对象,然后创建TeacherDaoImpl对象,将TeacherDaoImpl对象交给TeacherDaoProxy对象,再调相关方法

    代码实现:

    • TeacherDao.java:
    public interface TeacherDao {
        void teach();
    }
    
    • TeacherDaoImpl.java:
    public class TeacherDaoImpl implements TeacherDao {
        @Override
        public void teach() {
            System.out.println("今天又是没妹子的一天(ノへ ̄、)");
        }
    }
    
    • TeacherDaoProxy.java:
    public class TeacherDaoProxy implements TeacherDao {
        
        private TeacherDao target; // 被代理的对象
        
        public TeacherDaoProxy(TeacherDao target){
            this.target = target;
        }
        
        @Override
        public void teach() {
            System.out.println("代理开始");
            // 这里可以写一些额外的逻辑,以达到扩展被代理对象的目的,相当于spring的前置通知
            target.teach();
            // 这里也可以写一些额外的逻辑,以达到扩展被代理对象的目的,相当于spring的后置通知
            System.out.println("代理结束");
        }
    }
    
    • Client.java:调用代理对象
    public class Client {
    
        public static void main(String[] args){
            // 创建被代理的对象
            TeacherDao target = new TeacherDaoImpl();
            // 创建代理对象
            TeacherDaoProxy proxy = new TeacherDaoProxy(target);
            // 通过代理对象调用方法
            proxy.teach();
        }
    }
    

    3、静态代理的优缺点:

    • 优点:可以在不修改被代理对象的前提下扩展被代理的对象,做一些增强
    • 缺点:需要实现相同的接口或者继承相同的父类,所以代理类会很多,而且如果接口或者父类有改动,代理对象和被代理对象都需要维护

    三、动态代理(JDK代理)

    1、动态代理介绍:

    代理对象不要实现接口,但是被代理对象还是需要实现接口的。动态代理对象的生成,利用的是JDK的API,反射包下的Proxy类,动态地在内存中构建代理对象。

    2、java.lang.reflect.Proxy:

    这个类有一个newProxyInstance方法,该方法接收三个参数,如下:

    static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
    

    3、应用实例:

    • 定义一个接口:TeacherDao
    • 定义被代理的对象:TeacherDaoImpl,需要实现TeacherDao
    • 定义一个代理工厂ProxyFactory,有一个getProxyInstance方法,需要传入被代理的对象,然后返回代理对象实例,通过代理对象调用被代理对象的方法

    代码实现:

    • TeacherDao.java:
    public interface TeacherDao {
        void teach();
    }
    
    • TeacherDaoImpl.java:
    public class TeacherDaoImpl implements TeacherDao {
        @Override
        public void teach() {
            System.out.println("今天又是没妹子的一天(ノへ ̄、)");
        }
    }
    
    • ProxyFactory.java
    public class ProxyFactory {
    
        private Object target; // 被代理的对象
    
        public ProxyFactory(Object target){
            this.target = target;
        }
    
        // 给被代理的对象生成一个代理对象
        public Object getProxyInstance(){
            // 参数1:指定被代理对象的类加载器
            // 参数2:被代理对象实现的接口类型
            // 参数3:事件处理,执行被代理对象的方法
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("JDK代理开始");
                    // 调用方法,args的方法的参数
                    Object returnValue = method.invoke(target, args);
                    System.out.println("JDK代理结束");
                    // 将执行结果return
                    return returnValue;
                }
            });
        }
    }
    
    • Client.java:通过代理调用方法
    public class Client {
    
        public static void main(String[] args){
            // 创建被代理的对象
            TeacherDao target = new TeacherDaoImpl();
            // 创建代理对象
            TeacherDao proxy = (TeacherDao) new ProxyFactory(target).getProxyInstance();
            // 通过代理对象调用被代理对象的方法
            proxy.teach();
        }
    }
    

    四、cglib代理

    1、cglib代理介绍:

    静态代理和动态代理,被代理的对象,都需要实现接口,如果一个类没实现任何接口的,那就要用cglib代理了。cglib代理也叫子类代理,它会在内存中构建一个子类对象,从而实现对被代理对象的扩展。cglib代理底层是通过一个叫ASM的字节码处理框架来转换字节码并生成新的类从而实现代理的。被代理的类不能为final,否则会报错。被代理对象的方法如果是final/static,就不会被拦截,即不会执行被代理对象额外的业务方法。

    2、应用实例:

    • 首先要添加cglib相关依赖:
    <dependency>
         <groupId>cglib</groupId>
         <artifactId>cglib</artifactId>
         <version>3.3.0</version>
    </dependency>
    
    • TeacherDaoImpl.java:
    public class TeacherDaoImpl implements TeacherDao {
        @Override
        public void teach() {
            System.out.println("今天又是没妹子的一天(ノへ ̄、)");
        }
    }
    
    • CglibProxyFactory.java:
    // 需要实现MethodInterceptor并重写其方法
    public class CglibProxyFactory implements MethodInterceptor {
    
        private Object target;
    
        public CglibProxyFactory(Object target){
            this.target = target;
        }
    
        /**
         * 返回target的代理对象
         * @return
         */
        public Object getProxyInstance(){
            // 1. 创建工具类
            Enhancer enhancer = new Enhancer();
            // 2. 设置父类
            enhancer.setSuperclass(target.getClass());
            // 3. 设置回调函数
            enhancer.setCallback(this);
            // 4. 创建子类对象,即代理对象
            return enhancer.create();
        }
        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("CGLIB代理开始");
            Object returnValue = method.invoke(target, args);
            System.out.println("CGLIB代理结束");
            return returnValue;
        }
    }
    
    • Client.java:通过代理调用方法
    public class Client {
    
        public static void main(String[] args){
            // 创建被代理的对象
            TeacherDaoImpl target = new TeacherDaoImpl();
            // 获取代理对象,并将被代理对象传给代理对象
            TeacherDaoImpl proxy = (TeacherDaoImpl) new CglibProxyFactory(target).getProxyInstance();
            // 执行方法,触发intecept方法,从而实现执行被代理对象的方法
            proxy.teach();
        }
    }
    

    相关文章

      网友评论

          本文标题:代理模式

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