【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();
}
}
相关推荐
robin_suli25 分钟前
Java多线程八股(三)一>多线程环境使用哈希表和ArrayList
java·开发语言·多线程·哈希表
NiNg_1_23430 分钟前
Java中的多线程
java·开发语言
丁总学Java37 分钟前
nohup java -jar supporterSys.jar --spring.profiles.active=prod &
java·spring·jar
呆呆小雅38 分钟前
C# 结构体
android·java·c#
谢尔登39 分钟前
使用 Maven 创建 jar / war 项目
java·maven·jar
理想不理想v1 小时前
前端开发工程师需要学什么?
java·前端·vue.js·webpack·node.js
赶路人儿1 小时前
IntelliJ IDEA配置(mac版本)
java·macos·intellij-idea
jjw_zyfx1 小时前
docker 的各种操作
java·docker·eureka
生财1 小时前
获取字 short WORD 上指定的位是否有效
java·服务器·c#
hummhumm1 小时前
第 36 章 - Go语言 服务网格
java·运维·前端·后端·python·golang·java-ee