动态代理模式

文章介绍了JDK动态代理的使用,包括创建接口、实现接口、创建代理对象和调用目标方法的过程。同时,讲解了CGLib动态代理的原理,它是通过继承目标类来创建子类并重写方法。文章还对比了两者之间的共同点和区别,如JDK代理需要目标类实现接口,而CGLib则通过子类化实现。最后,讨论了动态代理的优缺点,如灵活性和性能影响。
摘要由CSDN通过智能技术生成

jdk动态代理

在程序执行过程中,使用jdk反射机制,创建代理类对象,并动态指定要代理的目标类

  • 创建代理接口
public interface IStudentService {
    void save(Student student);
    Student select();
}
  • 实现接口

    public class StudentService implements IStudentService{
        @Override
        public void save(Student student) {
            System.out.println("插入学生数据信息:" + student);
        }
    
        @Override
        public Student select() {
            return new Student();
        }
    }
    
  • 创建实体类

    public class Student {
        private String username;
        private String password;
        private Integer age;
    
        public Student() {
        }
    
        public Student(String username, String password, Integer age) {
            this.username = username;
            this.password = password;
            this.age = age;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "username='" + username + '\'' +
                    ", password='" + password + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    
  • 测试

    public class Test {
        public static void main(String[] args) {
            // 增强类对象
            StudentService studentService = new StudentService();
            // 使用动态代理获取增强类
            IStudentService proxy = (IStudentService) Proxy.newProxyInstance(
                    studentService.getClass().getClassLoader(),
                    new Class[]{IStudentService.class},
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            if ("save".equals(method.getName())) {
                                System.out.println("新增学生增强模拟");
                                Object invoke = method.invoke(studentService, args);
                                System.err.println("新增学生失败.....");
                                return invoke;
                            } else if ("select".equals(method.getName())) {
                                System.out.println("模拟返回查询学生增强...");
                                Student invoke = (Student) method.invoke(studentService, args);
                                invoke.setUsername("jack");
                                invoke.setPassword("123456");
                                invoke.setAge(20);
                                return invoke;
                            }
                            return method.invoke(args);
                        }
                    }
            );
            proxy.save(new Student("lisi", "123456", 18));
            Student res = proxy.select();
            System.out.println("查询结果:" + res);
    
        }
    }public class Test {
        public static void main(String[] args) {
            // 增强类对象
            StudentService studentService = new StudentService();
            // 使用动态代理获取增强类
            IStudentService proxy = (IStudentService) Proxy.newProxyInstance(
                    studentService.getClass().getClassLoader(),
                    new Class[]{IStudentService.class},
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            if ("save".equals(method.getName())) {
                                System.out.println("新增学生增强模拟");
                                Object invoke = method.invoke(studentService);
                                System.err.println("新增学生失败.....");
                                return invoke;
                            } else if ("select".equals(method.getName())) {
                                System.out.println("模拟返回查询学生增强...");
                                Student invoke = (Student) method.invoke(studentService, args);
                                invoke.setUsername("jack");
                                invoke.setPassword("123456");
                                invoke.setAge(20);
                                return invoke;
                            }
                            return method.invoke(args);
                        }
                    }
            );
            proxy.save();
            Student res = proxy.select();
            System.out.println("查询结果:" + res);
        }
    }
    
  • JDK 动态代理的执行原理:

    1. 定义接口:首先定义一个接口,该接口是目标对象和代理对象共同实现的接口。
    2. 实现 InvocationHandler 接口:创建一个实现 InvocationHandler 接口的类,该类中包含了对目标方法的增强逻辑。InvocationHandler 接口只有一个方法 invoke(Object proxy, Method method, Object[] args),在这个方法中可以对目标方法进行增强操作。
    3. 创建代理对象:通过 Proxy.newProxyInstance(ClassLoader, Class[], InvocationHandler) 方法创建代理对象。该方法需要传入一个类加载器、目标对象实现的接口列表以及实现了 InvocationHandler 接口的对象。在运行时,JDK 会动态生成一个代理类,并将接口方法的调用委托给 InvocationHandler 对象的 invoke 方法。
    4. 调用目标方法:当调用代理对象的方法时,实际上是调用 InvocationHandler 的 invoke 方法。在 invoke 方法中,可以根据需要执行一些额外的操作,例如记录日志、检查权限等。
    5. 委托给目标对象:在 invoke 方法中,可以通过反射机制调用目标对象的方法,并传入相应的参数。通过这种方式,实现了对目标对象方法的代理调用。
    6. 返回结果:根据目标方法的返回值类型,将其作为 invoke 方法的返回值返回。
  • jdk动态代理为什么一定要实现接口

    JDK动态代理是一种基于接口的代理机制,它要求被代理的类必须实现至少一个接口。这是因为JDK动态代理的现原理是通过生成理类的字节码,并在运行时动态地创建代理对象。由于Java是一静态类型语言,编译器在编译时需要知道对象的类型信息,便进行类型检查和方法调用。

    当一个类实现了接时,它就可以视为实现了该接定义的所有方法。在JDK动态代理中,代理类会现与目标类同的接口,并且在代理类的方法中调用目类的对应方法。因此,只有目标类实现了接口,才能够通过接口来定义代理类行为。

    另外,使用接口作为代理的基础也有助于实现松合的设计。通过面接口编程,可以将代理类与具体的标类解耦,得代理类可以适于多个不同的标类,提高了代码的灵活和可复用性。

    总结起来,JDK动态代理要实现接口是为了在编时获取类型信息,并且通过面向口编程实现松耦合的设计

cglib动态代理

cglib是三方工具库,原理是继承,cglib通过继承目标类,创建他的子类i,然后在子类中重写父类中重名的方法,然后实现功能的修改

因为cglib是继承,重写方法,所以要求目标类不能是final的,方法也不能是final的

  • 引入cglib的jar

    <dependency>
       	<groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>
    
  • 学生类,service与上面一致

  • 实现MethodInterceptor

    public class CGLibInterceptor implements MethodInterceptor {
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            if ("save".equals(method.getName())) {
                System.out.println("新增学生增强模拟");
                Object invoke = methodProxy.invokeSuper(o, objects);
                System.err.println("新增学生失败.....");
                return invoke;
            } else if ("select".equals(method.getName())) {
                System.out.println("模拟返回查询学生增强...");
                Student invoke = (Student) methodProxy.invokeSuper(o, objects);
                invoke.setUsername("jack");
                invoke.setPassword("123456");
                invoke.setAge(20);
                return invoke;
            }
            return methodProxy.invokeSuper(o, objects);
        }
    }
    
  • 测试

    public class Test {
        public static void main(String[] args) {
            // 得到方法拦截器
            CGLibInterceptor interceptor = new CGLibInterceptor();
            // 使用cglib框架生成目标类的子类(代理类)生成增强
            Enhancer enhancer = new Enhancer();
            // 设置父类字节码
            enhancer.setSuperclass(StudentService.class);
            // 设置拦截处理
            enhancer.setCallback(interceptor);
            StudentService service = (StudentService) enhancer.create();
            service.save(new Student("lisi", "123456", 18));
            Student student = service.select();
            System.out.println("cglib增强查询到学生信息"+student);
        }
    }
    

jdk动态代理和cglib动态代理共同点和区别

  • 共同点
    1. 都是在运行时生成代理类和代理对象。
    2. 都可以在代理对象中添加额外的逻辑,如前置处理、后置处理、异常处理等。
    3. 都可以实现横切关注点的解耦,使得代理类专注于横切逻辑,而不需要修改目标对象的代码。
  • 不同点
    1. JDK 动态代理只能代理实现了接口的目标对象,而 CGLib 动态代理可以代理没有实现接口的目标对象。
    2. JDK 动态代理是通过生成目标对象接口实现的代理类来实现的,而 CGLib 动态代理是通过生成目标对象的子类来实现的。
    3. JDK 动态代理使用 Java 标准库中的 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口来实现,而 CGLib 动态代理使用第三方库 CGLib 实现。
    4. JDK 动态代理基于接口,因此对于目标对象的方法调用是通过反射的方式来实现的,相对而言速度较慢。而 CGLib 动态代理则是通过继承目标对象的方式,因此对于目标对象的方法调用是直接调用的,速度较快。
    5. JDK 动态代理在生成代理类时需要耗费一定的时间,而 CGLib 动态代理在生成代理类时会消耗更多的内存。

优缺点

  1. 优点
    • 高度灵活性:动态代理允许在运行时对目标对象的方法进行增强或拦截,而无需修改源代码。通过使用动态代理,可以根据需要动态地添加、修改或删除代理逻辑,从而具备更高的灵活性。
    • 低耦合性:代理类作为中间层存在于目标对象和调用方之间,可以将具体的业务逻辑与代理逻辑解耦。这样一来,无论是修改代理逻辑还是替换目标对象,都不会影响到调用方的代码。
    • 概念清晰:动态代理采用统一的概念和模式,使得代码的结构更加清晰和易于维护。代理对象负责处理与目标对象相关的通用逻辑,而目标对象则专注于具体的业务逻辑,实现了职责的分离。
    • 可以实现横切关注点(cross-cutting concerns):横切关注点指的是在系统中多个模块或对象共享的通用功能,例如日志记录、事务管理等。动态代理可以在不修改目标对象的前提下,通过代理逻辑实现对这些横切关注点的统一处理。
  2. 缺点
    • 性能影响:动态代理在运行时需要通过反射等机制来生成代理对象,并将方法调用委托给代理对象。相比直接调用目标对象的方法,这种额外的过程会带来一定的性能开销。
    • 只能代理接口:JDK 动态代理只能代理实现了接口的目标对象,无法代理没有接口的类。如果目标对象没有继承或实现任何接口,就不能使用 JDK 动态代理。
    • 无法代理 final 方法:由于动态代理是通过生成代理类字节码来实现的,而 final 方法无法被子类覆写,因此无法对 final 方法进行代理。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值