主要取决于
DefaultAopProxyFactory 类的 createAopProxy 方法。
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (!IN_NATIVE_IMAGE &&
(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
-
private static final boolean IN_NATIVE_IMAGE = (System.getProperty("org.graalvm.nativeimage.imagecode") != null);(此环境是否存在于本机映像中)
-
config.isOptimize() (返回代理是否应该执行积极的优化。默认false)
-
config.isProxyTargetClass() (返回是否直接代理目标类以及任何接口。默认false。表示是否是代理目标类,配置的属性proxy-target-class值决定)
-
hasNoUserSuppliedProxyInterfaces(config) (就是在判断代理的对象是否有实现接口)
-
Proxy.isProxyClass(targetClass) (当且仅当使用getProxyClass方法或newProxyInstance方法将指定的类动态生成为代理类时,才返回 true)
但是springboot 2.x 默认使用cjlib 动态代理
org/springframework/boot/spring-boot-autoconfigure/2.4.0/spring-boot-autoconfigure-2.4.0.jar!/META-INF/spring-configuration-metadata.json
配置文件中写到:
{
"name": "spring.aop.proxy-target-class",
"type": "java.lang.Boolean",
"description": "Whether subclass-based (CGLIB) proxies are to be created (true), as opposed to standard Java interface-based proxies (false).",
"defaultValue": true
}
并且在AopAutoConfiguration中
如果要改为jdk动态代理需要在启动参数加上
-Dspring.aop.proxy-target-class=false
springboot团队之所以默认的代理模式设置成cglib代理,看看spring的官方团队是怎么解释的
This was changed in 1.4 (see https://github.com/spring-projects/spring-boot/issues/5423). We’ve generally found cglib proxies less likely to cause unexpected cast exceptions.
他们认为使用cglib更不容易出现转换错误
实例:
package com.demo.study1.aop;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* 有advice
*
* @author jiangtao
* @version 1.0
* @date 2021/6/5 12:04 上午
*/
@Aspect
@Component
public class AopConfig {
@Pointcut("execution(* com.demo.study1.aop.*.*(..))")
private void pointCut() {
}
@Around(value = "pointCut()")
public void around() {
System.out.println("aop");
}
}
package com.demo.study1.aop;
import org.springframework.stereotype.Service;
/**
* @author jiangtao
* @version 1.0
* @date 2021/6/4 9:39 下午
*/
@Service("cJLIBServiceImpl")
public class CJLIBServiceImpl {
public Integer test(Integer i) {
return i;
}
}
package com.demo.study1.aop;
public interface JDKService {
Integer test(Integer i);
}
package com.demo.study1.aop;
import org.springframework.stereotype.Service;
/**
* @author jiangtao
* @version 1.0
* @date 2021/6/4 9:39 下午
*/
@Service
public class JDKServiceImpl implements JDKService {
@Override
public Integer test(Integer i) {
return i;
}
}
package com.demo.study1;
import com.demo.study1.aop.CJLIBServiceImpl;
import com.demo.study1.aop.JDKService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableAspectJAutoProxy
public class Study1Application {
public static void main(String[] args) {
SpringApplication.run(Study1Application.class, args);
AnnotationConfigApplicationContext annotationConfigApplicationContext =
new AnnotationConfigApplicationContext(Study1Application.class);
//cjLib 动态代理 CJLIBServiceImpl 没有实现接口
CJLIBServiceImpl cjlibService = (CJLIBServiceImpl) annotationConfigApplicationContext.getBean("cJLIBServiceImpl");
Integer cjLibProxy = cjlibService.test(1);
//jdk 动态代理
JDKService service = annotationConfigApplicationContext.getBean(JDKService.class);
Integer test = service.test(1);
}
}
这就是没有实现接口的动态代理过程。
讨厌的代理问题
假设,我们有一个UserServiceImpl
和UserService
类,此时需要在UserContoller
中使用UserService
。在 Spring 中通常都习惯这样写代码:
@Autowired
UserService userService;
在这种情况下,无论是使用 JDK 动态代理,还是 CGLIB 都不会出现问题。
但是,如果你的代码是这样的呢:
@Autowired
UserServiceImpl userService;
这个时候,如果我们是使用 JDK 动态代理,那在启动时就会报错:
因为 JDK 动态代理是基于接口的,代理生成的对象只能赋值给接口变量。
而 CGLIB 就不存在这个问题。因为 CGLIB 是通过生成子类来实现的,代理对象无论是赋值给接口还是实现类这两者都是代理对象的父类。
SpringBoot 正是出于这种考虑,于是在 2.x 版本中,将 AOP 默认实现改为了 CGLIB。
更多的细节信息,读者可以自己查阅上述 issue。
总结
- Spring 5.x 中 AOP 默认依旧使用 JDK 动态代理。
- SpringBoot 2.x 开始,为了解决使用 JDK 动态代理可能导致的类型转化异常而默认使用 CGLIB。
- 在 SpringBoot 2.x 中,如果需要默认使用 JDK 动态代理可以通过配置项
spring.aop.proxy-target-class=false
来进行修改,proxyTargetClass
配置已无效。