背景
先看下面问题:
类A、B,类AppConfig是配置类加了@Configuration,并且对A、B加了@Configuration,要把beanA、beanB创建到spring容器中,注意创建B时,调用了getA()。
此时按照代码正常理解逻辑会打印两遍“new A()”,因为调用了两次new A()。但是实际你可以去试试,只打印了一遍。。。这就很奇怪了。。。
是因为spring默认bean是单例的这里不会创建两遍,那么spring是如何保证自己的“单例原则”没有被打破的往下看。。
public class A {
public A(){
System.out.println("new A()");
}
}
public class B {
public B(){
System.out.println("new B()");
}
}
@ComponentScan("com.yonghui.yh")
@Configuration
public class AppConfig {
@Bean
public A getA(){
System.out.print("new A()")
return new A();
}
@Bean
public B getB(){
//这里调用了getA()
getA();
return new B();
}
}
public class ApplicationTest {
public static void main(String[] args) {
//初始化spring容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// System.out.print("new A()")打印一遍
}
}
其实源于我们AppConfig类中加了一个@Configuration,这个东西说明该类是一个配置类,spring会对这种加了@Configuration注解的类进行特殊处理,也就是传说中的代理,我们可以在容器启动时,看看这个类,可以发现他被cglib代理了。
AppConfig是一个配置类,任何一个类都可以被指定成为配置类,但是这个类并不一定需要加@Configuration注解,这个注解的作用就是能够是AppConfig能够产生一个代理对象,确保AppConfig类中@Bean创建的bean是单例的,如果没有AppConfig没有@Configuration就不是代理对象,那么出现@Bean方法相互调用会使单例原则被破坏。
public class ApplicationTest {
public static void main(String[] args) {
//初始化spring容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(context.getBean(AppConfig.class));
// 因为AppConfig加了@Configuration: com.yonghui.yh.AppConfig$$EnhancerBySpringCGLIB$$4ce7fa7e@75881071
}
}
有了思路去看看,到底是怎么做的,其实就是在ConfigurationClassPostProcessor bean工厂后置处理器中进行的。这个类实现了BeanDefinitionRegistryPostProcessor、而BeanDefinitionRegistryPostProcessor实现了BeanDefinitionPostProcessor。
(ConfigurationClassPostProcessor 这个处理器听说很重要很重要很重要,是spring开天辟地的五个BeanDefinition之一,我还没看,今天分享的是其中冰山一小🦶🏻,后面在学学)
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
PriorityOrdered, ResourceLoaderAware, ApplicationStartupAware, BeanClassLoaderAware, EnvironmentAware {
说的这就要看看cglib代理的一个大概流程。
补充
这里的cglib代理原理如下:通过Enhancerr生成了一个继承了A类的子类,并创建对象(代理对象),你可以清楚的看到setSuperclass、setCallback、、、不就是在创建子类嘛,而setCallback方法就是设置一个MethodInterceptor拦截器,“增强”就是在这里面做的。
你可以回顾一下jdk动态代理得区别,可以在梳理梳理。
public class A {
public void a(){
System.out.println("aaaa");
}
}
public class CglibA {
public static void main(String[] args) {
MethodInterceptor methodInterceptor = new MethodInterceptor() {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("cglib before");
Object invoke = methodProxy.invoke(o, null);
System.out.println("cglib after");
return invoke;
}
};
//理解为通过Enhancer生成了一个继承了A类的子类,并创建对象(代理对象)
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(A.class);
// 这里可以调用setCallbacks方法就传多个增强,行程链式增强(拦截器组)
enhancer.setCallback(methodInterceptor);
enhancer.setUseFactory(false);
enhancer.setCallbackType(methodInterceptor.getClass());
A a = (A) enhancer.create();
a.a();
/*
打印:
cglib before
aaaa
cglib after
*/
}
}
解答
回到上面问题一开始的问题“为啥打印一遍”,直接看下源码:(我只截取了关键代码,并附上注释,可以进去点一点,跟着我的注释思路,很清晰的,最好是自己创建一个配置类,debug一哈)
ConfigurationClassPostProcessor实现的接口BeanDefinitionRegistryPostProcessor的父类接口的BeanFactoryPostProcessor的postProcessBeanFactory()
@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);
}
// 从这开始
enhanceConfigurationClasses(beanFactory);
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}
enhanceConfigurationClasses(beanFactory)
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
StartupStep enhanceConfigClasses = this.applicationStartup.start("spring.context.config-classes.enhance");
Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
for (String beanName : beanFactory.getBeanDefinitionNames()) {
//...省略部分源码.....
//判断是否是一个全配置类 full
if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
//...省略部分源码.....
// 全配置类进行记录,如果是lite非全配置类,那么不用管spring该怎么new就怎么new
configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
}
}
//无全配置类记录,直接结束
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());
}
//遍历全配置类configBeanDefs开始代理
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<?> 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()));
}
//设置实际的beanClass为这个代理类,后面用这个bd时,就是使用的这个代理过的类了
//要去看看,对他增强了什么
beanDef.setBeanClass(enhancedClass);
}
//...省略部分源码.....
}
想知道上面打印一次那个原因,就需要看看,对他增强了什么。ConfigurationClassEnhancer封装了对这个全配置类增强的逻辑(即上面演示的cglib、enhance等,其中增强拦截器就是BeanMethodInterceptor、BeanFactoryAwareMethodInterceptor,主要在BeanMethodInterceptor中)
class ConfigurationClassEnhancer {
// setCallbacks方法就传多个增强,行程链式增强(拦截器组)
// BeanMethodInterceptor、BeanFactoryAwareMethodInterceptor是这个类的内部类
private static final Callback[] CALLBACKS = new Callback[] {
new BeanMethodInterceptor(),
new BeanFactoryAwareMethodInterceptor(),
NoOp.INSTANCE
};
private static final ConditionalCallbackFilter CALLBACK_FILTER = new ConditionalCallbackFilter(CALLBACKS);
private static final String BEAN_FACTORY_FIELD = "$$beanFactory";
private static final Log logger = LogFactory.getLog(ConfigurationClassEnhancer.class);
private static final SpringObjenesis objenesis = new SpringObjenesis();
/**
* 调用enhancer.enhance(configClass, this.beanClassLoader);
*/
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;
}
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;
}
/**
* Creates a new CGLIB {@link Enhancer} instance.
*/
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(configSuperClass);
enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
enhancer.setUseFactory(false);
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
enhancer.setCallbackFilter(CALLBACK_FILTER);
enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
return enhancer;
}
/**
* Uses enhancer to generate a subclass of superclass,
* ensuring that callbacks are registered for the new subclass.
*/
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;
}
/**
* Marker interface to be implemented by all @Configuration CGLIB subclasses.
* Facilitates idempotent behavior for {@link ConfigurationClassEnhancer#enhance}
* through checking to see if candidate classes are already assignable to it, e.g.
* have already been enhanced.
* <p>Also extends {@link BeanFactoryAware}, as all enhanced {@code @Configuration}
* classes require access to the {@link BeanFactory} that created them.
* <p>Note that this interface is intended for framework-internal use only, however
* must remain public in order to allow access to subclasses generated from other
* packages (i.e. user code).
*/
public interface EnhancedConfiguration extends BeanFactoryAware {
}
/**
* Conditional {@link Callback}.
* @see ConditionalCallbackFilter
*/
private interface ConditionalCallback extends Callback {
boolean isMatch(Method candidateMethod);
}
/**
* A {@link CallbackFilter} that works by interrogating {@link Callback Callbacks} in the order
* that they are defined via {@link ConditionalCallback}.
*/
private static class ConditionalCallbackFilter implements CallbackFilter {
private final Callback[] callbacks;
private final Class<?>[] callbackTypes;
public ConditionalCallbackFilter(Callback[] callbacks) {
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];
if (!(callback instanceof ConditionalCallback) || ((ConditionalCallback) callback).isMatch(method)) {
return i;
}
}
throw new IllegalStateException("No callback available for method " + method.getName());
}
public Class<?>[] getCallbackTypes() {
return this.callbackTypes;
}
}
private static class BeanMethodInterceptor implements MethodInterceptor, ConditionalCallback {
/**
* Enhance a {@link Bean @Bean} method to check the supplied BeanFactory for the
* existence of this bean object.
* @throws Throwable as a catch-all for any exception that may be thrown when invoking the
* super implementation of the proxied method i.e., the actual {@code @Bean} method
*/
@Override
@Nullable
public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
MethodProxy cglibMethodProxy) throws Throwable {
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;
}
}
// To handle the case of an inter-bean method reference, we must explicitly check the
// container for already cached instances.
// First, check to see if the requested bean is a FactoryBean. If so, create a subclass
// proxy that intercepts calls to getObject() and returns any cached bean instance.
// This ensures that the semantics of calling a FactoryBean from within @Bean methods
// is the same as that of referring to a FactoryBean within XML. See SPR-6602.
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
return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
}
}
/**
* 判断是否正在被调用,同理:生命周期循环依赖的正在创建的bean
* 这里和循环依赖相似:
* 判断正在调用的方法 和 正在创建bean的方法
* 1、如果 正在调用的方法 和 正在创建bean的方法 相同就会直接调用父类的方法(我们的@Bean方法)进行创建bean
* 2、如果不相同就会先去getBean
* 这里不管是哪个方法都会进入代理的这个里面
*
* 例如:
* 创建beanA的生命周期中:
* 调用@Bean getA()方法时,此时记录set集合中:getA(),调用代理类的方法也是getA(),就会直接创建beanA,清空set集合中:getA()
* 调用 @Bean getB()方式时,此时记录set集合中:getB(),get()中又调用了getA(),(这里不管是哪个方法都会进入代理的这个里面),所以又会执行到
* 这里的判断当前执行(调用)的是getA(),当时当前创建的却是getB(),就会执行下面的resolveBeanReference(),resolveBeanReference()中会去getBean(),
* 保证了bean的单例,执行完getA(),后继续执行beanB的生命周期
*
*/
if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
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()));
}
//如果 正在调用的方法 和 正在创建bean的方法 相同就会直接调用父类的方法进行创建bean
return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
}
//如果不相同就会先去getBean
return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}
private Object resolveBeanReference(Method beanMethod, Object[] beanMethodArgs,
ConfigurableBeanFactory beanFactory, String beanName) {
// The user (i.e. not the factory) is requesting this bean through a call to
// the bean method, direct or indirect. The bean may have already been marked
// as 'in creation' in certain autowiring scenarios; if so, temporarily set
// the in-creation status to false in order to avoid an exception.
boolean alreadyInCreation = beanFactory.isCurrentlyInCreation(beanName);
try {
if (alreadyInCreation) {
beanFactory.setCurrentlyInCreation(beanName, false);
}
boolean useArgs = !ObjectUtils.isEmpty(beanMethodArgs);
if (useArgs && beanFactory.isSingleton(beanName)) {
// Stubbed null arguments just for reference purposes,
// expecting them to be autowired for regular singleton references?
// A safe assumption since @Bean singleton arguments cannot be optional...
for (Object arg : beanMethodArgs) {
if (arg == null) {
useArgs = false;
break;
}
}
}
//getBean 去获取bean ,没有时就会先去创建那个bean的生命周期
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;
}
else {
String msg = String.format("@Bean method %s.%s called as bean reference " +
"for type [%s] but overridden by non-compatible bean instance of type [%s].",
beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName(),
beanMethod.getReturnType().getName(), beanInstance.getClass().getName());
try {
BeanDefinition beanDefinition = beanFactory.getMergedBeanDefinition(beanName);
msg += " Overriding bean of same name declared in: " + beanDefinition.getResourceDescription();
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore - simply no detailed message then.
}
throw new IllegalStateException(msg);
}
}
Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod();
if (currentlyInvoked != null) {
String outerBeanName = BeanAnnotationHelper.determineBeanNameFor(currentlyInvoked);
beanFactory.registerDependentBean(beanName, outerBeanName);
}
return beanInstance;
}
finally {
if (alreadyInCreation) {
beanFactory.setCurrentlyInCreation(beanName, true);
}
}
}
}