前言
在Spring Boot中基本是@Configration注解使用较多,例如,配置类的启动加载,和@Component相比,两者都是为了实例化,那么它们有什么区别?
一、案例
用一个例子来予以说明,在下面的代码中,Spring启动中lison() 方法会被调用两次,但是同一个实例吗?先用@Component注解:
@Component
public class AnnoBean {
@Bean
public Lison lison() {
return new Lison();
}
@Bean
public LisonFactory lisonFactory() throws Exception {
LisonFactory lisonFactory = new LisonFactory();
lisonFactory.setLison(this.lison());
return lisonFactory;
}
}
单元测试看一下,被调用了几次:
@Test
public void test() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ScanBean.class);
Lison lison = applicationContext.getBean("lison",Lison.class);
System.out.println(lison.hashCode());
LisonFactory lisonFactory = applicationContext.getBean(LisonFactory.class);
System.out.println(lisonFactory.getLison().hashCode());
}
测试结果显示被调用了两次,每次调用生成的实例不一样,违背了Spring单例的思想。
如果上面的类用@Configuration注解,再进行单元测试,打印结果:
将上面示例中的代码修改一下,在lisonFactory() 方法中实例化LiLi这个类,我们手动调用它的getObject()方法:
@Component
public class AnnoBean {
@Bean
public LisonFactory lisonFactory() {
LiLi liLi = this.liLi();
// 手动调用getObject()方法
Object object = liLi.getObject();
// 通过beanFactory拿实例
// beanFactory.getBean()
System.out.println("lili getObject : " + object.hashCode());
return lisonFactory;
}
@Bean
public LiLi liLi() {
return new LiLi();
}
}
public class LiLi implements FactoryBean {
// 如果是getObject()方法,就不会调到这个方法本身,会通过代理直接返回beanFactory.getBean(beanName)方法
@Override
public Object getObject() {
return new Docker();
}
@Override
public Class<?> getObjectType() {
return Docker.class;
}
}
LiLi 类实现了FactoryBean 接口,在它的getObject() 方法中new了一个Docker实例,我们进行单元测试,看两次调用返回的Docker 是否为同一个:
@Test
public void test() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ScanBean.class);
Docker bean = applicationContext.getBean(Docker.class);
System.out.println(bean.hashCode());
}
调用的实例不是同一个:
再用@Configration注解进行测试,两次调用的实例为同一个:
@Configration注解进行实例化,不管调用多少次,返回的实例都是同一个,是单例的一种实现,上面获取实例的过程就是走的beanFactory.getBean(id) 方法,最终调到LiLi类中getObjectff返回的对象,并放到缓存中,所以拿到的为同一个。
二、源码解析
如何实现的,进入源码,看ConfigurationClassPostProcessor类,这个类很重要,在Springboot中绝大多数的注解,如:@Configration、@Component、@ComponentScan、@ComponentScans、@Conditional、@PropertySource、@Import、@Bean等都是被该类负责扫描、收集、解析,最终封装成我们熟悉的BeanDefiniiton,注册到BeanDefinitionRegistry,我们看它的postProcessBeanDefinitionRegistry()方法:
所属类:org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
/**
* Derive further bean definitions from the configuration classes in the registry.
* 这是该类顶层接口BeanFactoryPostProcessor的方法,进行了重写,(先执行这个方法)
*/
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
this.registriesPostProcessed.add(registryId);
// TODO 核心逻辑,重点看---->
processConfigBeanDefinitions(registry);
}
在实例化过程中,先调用BeanDefinitionRegistryPostProcessor接口的postProcessBeanDefinitionRegistry()方法,进行beanDefiniiton的收集、注册,然后再执行postProcessBeanFactory接口的postProcessBeanFactory()方法,生成代理:
/**
* 这是BeanFactoryPostProcessor接口的方法,(再执行这个方法)
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
int factoryId = System.identityHashCode(beanFactory);
if (this.factoriesPostProcessed.contains(factoryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + beanFactory);
}
this.factoriesPostProcessed.add(factoryId);
if (!this.registriesPostProcessed.contains(factoryId)) {
// BeanDefinitionRegistryPostProcessor hook apparently not supported...
// Simply call processConfigurationClasses lazily at this point then.
processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
}
// TODO CGLIB代理,字节码增强,动态生成了代理类,并加载到了JVM中
enhanceConfigurationClasses(beanFactory);
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}
上面代码中的enhanceConfigurationClasses(beanFactory)是生成代理的方法,它会先找到类上面有@Configuration注解的BeanDefinition:
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
StartupStep enhanceConfigClasses = this.applicationStartup.start("spring.context.config-classes.enhance");
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
// 拿到所有的BeanDefinition
for (String beanName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
// beanDef.getAttribute()拿到对应的值
Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
MethodMetadata methodMetadata = null;
if (beanDef instanceof AnnotatedBeanDefinition) {
methodMetadata = ((AnnotatedBeanDefinition) beanDef).getFactoryMethodMetadata();
}
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 {
abd.resolveBeanClass(this.beanClassLoader);
}
catch (Throwable ex) {
throw new IllegalStateException(
"Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
}
}
}
// 只有beanDefinition为"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");
}
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);
}
}
// 为空表示没有需要生成代理的BeanDefinition,直接返回
if (configBeanDefs.isEmpty()) {
// nothing to enhance -> return immediately
enhanceConfigClasses.end();
return;
}
if (IN_NATIVE_IMAGE) {
throw new BeanDefinitionStoreException("@Configuration classes need to be marked as proxyBeanMethods=false. Found: " + configBeanDefs.keySet());
}
// new一个ConfigurationClassEnhancer增强器
ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
// 循环所有需要被增强的类
for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
AbstractBeanDefinition beanDef = entry.getValue();
// If a @Configuration class gets proxied, always proxy the target class
beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
// Set enhanced subclass of the user-specified bean class
Class<?> configClass = beanDef.getBeanClass();
// TODO 增强的核心方法
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()));
}
// 通过完整限定名获取到类的反射对象,并设置到BeanDefinition中
beanDef.setBeanClass(enhancedClass);
}
}
enhanceConfigClasses.tag("classCount", () -> String.valueOf(configBeanDefs.keySet().size())).end();
}
进入增强的核心方法enhance() :
所属类:org.springframework.context.annotation.ConfigurationClassEnhancer
public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
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;
}
// createClass()方法返回类名,而不是实例;newEnhancer(configClass, classLoader)方法为CGLIB的增强过程
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;
}
看newEnhancer()方法,它会创建一个CGLIB的增强器:
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
Enhancer enhancer = new Enhancer();
// configSuperClass代表有@Configration注解的类
enhancer.setSuperclass(configSuperClass);
enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
enhancer.setUseFactory(false);
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
// 对应的CallbackFilter为new BeanMethodInterceptor()、new BeanFactoryAwareMethodInterceptor()、NoOp.INSTANCE
enhancer.setCallbackFilter(CALLBACK_FILTER);
enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
return enhancer;
}
/**
* 这里返回了类名,而不是类的实例,最后的实例化是Spring做的
*/
private Class<?> createClass(Enhancer enhancer) {
Class<?> subclass = enhancer.createClass();
// Registering callbacks statically (as opposed to thread-local)
// is critical for usage in an OSGi environment (SPR-5932)...
Enhancer.registerStaticCallbacks(subclass, CALLBACKS);
return subclass;
}
看registerStaticCallbacks()方法的入参CALLBACKS:
private static final Callback[] CALLBACKS = new Callback[] {
new BeanMethodInterceptor(),
new BeanFactoryAwareMethodInterceptor(),
NoOp.INSTANCE
};
会走到BeanMethodInterceptor 拦截器,实现了MethodInterceptor接口,重写了intercept()方法:
private static class BeanMethodInterceptor implements MethodInterceptor, ConditionalCallback {
/**
* 会进入intercept()方法
*/
@Override
@Nullable
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
MethodProxy cglibMethodProxy) throws Throwable {
// enhancedConfigInstance是代理对象
ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
// Determine whether this bean is a scoped-proxy
if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
beanName = scopedBeanName;
}
}
// 判断是否为FactoryBean类型的实例,如果一个对象加上”&“前缀和对象名称,拿到的是实现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) {
// Scoped proxy factory beans are a special case and should not be further proxied
}
else {
// It is a candidate FactoryBean - go ahead with enhancement
// 如果是FactoryBean对象,在这进行代理增强
return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
}
}
// 只有@Bean注解的方法是被Spring调用的时候,才会走下来
// 之前反射调用FactoryMethod的时候把FactoryMethod的值放到ThreadLocal中,当用代理对象调的时候,ThreadLocal中才有值
if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
----省略无关代码----
// 调用被代理方法
return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
}
// 用代理对象手动调用@Bean注解方法是才会执行到这里
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}
如果是代理对象手动调用,会走下面的方法:
private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs,
ConfigurableBeanFactory beanFactory, String beanName) {
----省略无关代码----
// TODO 核心代码就是这beanFactory.getBean(beanName),拿到的是同一个对象,因为有缓存
Object beanInstance = (useArgs ? beanFactory.getBean(beanName, beanMethodArgs) :
beanFactory.getBean(beanName));
if (!ClassUtils.isAssignableValue(beanMethod.getReturnType(), beanInstance)) {
// Detect package-protected NullBean instance through equals(null) check
if (beanInstance.equals(null)) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("@Bean method %s.%s called as bean reference " +
"for type [%s] returned null bean; resolving to null value.",
beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName(),
beanMethod.getReturnType().getName()));
}
beanInstance = null;
}
----省略无关代码----
// 将拿到的实例返回
return beanInstance;
如果是FactoryBean类型的实例,即&+beanName的形式,进入enhanceFactoryBean() 方法,同样也是CGLIB代理:
private Object createCglibProxyForFactoryBean(final Object factoryBean,
final ConfigurableBeanFactory beanFactory, final String beanName) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(factoryBean.getClass());
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setCallbackType(MethodInterceptor.class);
// Ideally create enhanced FactoryBean proxy without constructor side effects,
// analogous to AOP proxy creation in ObjenesisCglibAopProxy...
Class<?> fbClass = enhancer.createClass();
Object fbProxy = null;
if (objenesis.isWorthTrying()) {
try {
fbProxy = objenesis.newInstance(fbClass, enhancer.getUseCache());
}
catch (ObjenesisException ex) {
logger.debug("Unable to instantiate enhanced FactoryBean using Objenesis, " +
"falling back to regular construction", ex);
}
}
if (fbProxy == null) {
try {
fbProxy = ReflectionUtils.accessibleConstructor(fbClass).newInstance();
}
catch (Throwable ex) {
throw new IllegalStateException("Unable to instantiate enhanced FactoryBean using Objenesis, " +
"and regular FactoryBean instantiation via default constructor fails as well", ex);
}
}
// CGLIB的Callback赋值给代理对象,代理对象调方法,走的是切面,会进入这里
((Factory) fbProxy).setCallback(0, (MethodInterceptor) (obj, method, args, proxy) -> {
// 如果是getObject()方法,调用getBean(beanName)
if (method.getName().equals("getObject") && args.length == 0) {
return beanFactory.getBean(beanName);
}
// 如果调用的是非getObject方法,会调对象本身(原始实例)
return proxy.invoke(factoryBean, args);
});
return fbProxy;
}
总结,如果一个类加@Configration注解,CGLIB会生成字节码代理对象 lisonFactory.getLison()是通过代理对象this.lison() 调用的。然后用切面赋值给了 lisonFactory.setLison(this.lison()),两个走的都是beanFactory.getBean()方法,从缓存中拿,所以实例是同一个。
同样, lili会生成一个代理,因为代理对象调getObject()方法要走切面,advice生成同一个实例,然后通过beanFactory.getBean()方法返回实例。不管调用有@Bean注解的方法几次,生成的代理都是同一个。
而@Component注解却是对象本身,调用一次生成一次。