spring笔记
单例池
使用concurrenthashmap
@PostConstruct
遍历方法判断是否有该注解,执行注解的方法
初始化
继承InitializingBean,初始化时判断Bean是否是这种类型的,强转执行注解的方法
初始化和实例化
初始化如上,实例化执行构造方法
初始化后
可用于实现AOP,在放入单例池前执行
推断构造方法
多种构造方法执行无参构造方法,或@Autowire指定的构造方法,多种构造方法不指定也无无参,报错
AOP底层
JDK动态代理和cjlib动态代理
在代理对象中设置一个目标对象的引用,代理对象在重写方法中,用target调用目标对象的方法
事务底层
- 代理对象在重写方法中,先判断是否有@Transcational注解
- 有的话创建事务管理器,事务管理器新建一个数据库连接ThreadLocal<DateSource,conn>,同一个数据源拿到的是同一个连接
- 关闭连接的自动提交
- 调用普通方法中的jdbctemplate就使用这个连接
- 自动提交
事务失效
在代理对象重写的方法中调用目标对象的方法,目标对象的方法是没有事物的
可以注入自己
@Configuration
加了@Configuration首先是一个Bean,并且是一个配置Bean
加了该注解的类也是一个代理对象,使用cglib,但调用目标对象时使用的是super.方法名(),子类重写了父类方法,执行子类方法
在事务管理器和JdbcTemplate在@Bean中都要调用datasource(),如果不加@Configuration,则每次创建新对象,加了就会去单例池找,这样数据库连接关闭自动提交就能用到了
@Import导入的类是配置Bean
三级缓存
完整对象创建分两个步骤:实例化 + 初始化,只要初始化没进行完毕,就不完整
对象分三种类型:刚实例化的普通对象(放在三级缓存),提前进行AOP的不完整对象(放在二级缓存),完整对象(一级缓存/单例池)
循坏依赖带来了什么问题?重复创建,如何解决?第三级缓存通过存储刚创建的实例化普通对象来打破循环,但循环依赖需要的是完成了AOP的代理对象,所以将三级缓存中的对象取出,提前进行AOP并放入二级缓存
那为什么要二级缓存?因为需要区分“已进行AOP的不完整对象”和“完整的代理对象”,并防止多次AOP(多个循环依赖叠加可导致)
1.A创建将() -> getEarlyBeanReference(beanName, mbd, bean)表达式放入三级缓存singletonFactories
getEarlyBeanReference(beanName, mbd, bean)首先得到一个cachekey,cachekey就是beanName。然后把beanName和bean(这是原始对象)存入earlyProxyReferences中
调用wrapIfNecessary进行AOP,得到一个代理对象。
2.A属性注入B,需要创建B
3.B创建时属性注入需要A,从三级缓存拿出表达式执行,将A的执行完AOP的代理对象放入二级缓存earlySingletonObjects中,B属性注入注入的时A的代理对象,只是未初始化完,和A最后放入单例池的是同一个对象
4.A接着生命周期,由于已经AOP因此不用再AOP,利用earlyProxyReferences判断是否已经AOP,在BeanPostProcessor的执行之后,就需要把A对应的对象放入singletonObjects中了
**注意:**earlyProxyReferences中的对象还未AOP
三级缓存用hashmap
@Async
BeanPostProcessor:利用earlyProxyReferences判断是否已经AOP,根据结果返回bean
不出现循环依赖传入的bean和返回的bean不同
出现循环依赖传入的bean和返回的bean不同(不会再AOP,直接返回)
如果添加@Async,会多一个BeanPostProcessor,将传入的bean进行AOP,导致有两个代理对象,报错
@Lazy
在属性注入上添加@Lazy,这样就不会有循环依赖,不会有两个代理对象
@Lazy是将属性生成一个代理对象赋值,在使用时再去单例池找
构造方法的循环依赖
- 属性注入循环依赖
1 |
|
1 |
|
- 构造方法循环依赖,直接卡在创建普通对象,解决办法@Lazy,这样参数就能使用代理对象注入
1 |
|
1 | @Component |
- 构造方法循环依赖加多例会导致不停循环创建
@Vaule
- @Value(“${key}”),赋值配置文件,环境变量等中的值,不存在直接将字符串赋值
- @Value(“key}”),赋值字符串
- @Value(“#{key}”),类似@Autowire,@Resource的名字注入
- 自定义注解,避免耦合
@Bean
1 | @Bean(autowire = Autowire.BY_TYPE) |
- 使用到这个bean时利用bean的set方法进行自动注入
1 | @Bean(autowireCandidate = false) |
- 不能作为候选bean,不会用来注入
- 可以单独使用
- 自定义注解,加在自己的注解上,避免重复写
1 | @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) |
**注意:**不和@Configration一起使用,调用类种方法生成对象时,会调用类中的方法,返回1个实例对象,和@Configration一起使用会从单例池返回
加@Configration会生成一个代理对象,执行对应方法时查看对应方法的bena是否创建,创建就直接使用,否则创建放入单例池
@ComponentScan
注解参数
1 | @ComponentScan(value = "",includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,value = Zhouyu.class)) |
路径下的@Zhouyu注解也会被当作bean
扫描索引
在资源目录下创建META-INF/spring.components文件,key=value,key为类全类名,value为注解类型,有了这个文件,注解会失效
@Conditional
可以放在类或@Bean上
1 | @Conditional(value = {}) |
value存放Class<? extends Condition>数组
Condition类需要自定义类去实现Condition接口
通过matches返回true或false判断是否类需要加载
1 | public class Test implements Condition { |
@Autowire
放在属性上
1
2//这里使用的是unsafe直接将值填充到内存
unsafe.putObject(var1, this.fieldOffset, var2);放在set方法上(类似@PostConstruct,不过执行实际不同)
放在构造方法上
- 解决推断构造方法
- 属性注入
参数前(支持测试时注入)
自定义注解上,自己的注解会加上注解功能(可以加上一些参数)
注意:
- 不支持使用static,如果支持使用static会导致每次创建新的实例,static修饰的类对象会变化,其他实例由于用的时类对象,所以也会变化
- require属性表示是否必须有值
@Lazy
- 类上(创建bean的时机,创建Spring容器时或getBean时创建)
- 属性上,会先赋值一个代理对象,直到使用时才去单例池中找
- 方法上(加了@Autowire的set方法上,和属性上类似,先创建代理对象赋值)
- 参数上(同上类似)
- 构造方法上(可以解决循环依赖)
@Autowire和@Resource
JDK包中的@Resource,spring提供的@Autowire
1 | @Resource(name="") |
@Import
ImportSelector和DeferredImportSelector都是接口,自定义类去实现接口和相关方法,可以批量导入类
ImportBeanDefinitionRegistrar也是接口,可以重写相关方法,自己去注册bean
@Lookup
在抽象类中的方法上标注@Lookup,抽象类会创建一个代理bean,被标注@Lookup的抽象方法会返回value参数中的实例对象
@Primary
发现多个相同的bean,使用带有@Primary的bean,不能注解多个
注册一个bean的方式
FactoryBean
自定义类实现FactoryBean
会创建两个Bean,一个是ZhouyFactoryBean的bean,启动时创建,一个是UserService的bean,获取时创建
@Component不设置名字,getBean(“zhouyuFactoryBean”)会得到UserService的Bean
加一个& getBean(“&zhouyuFactoryBean”)获得ZhouyFactoryBean的bean
不会属性注入
SmartFactoryBean
可以设置spring启动时创建
依赖注入有哪些方式
- 自动注入,依赖@Bean里的autowire = Autowre.BY_NAME
获取ApplicationContext的方式
Bean的作用域
Spring类型有哪些转化方式
SpringMVC启动
tomcat启动
解析web.xml Listener中创建Spring容器(ServerletContext)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--2、部署applicationContext的xml文件-->
<!--如果在web.xml中不写任何参数配置信息,默认的路径是"/WEB-INF/applicationContext.xml,
在WEB-INF目录下创建的xml文件的名称必须是applicationContext.xml。
如果是要自定义文件名可以在web.xml里加入contextConfigLocation这个context参数:
在<param-value> </param-value>里指定相应的xml文件名,如果有多个xml文件,可以写在一起并以“,”号分隔。
也可以这样applicationContext-*.xml采用通配符,比如这那个目录下有applicationContext-ibatis-base.xml,
applicationContext-action.xml,applicationContext-ibatis-dao.xml等文件,都会一同被载入。
在ContextLoaderListener中关联了ContextLoader这个类,所以整个加载配置过程由ContextLoader来完成。-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/applicationContext.xml</param-value>
</context-param>DispatcherServerlet实例化化
DispatcherServerlet.init() 创建Spring容器
在web.xml中配置拦截器, listener,以及DispatcherServelet
web.xml中的Listener
用来创建父容器