spring 3.0 之后出提供了@Configuration和@Bean注解,我们可以通过编码的方式达到和 XML 配置等同的效果。 我们可以把@Configuration看作成<beans>标签,把@Bean看作成<bean>标签。
一个汽车配置类 CarConfiguration,包含 3 个工厂方法( @Bean),分别是创建一个 Engine对象和两个 Car对象,在创建 Car对象方法内调用创建 Engine对象的方法。
示例代码如下:
@Configurationpublic class CarConfiguration {/*** 发动机*/public static class Engine {public Engine() {System.out.println("Engine");}}/*** 汽车*/public static class Car {private String name;private Engine engine;public Car(String name, Engine engine) {this.name = name;this.engine = engine;}public String getName() {return name;}}/*** 造一个引擎*/@Beanpublic Engine engine() {return new Engine();}/*** 造一辆 LP 700-4*/@Beanpublic Car aventador() {return new Car("LP700-4", engine());}/*** 造一辆 X6*/@Beanpublic Car bmw() {return new Car("X6", engine());}}那么问题来了, System.out.println("Engine")这行代码会被执行几次?
揭密问题之前,我们来做个推理。我们知道 spring 中 bean 的 scope 默认是 singleton。上述代码上我们并没有在engine()方法中指定其 scope,按照单例的语义,那么只应该创建Engine对象一次。若因为engine()方法被调用多次而创建了多个Engine对象那岂不是违背了 scope 为 singleton 本身的语义。
带着问题,我们把程序跑起来,验证一下结果,果然和猜想中的一致,字符串 Engine只会被打印一次。
那么问题又来了,spring 是如何做到的呢?
AOP! 对,我们很容易联想到 spring 中的 AOP,通过 AOP 我可以增强类的行为,诸如大家都很熟悉的声明式事物。
为了验证猜想,我们在 pom 中添加依赖 spring-boot-starter-actuator,通过浏览器访问 http://<host>:<port>/beans 来查看 CarConfiguration的相关信息,如下:
{bean: "com.owenxh.springadvancedexamples.configure.CarConfiguration",aliases: [ ],scope: "singleton",type: "com.owenxh.springadvancedexamples.configure.CarConfiguration$$EnhancerBySpringCGLIB$$7c72aec8",resource: "null",dependencies: [ ]}我们看到了 CarConfiguration$$EnhancerBySpringCGLIB,这表明 CarConfiguration已经被 spring 通过 CGLIB 来进行增强了。我们现在可以确定 spring 通过 aop 对标注了 @Bean的工厂方法进行了拦截处以保证 scope 的语义正确性。
注:为了方便访问/beans, 我们需要在properties文件中添加如下配置:
management.security.enabled = false新的问题又来了,spring 在什么时候对 @Configuration类进行增强的呢?
这就需要去翻 spring 的源码了。
长话短说,最终我们找到两个类: ConfigurationClassPostProcessor 和 ConfigurationClassEnhancer。
ConfigurationClassPostProcessor是 BeanDefinitionRegistryPostProcessor(从 BeanFactoryPostProcessor派生)接口的实现,在 AbstractApplicationContext.refresh()方法中被触发执行。
看一下 ConfigurationClassPostProcessor中的方法 postProcessBeanFactory(ConfigurableListableBeanFactorybeanFactory)的代码:
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);}// 增强 Configuration 类enhanceConfigurationClasses(beanFactory);beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));}在 enhanceConfigurationClasses(beanFactory)这一行代码中用到了前面提到的 ConfigurationClassEnhancer对 Configuration 类进行增强(代码过长,不贴了)。 增强后的 Configuration 内置了拦截器 BeanMethodInterceptor和 BeanFactoryAwareMethodInterceptor,这两个都是 ConfigurationClassEnhancer中的内部类,我们来分别介绍。
BeanFactoryAwareMethodInterceptor增强后的 Configuration 类实现EnhancedConfiguration接口(也是ConfigurationClassEnhancer中的内部类),BeanFactoryAwareMethodInterceptor的作用就是给增强后的 Configuration 类注入BeanFactory。
EnhancedConfiguration代码如下:
public interface EnhancedConfiguration extends BeanFactoryAware {}BeanMethodInterceptorBeanMethodInterceptor中的逻辑就是决定直接执行@Bean工厂方法创建 bean 或者从BeanFactory中来获取 bean,核心代码如下:
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-proxyScope scope = AnnotatedElementUtils.findMergedAnnotation(beanMethod, Scope.class);if (scope != null && scope.proxyMode() != ScopedProxyMode.NO) {String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {beanName = scopedBeanName;}}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 enhancementreturn enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);}}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.// 省略 log 代码return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);}return obtainBeanInstanceFromFactory(beanMethod, beanMethodArgs, beanFactory, beanName);}通过以上案例分析以及对源码的解析,我们进一步了解了@Configuration注解。spring 通过 CGLIB 对标注了@Configuration注解的 Configuration 类进行了增强,增加后的后被注入BeanFactory,spring 会对@Bean方法进行拦截,根据触发点的不同,以决定直接执行工厂方法还是从BeanFactory中获取 bean。
推荐:Spring Cloud Alibaba基础教程:使用Nacos实现服务注册与发现
上一篇:Sentinel Dashboard中修改规则优雅同步到Apollo
点击原文阅读更多
Original url: Access
Created at: 2019-05-10 14:09:41
Category: default
Tags: none
未标明原创文章均为采集,版权归作者所有,转载无需和我联系,请注明原出处,南摩阿彌陀佛,知识,不只知道,要得到
最新评论