@[TOC]
1.常用的注解,以及@Configuration
的特殊性
Spring中提供了很多将对象注入到容器中的注解,这里将这些注解列举出来。@Component
,@Indexed
,@Controller
,@Repository
,@Service
,@Configuration
。另外还有一个需要跟@Configuration
,@Component
以及派生注解一起使用的注解@Bean
。
虽然我们经常使用这些注解中的,但是其实@Configuration
跟其他的注解有点不一样,这个注解在默认情况下Spring会通过CGLIB的方式对贴有这个注解的对象进行增强。而这个决定是否进行增强的关键,在于@Configuration
注解中的proxyBeanMethods
属性。当这个属性为true
的时候会进行增强,而默认情况这个属性值就是true
。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(annotation = Component.class)
String value() default "";
boolean proxyBeanMethods() default true;
}
&esmp;从上面其实能看到@Configuration
注解本身也是@Component
注解的一个派生注解,但是为什么Spring会对其进行增强呢,接下来进行分析。
2. @Configuration
特殊性的源码解析
2.1 贴有@Configuration
的bean的获取
spring对于@Configuration
注解的解析,跟@Conditional
注解的解析大部分过程一样都是在ConfigurationClassPostProcessor
中进行的,可以通过@Conditional
注解解析的文章看看前面的过程。Spring扩展------基于@Conditional
注解以及Condition
接口的扩展
。这里天过前面的步骤,直接进入到此类中进行分析。
2.1.1 bean注册前进行分类的方法checkConfigurationClassCandidate
在这个类中有一个方法checkConfigurationClassCandidate
用来检查当前注册的类是不是候选configuration class
也就是需要进行代理的类。这个方法的调用的位置都是在,将对应的候选bean放入到需要注册的候选bean的集合这个步骤前。这个贴代码看看
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
......
for (String beanName : candidateNames) {
//获取对应的BeanDefinition
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
//检查BeanDefinition是不是Configuration类型的候选bean,
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
......
//检查BeanDefinition是不是Configuration类型的候选bean,并且这没有被解析过
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
......
}
可以看到在将候选bean放入到需要注册的候选bean集合之前都会对bean进行的检查,这也可以看成是一个过滤 的过程,将那些需要进行代理的bean进行分类。分类的过程就在checkConfigurationClassCandidate
方法中。这个方法在ConfigurationClassUtils
类中,现在看看这个方法的主要步骤
public static boolean checkConfigurationClassCandidate(
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
......
//获取源数据中包含Configuration注解的信息
Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
//如果存在Configuration注解,并且proxyBeanMethods是true,则表示需要进行代理
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
//如果存在Configuration注解,但是proxyBeanMethods是false,则表示不需要进行代理
//如果不存在Configuration注解,但是如果bean中包含Component,ComponentScan,Import,ImportResource或者Bean注解,则也不需要代理,但是是候选bean
else if (config != null || isConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
//如果满足上面判断条件则表示不是候选bean
else {
return false;
}
......
}
这里做的就是获取传进来的候选bean上面的@Configuration
注解,然后进行判断,主要有以下几种判断。
贴有@Configuration 注解 |
@Configuration 注解的proxyBeanMethods 为true
|
是否需要代理 |
---|---|---|
满足 | 满足 | 需要 |
满足 | 不满足 | 不需要 |
不满足 | - | 不需要 |
上面满足条件需要代理的bean,会在封装其信息的BeanDefinition
对象中加上一个属性CONFIGURATION_CLASS_ATTRIBUTE
值为CONFIGURATION_CLASS_FULL
。这里需要注意这个属性有两种值,需要进行区分。
值 | 说明 | 情况 |
---|---|---|
CONFIGURATION_CLASS_FULL | 需要对bean进行代理 | 贴有@Configuration 注解的对象的内部所有的注入到容器的对象(贴有@Bean 注解的对象) |
CONFIGURATION_CLASS_LITE | 不需要对bean进行代理 | 没有贴有@Configuration 注解的对象,即使内部存在注入到容器的对象 |
在上面将bean进行分类之后,后面就是对bean进行代理增强的阶段了。对bean进行代理增强的过程的起始调用也是在ConfigurationClassPostProcessor
类中。
2.2 对bean进行增强
2.2.1 获取需要代理增强的bean
增强的初始过程在ConfigurationClassPostProcessor
类的enhanceConfigurationClasses
方法中。这个方法的调用在spring上下文初始化之后会调用。
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
for (String beanName : beanFactory.getBeanDefinitionNames()) {
//从容器中获取BeanDefinition
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
//获取BeanDefinition中的CONFIGURATION_CLASS_ATTRIBUTE属性
Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
MethodMetadata methodMetadata = null;
if (beanDef instanceof AnnotatedBeanDefinition) {
methodMetadata = ((AnnotatedBeanDefinition) beanDef).getFactoryMethodMetadata();
}
//从加载了@Configuration注解的BeanDefinition的beanClassLoader中获取对应的beanClass,因为是这个类加载的@Configuration注解的BeanDefinition
if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
// Configuration class (full or lite) or a configuration-derived @Bean method
// -> resolve bean class at this point...
AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;
if (!abd.hasBeanClass()) {
try {
//从beanClassLoader中获取到真正加载的bean的Classes对象
abd.resolveBeanClass(this.beanClassLoader);
}
catch (Throwable ex) {
throw new IllegalStateException(
"Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
}
}
}
//如果CONFIGURATION_CLASS_ATTRIBUTE属性是CONFIGURATION_CLASS_FULL
if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
if (!(beanDef instanceof AbstractBeanDefinition)) {
throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
}
//如果是AbstractBeanDefinition类型的BeanDefinition需要检查这个bean是不是已经被实例化了,如果是的则不能进行代理
else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
logger.info("Cannot enhance @Configuration bean definition '" + beanName +
"' since its singleton instance has been created too early. The typical cause " +
"is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +
"return type: Consider declaring such methods as 'static'.");
}
//放到需要进行代理的对象中
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
}
}
if (configBeanDefs.isEmpty()) {
// nothing to enhance -> return immediately
return;
}
ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
//获取AbstractBeanDefinition
AbstractBeanDefinition beanDef = entry.getValue();
// If a @Configuration class gets proxied, always proxy the target class
//如果一个bean已经代理过了,则设置这个状态未true
beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
// Set enhanced subclass of the user-specified bean class
//进行代理
Class<?> configClass = beanDef.getBeanClass();
Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
if (configClass != enhancedClass) {
if (logger.isTraceEnabled()) {
logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
}
beanDef.setBeanClass(enhancedClass);
}
}
}
总结以下上面的步骤:
- 从当前的容器中获取所有的需要注册的bean集合,然后循环开始2-3步骤,直到结束
- 获取
BeanDefinition
中的CONFIGURATION_CLASS_ATTRIBUTE
属性,然后获取需要代理的bean的真实的Class对象 - 检查
CONFIGURATION_CLASS_ATTRIBUTE
属性是不是CONFIGURATION_CLASS_FULL
,不是则跳过进入下一个bean然后到步骤2。是的则将bean放到需要代理的bean的集合中,然后进入2步骤 - 迭代需要代理的bean的集合进行代理增强。
2.2.2 对bean进行代理增强
&esmp;代理增强的逻辑在ConfigurationClassEnhancer
类的enhance
方法中。
public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
//如果当前的configClass已经是EnhancedConfiguration的子类,说明这个类已经被代理了,因为后面代理的类会设置接口是EnhancedConfiguration
if (EnhancedConfiguration.class.isAssignableFrom(configClass)) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("Ignoring request to enhance %s as it has " +
"already been enhanced. This usually indicates that more than one " +
"ConfigurationClassPostProcessor has been registered (e.g. via " +
"<context:annotation-config>). This is harmless, but you may " +
"want check your configuration and remove one CCPP if possible",
configClass.getName()));
}
return configClass;
}
//创建一个Enhancer然后进行代理
Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
if (logger.isTraceEnabled()) {
logger.trace(String.format("Successfully enhanced %s; enhanced class name is: %s",
configClass.getName(), enhancedClass.getName()));
}
//返回代理类
return enhancedClass;
}
可以看到上面的就是用CGILB进行代理的,在其创建Enhancer
的时候,会设置一些参数,这些参数就是用来与普通的CGLIB进行区分的。
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
Enhancer enhancer = new Enhancer();
//设置父类或者接口类为需要代理的目标类
enhancer.setSuperclass(configSuperClass);
//设置实现的接口EnhancedConfiguration
enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
//是否需要实现Factory接口
enhancer.setUseFactory(false);
//设置命名规则为了避免Spring的CGLIB版本与常规的CGLIB之间的冲突
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
//设置生成策略
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
//设置回调
enhancer.setCallbackFilter(CALLBACK_FILTER);
//设置回调类型
enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
return enhancer;
}
在这里需要关注一下setCallbackFilter
这个方法设置的CALLBACK_FILTER
。CALLBACK_FILTER
是一个对象的数组。
private static final Callback[] CALLBACKS = new Callback[] {
new BeanMethodInterceptor(),
new BeanFactoryAwareMethodInterceptor(),
NoOp.INSTANCE
};
private static final ConditionalCallbackFilter CALLBACK_FILTER = new ConditionalCallbackFilter(CALLBACKS);
这里包含四个类BeanMethodInterceptor
,BeanFactoryAwareMethodInterceptor
,NoOp
的空实现类SerializableNoOp
,ConditionalCallbackFilter
,其中第三个NoOp
在spring中是空实现不用管。主要看ConditionalCallbackFilter
,BeanFactoryAwareMethodInterceptor
跟BeanMethodInterceptor
这三个类。
2.2.3 方法回调的过滤处理ConditionalCallbackFilter
CallbackFilter
在CGLIB 中起到回调的作用,就是一个方法的执行时候的拦截过滤的作用。spring实现了自己的ConditionalCallbackFilter
。在这个里面会对方法进行过滤。
private static class ConditionalCallbackFilter implements CallbackFilter {
private final Callback[] callbacks;
private final Class<?>[] callbackTypes;
public ConditionalCallbackFilter(Callback[] callbacks) {
//设置Callback,这里就是BeanMethodInterceptor, BeanFactoryAwareMethodInterceptor, NoOp的空实现类SerializableNoOp这三个类
this.callbacks = callbacks;
this.callbackTypes = new Class<?>[callbacks.length];
for (int i = 0; i < callbacks.length; i++) {
this.callbackTypes[i] = callbacks[i].getClass();
}
}
@Override
public int accept(Method method) {
//进行循环处理
for (int i = 0; i < this.callbacks.length; i++) {
Callback callback = this.callbacks[i];
//如果是ConditionalCallback的实现类,并且isMatch方法通过则返回对应的callback所在的顺序
if (!(callback instanceof ConditionalCallback) || ((ConditionalCallback) callback).isMatch(method)) {
return i;
}
}
throw new IllegalStateException("No callback available for method " + method.getName());
}
可以看到这里最主要的作用就是设置自己的Callback
,也就是BeanMethodInterceptor
,BeanFactoryAwareMethodInterceptor
,NoOp
。然后进行过滤这些Callback
,其中ConditionalCallback
也是ConfigurationClassEnhancer
类的内部类。只定义了一个方法isMatch
,作用是检查对应的拦截方法是否需要进行拦截。
private interface ConditionalCallback extends Callback {
boolean isMatch(Method candidateMethod);
}
其中isMatch
方法由BeanFactoryAwareMethodInterceptor
跟BeanMethodInterceptor
实现。接下来就是这两个类
2.2.4 将容器设置到代理对象中的BeanFactoryAwareGeneratorStrategy
BeanFactoryAwareGeneratorStrategy
实现了CGLIB的DefaultGeneratorStrategy
用来在生成代理对象的时候,设置额外的信息到代理对象中。也是在这里将,容器对象BeanFactory
放到了代理对象中,这里只截取部分的代码。
private static class BeanFactoryAwareGeneratorStrategy extends DefaultGeneratorStrategy {
......
@Override
protected ClassGenerator transform(ClassGenerator cg) throws Exception {
//实现ClassEmitterTransformer,为代理对象增加额外的字段BEAN_FACTORY_FIELD,访问级别是PUBLIC,类型为BeanFactory
ClassEmitterTransformer transformer = new ClassEmitterTransformer() {
@Override
public void end_class() {
declare_field(Constants.ACC_PUBLIC, BEAN_FACTORY_FIELD, Type.getType(BeanFactory.class), null);
super.end_class();
}
};
return new TransformingClassGenerator(cg, transformer);
}
......
}
设置的这个值,在后面的两个拦截其中都会用到
2.2.5 增加容器到代理对象中的BeanFactoryAwareMethodInterceptor
BeanFactoryAwareMethodInterceptor
主要用来处理实现了BeanFactoryAware
类的setBeanFactory
方法类。
private static class BeanFactoryAwareMethodInterceptor implements MethodInterceptor, ConditionalCallback {
@Override
@Nullable
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
//从代理的对象中获取BEAN_FACTORY_FIELD字段
Field field = ReflectionUtils.findField(obj.getClass(), BEAN_FACTORY_FIELD);
Assert.state(field != null, "Unable to find generated BeanFactory field");
//将BEAN_FACTORY_FIELD字段设置到obj对象中
field.set(obj, args[0]);
//如果当前代理类的父类实现了BeanFactoryAware,则调用setBeanFactory方法,如果不是就推出
if (BeanFactoryAware.class.isAssignableFrom(ClassUtils.getUserClass(obj.getClass().getSuperclass()))) {
return proxy.invokeSuper(obj, args);
}
return null;
}
@Override
public boolean isMatch(Method candidateMethod) {
//检查是否是设置BeanFactory的方法
return isSetBeanFactory(candidateMethod);
}
public static boolean isSetBeanFactory(Method candidateMethod) {
//方法名是setBeanFactory,参数个数为1个,平且参数类型是BeanFactory类型,方法是BeanFactoryAware的实现类
return (candidateMethod.getName().equals("setBeanFactory") &&
candidateMethod.getParameterCount() == 1 &&
BeanFactory.class == candidateMethod.getParameterTypes()[0] &&
BeanFactoryAware.class.isAssignableFrom(candidateMethod.getDeclaringClass()));
}
}
2.2.6 处理@Configuration
注解中@Bean
注解的BeanMethodInterceptor
BeanMethodInterceptor
主要处理@Configuration
注解中贴有@Bean
注解的方法。也正是这个方法,让@Configuration
有了区别于@Component
注解中贴有@Bean
注解的方法的原因之一。主要逻辑集中在这个类的
intercept
方法中,现在来看看:
这里要先看看实现了ConditionalCallback
接口的isMatch
方法,因为这个会先被调用
public boolean isMatch(Method candidateMethod) {
//定义当前方法的对象不是Object,并且不是方法不是setBeanFactory方法,并且方法上面包含@Bean注解
return (candidateMethod.getDeclaringClass() != Object.class &&
!BeanFactoryAwareMethodInterceptor.isSetBeanFactory(candidateMethod) &&
BeanAnnotationHelper.isBeanAnnotated(candidateMethod));
}
到这里就知道,只要是声明方法的类Object
类,方法名不是setBeanFactory
贴了@Bean
注解就会被拦截。
接下来就是拦截之后的处理逻辑了。
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
MethodProxy cglibMethodProxy) throws Throwable {
//从代理的实例中获取beanFactory,根据BEAN_FACTORY_FIELD字段获取,而这个字段在enhanced对象创建设置stratege的BeanFactoryAwareGeneratorStrategy类中设置的
ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
//获取到方法上@Bean注解的name属性或者方法名来决定bean的名称
String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
// Determine whether this bean is a scoped-proxy
//检查缓存中是否有方法的@Scope注解信息缓存,如果没有则检查是否有@Scope注解并且proxyMode不是NO
if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
//生成作用域代理内用于引用目标bean的bean名称
String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
beanName = scopedBeanName;
}
}
/**
* 为了处理bean间方法引用的情况,我们必须显式地检查容器中已经缓存的实例。
* 检查当前的bean是不是一个FactoryBean,如果是这样创建一个子类roxy,拦截对getObject()的调用并返回所有缓存的bean实例
* 这确保了从@Bean方法中调用FactoryBean的语义与在XML中引用FactoryBean的语义相同
*/
if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
factoryContainsBean(beanFactory, beanName)) {
Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
if (factoryBean instanceof ScopedProxyFactoryBean) {
//作用域代理工厂bean是一种特殊情况,不应该进一步代理
// Scoped proxy factory beans are a special case and should not be further proxied
}
else {
// It is a candidate FactoryBean - go ahead with enhancement
//进行增强
return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
}
}
//检查当前的方法是不是当前调用的工厂方法,就是当前拦截的方法是不是当前贴有@Configuration注解类里面贴有@Bean注解的方法
if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
// The factory is calling the bean method in order to instantiate and register the bean (i.e. via a getBean() call) -> invoke the super implementation of the method to actually create the bean instance.
//工厂调用bean方法是为了实例化和注册bean(即通过getBean()调用)->调用父类方法来实际创建bean实例。
if (logger.isInfoEnabled() &&
BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {
logger.info(String.format("@Bean method %s.%s is non-static and returns an object " +
"assignable to Spring's BeanFactoryPostProcessor interface. This will " +
"result in a failure to process annotations such as @Autowired, " +
"@Resource and @PostConstruct within the method's declaring " +
"@Configuration class. Add the 'static' modifier to this method to avoid " +
"these container lifecycle issues; see @Bean javadoc for complete details.",
beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName()));
}
//调用父类方法
return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
}
//从容器获取bean
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}
这里需要说明的以下几点
-
BeanMethodInterceptor
是ConfigurationClassEnhancer
的一个内部类
enhancedConfigInstance
对象中的 - 当贴有
@Bean
注解的方法返回的是BeanFactory
的子类的时候,会先获取到实际的bean,也就是beanName前面加上“&”符号,然后进行代理 -
isCurrentlyInvokedFactoryMethod
用来验证当前拦截的方法是不是贴有@Bean
注解的方法,是的话会调用代理的方法。如果不是则直接从容器中获取bean。
3.@Configuration
跟@Component
例证
如果在@Configuration
注解的类的中有@Bean
注解在方法上来注入对象,当调用这个方法的时候,返回的是容器里面已经创建好的对象。举个例子
@Configuration
public class ConfigurationTest {
@Bean
public BeanB beanB(){
BeanB beanB = new BeanB();
System.out.println("beanB one---"+beanB);
return beanB;
}
@Bean
@DependsOn(value = "beanB")
public BeanA beanA(){
System.out.println("beanA");
BeanA beanA = new BeanA();
beanA.setBeanB(beanB());
System.out.println("beanB two---"+beanA.getBeanB());
return beanA;
}
}
当从容器中获取beanA
的时候。打印的结果如下
beanB one---springstudy.configuration.BeanB@2611b9a3
beanA
beanB two---springstudy.configuration.BeanB@2611b9a3
当我们把ConfigurationTest
类上面的@Configuration
注解换成@Component
的时候,看结果如下
beanB one---springstudy.configuration.BeanB@6d24ffa1
beanA
beanB one---springstudy.configuration.BeanB@65a4798f
beanB two---springstudy.configuration.BeanB@65a4798f
发现会创建两次BeanB
对象。
会发生这种区别的原因就是,当时存在@Configuration
注解的时候,beanB
跟beanA
方法会被代理增强,在beanA
方法中调用beanB
方法的时候会被拦截,并返回容器中已经创建的BeanB
对象。