@EnableAspectJAutoProxy
信息
最近在阅读Spring AOP的源码(基于Spring 5.2.8.RELEASE)中,发现@EnableAspectJAutoProxy
注解中的proxyTargetClass
参数并不如注释(是否创建基于子类的CGLIB代理
)中说所的哪样生效。无论我设置成true/false都会使用CGLIB代理。
自定义配置代码
@Configuration
@EnableAspectJAutoProxy
@ComponentScan("org.springframework.chapter13")
public class AopConfiguration {
}
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(
AopConfiguration.class);
// ....
}
}
源码解析
问题的切入口
我们知道Spring AOP是基于后置处理器实现对Bean的代理对象的创建,其核心类就是AbstractAutoProxyCreator
。所以我们尝试从AOP核心类进行问题的解析。
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
// 判断bean是否需要被代理
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
// wrapIfNecessary中会调用此方法创建代理对象
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
// 省略不相关代码
// AOP核心判断逻辑
// 上文我们没有设置proxyTargetClass,所以默认是false
if (!proxyFactory.isProxyTargetClass()) {
// 问题的核心入口!!!
// 判断是否需要代理
if (shouldProxyTargetClass(beanClass, beanName)) {
// 设置proxyTargetClass属性为true,用于走cglib代理
proxyFactory.setProxyTargetClass(true);
}
else {
// 否则走jdk代理的校验逻辑
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
// 省略无关代码
// 调用代理工厂创建代理
return proxyFactory.getProxy(getProxyClassLoader());
}
protected void evaluateProxyInterfaces(Class<?> beanClass, ProxyFactory proxyFactory) {
// 查找目标对象的所有实现接口,并筛选合适的代理接口
Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(
beanClass, getProxyClassLoader());
boolean hasReasonableProxyInterface = false;
for (Class<?> ifc : targetInterfaces) {
if (!isConfigurationCallbackInterface(ifc) && !isInternalLanguageInterface(ifc) &&
ifc.getMethods().length > 0) {
hasReasonableProxyInterface = true;
break;
}
}
if (hasReasonableProxyInterface) {
// 设置proxyFactory的interface属性,用于实现jdk代理
for (Class<?> ifc : targetInterfaces) {
proxyFactory.addInterface(ifc);
}
}
else {
// 如果当前被代理的目标对象不是基于接口的,那么还是会设置proxyTargetClass=true走CGLIB代理
proxyFactory.setProxyTargetClass(true);
}
}
}
从上面的代码切入,我们知道:
- 因为
shouldProxyTargetClass
返回了true,所以设置了proxyTargetClass=true
,从而走了CGLIB代理。- 即使
shouldProxyTargetClass
返回false,走到了evaluateProxyInterfaces
中,也需要找到适合的代理接口(JDK代理基于接口),否则还是会设置proxyTargetClass=true
。但我产生了一个疑惑:这个
shouldProxyTargetClass()
是做什么的,为什么在没有设置proxyTargetClass=true
前提下返回了true来走CGLIB代理?
问题的深入
书接上文,针对上面的问题,我们继续阅读相关源码。
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
protected boolean shouldProxyTargetClass(Class<?> beanClass, @Nullable String beanName) {
// 我们是基于AnnotationConfigApplicationContext创建的上下文,默认是其父类
// GenericApplicationContext创建了DefaultListableBeanFactory类
// 而前者是ConfigurableListableBeanFactory的默认实现,所以第一个是true
return (this.beanFactory instanceof ConfigurableListableBeanFactory &&
AutoProxyUtils.shouldProxyTargetClass(
(ConfigurableListableBeanFactory) this.beanFactory, beanName));
}
}
public abstract class AutoProxyUtils {
public static final String PRESERVE_TARGET_CLASS_ATTRIBUTE =
Conventions.getQualifiedAttributeName(AutoProxyUtils.class, "preserveTargetClass");
public static boolean shouldProxyTargetClass(
ConfigurableListableBeanFactory beanFactory, @Nullable String beanName) {
// isNull判断 & 容器中是否包含当前bean
if (beanName != null && beanFactory.containsBeanDefinition(beanName)) {
// 如果包含,那么获取BeanDefinition判断是否包含PRESERVE_TARGET_CLASS_ATTRIBUTE属性
BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
return Boolean.TRUE.equals(bd.getAttribute(PRESERVE_TARGET_CLASS_ATTRIBUTE));
}
return false;
}
}
阅读完上述代码,我们可以了解到为什么
shouldProxyTargetClass()
返回了true
,因为当前的BeanDefinition中包含了PRESERVE_TARGET_CLASS_ATTRIBUTE
属性。那么:这个属性是什么时候设置的呢?
问题继续深入
借助IDEA我们可以知道Spring在ConfigurationClassPostProcessor.enhanceConfigurationClasses()方法
中设置了PRESERVE_TARGET_CLASS_ATTRIBUTE
属性。
// 解析配置类注解的核心后置处理器,启动时注入到容器中
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
// 省略无关代码
// 最终调用了ConfigurationClassUtils.checkConfigurationClassCandidate设置了
// CONFIGURATION_CLASS_ATTRIBUTE属性
processConfigBeanDefinitions(registry);
}
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
// 核心 如果当前类被具有full属性,那么最终会添加PRESERVE_TARGET_CLASS_ATTRIBUTE属性
// 核心,在BeanDefinition通过registry注册的时候会设置参数
// 最终调用ConfigurationClassUtils.checkConfigurationClassCandidate中设置
if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
// 省略无关代码
// 添加成员到configBeanDefs中
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
}
for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
// 如果@Configuration的proxyBeanMethods=true那么会被设置PRESERVE_TARGET_CLASS_ATTRIBUTE
beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
// 设置Bean增强时被回调的操作
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
// 省略无关代码
}
}
}
abstract class ConfigurationClassUtils {
public static boolean checkConfigurationClassCandidate(
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
// 省略无关代码
// 获取配置类上@Configuration中的参数
Map<String, Object> config = metadata.getAnnotationAttributes(
Configuration.class.getName());
// 如果proxyBeanMethods=true,那么设置为full(默认)
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
// 否则设置为lite
else if (config != null || isConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
}
}
class ConfigurationClassEnhancer {
// CGLIB callback回调的逻辑
private static final Callback[] CALLBACKS = new Callback[] {
new BeanMethodInterceptor(), // 拦截@Bean注解的核心类
new BeanFactoryAwareMethodInterceptor(),
NoOp.INSTANCE
};
private static final ConditionalCallbackFilter CALLBACK_FILTER =
new ConditionalCallbackFilter(CALLBACKS);
public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
if (EnhancedConfiguration.class.isAssignableFrom(configClass)) {
return configClass;
}
// 基于CGLIB创建增强代理类
Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
return enhancedClass;
}
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(configSuperClass);
// 配置类会增加对EnhancedConfiguration接口的实现
enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
enhancer.setUseFactory(false);
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
enhancer.setCallbackFilter(CALLBACK_FILTER); // 设置CGLIB CALLBACK 核心!
enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
return enhancer;
}
}
通过上述代码的解析,我们可知,因为目标类上的
@Configuration(proxyBeanMethods=true)
注解,设置了CONFIGURATION_CLASS_FULL
属性,继而设置了PRESERVE_TARGET_CLASS_ATTRIBUTE
,最终设置了proxyTargetClass=true
属性,用于创建CGLIB代理并设置默认的CALLBACK回调(Spring CGLIB代理的通知类都是通过实现MethodInterceptor接口来实现的,CALLBACK中的BeanMethodInterceptor也是实现此接口,在proceed()执行时会被调用
)。我们继续思考:
BeanMethonInterceptor
是做什么的呢?为什么
@Configuration(proxyBeanMethods=true)修饰的类需要被CGLIB代理进行增强呢??
@Configuration
经过上面的源码阅读,我们知道解决问题的关键在于@Configuration注解。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
boolean proxyBeanMethods() default true;
}
翻译注释可知:
proxyBeanMethods属性是用来指定
@Bean注解标注的方法是否使用代理(默认为true)
,如果开启代理那么会直接从IOC容器之中取得对象
;如果关闭则每次调用@Bean标注的方法获取到的对象则是一个新的对象(与容器中不同)
。
我们还是写个demo测试下把:
@Configuration
public class SchoolConfiguration {
@Bean
public Teacher teacher() {
return new Teacher();
}
@Bean
public Student student() {
// 我们在student方法中调用teacher()
teacher();
return new Student();
}
@Data
static class Teacher {
public Teacher() {
System.out.println("teacher ...");
}
}
@Data
static class Student {
public Student() {
System.out.println("student ...");
}
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new
AnnotationConfigApplicationContext(SchoolConfiguration.class);
SchoolConfiguration.Teacher teacher = context.getBean(
SchoolConfiguration.Teacher.class);
SchoolConfiguration.Student student = context.getBean(
SchoolConfiguration.Student.class);
// 问teacher ...会被调用几次
}
}
默认开启
proxyBeanMethods
那么会调用一次,如果关闭则会调用两次。原理:开启
proxyBeanMethods
会在CGLIB的CALLBACK中设置了BeanMethodInterceptor
回调,在@Bean修饰的方法被调用时候通过CGLIB代理进行拦截,从容器中返回@Bean创建的对象,保证了@Bean方法的单例原则
。注意:如果@Bean的方法上加上了
@Scope("prototype")
注解时仍会生效,多次调用会返回多个对象。
总结
- 因为@EnableAspectJAutoProxy的
proxyTargetClass
属性不生效,从而引出的@Configuration
的proxyBeanMethods
问题,如果我们需要使用JDK动态代理,那么我们除了设置proxyTargetClass=false
、目标对象实现了接口,还需要设置proxyBeanMethods = false
或不适用@Configuration注解修饰AOP相关配置类。 - 即使设置了
proxyBeanMethods = true
,如果在@Bean修饰的方法上添加了@Scope("prototype")
,那么方法不会返回容器中的对象,对象会创建多次。