spring 5.0.8
最新5.2.4
注解版
文章目录
AnnotationConfigApplicationContext actx
=new AnnotationConfigApplicationContext(Config.class);
HelloWorld helloWorld = actx.getBean(HelloWorld.class);
helloWorld.sayHello("hello");
类关系图
初始化流程分析
AnnotationConfigApplicationContext
可以看出,先调用了一个静态代码块:
static {
// Eagerly load the ContextClosedEvent class to avoid weird classloader issues
// on application shutdown in WebLogic 8.1. (Reported by Dustin Woods.)
//就把这个注释翻译以下呗 就是说要饿汉式的加载关闭事件去防止奇怪的类加载器事件在应用关闭的时候
ContextClosedEvent.class.getName();
}
那就来看看呗,这个ContextClosedEvent是个啥?可咱初学者,咱也看不懂啊?
ContextClosedEvent的作用是什么?
可见是属于事件监听的一类,这里就不深揪了,暂且估计是触发相应事件;继续看AnnotationConfigApplicationContext的构造方法:
//根据参数类型可以知道,其实可以传入多个annotatedClasses,但是这种情况出现的比较少
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
//调用无参构造函数,会先调用父类GenericApplicationContext的构造函数
//父类的构造函数里面就是初始化DefaultListableBeanFactory,并且赋值给beanFactory
//本类的构造函数里面,初始化了一个读取器:AnnotatedBeanDefinitionReader read,一个扫描器ClassPathBeanDefinitionScanner scanner
//scanner的用处不是很大,它仅仅是在我们外部手动调用 .scan 等方法才有用,常规方式是不会用到scanner对象的
this();
//把传入的类进行注册,这里有两个情况,
//传入传统的配置类
//传入bean(虽然一般没有人会这么做
//看到后面会知道spring把传统的带上@Configuration的配置类称之为FULL配置类,不带@Configuration的称之为Lite配置类
//但是我们这里先把带上@Configuration的配置类称之为传统配置类,不带的称之为普通bean
register(annotatedClasses);
//刷新
refresh();
}
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
//注解bean定义读取器,主要作用是用来读取被注解的了bean
private final AnnotatedBeanDefinitionReader reader;
//扫描器,它仅仅是在我们外部手动调用 .scan 等方法才有用,常规方式是不会用到scanner对象的
private final ClassPathBeanDefinitionScanner scanner;
/**
* Create a new AnnotationConfigApplicationContext that needs to be populated
* through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
*/
//this
public AnnotationConfigApplicationContext() {
//会隐式调用父类的构造方法,初始化DefaultListableBeanFactory
//初始化一个Bean读取器
this.reader = new AnnotatedBeanDefinitionReader(this);
//初始化一个扫描器,它仅仅是在我们外部手动调用 .scan 等方法才有用,常规方式是不会用到scanner对象的
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
}
this()
这是一个有参的构造方法,可以接收多个配置类,不过一般情况下,只会传入一个配置类。
这个配置类有两种情况,一种是传统意义上的带上@Configuration注解的配置类,还有一种是没有带上@Configuration,但是带有@Component,@Import,@ImportResouce,@Service,@ComponentScan等注解的配置类,在Spring内部把前者称为Full配置类,把后者称之为Lite配置类。有些地方也把Lite配置类称为普通Bean。
AnnotationConfigApplicationContext类是有继承关系的,会隐式调用父类的构造方法:
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
private final DefaultListableBeanFactory beanFactory;
public GenericApplicationContext() {
//就是初始化了一个DefaultListableBeanFactory
this.beanFactory = new DefaultListableBeanFactory();
}
}
DefaultListableBeanFactory是相当重要的,从字面意思就可以看出它是一个Bean的工厂,什么是Bean的工厂?当然就是用来生产和获得Bean的。
接着看:
AnnotatedBeanDefinitionReader
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
this(registry, getOrCreateEnvironment(registry));
}
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
Assert.notNull(environment, "Environment must not be null");
this.registry = registry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
再进入AnnotationConfigUtils
public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
registerAnnotationConfigProcessors(registry, null);
}
这又是一个门面方法,再点进去,这个方法的返回值Set<BeanDefinitionHolder>,但是上游方法并没有去接收这个返回值,所以这个方法的返回值也不是很重要了,当然方法内部给这个返回值赋值也不重要了。由于这个方法内容比较多,这里就把最核心的贴出来,这个方法的核心就是注册Spring内置的多个Bean:
public static final String CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME =
"org.springframework.context.annotation.internalConfigurationAnnotationProcessor";
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
判断容器中是否已经存在了ConfigurationClassPostProcessorBean
如果不存在(当然这里肯定是不存在的),就通过RootBeanDefinition的构造方法获得ConfigurationClassPostProcessor的BeanDefinition,RootBeanDefinition是BeanDefinition的子类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
当然这里注册其他Bean也是一样的流程。
BeanDefinition是什么,顾名思义,它是用来描述Bean的,里面存放着关于Bean的一系列信息,比如Bean的作用域,Bean所对应的Class,是否懒加载,是否Primary等等,这个BeanDefinition也相当重要,我们以后会常常和它打交道。
registerPostProcessor方法:
private static BeanDefinitionHolder registerPostProcessor(
BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(beanName, definition);
return new BeanDefinitionHolder(definition, beanName);
}
这方法为BeanDefinition设置了一个Role,ROLE_INFRASTRUCTURE代表这是spring内部的,并非用户定义的,然后又调用了registerBeanDefinition方法,再点进去,Oh No,你会发现它是一个接口,没办法直接点进去了,首先要知道registry实现类是什么,那么它的实现是什么呢?答案是DefaultListableBeanFactory:
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
this.beanFactory.registerBeanDefinition(beanName, beanDefinition);
}
这又是一个门面方法,再点进去,核心在于下面两行代码:
//beanDefinitionMap是Map<String, BeanDefinition>,
//这里就是把beanName作为key,ScopedProxyMode作为value,推到map里面
this.beanDefinitionMap.put(beanName, beanDefinition);
//beanDefinitionNames就是一个List<String>,这里就是把beanName放到List中去
this.beanDefinitionNames.add(beanName);
从这里可以看出DefaultListableBeanFactory就是我们所说的容器了,里面放着beanDefinitionMap,beanDefinitionNames,beanDefinitionMap是一个hashMap,beanName作为Key,beanDefinition作为Value,beanDefinitionNames是一个集合,里面存放了beanName。打个断点,第一次运行到这里,监视这两个变量:
DefaultListableBeanFactory中的beanDefinitionMap,beanDefinitionNames也是相当重要的,以后会经常看到它,最好看到它,第一时间就可以反应出它里面放了什么数据
这里仅仅是注册,可以简单的理解为把一些原料放入工厂,工厂还没有真正的去生产。
上面已经介绍过,这里会一连串注册好几个Bean,在这其中最重要的一个Bean(没有之一)就是BeanDefinitionRegistryPostProcessor Bean。
ConfigurationClassPostProcessor实现BeanDefinitionRegistryPostProcessor接口,BeanDefinitionRegistryPostProcessor接口又扩展了BeanFactoryPostProcessor接口,BeanFactoryPostProcessor是Spring的扩展点之一,ConfigurationClassPostProcessor是Spring极为重要的一个类,必须牢牢的记住上面所说的这个类和它的继承关系。
BeanPostProcessor接口也是Spring的扩展点之一。
至此,实例化AnnotatedBeanDefinitionReader reader分析完毕。
register()
把目光回到最开始,再分析第二行代码:
register(annotatedClasses);
public void register(Class<?>... annotatedClasses) {
Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
this.reader.register(annotatedClasses);
}
public void register(Class<?>... annotatedClasses) {
for (Class<?> annotatedClass : annotatedClasses) {
registerBean(annotatedClass);
}
}
<T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
@Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
//AnnotatedGenericBeanDefinition可以理解为一种数据结构,是用来描述Bean的,这里的作用就是把传入的标记了注解的类
//转为AnnotatedGenericBeanDefinition数据结构,里面有一个getMetadata方法,可以拿到类上的注解
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
//判断是否需要跳过注解,spring中有一个@Condition注解,当不满足条件,这个bean就不会被解析
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
abd.setInstanceSupplier(instanceSupplier);
//解析bean的作用域,如果没有设置的话,默认为单例
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());
//获得beanName
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
//解析通用注解,填充到AnnotatedGenericBeanDefinition,解析的注解为Lazy,Primary,DependsOn,Role,Description
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
//限定符处理,不是特指@Qualifier注解,也有可能是Primary,或者是Lazy,或者是其他(理论上是任何注解,这里没有判断注解的有效性),如果我们在外面,以类似这种
//AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Appconfig.class);常规方式去初始化spring,
//qualifiers永远都是空的,包括上面的name和instanceSupplier都是同样的道理
//但是spring提供了其他方式去注册bean,就可能会传入了
if (qualifiers != null) {
//可以传入qualifier数组,所以需要循环处理
for (Class<? extends Annotation> qualifier : qualifiers) {
//Primary注解优先
if (Primary.class == qualifier) {
abd.setPrimary(true);
}
//Lazy注解
else if (Lazy.class == qualifier) {
abd.setLazyInit(true);
}
//其他,AnnotatedGenericBeanDefinition有个Map<String,AutowireCandidateQualifier>属性,直接push进去
else {
abd.addQualifier(new AutowireCandidateQualifier(qualifier));
}
}
}
for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
customizer.customize(abd);
}
//这个方法用处不大,就是把AnnotatedGenericBeanDefinition数据结构和beanName封装到一个对象中
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
//注册,最终会调用DefaultListableBeanFactory中的registerBeanDefinition方法去注册,
//DefaultListableBeanFactory维护着一系列信息,比如beanDefinitionNames,beanDefinitionMap
//beanDefinitionNames是一个List<String>,用来保存beanName
//beanDefinitionMap是一个Map,用来保存beanName和beanDefinition
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}
在这里又要说明下,以常规方式去注册配置类,此方法中除了第一个参数,其他参数都是默认值。
-
通过AnnotatedGenericBeanDefinition的构造方法,获得配置类的BeanDefinition,这里是不是似曾相似,在注册ConfigurationClassPostProcessor类的时候,也是通过构造方法去获得BeanDefinition的,只不过当时是通过RootBeanDefinition去获得,现在是通过AnnotatedGenericBeanDefinition去获得。
-
判断需不需要跳过注册,Spring中有一个@Condition注解,如果不满足条件,就会跳过这个类的注册。
-
然后是解析作用域,如果没有设置的话,默认为单例。
-
获得BeanName。
-
解析通用注解,填充到AnnotatedGenericBeanDefinition,解析的注解为Lazy,Primary,DependsOn,Role,Description。
-
限定符处理,不是特指@Qualifier注解,也有可能是Primary,或者是Lazy,或者是其他(理论上是任何注解,这里没有判断注解的有效性)。
-
把AnnotatedGenericBeanDefinition数据结构和beanName封装到一个对象中(这个不是很重要,可以简单的理解为方便传参)。
-
注册,最终会调用DefaultListableBeanFactory中的registerBeanDefinition方法去注册:
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { //获取beanName // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); //注册bean registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); //Spring支持别名 // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
这个registerBeanDefinition是不是又有一种似曾相似的感觉,没错,在上面注册Spring内置的Bean的时候,已经解析过这个方法了,这里就不重复了,此时,让我们再观察下beanDefinitionMap beanDefinitionNames两个变量,除了Spring内置的Bean,还有我们传进来的Bean,这里的Bean当然就是我们的配置类了;
到这里注册配置类也分析完毕了。
大家可以看到其实到这里,Spring还没有进行扫描,只是实例化了一个工厂,注册了一些内置的Bean和我们传进去的配置类,真正的大头是在第三行代码;
##refresh()
https://www.cnblogs.com/leeSmall/p/10188408.html
org.springframework.context.support.AbstractApplicationContext.refresh()方法的处理流程
d)beanfactory中getBean()时的创建实例流程
BeanFactoryPostProcessor在实例化bean之前对BeanFactory进行处理的
BeanPostProcessor在bean实例化后,对bean进行处理的
这两个类用了观察者模式
AbstractApplicationContextrefresh模板方法模式
执行优先级:priorityOrded>orded
Bean的生命周期
https://www.jianshu.com/p/00b29d8681ca
BeanDefinition
https://www.cnblogs.com/leeSmall/p/10134307.html
依赖注入DI
https://www.cnblogs.com/leeSmall/p/10228771.html
属性循环依赖怎么处理?
a)Bean1和Bean2都是单例bean。
循环依赖成功
b)Bean1和Bean2一个是单例的,一个是原型的。
循环依赖成功
c)Bean1和Bean2都是原型的
循环依赖不成功
3)思考:为什么两个是原型时不能循环依赖,而单例时可以?
一个Bean的属性依赖另一个bean实例,注入的过程是否就是从BeanFactory中getBean(),再赋值给属性?
是,首先从Bean工厂里面获取依赖的bean,没有就创建
那为什么单例可以,原型不可以?
依据上面的逻辑,那就单例时可以getBean()获得依赖的bean实例,原型时不能,为什么?
再来回想一下单例bean和原型bean在BeanFactory中的区别:
单例Bean是缓存在BeanFactory中的,而原型Bean是不缓存的。
缓存和不缓存对于循环依赖的处理有什么不同呢?
思考一下创建Bean实例的过程:
先Bean1,创建Bean1的实例——>然后对其属性进行依赖注入处理——>依赖Bean2,从BeanFactorygetBean(“bean2”)——>创建Bean2的实例——>对Bean2实例的属性进行依赖注入处理——>依赖Bean1,从BeanFactory中获取Bean1的实例
缓存的就可以通过beanFactory的getBean()获得前一个Bean的实例。而如果不缓存的,则bean2实例依赖注入Bean1时,从BeanFactorygetBean()就会又创建一个Bean1的实例,如此会无限循环依赖创建下去。
再仔细想一下,对于单例bean的缓存有时机的要求吗?
有,一定要在进行属性依赖注入处理前缓存(暴露)到BeanFactory中。
如果有多个有参数的构造方法并且每个构造方法的参数列表里面都有要注入的属性,那userDaoJdbc会注入到哪里呢?
结果:会注入到只有一个参数的构造方法中,并且经过测试注入哪一个构造方法与构造方法的顺序无关
问题二:如果只有一个构造方法,但是有两个参数,一个是待注入的参数,另一个是其他类型的参数,那么这次注入可以成功吗?
结果:失败了,即使在costract-arg标签里面通过name属性指定要注入的参数名userDao也会失败.
问题三:如果我们想向有多个参数的构造方法中注入值该在配置文件中怎么写呢?
参考写法:通过name属性指定要注入的值,与构造方法参数列表参数的顺序无关。
<!-- 注册userService -->
<bean id="userService" class="com.lyu.spring.service.impl.UserService">
<constructor-arg name="userDao" ref="userDaoJdbc"></constructor-arg>
<constructor-arg name="user" ref="user"></constructor-arg>
</bean>
<!-- 注册实体User类,用于测试 -->
<bean id="user" class="com.lyu.spring.entity.User"></bean>
<!-- 注册jdbc实现的dao -->
<bean id="userDaoJdbc" class="com.lyu.spring.dao.impl.UserDaoJdbc"></bean>
问题四:如果有多个构造方法,每个构造方法只有参数的顺序不同,那通过构造方法注入多个参数会注入到哪一个呢?
结果:哪个构造方法在前就注入哪一个,这种情况下就与构造方法顺序有关。
setter注入
spring会将name值的每个单词首字母转换成大写,然后再在前面拼接上"set"构成一个方法名,然后去对应的类中查找该方法,通过反射调用,实现注入。
切记:name属性值与类中的成员变量名以及set方法的参数名都无关,只与对应的set方法名有关
springaop
https://www.cnblogs.com/leeSmall/p/10236553.html
Aspect=Advice+Pointcut
AspectJ注解的切面在哪里读取加载的?
可能的地方:
BeanDefinitionRegistryPostProcessor x
BeanFactoryPostProcessor x
InstantiationAwareBeanPostProcessor Bean 实例创建前后
BeanPostProcessor
Xml方式的解析:
org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator
注解方式的解析:
org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
两者的关系:
public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator
org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors()
a) 在哪里做的织入?
在org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors()里面打个断点拿到调用栈
入口:
com.study.leesmall.spring.sample.aop.AopMainUseAspectAnnotation
b) 如何判断 Bean 要不要被创建代理?如何排除 advice Bean 的?
排除 advice Bean :
跳过advice bean 跳过带有@Aspect注解的自己,不能自己为自己创建代理,否则进入死循环,如AspectAdviceBeanUseAnnotation
org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator.shouldSkip(Class<?>, String)
c)如何选择 jdk动态代理 还是cglib的动态代理
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.createProxy(Class<?>, String, Object[], TargetSource)
org.springframework.aop.framework.ProxyFactory.getProxy(ClassLoader)
org.springframework.aop.framework.ProxyCreatorSupport.createAopProxy()
org.springframework.aop.framework.DefaultAopProxyFactory.createAopProxy(AdvisedSupport)
d) 如何来创建代理的,涉及哪些类,如何协作的。
AutoProxyCreator
ProxyConfig ProcxyFactory
AopProxyFactory
AopProxy
a) 在代理中如何决定对当前方法合格的 advice 的?
调用的 Advisor 中 Pointcut 进行匹配
b) 如何组织多个 advice 执行的?
责任链模式
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(Object, Method, Object[])
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed()
事务管理
https://www.cnblogs.com/leeSmall/p/10306672.html
@EnableTransactionManagement
<!-- 配置事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<!-- 配置初始化大小、最小、最大连接数 -->
<property name="initialSize" value="1" />
<property name="minIdle" value="1" />
<property name="maxActive" value="10" />
</bean>
<!-- 配置事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- ********************** 声明式事务配置 begin ************** -->
<!-- 配置事务增强的advice -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!-- all methods starting with 'get' are read-only -->
<tx:method name="get*" read-only="true" />
<!-- other methods use the default transaction settings (see below) -->
<tx:method name="*" />
</tx:attributes>
</tx:advice>
<!-- 配置事务的AOP切面 -->
<aop:config>
<aop:pointcut id="allService" expression="execution(* com.study.leesmall.spring.sample.tx.service.*Service.*(..)))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="allService"/>
</aop:config>