Spring源码解析(四)配置类上加与不加@Configuration的区别

之前写过一篇关于@Configuration的博客,但是写的不全,尤其是对加了@Configuration的类进行cjlib动态代理的地方;这篇来详细说一下;

cglib被hibernate用来动态生成po的字节码。CGLIB的底层是java字节码操作框架ASM。

今天简单看下cglib的用法。cglib的Enhancer说起来神奇,用起来一页纸不到就讲完了。它的原理就是用Enhancer生成一个原有类的子类,并且设置好callback到proxy, 则原有类的每个方法调用都会转为调用实现了MethodInterceptor接口的proxy的intercept() 函数。

我们来看一个cjlib代理类的例子实现对配置类(AppConfig)的代理

package com.luban.app;

import com.luban.anno.EanbleLuabn;
import com.luban.dao.Dao;
import com.luban.dao.IndexDao;
import com.luban.dao.IndexDao1;
import com.luban.imports.MyImportSelector;
import org.springframework.context.annotation.*;
@ComponentScan({"com.luban"})
@Configuration
@Import(MyImportSelector.class)
public class Appconfig {

	@Bean
	public IndexDao1 indexDao1(){

		return new IndexDao1();
	}
    @Bean
	public IndexDao2 indexDao2(){
        indexDao1();
		return new IndexDao2();

	}
    @Bean
	public void testProxy(){
		System.out.println("Appconfig的cjlib代理");
	}
}
package com.luban.test;

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class TestMethodCallback implements MethodInterceptor {

	@Override
	public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
		System.out.println("methods---enhance");
		return methodProxy.invokeSuper(o,objects);

	}
}
public class Test{

    public static void main(String args[]){ 
        Enhancer enhancer = new Enhancer();
		//增强父类,cglib是基于继承来的
		enhancer.setSuperclass(Appconfig.class);
		//不继承Factory接口
		enhancer.setUseFactory(false);
		enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
	 
		//过滤方法,不能每次都去new
        //传入一个方法拦截器MethodInterceptor 的实现类
		enhancer.setCallback(new TestMethodCallback());
		Appconfig chil=(Appconfig) enhancer.create(); //返回AppConfig的实现类
		chil.testProxy();//当调用AppConfig代理类中的方法时,该方法会被增强
    }

}

输出:

methods---enhance
AppConfig的cjlib代理

这个过程就是对AppConfig进行的cjlib动态代理(当然了,spring做的cjlib代理的intercept方法当然更加复杂),那么现在的问题是,Spring在解析AppConfig的@Bean注解的方法时,会调用几次new IndexDao1()呢? 

    @Bean
    public IndexDao1 indexDao1(){

        return new IndexDao1();
    }
    @Bean
    public IndexDao2 indexDao2(){
        indexDao1();//??调用几次??
        return new IndexDao2();

    }

答案1次,indexdao1这个对象在BeanFactroy中只会存在一个(单例);显然如果没有cjlib增强AppConfig中的方法,他当然会按照正常的解析顺序new两个IndexDao,但是有了cjlib动态代理,该方法会被拦截,我们可以设想在intercept()方法中,会判断IndexDao1是否是第一次创建(是否已经存在于BeanFactory),如果说已经存在,那么spring应该直接去容器中拿这个beaninstance;如果不存在,那么spring会调用父类(AppConfig)中的方法(indexDao1())去创建这个IndexDao1;

那么,以上的猜想是否正确呢?

 

我们从 ConfigurationClassPostProceessor 这个类源码看起:这个类中有两个重要的方法;

 postProcessBeanDefinitionRegistry()//解析了加了Configuration注解的类,同时解析出 @ComponentScan 和 @ComponentScans 扫描出的Bean,也会解析出加了 @Bean 注解的方法所注册的Bean,以及通过 @Import 注解注册的Bean和 @ImportResource 注解导入的配置文件中配置的Bean。之前的文章已经讲过就不在说了!

postProcessBeanFactory()//对加了@Configuration的配置类实现cjlib  主要看

 

	/**
	 * 手动调用的
	 * Derive further bean definitions from the configuration classes in the registry.
	 */
	@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);

		processConfigBeanDefinitions(registry);
	}

postProcessBeanFactory()

/**
	 * Prepare the Configuration classes for servicing bean requests at runtime
	 * by replacing them with CGLIB-enhanced subclasses.
	 */	
@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);
		}
		//给配置类产生cglib代理
		//为什么需要产生cglib代理?
		enhanceConfigurationClasses(beanFactory);
		beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
	}

进入enhanceConfigurationClasses(beanFactory);

full:全配置类,加了@Configuration

lite:部分配置类,没加@Configuration

	public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
		Map configBeanDefs = new LinkedHashMap<>();
		for (String beanName : beanFactory.getBeanDefinitionNames()) {
			BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
			//判断是否是一个全注解类
			//扫描是全注解类?full和lite的关系
			if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {
				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.isWarnEnabled() && beanFactory.containsSingleton(beanName)) {
					logger.warn("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 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);
			try {
				// Set enhanced subclass of the user-specified bean class
				Class configClass = beanDef.resolveBeanClass(this.beanClassLoader);
				if (configClass != null) {
					//完成对全注解类的cglib代理
					Class enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
					if (configClass != enhancedClass) {
						if (logger.isDebugEnabled()) {
							logger.debug(String.format("Replacing bean definition '%s' existing class '%s' with " +
									"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
						}
						beanDef.setBeanClass(enhancedClass);
					}
				}
			}
			catch (Throwable ex) {
				throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
			}
		}
	}

ConfigurationClassEnhancer.class

Class enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);

/**
	 * Loads the specified class and generates a CGLIB subclass of it equipped with
	 * container-aware callbacks capable of respecting scoping and other bean semantics.
	 * @return the enhanced subclass
	 */
	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 " +
						"). This is harmless, but you may " +
						"want check your configuration and remove one CCPP if possible",
						configClass.getName()));
			}
			return configClass;
		}
		//如果没有被代理   那么cglib代理
		Class enhancedClass = createClass(newEnhancer(configClass, classLoader));
		if (logger.isDebugEnabled()) {
			logger.debug(String.format("Successfully enhanced %s; enhanced class name is: %s",
					configClass.getName(), enhancedClass.getName()));
		}
        //返回被cjlib代理的对象
		return enhancedClass;
	}

Class enhancedClass = createClass(newEnhancer(configClass, classLoader));

创建代理类

/**
	 * Creates a new CGLIB {@link Enhancer} instance.
	 */
	private Enhancer newEnhancer(Class configSuperClass, @Nullable ClassLoader classLoader) {
		Enhancer enhancer = new Enhancer();
		//增强父类,地球人都知道cglib是基于继承来的
		enhancer.setSuperclass(configSuperClass);
		//增强接口,为什么要增强接口?
		//便于判断,表示一个类以及被增强了
		enhancer.setInterfaces(new Class[] {EnhancedConfiguration.class});
		//不继承Factory接口
		enhancer.setUseFactory(false);
		enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
		// BeanFactoryAwareGeneratorStrategy是一个生成策略
		// 主要为生成的CGLIB类中添加成员变量$$beanFactory
		// 同时基于接口EnhancedConfiguration的父接口BeanFactoryAware中的setBeanFactory方法,
		// 设置此变量的值为当前Context中的beanFactory,这样一来我们这个cglib代理的对象就有了beanFactory
		//有了factory就能获得对象,而不用去通过方法获得对象了,因为通过方法获得对象不能控制其过程
		//该BeanFactory的作用是在this调用时拦截该调用,并直接在beanFactory中获得目标bean
		enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
		//过滤方法,不能每次都去new
		enhancer.setCallbackFilter(CALLBACK_FILTER);
		enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
		return enhancer;
	}

 

enhancer.setCallbackFilter(CALLBACK_FILTER) //cjlib设置的拦截器

// The callbacks to use. Note that these callbacks must be stateless.
	private static final Callback[] CALLBACKS = new Callback[] {
			//增强方法,主要控制bean的作用域  单例还是原型
			//不每一次都去调用new
			new BeanMethodInterceptor(),
			//设置一个beanFactory
			new BeanFactoryAwareMethodInterceptor(),
			NoOp.INSTANCE
	};

private static final ConditionalCallbackFilter CALLBACK_FILTER = 
                         new ConditionalCallbackFilter(CALLBACKS);

private static final String BEAN_FACTORY_FIELD = "$$beanFactory";

 private static final Callback[] CALLBACKS = new Callback[] {
            //增强方法,主要控制bean的作用域  单例还是原型
            //不每一次都去调用new
            new BeanMethodInterceptor(),    //方法拦截器 
            //设置一个beanFactory
            new BeanFactoryAwareMethodInterceptor(),  
            NoOp.INSTANCE  
    };

CALLBACK_FILTER =  new ConditionalCallbackFilter(CALLBACKS);    

下面就是AppConfig的方法拦截器;我们们来看一下思路和我们在最上面猜想的是不是一样?

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 {

			//enhancedConfigInstance 代理
			// 通过enhancedConfigInstance中cglib生成的成员变量$$beanFactory获得beanFactory。
			ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);

			String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);

			// Determine whether this bean is a scoped-proxy
			Scope scope = AnnotatedElementUtils.findMergedAnnotation(beanMethod, Scope.class);
			if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {
				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);
				}
			}

			//一个非常重要的判断
			//判断到底是new 还是get
			//判断执行的方法和调用方法是不是同一个方法
			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.
				if (logger.isWarnEnabled() &&
						BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {
					logger.warn(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);
			}

			return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
		}

//xxxx其他方法.......

}

 解释:

//一个非常重要的判断

 //判断到底是new 还是get
 //判断执行的方法和调用方法是不是同一个方法  

 if (isCurrentlyInvokedFactoryMethod(beanMethod)) {                 

           return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
 }

            return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName); 

  回到最初的问题:      indexDao1();//调用几次??

  @Bean
    public IndexDao1 indexDao1(){

        return new IndexDao1();
    }
    @Bean
    public IndexDao2 indexDao2(){   
        indexDao1();
        return new IndexDao2();

    }

当Spring解析到上面的indexDAo2方法时,执行的方法是 indexDao2(),调用的方法是  indexDao1();

当出现上面这种方法嵌套调用时,method.getName()为“indexdao2”;currentlyInvoked.getName()为“indexdao1” 不相等,

method.getName().equals(currentlyInvoked.getName()) 

所以isCurrentlyInvokedFactoryMethod(beanMethod)方法返回false,直接从beanfactory中拿;

如果是true,说明是第一次调用该方法,那么就调用父类中的方法去创建该对象!!!

private boolean isCurrentlyInvokedFactoryMethod(Method method) {
			Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod();
			return (currentlyInvoked != null && method.getName().equals(currentlyInvoked.getName()) &&
					Arrays.equals(method.getParameterTypes(), currentlyInvoked.getParameterTypes()));
		}

 

现在,这篇文章里的思考应该很好解释了!

https://blog.csdn.net/weixin_43107805/article/details/102956805

你可能感兴趣的:(Java开发)