面试题:Spring AOP默认用的是什么动态代理,两者的区别?

Spring AOP 默认使用的动态代理机制取决于目标对象是否实现了接口:

  1. 如果目标对象实现了接口,Spring AOP 默认使用 JDK 动态代理
  2. 如果目标对象没有实现接口,Spring AOP 默认使用 CGLIB 动态代理

这两种动态代理机制在实现方式和性能上有显著区别。以下是它们的详细对比:


1. JDK 动态代理

(1)实现原理

  • JDK 动态代理基于 接口 实现。
  • 通过 java.lang.reflect.Proxy 类动态生成代理类。
  • 代理类实现了目标对象的接口,并将方法调用委托给 InvocationHandler

(2)使用条件

  • 目标对象必须实现至少一个接口。

(3)示例代码

public interface UserService {
    void save();
}

public class UserServiceImpl implements UserService {
    @Override
    public void save() {
        System.out.println("Save user");
    }
}

public class JdkProxyDemo {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        UserService proxy = (UserService) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            (proxy1, method, args1) -> {
                System.out.println("Before method: " + method.getName());
                Object result = method.invoke(target, args1);
                System.out.println("After method: " + method.getName());
                return result;
            }
        );
        proxy.save();
    }
}

(4)优点

  • 无需引入第三方库,直接使用 JDK 自带功能。
  • 性能较好,适合代理接口方法。

(5)缺点

  • 只能代理实现了接口的目标对象。
  • 对于没有接口的类无法使用。

2. CGLIB 动态代理

(1)实现原理

  • CGLIB(Code Generation Library)基于 继承 实现。
  • 通过字节码技术动态生成目标类的子类,并重写父类的方法。
  • 代理类继承了目标类,并拦截方法调用。

(2)使用条件

  • 目标对象不需要实现接口。
  • 目标类和方法不能是 final 的(因为需要继承和重写)。

(3)示例代码

public class UserService {
    public void save() {
        System.out.println("Save user");
    }
}

public class CglibProxyDemo {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserService.class);
        enhancer.setCallback((MethodInterceptor) (obj, method, args1, proxy) -> {
            System.out.println("Before method: " + method.getName());
            Object result = proxy.invokeSuper(obj, args1);
            System.out.println("After method: " + method.getName());
            return result;
        });
        UserService proxy = (UserService) enhancer.create();
        proxy.save();
    }
}

(4)优点

  • 可以代理没有接口的类。
  • 功能更强大,支持对普通类的方法拦截。

(5)缺点

  • 需要引入第三方库(CGLIB)。
  • 性能略低于 JDK 动态代理,因为需要生成子类。
  • 无法代理 final 类或 final 方法。

3. JDK 动态代理 vs CGLIB 动态代理

特性JDK 动态代理CGLIB 动态代理
实现方式基于接口,使用 Proxy 类生成代理。基于继承,使用字节码技术生成子类。
使用条件目标对象必须实现接口。目标对象不需要实现接口。
性能较高。略低,因为需要生成子类。
依赖JDK 自带,无需额外依赖。需要引入 CGLIB 库。
限制无法代理没有接口的类。无法代理 final 类或 final 方法。
适用场景适合代理接口方法。适合代理普通类的方法。

4. Spring AOP 的选择策略

Spring AOP 根据目标对象是否实现接口自动选择代理机制:

  • 如果目标对象实现了接口,默认使用 JDK 动态代理
  • 如果目标对象没有实现接口,默认使用 CGLIB 动态代理

如果需要强制使用 CGLIB 代理,可以通过配置 proxyTargetClass=true 实现:

(1)基于注解的配置

@EnableAspectJAutoProxy(proxyTargetClass = true)

(2)基于 XML 的配置

<aop:aspectj-autoproxy proxy-target-class="true"/>

5. 总结

  • JDK 动态代理:基于接口实现,性能较高,但只能代理实现了接口的目标对象。
  • CGLIB 动态代理:基于继承实现,功能更强大,可以代理没有接口的类,但性能略低。

Spring AOP 默认根据目标对象是否实现接口自动选择代理机制,同时也支持手动配置强制使用 CGLIB 代理。理解这两种代理机制的区别有助于在实际开发中更好地使用 Spring AOP。

THE END
点赞14 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容