用了这么多年的Spring 你还记得?

2024-08-21 00:08
文章标签 java spring 记得 多年

本文主要是介绍用了这么多年的Spring 你还记得?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文的阅读最好的方式结合代码一起看,在很久没有回顾的时候可能会忘记一些spring的扩展点,已经spring 到底怎么玩的?本文是作者了解spring 到工作的第四个年头 从实习开始2016年,这里做个总结,用了这么多,估计写着crud 忘了spring的奥秘了。

一、yy一下Bean创建的过程

首先得承认spring 这个太强大,一般的java开发者根本创造不出来这么强大的一个玩意,从扩展性和维护性(…不知道诞生多少年了,反正我一毕业就开始学习),程序员都是模仿动物,都是不断的在模仿中学习和进步,和小孩子学会说话、吃奶一个道理,要想自己更强大,还是站在巨人的肩膀上。

1.1 简述Ioc 创建Bean

玩过spring的都知道,如果让我写一个类似spring的玩意。

  • 首先呢得模仿spring @conpentScan 首先要收集那些类被管理,那么首先自己得编写一波代码扫描需要收集的类;
  • spring 支持xml、注解两种配置方式,现在大佬们都是谈各种需要创建领域、元数据,那么针对一个Bean的创建 我们需要那些数据呢?翻翻自己的代码看一下子,注入了哪些其他的Bean?对于哪些Bean有依赖,注入了哪些属性值… 这个Bean的名称、类型,这个Bean是哪个class ,有哪些注解呢等等,这个玩意叫做模型,用来描述一个Bean构建过程需要的一些基础元数据信息,spring 中 通过BeanDefinition来定义, BeanDefinition是Bean 管理的基础元数据 信息,Spring IoC实际上是Bean的管理容器,BeanDefinition是有关Bean定义信息的抽象,后续框架中要使用bean定义信息来实例化一个Bean,在spring 中无论 通过xml或annotation两种方式构建Bean 都是转化为BeanDefinition来表示。
  • 有了 BeanDefinition 的信息,我们就可以通过这个来实例化一个Bean,简单粗暴,通过class 反射创建一个对象,然后注入属性,需要注入其他的Bean,获取缓存有没有有没有继续递归创建(不考虑循环依赖),完事~。

在这里插入图片描述

二、spring 扩展点的理解

2.1 BeanFactoryPostProcessor

BeanFactoryPostProcessor是针对容器的扩展点,加载收集BeanDefinition之后容器就会触发该扩展点,实现了该接口的扩展点可以修改元信息也就是BeanDefinition对象中的信息,(See PropertyResourceConfigurer and its concrete implementations for out-of-the-box solutions that address such configuration needs. spring 的占位符替换就是这样的一种开箱即可使用的方案,在bean 还没有初始化的时候就将数据中特殊标记熟悉进行占位符的替换) 。

代码地址 : org.springframework.beans.factory.config.PropertyResourceConfigurer
根据下面的代码自己好好研究一下即可简单的了解这个字符串替换的逻辑

// org.springframework.beans.factory.config.PropertyResourceConfigurer#postProcessBeanFactorypublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {try {Properties mergedProps = mergeProperties();// Convert the merged properties, if necessary.convertProperties(mergedProps);// Let the subclass process the properties.processProperties(beanFactory, mergedProps);}catch (IOException ex) {throw new BeanInitializationException("Could not load properties", ex);}}// org.springframework.beans.factory.config.PlaceholderConfigurerSupport#doProcessProperties protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,StringValueResolver valueResolver) {BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();for (String curName : beanNames) {// Check that we're not parsing our own bean definition,// to avoid failing on unresolvable placeholders in properties file locations.if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);try {// 替换属性值visitor.visitBeanDefinition(bd);}catch (Exception ex) {throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex);}}}}

2.1.1 BeanDefinitionRegistryPostProcessor

BeanDefinitionRegistryPostProcessor 继承了 BeanFactoryPostProcessor,允许在常规BeanFactoryPostProcessor检测开始之前注册更多的bean定义,因此这个子类是在BeanFactoryPostProcessor 之前被调用,很多的中间件注册通过编码注册bean 就是采用这种形式,一个非常好的例子就是扫描 @Configuration 这种spring 4.0 后续增加的通过javaBean的方式注册的Bean。

代码例子: org.springframework.context.annotation.ConfigurationClassPostProcessor 扫描 @Configuration 注册bean

代码例子: org.apache.dubbo.config.spring.beans.factory.annotation.ServiceAnnotationBeanPostProcessor 扫描收集dubbo 提供的服务 其实dubbo的这个处理就是一个非常好的学习的例子,让我们感受到了收集、注册Bean的过程。同时可以模仿处理自定义.


//org.apache.dubbo.config.spring.beans.factory.annotation.ServiceAnnotationBeanPostProcessor#buildServiceBeanDefinition
private AbstractBeanDefinition buildServiceBeanDefinition(Service service, Class<?> interfaceClass,String annotatedServiceBeanName) {BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ServiceBean.class);AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol","interface", "interfaceName", "parameters");propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(service, environment, ignoreAttributeNames));// References "ref" property to annotated-@Service BeanaddPropertyReference(builder, "ref", annotatedServiceBeanName);// Set interfacebuilder.addPropertyValue("interface", interfaceClass.getName());// Convert parameters into mapbuilder.addPropertyValue("parameters", convertParameters(service.parameters()));return builder.getBeanDefinition();}// org.apache.dubbo.config.spring.beans.factory.annotation.ServiceAnnotationBeanPostProcessor#registerServiceBean
private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,DubboClassPathBeanDefinitionScanner scanner) {Class<?> beanClass = resolveClass(beanDefinitionHolder);Service service = findMergedAnnotation(beanClass, Service.class);Class<?> interfaceClass = resolveServiceInterfaceClass(beanClass, service);String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();AbstractBeanDefinition serviceBeanDefinition =buildServiceBeanDefinition(service, interfaceClass, annotatedServiceBeanName);// ServiceBean Bean nameString beanName = generateServiceBeanName(service, interfaceClass);// 注册beanregistry.registerBeanDefinition(beanName, serviceBeanDefinition);}

需要注意: BeanFactoryPostProcessor可以与bean定义交互并修改它们,但不能与bean实例交互。这样做可能会导致过早的bean实例化,违反容器并导致意外的副作用。如果需要bean实例交互,请考虑实现 BeanPostProcessor 替换。

2.2 BeanPostProcessor

BeanPostProcessor 扩展点是针对Bean 实例,允许自定义修改新bean实例的工厂的扩展,通常在代理Bean、标记接口(xxxAware的实现方案)。这里要记住的一点,这个的扩展是针对对象的,证明当前对象已经通过反射创建出来啦。

2.2.1 标记接口

比如我们常见的EnvironmentAware、ResourceLoaderAware、ApplicationContextAware等等 这些接口的标记,标记的处理结果就是针对当前对象进行当前接口的会掉的处理。 比如下面两个代码地址非常值得学习。

代码地址: org.springframework.context.support.ApplicationContextAwareProcessor
org.springframework.web.context.support.ServletContextAwareProcessor
标记接口、非常简单,存在这个接口做特定的事情处理一下这个bean 。

// Configure the bean factory with context callbacks.
// org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory  
//这个实例的初始化是在spring 容器启动的时候处理的!// beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
// beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
// beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
// beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
// beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
// beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
@Override
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {if (bean instanceof Aware) {if (bean instanceof EnvironmentAware) {((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());}if (bean instanceof EmbeddedValueResolverAware) {((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);}if (bean instanceof ResourceLoaderAware) {((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);}if (bean instanceof ApplicationEventPublisherAware) {((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);}if (bean instanceof MessageSourceAware) {((MessageSourceAware) bean).setMessageSource(this.applicationContext);}if (bean instanceof ApplicationContextAware) {((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);}}return bean;
}

2.2.2 代理包裹

能够处理代理的地方不仅仅只有这一个扩展的地方,蛮多的~ 但是可以相信的是都是这个类的子类!

代码地址: org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor
这里就是针对性创建代理对象,比如async 异步注解的实现 org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {if (bean instanceof AopInfrastructureBean) {// Ignore AOP infrastructure such as scoped proxies.return bean;}if (bean instanceof Advised) {Advised advised = (Advised) bean;if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {// Add our local Advisor to the existing proxy's Advisor chain...if (this.beforeExistingAdvisors) {advised.addAdvisor(0, this.advisor);}else {advised.addAdvisor(this.advisor);}return bean;}}if (isEligible(bean, beanName)) {ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);if (!proxyFactory.isProxyTargetClass()) {evaluateProxyInterfaces(bean.getClass(), proxyFactory);}proxyFactory.addAdvisor(this.advisor);customizeProxyFactory(proxyFactory);return proxyFactory.getProxy(getProxyClassLoader());}// No proxy needed.return bean;
}

2.3 Aware 扩展

开发中有一个常见场景,一个不受Spring管理的对象,需要引用或获取Spring容器中的bean,我们需要先拿到ApplicationContext对象,然后调用getBean方法获取容器中的bean。那ApplicationContext又怎样获取呢,系统提供了一个扩展点ApplicationContextAware,实现了该接口在创建对象后会把上下文通过接口定义的set方法传给使用方。根据上面的方法很明显的知道了实现原理。arthas-idea 插件中的Static Spring context invoke method field 就是通过获取静态的ApplicationContext 实现getBean的。其实这个也是一种BeanPostProcessor的变体。

import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;/*** 提供给arthas ognl 获取context的信息** @author 汪小哥* @date 30-28-2020*/
@Component
public class ApplicationContextProvider implements ApplicationContextAware {private static ApplicationContext context;public ApplicationContext getApplicationContext() {return context;}@Overridepublic void setApplicationContext(ApplicationContext ctx) {context = ctx;}
}

2.4 生命周期初始化

2.4.1 InitializingBean && @PostConstruct

对象初始化接口,该接口有一个afterPropertiesSet方法,对象初始化&属性注入之后调用方法
实现类: InitDestroyAnnotationBeanPostProcessor 处理 @PostConstruct

// org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#postProcessBeforeInitialization
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());try {metadata.invokeInitMethods(bean, beanName);}catch (InvocationTargetException ex) {throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());}catch (Throwable ex) {throw new BeanCreationException(beanName, "Failed to invoke init method", ex);}return bean;
}

2.4.2 DisposableBean && @PreDestroy

有初始化接口必然就有销毁接口, 扩展点会在对象被销毁时调用。
DestructionAwareBeanPostProcessor -> BeanPostProcessor

将此BeanPostProcessor应用于给定的bean实例,然后再对其进行销毁,例如调用自定义销毁回调。与DisposableBean的{@code destroy}和自定义的destroy方法一样,此回调将只应用于容器完全管理其生命周期的Bean。
实现类: InitDestroyAnnotationBeanPostProcessor

// org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#postProcessBeforeDestruction
@Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());try {metadata.invokeDestroyMethods(bean, beanName);}catch (InvocationTargetException ex) {String msg = "Invocation of destroy method failed on bean with name '" + beanName + "'";if (logger.isDebugEnabled()) {logger.warn(msg, ex.getTargetException());}else {logger.warn(msg + ": " + ex.getTargetException());}}catch (Throwable ex) {logger.error("Failed to invoke destroy method on bean with name '" + beanName + "'", ex);}
}

2.5 生命周期小例子

/*** 生命周期小例子** @author 汪小哥* @date 22-06-2020*/
@Slf4j
public class TestLifeService implements InitializingBean, DisposableBean, ApplicationContextAware {@PostConstructpublic void postConstruct() {log.info("postConstruct");}@PreDestroypublic void preDestroy() {log.info("preDestroy");}@Overridepublic void destroy() throws Exception {log.info("destroy");}@Overridepublic void afterPropertiesSet() throws Exception {log.info("afterPropertiesSet");}public void initMethod() {log.info("initMethod");}public void destroyMethod() {log.info("destroyMethod");}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {log.info("applicationContextAware");}
}
@Configuration
public class TestLifeConfiguration {@Bean(initMethod = "initMethod", destroyMethod = "destroyMethod")public TestLifeService testLifeService() {return new TestLifeService();}
}//  1  : applicationContextAware --> ApplicationContextAware
//  2  : postConstruct--------->@PostConstruct
//  3  : afterPropertiesSet--------->InitializingBean#afterPropertiesSet
//  4  : initMethod--------->@Bean(initMethod)
//  5  : preDestroy--------->@PreDestroy
//  6  : destroy--------->DisposableBean#destroy
//  7  : destroyMethod--------->@Bean(destroyMethod)

2.6 扩展点流程

org.springframework.context.support.AbstractApplicationContext#refresh
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
你会找到答案的~ 跟踪代码更好学习
在这里插入图片描述

1、收集BeanDefintion;

从xml 或者注解中收集到的BeanDefintion,从这里开始 org.springframework.context.support.AbstractApplicationContext#refresh

2、postProcessBeanDefinitionRegistry

spring 扩展点 手动注册 BeanDefintion 到 BeanDefinitionRegistry

例子 手动注册Bean
ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry

RootBeanDefinition iabpp = new RootBeanDefinition(ImportAwareBeanPostProcessor.class);
iabpp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(IMPORT_AWARE_PROCESSOR_BEAN_NAME, iabpp);

3、postProcessBeanFactory;

BeanFactoryPostProcessor是针对容器的扩展点,加载BeanDefinition之后容器就会触发该扩展点,实现了该接口的扩展点可以修改元信息也就是BeanDefinition对象中的信息,spring 的占位符替换就是这样的一种开箱即可使用的方案,在bean 还没有初始化的时候就将数据中特殊标记熟悉进行占位符的替换org.springframework.beans.factory.config.PropertyResourceConfigurer
代码: BeanFactoryPostProcessor#postProcessBeanFactory;

4、doGetBean;

常见的applicationContext.getBean 这个是一切实例创建的起点,最终调用方法doGetBean
创建Bean的实例,后续的都是和 Bean实例相关,比如常见的实例代理、标记等等的实现都在实例扩展
BeanFactoryPostProcessor 容器扩展相关,BeanPostProcessor 实例扩展相关
代码: org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

5、postProcessBeforeInstantiation;

这里的回调是都还没有没有实例化,也就是还没有反射初始化。
如果返回对象,直接结束Bean的创建
在目标bean被实例化之前应用这个BeanPostProcessor。
返回的bean对象可以是要使用的代理,而不是目标bean,从而有效地抑制了目标bean的默认实例化。
InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation(Class<?> beanClass, String beanName)

5、doCreateBean;

创建Bean的实例,进行生命周期的初始化回调

6、createBeanInstance;

创建Bean的实例,进行生命周期的初始化回调
AbstractAutowireCapableBeanFactory#createBeanInstance

7、determineCandidateConstructors;

在createBeanInstance 内部 寻找构造函数的一个扩展
SmartInstantiationAwareBeanPostProcessor#determineCandidateConstructors

8、postProcessMergedBeanDefinition;

看名字合并bean的定义信息,运行时合并bean定义的后处理器回调接口。
The postProcessMergedBeanDefinition method may for example introspect the bean definition in order to prepare some cached metadata before post-processing actual instances of a bean.
1、在后处理bean的实际实例之前准备一些缓存的元数据
2、允许修改bean定义,但仅限于实际用于并发修改的定义属性(只能修改 RootBeanDefinition)
AutowiredAnnotationBeanPostProcessor 的实现就是利用上诉两个属性

MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition

9、getEarlyBeanReference;

addSingletonFactory 将当前的实例创建到提前初始化的工厂里面,singletonFactories 插入ObjectFactory 这个也是循环依赖的解决方案 doGetBean 获取singletonObjects->earlySingletonObjects->singletonFactories
singletonFactories获取到了将earlySingletonObjects插入。如果被引用直接返回一个代理对象getEarlyBeanReference

SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference

10、populateBean;

这里将进行属性填充

11、postProcessAfterInstantiation;

populateBean内部还有一个是否进行属性填充的扩展,返回一个boolean
InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation

12、MutablePropertyValues;

populateBean内部 收集autowireByName、type的值放值在MutablePropertyValues
容器中,这个时候还没有注入到Bean中

13、postProcessPropertyValues;

populateBean内部 给定属性值应用于给定bean之前,对其进行后处理。允许检查是否满足所有依赖项,例如基于bean属性设置器上的“必需”注释。还允许替换要应用的属性值,通常是通过基于原始属性值创建新的MutablePropertyValues实例,添加或删除特定值。
AutowiredAnnotationBeanPostProcessor 也是在这个时候基于之前收集的元数据进行注入属性值
InstantiationAwareBeanPostProcessor#postProcessPropertyValues

14、applyPropertyValues;

populateBean内部 将收集到的MutablePropertyValues
注入到bean的实例中

15、initializeBean 填充后初始化Bean的生命周期;

  • invokeAwareMethods 特殊的Aware

BeanNameAware
BeanClassLoaderAware
BeanFactoryAware

  • 标记接口(ohter Aware)、代理等等处理都行

在invokeInitMethods之前的回调。
BeanPostProcessor#postProcessBeforeInitialization
@PostConstruct、

  • invokeInitMethods 由此 @PostConstruct、在初始化方法之前

InitializingBean、invokeCustomInitMethod

  • 初始化方法之后的回调,这里也可以返回代理

BeanPostProcessor#postProcessAfterInitialization

16、检查是否已经提前初始化;

earlySingletonReference 将这个对象作为最后的值返回。
让之前的引用保持一个

17、registerDisposableBeanIfNecessary;

处理最后的销毁

三、spring 中的一些概念

如何理解BeanDefinition?

顾名思义,BeanDefinition是用来描述一个Bean的,Spring会根据BeanDefinition来生成一个Bean。

对象和Bean的区别?

所谓的bean也是一个java对象,只不过这个对象是通过spring定义的,早期的版本JavaBean xml 中管理 bean 所以叫做bean。

普通对象和Bean对象还有其他区别,因为Bean对象是由Spring生成的,Spring在生成Bean对象的过程中,会历经很多其他步骤,比如属性注入,aop,new实例,调用初始化方法。

BeanFactory和FactoryBean的区别

BeanFactory

BeanFactory是Spring IOC容器的顶级接口,其实现类有XMLBeanFactory,DefaultListableBeanFactory以及AnnotationConfigApplicationContext等。BeanFactory为Spring管理Bean提供了一套通用的规范。可以通过BeanFactory获得Bean。

FactoryBean

FactoryBean首先也是一个Bean,但不是简单的Bean,而是一个能生产对象的工厂Bean,可以通过定义FactoryBean中的getObject()方法来创建生成过程比较复杂的Bean。比如常见的使用场景 代理AOP的实现,dubbo 消费者实现等等。

如何理解BeanDefinitionRegistry和BeanFactory?

BeanFactory表示Bean工厂,可以利用BeanFactory来生成bean。
BeanDefinitionRegistry表示BeanDefinition的注册表,可以用来添加或移除BeanDefinition。

如何理解@Import与ImportBeanDefinitionRegistrar?

通常我们实现EnableXXXX 都是通过@Import 跟上某个特殊的接口。
@EnableAsync、@EnableConfigurationProperties 等等 都是通过 @Configuration 在 BeanDefinitionRegistryPostProcessor 扩展中注册Bean定义~ 然后间接了实现了部分的ImportBeanDefinitionRegistrar 这种特殊的接口的特殊处理,带来了一些选择性 ,比如@EnableAsync 选择哪种代理类型、@EnableConfigurationProperties 注册多个Bean的定义。

Import注解

@Import首先是一个注解,在Spring中是用来向Spring容器中导入Bean的。换个角度理解,就是我们一般都是通过在某个类上加上@Component注解来标志一个bean的,但如果我们希望以一种更灵活的方式去定义bean的话,就可以利用@Import注解。
@Import注解所指定的类,在Spring启动过程中会对指定的类进行判断,判断当前类是不是实现了比较特殊的接口,比如ImportBeanDefinitionRegistrar,如果存在特殊的接口就执行特殊的逻辑,如果没有则生成该类对应的BeanDefinition并放入BeanFactory中。

ImportBeanDefinitionRegistrar

通过Import注解可以注册bean,虽然它也支持同时注册多个bean,但是不方便,特别是当我们想通过实现一些复杂逻辑来注册bean的话,仅仅通过Import注解是不方便的,这时就可以使用ImportBeanDefinitionRegistrar这个接口来动态的注册bean了,我这里说的注册bean指的是:通过生成BeanDefinition,并且把BeanDefinition放入BeanFactory中。

四、看完之后需要理解的几个问题

循环依赖如何解决的?

https://blog.csdn.net/chejinqiang/article/details/80003868

有哪几个地方可以返回代理,代理如何处理的?

  • 在实例化之前返回代理对象org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation
Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException;
  • 循环依赖处理返回代理对象org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference
Object getEarlyBeanReference(Object bean, String beanName) throws BeansException;
  • 正常的后置处理器返回代理对象
org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitializationorg.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization

BeanFactoryPostProcessor and BeanPostProcessor

BeanFactoryPostProcessor是针对容器的扩展点,加载BeanDefinition之后容器就会触发该扩展点,实现了该接口的扩展点可以修改元信息也就是BeanDefinition对象中的信息。
BeanPostProcessor 是实例的扩展点,(在熟悉填充完成之后,对于实例的扩展),但是这个不是绝对的哦~ 具体看上面的流程。
在这里插入图片描述

手动编程如何注入一个Bean?

https://blog.csdn.net/qq_22076345/article/details/105892077 上面讲解扩展点的时候说dubbo 手动注册bean ,这个是理解spring的一个核心,各种各样的框架对于spring的扩展都是基于这个,那这个什么时机好呢?BeanDefinitionRegistryPostProcessor 这个不能再好了。
**

接着上一个问题 如何注入一个Bean,@Configuration 如何实现?

@Configuration 是一个非常重要的注解 不光是@Bean @Enable… @PropertySource @ComponentScan @ImportResource 等等的实现,其实也就是 BeanDefinitionRegistryPostProcessor 扩展点中去处理的,然后通过BeanDefinitionRegistry.registerBeanDefinition bean 实现,然后注入到BeanDefinitionRegistry bean 定义的元数据仓库中,具体可以查看,那么对于好舒服、dubbo、nacos等等注解处理原理理解还难? org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass
这里需要理解好,实例的创建,和 BeanDefinition 信息的构造两个问题。切记: 不要违反spring的处理原则,在BeanFactoryPostProcessor 实例化bean。

@PropertySource 和 PropertyResourceConfigurer

上面的问题@Configuration 实现了这么多逻辑,那么 @PropertySource和 PropertyResourceConfigurer熟悉值替换冲突?回答是不冲突的~
BeanDefinitionRegistryPostProcessor 继承了 BeanFactoryPostProcessor,允许在常规BeanFactoryPostProcessor检测开始之前注册更多的bean定义,PropertyResourceConfigurer 继承了 BeanFactoryPostProcessor ,由于 BeanDefinitionRegistryPostProcessor 优先于父类,因此这里的收集的环境属性优先于 PropertyResourceConfigurer 处理环境变量值的替换。

五、参考

Spring循环依赖及解决方式
Spring BeanWrapper分析
Spring 手动注册bean
Spring - lookup-method方式实现依赖注入
Spring生成bean的过程
Spring扩展点总结

六、总结

spring 代码较多,看着也比较头疼,可能看着看着就忘了… 毕竟开发框架、中间件 扩展的机会还是比较少,但是对于spring的使用还是必须铭记于心,本文主要是给自己总结~没事的时候回来看看。–2020-06-22 于杭州

这篇关于用了这么多年的Spring 你还记得?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1091561

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定