" />
文章

基础!看你了解吗!动态代理实现两种方式

Java动态代理

JDK 动态代理是 Java 提供的一种机制,它允许在运行时创建一个代理对象,而不需要为每个被代理的类手动编写代理类。使用 JDK 动态代理的关键是通过 java.lang.reflect.Proxy 类和 InvocationHandler 接口来实现。以下是 JDK 动态代理的具体使用方式:

使用步骤

  1. 定义接口:首先,定义一个接口,目标类和代理类都实现这个接口。

    public interface UserService {
        void addUser();
    }
    
  2. 实现目标类:创建目标类,实现上述接口。

    public class UserServiceImpl implements UserService {
        @Override
        public void addUser() {
            System.out.println("添加用户");
        }
    }
    
  3. 实现 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;
        }
    }
    
  4. 创建代理对象:使用 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:该方法用于创建动态代理对象。参数分别为:

    1. ClassLoader:代理对象的类加载器,通常使用目标对象的类加载器。
    2. Class<?>[]:目标对象实现的接口数组,代理对象将实现这些接口。
    3. InvocationHandler:自定义的处理器,用于定义增强逻辑。
  • InvocationHandler 的 invoke 方法:在这里,你可以添加任何前置或后置逻辑,比如事务管理、日志记录等,实际调用目标对象的方法则是通过 method.invoke(target, args)

优点和缺点

优点

  • 动态生成代理类,减少了手动编写代理类的工作。
  • 可以在运行时选择不同的目标对象和增强逻辑,灵活性较高。

缺点

  • JDK 动态代理只能代理实现了接口的类,如果没有接口,则无法使用 JDK 动态代理。
  • 性能略低于静态代理,因为在调用时需要反射。

CGLIB代理

使用 Spring 的 CGLIB 代理可以在不需要接口的情况下为类生成代理,这适用于那些没有实现接口的目标对象。CGLIB 通过继承目标类来创建代理,因此它能提供更高的灵活性。以下是使用 Spring CGLIB 代理的具体步骤:

使用步骤

  1. 添加依赖:首先,确保你的项目中引入了 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>
    
  2. 创建目标类:定义一个没有实现接口的目标类。

    public class UserService {
        public void addUser() {
            System.out.println("添加用户");
        }
    }
    
  3. 创建切面类:使用 @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("开始事务");
        }
    }
    
  4. 配置 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 {
    }
    
  5. 使用 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 编程。

License:  CC BY 4.0