ApplicationContext refresh的流程
12个步骤
-
prepareRefresh
这一步创建和准备了Environment对象,并赋值给了ApplicationContext的成员变量
要理解Environment对象的作用 -
obtainFreshBeanFactory
ApplicationContext 里面有一个成员变量,Beanfactory
bean的来源有 xml配置文件,配置类,扫描 -
prepareBeanFactory
-
postProcessBeanFactory
是一个空实现,留给子类实现的 -
invokeBeanFactoryPostProcessors
bean工厂后处理器 -
registerBeanPostProcessors
注册bean后处理器 对bean的创建过程中,各种功能的增强 -
initMessageSource
MessageSource实现国际化的 -
initApplicationEventMulticaster
应用事件广播器 -
onRefresh
空实现,留给子类实现 -
registerListeners
事件监听器 -
finishBeanFactoryInitialization
-
finishRefresh
spirng bean 生命周期
- 阶段一:处理名称,检查缓存
- 1.1 把别名解析成实际名称,再进行后续处理
- 1.2若要factoryBean本身,需要使用&名称获取
- 1.3singletonobjects是一级缓存,放单例成品对象
- 1.4singletonFactories是三级缓存,放单例工厂
- 1.5earlySingletonObjects是二级缓存,放单例工厂的成品,可称为提前单例对象
2.阶段二:检查父工厂
- 1.父子容器的bean名称可以重复
- 2.优先找子容器的bean,找到了直接返回,找不到继续到父容器找
3.阶段三:检查DependsOn
4.阶段四:按Scope 创建bean
* 1.创建singleton
* 2.创建prototype
* 3.创建其他scope
-
阶段五:创建bean
- 1.创建bean实例 @Autowired,唯一带参构造,默认构造
- 2.依赖注入 @Autowired @value@Resource,ByName ByType 精确指定
- 3.初始化- Aware接口处理,@PostConstruct,InitializingBean,initMethod
- 4.登记可注销bean
-
类型转换
-
销毁bean
spring 事务失效的几种场景及原因
1.抛出检查异常导致事务不能正确回滚
spring默认情况下,只对,runtimeException,和Error这两个异常及其子类会回滚。如果是检查异常,是不会回滚的
解决方案:
@Transactional(rollbackFor = Exception.class)
2.业务方法内自己try-cach 异常导致事务不能正确回滚
原因:事务通知只有捉到了目标抛出的异常,才能进行后续的回滚处理,如果目标自己处理掉异常,事务通知无法知悉
解决1:异常原样抛出
解决2:手动设置TransactionStatus.setRollbackOnly()
3.aop切面顺序导致事务不能正确回滚
原因:事务切面优先级最低,但如果自定义的切面优先级和他一样,则还是自定义切面在内层,这时若自定义切面没有正确抛出异常,那外层的事务切面没有办法感知异常。
解法:同情况2
4.@Transactional 一定要放在public的方法上,否则无效
原因:spring为方法创建代理、添加事务通知、前提条件都是该方法是public的
5.父子容器导致的事务失效
原因:子容器的扫描范围过大,把未加事务配置的service扫描进来
解法1:各扫描各的,不要图简便
解法2:不要用父子容器,所有bean放在同一个容器
6.调用本类方法导致传播行为失效
原因:本类方法调用不经过代理,因此无法增强
解法1:依赖注入自己(代理)来调用
解法2:通过AopContext拿到代理对象,来调用
解法3:通过CTW,LTW实现功能增强
- @Transactional没有保证原子行为
原因:事务的原子性仅涵盖insert,update,delete,select…for update语句,select方法并不阻塞
8.@Transactional方法导致的synchronized失效
原因:synchronized保证的是目标方法的原子性,环绕目标方法的还有commit等操作,他们并未处于sync块内
解法1:synchronized 范围阔大至代理方法调用
解法2:使用select…for update替换select
SpringMVC 执行流程
初始化阶段:
1.在Web容器第一次用到DispatcherServlet的时候,会创建其对象并执行init方法
2.init方法会创建Spring Web容器,并调用容器refresh方法
3.refresh过程中会创建并初始化SpringMVC中的重要组件,例如MultipartResolver,HandlerMapping,HandlerAdapter,HandleExceptionResolver,ViewResolver等
4.容器初始化后,会将上一步初始化好的重要组件,赋值给DispatcherServlet的成员变量,留待后用
匹配阶段:
1.用户发送的请求统一到达的前端控制器DispatcherServlet
2.DispatcherServlet遍历所有HandlerMapping,找到与路径匹配的处理器对象
- 1.HandlerMapping有多个,每个HandlerMapping会返回不同的处理器对象,谁先匹配,返回谁的处理器。其中能识别@RequestMapping的优先级最高
- 2.对应@RequestMapping的处理器是HandlerMethod,它包含了控制器对象和控制器方法信息
- 3.其中路径与处理器的映射关系在HandlerMapping初始化时就会建立好
3.将HandlerMethod连同匹配到拦截器,生成调佣链对象HandlerExecutionChain返回
4.遍历HandlerAdpter处理器适配器,找到能处理HandlerMethod的适配器对象,开始调用
执行阶段:
1.执行拦截器preHandle
2.由HandlerAdapter(适配器)调用HandlerMethod(处理器)
- 1.调用前处理不同类型的参数
- 2.调用后处理不同类型的返回值
3.第二步没有异常 - 1.返回ModelAndView
- 2.执行拦截器postHandle方法
- 3.解析视图,得到View对象,进行视图渲染
4.第2步有异常,进入HandlerExceptionResolver异常处理流程
5.最后都会执行拦截器的afterCompletion方法
6.如果控制器方法标注了@ResponseBody注解,则在第二步,就会生成json结果,并标记ModelAndView已处理,这样就不会执行第3步的试图渲染
spring注解大全
1.事务
@EnableTransactionManagement 启用声明式的事务
@Transactional
2.核心
@Order 多个bean控制执行顺序
3.切面
@EnableAspectAutoProxy 启用Aop自动代理
以下这些不是spring的注解,是第三方spring-aspects的注解
@Aspect //标记该类为切面类
@Before
@After
4.组件扫描和配置类的
@Component
@Controller
@Service
@Repository
@ComponentScan
@Conditional 组件扫描时,做条件判断 bean加载时,做条件判断
@Configuration
@Bean
@Import
@Lazy 标注在类上,表示类是延迟实例化和初始化的
@PropertySource 加载外部properties文件
5.缓存
@EnableCaching
@CacheConfig
@CacheEvict
@CachePut
@Cacheable
@Caching
6.依赖注入
@Autowired
@Qualifier 依赖注入时,同一类型有多个bean,可以用名字进行区分
@Value
7.mapping
@Mapping
@RequestMapping 建立请求路径和控制器方法的映射关系
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping
8.rest
@RequestBody 请求体里面的json数据,转换成java数据
@ResponseBody,将java数据,转换成json数据放到响应体
@ResponseStatus控制响应的状态
@RestController
是一个组合注解 组合了: @Controller 和 @ResponseBody
9.统一处理
@ControllerAdvice
@ExceptionHandler
@RestControllerAdvice
10.参数
@RequestHeader 请求头的信息
@CookieValue
@PathVariable 获取路径参数
@RequestParam 获取请求参数 ?后的,或者表单的
11.转换和格式化
@DateTimerFormat
@NumberFormat
@InitBinder
12.validation bean的校验
@Validated
13 scope
@ApplicationScope
@RequestScope
@SessionScope
@ModelAttribute
@RequestAttribute
@SessionAttribute
@SessionAttributes
14.ajax
@CrossOrigin 解决ajax的跨域问题
spring-boot注解
1.@EnableConfigurationProperties 启用
2.@ConfigurationProperties bean 值的初始化
3.@Configuration!!!!!!!!!!!!!!!!!!!!
注意:
-
- 配置类其实就相当于一个工厂。
-
- @bean注解的方法,就是工厂方法 @Bean,不支持方法重载,如果有多个重载方法,仅有一个能入选为工厂方法。参数越多,权重越高
@Bean
public Bean1 bean1(){
System.out.println("new bean2.....");
return new Bean1();
}
@Bean
public Bean1 bean1(@Value("${java.class.version}") String a){
System.out.println("a:"+a);
return new Bean1();
}
@Bean //这个参数有2个权重高
public Bean1 bean1(@Value("${java.class.version}") String a,@Value("${JAVA_HOME}") String b){
System.out.println("bena1 a:"+a+" b:"+b);
return new Bean1();
}
-
- 注意点3:@Configuration 默认会为标注的类生成代理,其目的是保证@Bean 方法互相调用时,仍然能保证其单例特性
-
4.注意点4:@Configuration中如果含有BeanFactory 后处理器,则实例方法会导致MyConfig提前创建,造成其依赖注入失败。
// 问题复现
public class TestConfiguration {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
context.registerBean("myConfig",Myconfig.class);
context.refresh();
System.out.println(context.getBean(Myconfig.class));
System.out.println();
}
@Configuration()
static class Myconfig{
@Bean
public Bean1 bean1(){
System.out.println("bean1().....");
return new Bean1();
}
@Bean
public Bean2 bean2(){
System.out.println("bean2().....");
return new Bean2();
}
@Value("${java.class.version}")
private String version;
@Bean // 这个实现了BeanFactory后处理器
public MapperScannerConfigurer configurer(){
MapperScannerConfigurer scanner = new MapperScannerConfigurer();
scanner.setBasePackage("aaa");
return scanner;
}
@Bean
public Bean3 bean3(){
System.out.println("Bean3():"+version); // 这个version无法获取了,因为提前有后处理器在这个Configuration类里面,所以这个Myconfig提前创建了,所以他的功能没有增强。@Value 这些增强没有了。
return new Bean3();
}
class Bean3{}
class Bean1{}
class Bean2{}
}
}
- 解决办法:
-
- bean工厂后处理器在配置类中定义,就用 static 修饰
-
- 如果想在@Bean修饰的方法依赖注入,用局部变量就可以,用参数注入,尽量不要用成员变量来注入
@Bean public Bean3 bean3(@Value("${java.class.version}") String version){ System.out.println("Bean3():"+version); // 这个version无法获取了,因为提前有后处理器在这个Configuration类里面,所以这个Myconfig提前创建了,所以他的功能没有增强。@Value 这些增强没有了。 return new Bean3(); }
-
15.@Import 放在 @Configuration配置类上
import5种用法
public class TestImport {
public static void main(String[] args) {
// 普通容器
GenericApplicationContext context = new GenericApplicationContext();
//后处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
//注册bean
context.registerBean(Myconfig.class);
//refresh
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
}
@Configuration
// @Import(bean1.class) // 1.引入单独bean
// @Import(OtherConfig.class) // 2.引入一个配置类
// @Import(MySelector.class) // 3.通过MySelector实现了引入多个类 ,MySelector本身不会注册成bean
// @Import(MyRegister.class) // 4.通过MyRegister注册器引入多个类,MyRegister本身不会注册成bean
@Import(MySelector2.class) // 5.通过MySelector2注册器实现了DeferredImportSelector引入多个类,MySelector2本身不会注册成bean
static class Myconfig{
}
static class MySelector2 implements DeferredImportSelector{
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{Bean3.class.getName(),Bean4.class.getName()};
}
}
static class MyRegister implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
registry.registerBeanDefinition("bean5", BeanDefinitionBuilder.genericBeanDefinition(Bean5.class).getBeanDefinition());
}
}
static class MySelector implements ImportSelector{
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{Bean3.class.getName(),Bean4.class.getName()};
}
}
@Configuration
static class OtherConfig{
@Bean
public Bean2 bean2(){
return new Bean2();
}
}
static class bean1{}
static class Bean2{}
static class Bean3{}
static class Bean4{}
static class Bean5{}
}
16.@Import-DeferredImportSelector
public class TestDeferredImport {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();
beanFactory.setAllowBeanDefinitionOverriding(false); // 不允许同名定义覆盖
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
context.registerBean(Myconfig.class);
context.refresh();
System.out.println(context.getBean(MyBean.class));
}
// 1.同一个配置类中,@Import 先解析 @Bean 后解析
// 2.同名定义,默认后面解析的会覆盖前面解析的
// 3.不允许覆盖的情况下,如何能让Myconfig(主配置类)的配置优先?(虽然覆盖方式能解决)
// 4.@Import 导入的类MyDeferredImportSelector 实现了DeferredImportSelector 最后工作,可以简单认为先解析@Bean,再解析@Import
@Configuration
@Import(MyDeferredImportSelector.class)
static class Myconfig{ // 主配置 -程序员自己配置的
@Bean
public MyBean myBean(){
return new Bean1();
}
}
static class MyDeferredImportSelector implements DeferredImportSelector{
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{OtherConfig.class.getName()};
}
}
@Configuration
static class OtherConfig{ // 从属配置 - 自动配置
@Bean
@ConditionalOnMissingBean
public MyBean myBean(){
return new Bean2();
}
}
interface MyBean{}
static class Bean1 implements MyBean {}
static class Bean2 implements MyBean {}
}
Springboot 自动配置
@SpringbootApplication 是一个组合注解这个注解包含下面这些
- @SpringBootConfiguration 标记了这个类其实就是个配置类,没什么特别的
- @ComponentScan 用来在组件扫描时进行排除,也会排除自动配置类
- @EnableAutoConfiguration是一个组合注解
- @AutoConfigurationPackage -用来记住扫描的起始包
- @Import(AutoConfigurationImportSelector.class)可以分离主从配置。用来加载META-INF/spring.factories中的自动配置类
Spring中有哪些设计模式
1.Spring中的Singleton Bean 是否是单例模式?
- spring中的singleton bean 并非实现了单例模式,singleton bean 只能保证每个容器内,想通的id的bean单例
- spirng中也有单例模式
2.Spring中的Builder 构建器模式
他的主要亮点有三处:
1.较为灵活的构建产品对象
2.在不执行最后的build方法前,产品对象都不可用
3.构建过程采用链式调佣,看起来比较爽
spring中体现Builder模式的地方:
- org.springframework.beans.factory.support.BeanDefinitionBuilder
- org.springframework.web.util.UriComponentsBuilder
- org.springframework.http.ResponseEntity.HeadersBuilder
- org.springframework.http.ResponseEntity.BodyBuilder
3.Spirng中的Factory Method 工厂方法模式
4.Spring 中的 Adapter 适配器模式
把一套接口转换成调用者所期望的接口
5.Spring 中的 Composite 组合器模式
6.spring 中的 Decorator 装饰器模式
对目标对象做功能增强,避免子类继承进行功能可扩展
7.spring中的 Proxy 代理模式
对目标对象的控制和访问
8.Spring 中的 Chain of Responsibility 责任链模式
拦截器
9.Spirng 中的 Observer 观察者模式
ApplicationListener 监听器
ApplicationEventMulticaster 发送器
ApplicationEvent 事件对象
10.Spring 中的Strategy 策略模式
11.Spring 中 的 Template Method 模板方法
- 大部分以Template 命名的类, 如jdbcTemplate TransactionTemplate
- 很多以 Abstract命名的类,如AbstractApplicationContext
创建代理要点:
- 要完全理解循环依赖,需要理解代理对象的创建时机
- 掌握proxyFactory创建代理的过程,理解Advisor,Advice,Ponitcut 与 Aspect
- 掌握AnnotationAwareAspectJAutoProxyCreator筛选Advisor合格者,创建代理的过程
proxyFactory的基本使用
总结: - Advisor 是最基本的切面,Aspect 切面对应一个或多个Advisor切面
- 最基本的Advice 是MethodInterceptor,其他的Advice最终都将适配为MethodInterceptor
- 创建代理的方式:
- 实现了用户自定义接口,采用jdk动态代理
- 没有实现用户自定义接口,采用cglib代理
- 设置了setProxyTargetClass(true),同意采用cglib代理
- 切面、切点、通知等不会被代理
- AnnotationAwareAspectJAutoProxyCreator 调用时机:创建阶段、依赖注入阶段、初始化阶段
// 编程方式
public class APP64_2 {
public static void main(String[] args) {
// aspect = (通知)advice + pointcut(切点),一个切面类可能有一个多个通知方法
// advisor = 更细粒度的切面,包含一个通知和切点
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(new Target1()); // 设置目标对象
// 添加通知:MethodInterceptor 是环绕通知
// proxyFactory.addAdvice((MethodInterceptor) invocation -> {
// try {
// System.out.println("befor...");
// return invocation.proceed(); // 调用目标
// } finally {
// System.out.println("atfer....");
// }
// });
// 设置切点
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
proxyFactory.addAdvisor(new DefaultPointcutAdvisor(pointcut, (MethodInterceptor) invocation -> {
try {
System.out.println("before1...");
return invocation.proceed(); // 调用目标
} finally {
System.out.println("after1...");
}
}));
proxyFactory.addAdvisor(new DefaultPointcutAdvisor(pointcut, (MethodInterceptor) invocation -> {
try {
System.out.println("before2...");
return invocation.proceed(); // 调用目标
} finally {
System.out.println("after2...");
}
}));
proxyFactory.addInterface(I1.class);
// proxyFactory.setProxyTargetClass(true);
I1 proxy = (I1) proxyFactory.getProxy(); // 创建代理对象
System.out.println(proxy.getClass());
proxy.foo();
proxy.bar();
}
interface I1{
void foo();
void bar();
}
static class Target1 implements I1{
@Override
public void foo() {
System.out.println("target1 foo");
}
@Override
public void bar() {
System.out.println("target1 bar");
}
}
}
// 使用注解的方式
package org.springframework.aop.framework.autoproxy;
public class APP64_1 {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("aspect1",Aspect1.class);
context.registerBean("aspect2",Aspect2.class);
context.registerBean("aspect3",Aspect3.class);
context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class); // 自动代理后处理器
context.refresh();
AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
// 这里是演示一下 自动代理后处理器 是怎么创建代理对象的
// wrapIfNecessary 是protected 为了实验可以成功,把这个当前这个类放在了同包下测试
Object o = creator.wrapIfNecessary(new Target1(), "target1", "target1");
System.out.println(o.getClass()); // 是代理类
Object b = creator.wrapIfNecessary(new Aspect1(), "aspect1", "aspect1");
System.out.println(b.getClass()); // Aspect1是aop的基础设施,所有就不用代理对象
}
@Aspect
static class Aspect1{
@Around("execution(* com.libin..autoproxy..foo())") // 对应成一个Advisor切面
public Object around(ProceedingJoinPoint pjp) throws Throwable {
try {
System.out.println("aspect1 around before");
return pjp.proceed();
} finally {
System.out.println("after....");
}
}
}
@Aspect
static class Aspect2{ // 对应成两个Advisor切面
@Before("execution(* foo())")
public void before() {
System.out.println("aspect2 before");
}
@After("execution(* foo())")
public void after() {
System.out.println("aspect2 after");
}
}
@Aspect
static class Aspect3{
@Before("execution(* bar())") // 对应成一个Advisor切面
public void around() {
System.out.println("aspect3 before...");
}
}
static class Target1 {
public void foo() {
System.out.println("target1 foo");
}
}
static class Target2 {
public void bar() {
System.out.println("target1 bar");
}
}
}
set循环依赖
一级缓存,限制bean 在BeanFactory中只存一份,实现 Singleton scope
循环依赖图示,只有一级缓存的情况下
二级缓存解决:但是解决不了循环依赖里面有代理的情况 。
a先生成半成品代理对象,后依赖注入,但是b注入的时候是半成品的a,不是成品的代理对象a
三级缓存
构造方法循环依赖
解决思路:
第一种方式解决构造方法循环依赖
public class App60_1 {
static class A{
private static final Logger log = LoggerFactory.getLogger("A");
private B b;
// 使用@Lazy 让B后面再加载使用代理方式
public A(@Lazy B b){
log.debug("A(B b){}",b.getClass());
this.b =b;
}
@PostConstruct
public void init(){
log.debug("init()");
}
}
static class B{
private static final Logger log = LoggerFactory.getLogger("B");
private A a;
public B(A a) {
log.debug("B({})",a);
this.a = a;
}
@PostConstruct
public void init(){
log.debug("init()");
}
}
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(A.class);
context.registerBean(B.class);
AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
context.refresh();
System.out.println(context.getBean(B.class));
}
}
第二种方式解决构造方法循环依赖
public class App60_1 {
static class A{
private static final Logger log = LoggerFactory.getLogger("A");
private ObjectFactory<B> b;
// 构造方法注入 ObjectFactory 工厂或者他的子类ObjectProperty,可以延迟bean加载
public A(ObjectFactory<B> b){
log.debug("A(B b){}",b.getClass());
this.b =b;
}
@PostConstruct
public void init(){
log.debug("init()");
}
}
static class B{
private static final Logger log = LoggerFactory.getLogger("B");
private A a;
public B(A a) {
log.debug("B({})",a);
this.a = a;
}
@PostConstruct
public void init(){
log.debug("init()");
}
}
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(A.class);
context.registerBean(B.class);
AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
context.refresh();
System.out.println(context.getBean(B.class));
System.out.println(context.getBean(A.class).b.getObject());
}
}
第三种方式
public class App60_1 {
static class A{
private static final Logger log = LoggerFactory.getLogger("A");
private Provider<B> b;
// 注入 Provider 需要pom依赖
//<dependency>
// <groupId>org.aspectj</groupId>
// <artifactId>aspectjweaver</artifactId>
// <version>1.9.19</version>
// </dependency>
public A(Provider<B> b){
log.debug("A(B b){}",b.getClass());
this.b =b;
}
@PostConstruct
public void init(){
log.debug("init()");
}
}
static class B{
private static final Logger log = LoggerFactory.getLogger("B");
private A a;
public B(A a) {
log.debug("B({})",a);
this.a = a;
}
@PostConstruct
public void init(){
log.debug("init()");
}
}
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(A.class);
context.registerBean(B.class);
AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
context.refresh();
System.out.println(context.getBean(A.class).b.get());
System.out.println(context.getBean(B.class));
}
}
第四种方式:用@Scope方式,不推荐,就不看了了