Spring 框架中都用到了哪些设计模式?
单例模式——Bean的默认scope
工厂模式——用BeanFactory创建对象
代理模式——AOP
模板方法——jdbcTemplate, RestTemplate
前端控制器——DispatchServlet来对请求分发
可以通过多少种方式完成依赖注入?各种方式比较
spring中使用构造注入和setter注入这两种。没有绝对好坏之分,区别主要在于使用场景不同。
建议采用以设值注入为主,构造注入为辅的注入策略。对于依赖关系无需变化的注入,尽量采用构造注入;而其他的依赖关系的注入,则考虑采用设值注入。
两种容器BeanFactory和ApplicationContext
BeanFactory只提供容器最基本的IOC,AOP功能,ApplicationContext则在此基础上扩展,提供了以下功能
- 继承 MessageSource,提供国际化的标准访问策略。
- 继承 ApplicationEventPublisher ,提供强大的事件机制。
- 扩展 ResourceLoader,可以用来加载多个 Resource,可以灵活访问不同的资源。
- 对 Web 应用的支持。
BeanFactory 粗暴简单,可以理解为就是个 HashMap,Key 是 BeanName,Value 是 Bean 实例。通常只提供注册(put),获取(get)这两个功能。我们可以称之为 “低级容器”。
ApplicationContext 可以称之为 “高级容器”。因为他比 BeanFactory 多了更多的功能。他继承了多个接口。因此具备了更多的功能。例如资源的获取,支持多种消息(例如 JSP tag 的支持),对 BeanFactory 多了工具级别的支持等待。所以你看他的名字,已经不是 BeanFactory 之类的工厂了,而是 “应用上下文”, 代表着整个大容器的所有功能。该接口定义了一个 refresh 方法,此方法是所有阅读 Spring 源码的人的最熟悉的方法,用于刷新整个容器,即重新加载/刷新所有的 bean。
Spring IOC原理机制
工厂模式+反射机制,工作原理步骤如下:
a. 加载配置文件,解析成 BeanDefinition 放在 Map 里。
b. 调用 getBean 的时候,从 BeanDefinition 所属的 Map 里,拿出 Class 对象进行实例化,同时,如果有依赖关系,将递归调用 getBean 方法 —— 完成依赖注入。
Spring有哪些事件
Spring监听事件的方法:实现ApplicationListener 接口,重写onApplicationEvent()方法,如下
public class AllApplicationEventListener implements ApplicationListener<ApplicationEvent> {
@Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
// process event
}
}
- 上下文更新事件(ContextRefreshedEvent):该事件会在ApplicationContext 被初始化或者更新时发布。也可以在调用ConfigurableApplicationContext 接口中的 #refresh() 方法时被触发。
- 上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext 的 #start() 方法开始/重新开始容器时触发该事件。
- 上下文停止事件(ContextStoppedEvent):当容器调用 ConfigurableApplicationContext 的 #stop() 方法停止容器时触发该事件。
- 上下文关闭事件(ContextClosedEvent):当ApplicationContext 被关闭时触发该事件。容器被关闭时,其管理的所有单例 Bean 都被销毁。
- 请求处理事件(RequestHandledEvent):在 We b应用中,当一个HTTP 请求(request)结束触发该事件。
Bean scope (简单了解)
实际常用的只有 Singleton 和 Prototype 两种级别。
Spring Bean生命周期(高级)
整体流程如下
另外一个
各个步骤解释如下
- 实例化Bean对象:容器通过BeanDefinition(由XML/注解/config提供)来实例化出一个对象
- 设置对象属性:即依赖注入
- 调用BeanNameAwre接口的setBeanName()方法:扩展接口。让Bean获得自己的name相关信息,可以配合实现log打印。
- 调用BeanFactoryAware的setBeanFactory()方法:扩展接口。让Bean获得容器(上下文),可以使用到容器中的Bean等资源(比如实例化一个lazy Bean)
- 调用ApplicationContextAware的setApplicationContext()方法():扩展接口。让Bean获取容器,特别是web容器,例如获取servlet上下文。
- 调用BeanProcessor的preProcessBeforeInitialization()方法:扩展接口,实例化前调用对Bean增强。
- 调用InitializingBean的afterPropertiesSet()方法:扩展接口,实例化
- 调用bean配置的init方法
- 调用BeanPostProcessor的postProcessAfterInitialization()方法:扩展接口。实例化后对Bean增强。
- 单例Bean缓存在容器中待使用,原型Bean交给用户管理
- 调用DisposableBean的destroy()方法:扩展接口
- 调用bean中配置的destroy方法
注意以上一系列接口只是对ApplicationContext容器才会自动加载并初始化。
Spring 框架中的单例 Bean 是线程安全的么?
spring对单例bean没有做安全方面的设置,需要开发者自己控制
Spring Bean 怎么解决循环依赖的问题?(高级)
首先要明白的是spring只解决单例的循环依赖,对原型bean的循环依赖直接抛出异常。
两种场景:
- 构造器的循环依赖。
- field 属性的循环依赖。
对于构造器的循环依赖,Spring 是无法解决的,也是直接抛出异常。
解决单例循环依赖的手段是,将预初始化(未初始化完成)的Bean加入特殊缓存容器,这个容器中的Bean是个半成品,但是可以提前曝光给需要依赖他的Bean,从而让目标Bean走完初始化流程。
Spring AOP 有哪些实现方式?
广义上分为静态代理(AspectJ AOP)和动态代理(Spring AOP)。
- 静态代理是在编译阶段完成,要么通过特殊的编译器完成,要么通过特殊的类加载器完成。
- 动态代理是在运行期间,在内存中动态生成代理类,然后用反射创建对象。
spring中使用了JDK和CGLIB两种动态代理,区别如下
JDK 动态代理
通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是 InvocationHandler 接口和 Proxy 类。
CGLIB 动态代理
CGLIB 是通过继承的方式做的动态代理,因此如果某个类被标记为 final
,那么它是无法使用 CGLIB 做动态代理的
spring推荐JDK动态代理,因为一是无需依赖第三方,二是不受final影响,三是JDK8后与CGLIB性能差异不大。但是如果被代理类没有实现接口,就只能用CGLIB做动态代理。
列举 Spring 支持的事务管理类型?
声明式事务:通过XML或者注解,使事务控制与代码分离
编程式事务:通过编码方式显式调用提交,回滚等操作。
实际开发中通常用springboot+注解声明事务。
- 开启事务支持
spring Boot 使用事务非常简单,首先使用注解 @EnableTransactionManagement 开启事务支持后,然后在访问数据库的Service方法上添加注解 @Transactional 便可。
关于事务管理器,不管是JPA还是JDBC等都实现自接口 PlatformTransactionManager 如果你添加的是 spring-boot-starter-jdbc 依赖,框架会默认注入 DataSourceTransactionManager 实例。如果你添加的是 spring-boot-starter-data-jpa 依赖,框架会默认注入 JpaTransactionManager 实例。
各种框架的PlatformTransactionManager实例
- 声明式事务一些常用语法
开启事务
@EnableTransactionManagement // 启注解事务管理,等同于xml配置方式的 <tx:annotation-driven />
@SpringBootApplication
public class ProfiledemoApplication {
指定回滚条件
@Transactional(rollbackFor=Exception.class)
public void methodName() {
// 不会回滚
throw new Exception("...");
}
@Transactionl常用属性(value,propagation,isolation,timeout,rollbackFor。。。)
什么是事务的传播级别?分成哪些传播级别?(Spring特有)
事务的传播行为,指的是当前带有事务配置的方法,需要怎么处理事务。事务传播级别是spring自己定义的。
例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行
三类七种事务传播类型
// TransactionDefinition.java
// ========== 支持当前事务的情况 ==========
/**
* 如果当前存在事务,则使用该事务。
* 如果当前没有事务,则创建一个新的事务。
*/
int PROPAGATION_REQUIRED = 0;
/**
* 如果当前存在事务,则使用该事务。
* 如果当前没有事务,则以非事务的方式继续运行。
*/
int PROPAGATION_SUPPORTS = 1;
/**
* 如果当前存在事务,则使用该事务。
* 如果当前没有事务,则抛出异常。
*/
int PROPAGATION_MANDATORY = 2;
// ========== 不支持当前事务的情况 ==========
/**
* 创建一个新的事务。
* 如果当前存在事务,则把当前事务挂起。
*/
int PROPAGATION_REQUIRES_NEW = 3;
/**
* 以非事务方式运行。
* 如果当前存在事务,则把当前事务挂起。
*/
int PROPAGATION_NOT_SUPPORTED = 4;
/**
* 以非事务方式运行。
* 如果当前存在事务,则抛出异常。
*/
int PROPAGATION_NEVER = 5;
// ========== 其他情况 ==========
/**
* 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行。
* 如果当前没有事务,则等价于 {@link TransactionDefinition#PROPAGATION_REQUIRED}
*/
int PROPAGATION_NESTED = 6;
其中绝大部分场景都用第1种 PROPAGATION_REQUIRED
其中前6中继承自EJB,第7种为spring特有,即嵌套事务(在事务中创建子事务,内部事务依赖外部事务的提交或者回滚),很少用到。
Spring的各种扩展增强接口
主要分三类,一类是容器扩展接口例如BeanFactoryPostProcessor,
另一类是Bean扩展接口例如BeanPostProcessor
再就是感知类接口,xxxAwre
容器扩展接口
BeanFactoryPostProcessor:在beanfactory完成标准初始化并加载了definitions集合之后,对beanfactory的一些属性扩展,例如PropertyPlaceholderConfigurer用来替代配置文件中的一些变量,CustomEditorConfigurer用来定义配置文件中一些特殊类型(例如日期)的处理器。
FactoryBean:只要重写了其中的getObject方法,就能按预定方式生成bean。例如要扩展XML的属性,定义特殊属性,然后实例化Bean,整个过程也是AOP的过程。
Bean扩展接口
BeanPostProcess:可以在Bean实例化前后做一些扩展,这里又分为两类。
BeanPostProcess的InstantiationAwareBeanPostProcessor接口方法将在Bean实例话之前调用,一般用来做代理返回代理实例,也就是AOP原理。
postProcessBeforeInitialization 和 postProcessAfterInitialization 则是在Bean实例化后,在初始化前后调用,增强Bean
感知类接口
XXXXXAware:用来获取Bean本身信息,例如BeanNameAware在Factory.getBean()中可以先获取名字。 或者用来获取容器,例如ApplicationContextAware