• 设计模式-代理模式


    Java设计模式(代理模式

    Java中的设计模式-代理模式

    • 静态代理(定义一个平级的实现类完成代理模式)

    • 动态代理

           — JDK动态代理(定义拦截器实现InvocationHandler接口,通过Proxy产生代理对象)
      
           —CGLIB动态代理(定义拦截器实现MethodInterceptor接口,通过Enhancer类生成代理对象)
      
      • 1
      • 2
      • 3

    静态代理

    代理对象和实际对象都继承了同一个接口,在代理对象中指向的是实际对象的实例,这样对外暴露的是代理对象而真正调用的是 Real Object.

    静态代理就是通过创建平级的实现类的形式完成代理,能够实现代理的优势,但是同时缺点也很多:

    以下就是基于静态代理进行的操作。静态代理存在如下几个缺点:

    1、如果项目中存在N个接口,那么就会产生N个对应的实现类。我们的代理类也会随着实现类的增加而增加。

    2、随着接口中的功能的增加,实现类中的功能也会同步增加。我们的代理类中的增强方法也会增加。

    3、从严格意义上来讲:我们定义的增强的内容(事务处理)。其实是没有达到彻底复用。

    4、优点:就是通过代理的方式,完成了目标对象的代理,并且在方法中置入了增强的处理,而且没有针对源代码进行修改。

    • 需要代理的接口
    package com.sin.service;
    
    /**
     * @author sin
     * @date 2022/10/27
     * @apiNote
     */
    public interface ProxySubject {
        public String inrtroduce(String name);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 实现类
    package com.sin.service.impl;
    
    import com.sin.service.ProxySubject;
    
    /**
     * @author sin
     * @date 2022/10/27
     * @apiNote
     */
    public class ProxySubjectImpl implements ProxySubject {
        @Override
        public String inrtroduce(String name) {
            System.out.println("我的名字是:" + name);
            return name;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 代理类
    package com.sin.proxy;
    
    import com.sin.service.ProxySubject;
    import com.sin.service.impl.ProxySubjectImpl;
    
    /**
     * @author sin
     * @date 2022/10/27
     * @apiNote
     */
    public class StaticProxy implements ProxySubject {
    
        private ProxySubjectImpl proxySubject;
    
    
        public StaticProxy(ProxySubjectImpl proxySubject){
            this.proxySubject = proxySubject;
        }
    
        @Override
        public void inrtroduce(String name) {
            System.out.println("调用前业务");
            proxySubject.inrtroduce(name);
            System.out.println("调用后的业务");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 测试
    package com.sin.test.proxy;
    
    import com.sin.proxy.StaticProxy;
    import com.sin.service.ProxySubject;
    import com.sin.service.impl.ProxySubjectImpl;
    import org.junit.Test;
    
    /**
     * @author sin
     * @date 2022/10/27
     * @apiNote
     */
    public class StaticProxyTest {
        @Test
        public void test(){
            ProxySubjectImpl  proxySubject = new ProxySubjectImpl();
    
            StaticProxy staticProxy = new StaticProxy(proxySubject);
            staticProxy.inrtroduce("张三");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 测试结果

    在这里插入图片描述

    JDK动态代理

    动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术。在生成代理对象的· 过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法。可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作。

    Java1.3 以后, JAVA 实现了动态代理技术,允许开发者在运行期创建接口的代理实例 。

    JDK 的动态代理主要使用的是 java.lang.reflect 包中的Proxy 和 InvocationHandler。

    • InvocationHandler 是接口,可以通过实现该接口来定义横切逻辑,并通过反射机制调用目标类的代码,动态地将横切逻辑和业务逻辑编织在一起 。
    • Proxy 利用 InvocationHandler 动态创建出一个实现某一接口的实例,来生成目标类的代理对象 。
    //自定义定获取被代理接口实现类的实例对象
    public <T> T getProxy() {
    	        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), 
                                                  target.getClass().getInterfaces(), 
                                                  this);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    提示!

    jdk动态代理必须要依赖于接口。

    • 需要动态代理的接口
    package com.sin.service;
    
    /**
     * 需要动态代理的接口
     * @author sin
     * @date 2022/10/27
     * @apiNote
     */
    public interface JDKAgentSubjectService {
        /**
         * 你好
         * @param name
         * @return
         */
        public String sayHello(String name);
    
        /**
         * 再见
         * @return
         */
        public String sayGoodBye();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 需要代理的实际对象
    package com.sin.service.impl;
    
    import com.sin.service.JDKAgentSubjectService;
    
    /**
     * 实例接口对象
     *
     * @author sin
     * @date 2022/10/27
     * @apiNote
     */
    public class JDKAgentSubjectServiceImpl implements JDKAgentSubjectService {
        /**
         * 你好
         *
         * @param name
         * @return
         */
        @Override
        public String sayHello(String name) {
            return "你好" + name;
        }
    
        /**
         * 再见
         *
         * @return
         */
        @Override
        public String sayGoodBye() {
            return "再见";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 调用处理器实现类
    package com.sin.proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    /**
     * @author sin
     * @date 2022/10/27
     * @apiNote
     */
    public class InvocationHandlerImpl  implements InvocationHandler {
    
        //要代理的对象
        private Object subject;
    
        public InvocationHandlerImpl(Object subject){
            this.subject = subject;
        }
    
        /**
         * 负责集中处理动态代理上的所有方法调用
         * @param proxy 代理类实例
         * @param method 被调用的方法对象
         * @param args 调用参数
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("模拟的添加操作");
    
            System.out.println("Method" + method);
    
            Object value  = method.invoke(subject,args);
    
            System.out.println("接收后模拟的添加操作");
    
            return value;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 测试
    package com.sin.test.proxy;
    
    import com.sin.proxy.InvocationHandlerImpl;
    import com.sin.service.JDKAgentSubjectService;
    import com.sin.service.impl.JDKAgentSubjectServiceImpl;
    import org.junit.Test;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;
    
    /**
     * @author sin
     * @date 2022/10/27
     * @apiNote
     */
    public class InvocationHandlerTest {
    
        @Test
        public void testReal(){
            //代理的对象
             Object  subjectService =new JDKAgentSubjectServiceImpl();
    
            /**
             * InvocationHandlerImpl 实现了InvocationHandler接口,实现方法调用从代理类到委托类的发派转发
             * 内部包含了指向委托类实例的引用,用于真正执行分派转发过来的方法调用
             * 要代理那个对象就将那个对象传进来,最后通过该对象来调用其方法
             */
            InvocationHandler handler=  new InvocationHandlerImpl(subjectService);
    
            ClassLoader loader = subjectService.getClass().getClassLoader();
            Class[] interfaces = subjectService.getClass().getInterfaces();
    
            JDKAgentSubjectService subject = (JDKAgentSubjectService) Proxy.newProxyInstance(loader,interfaces,handler);
    
            System.out.println("要动态代理对象的类型:"+subject.getClass().getName());
    
            String hello = subject.sayHello("张三");
            System.out.println(hello);
    
            String bye = subject.sayGoodBye();
            System.out.println(bye);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 测试结果

    在这里插入图片描述

    CGLIB动态代理

    采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑,来完成动态代理的实现。

    实现方式实现 MethodInterceptor 接口,重写 intercept 方法,通过 Enhancer 类的回调方法来实现。

    • 要代理对象接口
    package com.sin.service;
    
    /**
     * @author sin
     * @date 2022/10/27
     * @apiNote
     */
    public interface UserServiceCGlib {
        public void addUser();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 要代理对象接口实现类
    package com.sin.service.impl;
    
    import com.sin.service.UserServiceCGlib;
    
    /**
     * @author sin
     * @date 2022/10/27
     * @apiNote
     */
    public class UserServiceCGlibImpl implements UserServiceCGlib {
        @Override
        public void addUser() {
            System.out.println("增加了一条用户");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • CGlib动态代理实现类
    package com.sin.proxy;
    
    import org.springframework.cglib.proxy.Enhancer;
    import org.springframework.cglib.proxy.MethodInterceptor;
    import org.springframework.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    /**
     * CGlib 动态代理实现类
     * @author sin
     * @date 2022/10/27
     * @apiNote
     */
    public class MethodInterceptorImpl implements MethodInterceptor {
    
        //CGlib需要代理的对象
        private Object object;
    
        public Object createProxyObject( Object o){
             this.object = o;
    
             //通过CGlib动态代理获取代理对象的过程
            Enhancer enhancer = new Enhancer();
    
            //设置Enhancer对象的父类
            enhancer.setSuperclass(o.getClass());
    
            //设置Enhancer的回顾对象
            enhancer.setCallback(this);
    
            //设置Enhancer对象的父类
            Object o1 = enhancer.create();
            return o1;
        }
    
    
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            Object obj ;
            System.out.println("调用前的业务");
            obj = method.invoke(object,objects);
            System.out.println("调用后的业务");
            return obj;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 测试
    package com.sin.test.proxy;
    
    import com.sin.proxy.MethodInterceptorImpl;
    import com.sin.service.impl.UserServiceCGlibImpl;
    import org.junit.Test;
    
    /**
     * @author sin
     * @date 2022/10/27
     * @apiNote
     */
    public class MethodInterceptorTest {
        @Test
        public void test(){
            UserServiceCGlibImpl userServiceCGlib = new UserServiceCGlibImpl();
    
            MethodInterceptorImpl methodInterceptorimpl = new MethodInterceptorImpl();
    
            UserServiceCGlibImpl serviceCGlib  = (UserServiceCGlibImpl)methodInterceptorimpl.createProxyObject(userServiceCGlib);
    
            serviceCGlib.addUser();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 测试结果

    在这里插入图片描述

    静态代理

    • 静态代理模式并没有做到事务的重用
    • 假设dao有100个类,100个proxy,接口中有多少方法,在proxy层就得实现多少方法,有多少方法就要开启和提交多少事务
    • 如果一个proxy实现了多个接口,如果其中的一个接口发生变化(添加了一个方法),那么proxy也要做相应改变

    JDK动态代理总结

    动态代理类:在程序运行时,运用反射机制动态创建而成。
    JDK的动态代理必须具备四个条件:1、目标接口 2、目标类 3、拦截器 4、代理类

    • 因为利用JDKProxy生成的代理类实现了接口,所以目标类中所有的方法在代理类中都有。
    • 生成的代理类的所有的方法都拦截了目标类的所有的方法。而拦截器中invoke方法的内容正好就是代理类的各个方法的组成体。
    • 利用JDKProxy方式必须有接口的存在。
    • invoke方法中的三个参数可以访问目标类的被调用方法的API、被调用方法的参数、被调用方法的返回类型。
    • 缺点:
      • 在拦截器中除了能调用目标对象的目标方法以外,功能是比较单一的,在这个例子中只能处理事务
      • 使用JDK创建代理有一个限制,即JDK动态代理只能为接口创建代理。

    CGLIB动态代理总结:

    • CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
    • 用CGlib生成代理类是目标类的子类。
    • 用CGlib生成 代理类不需要接口
    • 用CGLib生成的代理类重写了父类的各个方法。
    • 拦截器中的intercept方法内容正好就是代理类中的方法体

    JDK代理和CGLib 代理区别

    • JDK动态代理只能针对接口实现类生成代理实例,而不能针对类;也就是说它是面向接口的
    • CGLIB是针对类实现代理,主要是对指定的类生成一个子类,并覆盖其中方法实现增强,但是因为采用的是继承,
    • JDK 代理是java 语言自带的,无需加载第三方类去实现,CGLIB是一个基于ASM实现的
    • 使用动态代理的对象必须实现一个或多个接口
    • 使用cglib代理的对象则无需实现接口,达到代理类无侵入。
  • 相关阅读:
    软设上午题错题知识点7
    【每日一题】1654. 到家的最少跳跃次数
    Spring教程_编程入门自学教程_菜鸟教程-免费教程分享
    S7-200SMART PLC实现冒泡排序的具体方法和程序示例
    【VS2022】Microsoft 源代码注释语言 SAL
    攻防世界WEB练习-fileclude
    Clickhouse初认识
    Linux系统:多线程
    pandas 筛选数据的 8 个骚操作
    千万不要错过,新媒体运营15个宝藏公众号分享
  • 原文地址:https://blog.csdn.net/qq_44715376/article/details/127736232