【Spring】代理模式——动态代理

1、基本概念

  • 动态代理允许我们在运行时根据指定的接口动态地创建代理对象,这个代理对象与目标对象实现了相同的接口,因此可以完全替代目标对象执行相同的操作。
  • 通过动态代理,我们可以在不修改目标对象代码的情况下,为目标对象添加额外的功能或逻辑。
  • JDK动态代理和CGLIB动态代理是Java中两种常见的动态代理实现方式,它们各自有不同的特点和适用场景。

2、原理

  • 动态代理的实现原理主要基于Java的反射机制。
  • 在运行时,动态代理会根据指定的接口创建一个代理类,并在代理类中重写接口中的所有方法。
  • 当代理对象调用接口方法时,实际执行的是代理类中的对应方法。
  • 在代理类中,我们可以通过反射机制调用目标对象的方法,并在执行前后添加自己的逻辑。

3、应用场景

  • 动态代理在Spring框架中得到了广泛应用,主要用于实现AOP(面向切面编程)功能。
  • AOP是一种编程范式,用于处理系统中散布于各个模块之间的交叉关注点问题,如事务管理、日志记录、安全控制等。
  • 通过动态代理,Spring可以在运行时为目标对象创建代理对象,并在代理对象中织入切面逻辑,从而实现对这些交叉关注点问题的统一处理。

4、常见实现方式

4.1 JDK动态代理

4.1.1简介

  • JDK动态代理是Java标准库提供的一种动态代理实现方式,它基于Java的反射机制。
  • JDK动态代理只能代理实现了接口的类,通过动态生成代理类来实现对目标对象的代理。
  • 在JDK动态代理中,代理类是由Java运行时环境动态生成的,每个代理类都会实现与被代理对象相同的接口。
  • 通过使用Proxy、Method、InvocationHandler类库来完成动态代理
  • JDK动态代理的优点是简单易用,因为它是Java标准库的一部分,无需引入额外的依赖。

4.1.2代码示例

步骤1:先创建UserService接口

java 复制代码
public interface UserService {
void save();
void delete();
}

步骤2:创建一个接口的实现类UserServiceImpl(目标类对象)

java 复制代码
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("主业务逻辑:增加用户管理");
}
@Override
public void delete() {
System.out.println("主业务逻辑:删除用户管理");
}
}

步骤3:创建动态代理(InvocationHandler接口的实现类)

java 复制代码
public class MyInvocationHandler implements InvocationHandler {
private Object obj;
public MyInvocationHandler(Object obj){
this.obj=obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
        System.out.println("开始监控系统...");
long t1 = System.currentTimeMillis();//当前时间
try {
Thread.sleep((int) (Math.random() * 2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
Object o = method.invoke(obj, args);
long t2 = System.currentTimeMillis();//当前时间
System.out.println("结束监控系统...");
System.out.println("一共耗费了" + (t2 - t1) + "毫秒");
//加日志处理
System.out.println(LocalDate.now() + "访问系统");
return o;
}
}

步骤4:使用Proxy,创建代理对象

java 复制代码
public class ProxyUtil {
public static Object getProxy(Object target, InvocationHandler handler)
{
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), handler);
}
}

步骤5:测试

java 复制代码
public static void main(String[] args) {
UserService service= new UserServiceImpl();
UserService proxy = (UserService) ProxyUtil.getProxy(service,
new MyInvocationHandler(service));
proxy.save();
System.out.println("------------------------------------");
proxy.delete();
}
}

4.2 CGLIB动态代理

4.2.1简介

  • CGLIB(Code Generation Library)是一个强大的、高性能的代码生成库,它可以扩展Java类和实现Java接口。
  • 与JDK动态代理不同,CGLIB动态代理是通过继承目标类来创建代理对象的,因此它可以代理没有实现接口的类。
  • CGLIB动态代理底层使用ASM(一个Java字节码操控和分析框架)来操作字节码生成新的类,为对象引入间接级别,以控制对象的访问。
  • CGLIB动态代理的优点在于它可以代理没有实现接口的类,提供了更多的灵活性。
  • CGLIB动态代理不能对final类及final方法进行代理。

4.2.2代码示例

步骤1:创建目标类

java 复制代码
public class AccountService {
public void save(){
System.out.println("保存账务管理业务");
}
public void pay(){
System.out.println("支持管理业务");
}
}

步骤2:编写增强类

java 复制代码
public class MyInterceptor implements MethodInterceptor {
private Object obj;
public MyInterceptor(Object obj) {
this.obj = obj;
}
@Override
public Object intercept(Object o, Method method, Object[] objects,
MethodProxy methodProxy) throws Throwable {
System.out.println("进入系统");
Object invoke = method.invoke(obj, objects);
System.out.println(LocalDate.now());
return invoke;
}
}

步骤3:测试

java 复制代码
public class Test {
public static void main(String[] args) {
AccountService service = new AccountService();
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(AccountService.class.getClassLoader());
enhancer.setSuperclass(AccountService.class);
enhancer.setCallback(new MyInterceptor(service));
AccountService accountService = (AccountService) enhancer.create();
accountService.save();
System.out.println("------------------------------");
accountService.pay();
}
}
相关推荐
iQM7516 分钟前
Spring Boot 中实现任务后台处理的几种常见方式
java·spring boot·后端
2401_8576176218 分钟前
创新学生宿舍管理:Spring Boot框架实践
java·spring boot·后端
shall_zhao21 分钟前
Spring异常处理-@ExceptionHandler-@ControllerAdvice-全局异常处理
java·spring
海海不掉头发24 分钟前
【已解决】如何使用JAVA 语言实现二分查找-二分搜索折半查找【算法】手把手学会二分查找【数据结构与算法】
java·开发语言·算法
Data 3171 小时前
关于 SQL 的 JOIN 操作
java·大数据·数据库·数据仓库·sql
BeyondESH2 小时前
C++—单例设计模式
java·c++·设计模式
好奇的菜鸟2 小时前
探索 JUnit 5:下一代 Java 测试框架
java·开发语言·junit
爱吃土豆的程序员2 小时前
Lucene 倒排索引原理详解:深入探讨相关算法设计
java·算法·elasticsearch·全文检索·lucene
林小果12 小时前
桥接模式
java·开发语言·设计模式
MicrosoftReactor3 小时前
技术速递|宣布 Azure Container Apps 上的 Java 体验正式推出
java·azure