基础!看你了解吗!动态代理实现两种方式
Java动态代理
JDK 动态代理是 Java 提供的一种机制,它允许在运行时创建一个代理对象,而不需要为每个被代理的类手动编写代理类。使用 JDK 动态代理的关键是通过 java.lang.reflect.Proxy
类和 InvocationHandler
接口来实现。以下是 JDK 动态代理的具体使用方式:
使用步骤
-
定义接口:首先,定义一个接口,目标类和代理类都实现这个接口。
public interface UserService { void addUser(); }
-
实现目标类:创建目标类,实现上述接口。
public class UserServiceImpl implements UserService { @Override public void addUser() { System.out.println("添加用户"); } }
-
实现 InvocationHandler 接口:创建一个处理器类,实现
InvocationHandler
接口,并在invoke
方法中添加增强逻辑。import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class MyInvocationHandler implements InvocationHandler { private Object target; public MyInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 增强逻辑:在调用目标方法前 System.out.println("开始事务"); Object result = method.invoke(target, args); // 调用目标方法 // 增强逻辑:在调用目标方法后 System.out.println("提交事务"); return result; } }
-
创建代理对象:使用
Proxy.newProxyInstance
方法创建代理对象。import java.lang.reflect.Proxy; public class Main { public static void main(String[] args) { UserService userService = new UserServiceImpl(); MyInvocationHandler handler = new MyInvocationHandler(userService); // 创建代理对象 UserService proxy = (UserService) Proxy.newProxyInstance( userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), handler); // 使用代理对象 proxy.addUser(); } }
代码解析
-
Proxy.newProxyInstance:该方法用于创建动态代理对象。参数分别为:
- ClassLoader:代理对象的类加载器,通常使用目标对象的类加载器。
- Class<?>[]:目标对象实现的接口数组,代理对象将实现这些接口。
- InvocationHandler:自定义的处理器,用于定义增强逻辑。
-
InvocationHandler 的 invoke 方法:在这里,你可以添加任何前置或后置逻辑,比如事务管理、日志记录等,实际调用目标对象的方法则是通过
method.invoke(target, args)
。
优点和缺点
优点:
- 动态生成代理类,减少了手动编写代理类的工作。
- 可以在运行时选择不同的目标对象和增强逻辑,灵活性较高。
缺点:
- JDK 动态代理只能代理实现了接口的类,如果没有接口,则无法使用 JDK 动态代理。
- 性能略低于静态代理,因为在调用时需要反射。
CGLIB代理
使用 Spring 的 CGLIB 代理可以在不需要接口的情况下为类生成代理,这适用于那些没有实现接口的目标对象。CGLIB 通过继承目标类来创建代理,因此它能提供更高的灵活性。以下是使用 Spring CGLIB 代理的具体步骤:
使用步骤
-
添加依赖:首先,确保你的项目中引入了 Spring AOP 和 CGLIB 依赖。对于 Maven 项目,你可以在
pom.xml
中添加以下内容:<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>${cglib.version}</version> </dependency>
-
创建目标类:定义一个没有实现接口的目标类。
public class UserService { public void addUser() { System.out.println("添加用户"); } }
-
创建切面类:使用
@Aspect
注解创建切面类,定义增强逻辑。import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect @Component public class MyAspect { @Before("execution(* UserService.addUser(..))") public void beforeAddUser() { System.out.println("开始事务"); } }
-
配置 Spring:在 Spring 配置中启用 AOP 功能,通常是在
@Configuration
类中。import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.context.annotation.Configuration; @Configuration @EnableAspectJAutoProxy(proxyTargetClass = true) // 启用 CGLIB 代理 @ComponentScan(basePackages = "your.package.name") // 替换为你的包名 public class AppConfig { }
-
使用 Spring 容器获取代理对象:在主程序中获取 Spring 上下文,并通过容器获取目标对象的代理实例。
import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class Main { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); UserService userService = context.getBean(UserService.class); userService.addUser(); // 调用代理方法 } }
代码解析
- @EnableAspectJAutoProxy:这个注解启用 Spring AOP 功能,
proxyTargetClass = true
表示使用 CGLIB 代理。 - @Aspect:这个注解标记了切面类,定义增强逻辑。
- @Before:指定在目标方法执行前执行的增强逻辑。
优点和缺点
优点:
- 可以代理没有实现接口的类。
- CGLIB 代理效率高于 JDK 动态代理,因为不需要进行反射。
缺点:
- CGLIB 通过继承目标类,因此目标类不能为
final
类,且其方法也不能为final
。 - 可能在某些情况下比 JDK 动态代理占用更多的内存。
使用 CGLIB 代理时,你可以在任何类中添加增强逻辑,而不必依赖接口,这样可以更加灵活地进行 AOP 编程。