zoukankan      html  css  js  c++  java
  • 设计模式:代理模式

    代理模式定义

    代理模式(proxy pattern)指为对象提供一层代理,以控制对象的访问,属于结构型设计模式。

    当无法或不想直接引用某个对象或访问某个对象困难时,可以通过代理对象来间接访问。使用代理模式主要有两个目的:1.保护目标对象 2.增强目标对象。

    通用实现(写法)

    public interface ISubject {
        public void invokeMethod();
    }
    
    public class Subject implements ISubject{
        @Override
        public void invokeMethod() {
            System.out.println("Subject invokeMethod");
        }
    }
    //代理类
    public class ProxySubject implements ISubject{
    
        private ISubject iSubject;
    
        ProxySubject(ISubject iSubject) {
            this.iSubject = iSubject;
        }
    
        @Override
        public void invokeMethod() {
            before();
            iSubject.invokeMethod();
            after();
        }
    
        private void before(){
            System.out.println("before");
        }
        private void after(){
            System.out.println("after");
        }
    }
    

    uml:

    image-20201223224906641

    测试:

        @Test
        public void test(){
            ProxySubject proxySubject = new ProxySubject(new Subject());
            proxySubject.invokeMethod();
        }
    

    image-20201223225020698

    代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个类

    上述写法是静态代理,下面介绍动态代理

    Jdk动态代理

    上述的ProxySubject是代理类,我们需要实现Isubject接口,并手动创建该代理对象,调用目标方法。

    而在动态代理中,我们可以让程序在运行的时候自动在内存中创建一个实现 Isubject接口的代理,而不需要去定义 ProxySubject这个类。

    目前普遍使用的是Jdk动态代理和Cglib动态代理。现介绍Jdk动态代理。

    两种写法:

    实现InvocationHandler接口

    public interface IJdkSubject {
        public void invokeMethod();
    }
    public class JdkSubject implements IJdkSubject{
        @Override
        public void invokeMethod() {
            System.out.println("JdkSubject invokeMethod");
        }
    }
    
    public class JdkProxySubject1 implements InvocationHandler {
    
        private IJdkSubject jdkSubject;
    
        /**
         * 创建代理对象
         */
        public IJdkSubject createProxy(IJdkSubject jdkSubject){
            this.jdkSubject = jdkSubject;
            return (IJdkSubject)Proxy.newProxyInstance(jdkSubject.getClass().getClassLoader(), jdkSubject.getClass().getInterfaces(),this);
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("before");
            Object invoke = method.invoke(jdkSubject, args);
            System.out.println("after");
            return invoke;
        }
    }
    

    image-20201223231521182

    实现匿名内部类

    public class JdkProxySubject2 {
    
        public IJdkSubject createProxy(IJdkSubject jdkSubject){
            return (IJdkSubject)Proxy.newProxyInstance(jdkSubject.getClass().getClassLoader(), jdkSubject.getClass().getInterfaces(), (proxy, method, args) -> {
                System.out.println("before");
                Object invoke = method.invoke(jdkSubject, args);
                System.out.println("after");
                return invoke;
            });
        }
    }
    

    测试:

        @Test
        public void test(){
            JdkProxySubject1 jdkProxySubject1 = new JdkProxySubject1();
            IJdkSubject proxy = jdkProxySubject1.createProxy(new JdkSubject());
            proxy.invokeMethod();
        }
    
        @Test
        public void test2(){
            JdkProxySubject2 jdkProxySubject2 = new JdkProxySubject2();
            IJdkSubject proxy = jdkProxySubject2.createProxy(new JdkSubject());
            proxy.invokeMethod();
        }
    

    两种方法本质是一种,都是创建动态代理对象执行目标方法,都会进入InvocationHandler的invoke回调方法,通过此方法,可以对目标方法进行增强。

    对于jdk动态代理来说,我们需要被代理类实现接口,那么没有实现接口的类就不能使用jdk动态代理了。由此引入cglib动态代理。

    Cglib动态代理

    导入相关jar包(在spring-core自带cglib可直接测试,或者导入其他cglib依赖包)

    <!-- https://mvnrepository.com/artifact/cglib/cglib -->
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>
    
    class CglibSubject {
        void methodInvoke(){
            System.out.println("CglibSubject methodInvoke");
        }
    }
    
    public class CglibProxySubject implements MethodInterceptor {
    
        CglibSubject createProxy(Class<CglibSubject> clazz){
            //创建核心类,让它帮助我们创建代理对象
            Enhancer enhancer = new Enhancer();
            //设置父类
            enhancer.setSuperclass(clazz);
            //设置回调
            enhancer.setCallback(this);
            //创建代理
            return (CglibSubject)enhancer.create();
        }
    
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("before");
            Object invoke = proxy.invokeSuper(obj, args);
            System.out.println("after");
            return invoke;
        }
    }
    

    MethodInterceptor接口:

    image-20201223234258392

    测试:

    public class CglibTest {
        @Test
        public void test(){
            CglibProxySubject cglibProxySubject = new CglibProxySubject();
            CglibSubject proxy = cglibProxySubject.createProxy(CglibSubject.class);
            proxy.methodInvoke();
        }
    }
    

    我们发现:cglib动态代理的目标对象不需要实现任何接口,它是通过动态继承目标对象实现动态代理的

  • 相关阅读:
    jQuery的动画以及扩展功能
    yii2 redirect重定向
    nvaicat mysql ssh 跳板机(堡垒机???)连接服务器
    Linux下Redis的安装
    深入理解PHP的运行模式
    thinkphp5 如何监听sql?
    thinkphp5 如何使用查询事件?
    layui laydate is not defined
    CSS 实现图片灰度效果 兼容各种浏览器
    PHP基础学习----函数
  • 原文地址:https://www.cnblogs.com/wwjj4811/p/14181840.html
Copyright © 2011-2022 走看看