• Java代理模式


    什么是Java代理模式(Proxy或Surrogate)

    代理就是为其他对象提供一个代理以控制对某个对象的访问。
    在这里插入图片描述
    代理模式的优点:

    1. 可以隐藏真实目标类的实现;
    2. 可以实现客户与真实目标类间的解耦,在不修改真实目标类代码的情况下能够做一些额外的处理

    代理模式的分类

    代理可以分为静态代理和动态代理

    静态代理

    由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了

    package com.service;
    /**
     * @author Una
     * @date 2022/8/11 12:46
     * @description:提供给实体类继承
     */
    public interface Animals {
        public void eat();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    package com.beans;
    
    import com.service.Animals;
    
    /**
     * @author Una
     * @date 2022/8/11 12:46
     * @description:Cat类实现eat方法
     */
    
    
    public class Cat implements Animals {
        @Override
        public void eat() {
            System.out.println("猫吃鱼!!!!");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    代理类

    
    import com.beans.Cat;
    import com.service.Animals;
    
    /**
     * @author Una
     * @date 2022/8/11 12:47
     * @description:
     */
    
    public class CatProxy implements Animals {
        private Animals animal;
        //相当于签约绑定
        @Override
        public void eat() {
            animal=new Cat();
            animal.eat();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    测试类

    package com.proxy;
    import org.junit.Test;
    
    import static org.junit.Assert.*;
    
    /**
     * @author Una
     * @date 2022/8/11 12:53
     * @description:
     */
    
    
    public class CatProxyTest {
        @Test
        public void testAnimal(){
            CatProxy catProxy=new CatProxy();
            catProxy.eat();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    由上可知;当我们需要使用时;在测试中;我们实际上看不到cat的身影就能执行cat的方法。

    动态代理:

    在程序运行时,运用反射机制动态创建而成,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码
    动态代理分两类:
    基于接口的代理和基于继承的代理;两类实现的代表分别是:JDK代理与CGlib代理。

    JDK代理

    JDK动态代理主要涉及java.lang.reflect包下的Proxy类和InvocationHandler接口。
    jdk代理的要求:

           (a)动态代理类需要实现InvocationHandler接口
    	   
    	   (b)被代理的类也需要实现相应的接口
    	   
    	     缺陷:不能直接代理Java类
    
    • 1
    • 2
    • 3
    • 4
    • 5
    package com.jdkProxy;
    
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * @author Una
     * @date 2022/8/11 13:18
     * @description:
     */
    
    
    public class CatProxy2 implements InvocationHandler {
        private Object target;//被代理的类对象
        //绑定
        public Object bind(Object target){
            this.target=target;
            return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object result = null;
            //执行代理之前的业务逻辑
            System.out.println("------调用核心业务前的代码--------");
    
            //调用真正的业务方法,核心业务的执行结果在result中
            result = method.invoke(target,args);
    
            //执行代理之后的业务逻辑
            System.out.println("------调用核心业务后的代码--------");
            return result;
        }
    }
    
    • 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

    结果:
    在这里插入图片描述

    CGLIB代理

    CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,为一个类创建子类,并在子类中采用方法拦截的技术拦截所有对父类方法的调用,并顺势加入横切逻辑。CGlib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理,因为采用的是继承,所以不能对final修饰的类进行代理。CGlib和JDK的原理类似,也是通过方法去反射调用目标对象的方法。

      (a)代理类需要实现MethodInterceptor接口,重写该接口的interceptor()方法
      
      (b)优点:可以直接代理类
    
    • 1
    • 2
    • 3
    package com.cglib;
    import org.springframework.cglib.proxy.Enhancer;
    import org.springframework.cglib.proxy.MethodInterceptor;
    import org.springframework.cglib.proxy.MethodProxy;
    import java.lang.reflect.Method;
    
    /**
     * @author Una
     * @date 2022/8/11 13:36
     * @description:
     */
    
    public class CatProxy3 implements MethodInterceptor {
    
        private Object target;
        public Object bind(Object target){
            //给业务对象赋值
            this.target = target;
            //创建加强器,用来创建动态代理类
            Enhancer enhancer = new Enhancer();
            //为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
            enhancer.setSuperclass(this.target.getClass());
            //设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦截
            enhancer.setCallback(this);
            // 创建动态代理类对象并返回
            return enhancer.create();
        }
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("之前");
            Object result = null;
            try{
                result = methodProxy.invokeSuper(o, objects);
            }catch (Exception e){
                System.out.println("得到:"+e.getMessage());
                throw e;
            }finally {
                System.out.println("之后");
            }
            return result;
        }
    }
    
    • 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

    测试:

    package com.cglib;
    import com.beans.Cat;
    import com.service.Animals;
    import com.stillProxy.CatProxy;
    import org.junit.Test;
    import static org.junit.Assert.*;
    
    /**
     * @author Una
     * @date 2022/8/11 14:01
     * @description:
     */
    
    
    public class CatProxy3Test {
        @Test
        public void test(){
            CatProxy3 catProxy3=new CatProxy3();
            Cat cat=new Cat();
            Animals animals=(Animals) catProxy3.bind(cat);
            animals.eat();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    结果
    在这里插入图片描述

    如何选用JDK和CGLib

    1. 当Bean实现接口时,Spring就会用JDK的动态代理;
    2. 当Bean没有实现接口时,Spring使用CGlib的代理实现;
    3. 可以通过修改配置文件强制使用CGlib;
  • 相关阅读:
    解决ConfigurationBuilder未包含“SetBasePath”的定义
    【kafka】三、kafka命令行操作
    并查集的简单学习笔记
    每日学习——面试题1
    Ubuntu20.04安装 nginx1.23.1
    学习ASP.NET Core Blazor编程系列九——服务器端校验
    在线正则表达式解析器和可视化工具
    App移动端测试(5)—— adb命令【续】
    Android混合式开发框架搭建集成Flutter3.0.1
    安全防御——密码学
  • 原文地址:https://blog.csdn.net/m0_50744075/article/details/126282926