Spring番外篇
Spring 番外篇
😉 本文共4405字,阅读时间约8min
Spring refresh
方法
-
refresh()是 Spring 最核心的方法,没有之一。refresh 是 AbstractApplicationContext 中的一个方法,负责初始化 ApplicationContext 容器,容器必须调用 refresh 才能正常工作。
-
它的内部主要会调用 12 个方法,我们把它们称为 refresh 的 12 个步骤:
- 注意下面的
Environment
和BeanFactory
都是ApplicationContext
的成员变量。
- 注意下面的
- Environment
- BeanFactory初始化成员变量(bean definiton、解析SpEL器、特殊bean解析器、注册bean后处理器),调用BeanFactory后处理器来补充bean definiton。
- ApplicationContext初始化成员变量(国际化、事件广播器、事件监听器,生命周期管理器),初始化单例Bean,并执行Bean后处理器扩展。
01_Environment
- Environment 对象的作用之一是为后续 @Value,值注入时提供键值
- Environment 分成三个主要部分
- systemProperties - 保存 java 环境键值
- systemEnvironment - 保存系统环境键值
- 自定义 PropertySource - 保存自定义键值,例如来自于 *.properties 文件的键值
02_获取BeanFactory
- 这一步获取(或创建) BeanFactory,它也是作为 ApplicationContext 的一个成员变量
- BeanFactory 的作用是负责 bean 的创建、依赖注入和初始化,bean 的各项特征由 BeanDefinition 定义
- BeanDefinition 作为 bean 的设计蓝图,规定了 bean 的特征,如单例多例、依赖关系、初始销毁方法等
- BeanDefinition 的来源有多种多样,可以是通过 xml 获得、配置类获得、组件扫描获得,也可以是编程添加。所有的 BeanDefinition 会存入 BeanFactory 中的 beanDefinitionMap 集合。
03_准备BeanFactory
这一步会进一步完善 BeanFactory,为它的各项成员变量赋值
-
解析SpEL器,比如
#{}
-
特殊bean注册器:注册 beanFactory 以及 ApplicationContext(不在bean definition里)
-
beanPostProcessors 是 bean 后处理器集合,会工作在 bean 的生命周期各个阶段,此处会添加两个:
- ApplicationContextAwareProcessor 用来解析 Aware 接口
- ApplicationListenerDetector 用来识别容器中 ApplicationListener 类型的 bean
04_05_扩展BeanFactory
-
第四步是空实现,留给子类扩展。一般 Web 环境的 ApplicationContext 都要利用它注册新的 Scope,完善 Web 下的 BeanFactory。
- 这里体现的是模板方法设计模式
-
第五步会调用 beanFactory 后处理器
- beanFactory 后处理器,充当 beanFactory 的扩展点,可以用来补充或修改 BeanDefinition
- 常见的 beanFactory 后处理器有
- ConfigurationClassPostProcessor – 解析 @Configuration、@Bean、@Import、@PropertySource 等
- PropertySourcesPlaceHolderConfigurer – 替换 BeanDefinition 中的 $
06_注册Bean后处理器
- 这一步是继续从 beanFactory 中找出 bean 后处理器,添加至 beanPostProcessors 集合中
- bean 后处理器,充当 bean 的扩展点,可以工作在 bean 的实例化、依赖注入、初始化阶段,常见的有:
- AutowiredAnnotationBeanPostProcessor 功能有:解析 @Autowired,@Value 注解
- CommonAnnotationBeanPostProcessor 功能有:解析 @Resource,@PostConstruct,@PreDestroy
- AnnotationAwareAspectJAutoProxyCreator 功能有:为符合切点的目标 bean 自动创建代理
07_10 国际化 & 广播器 & 监听器
-
为 ApplicationContext 添加 messageSource 成员,实现国际化功能
-
为 ApplicationContext 添加事件广播器成员,即 applicationContextEventMulticaster,它的作用是发布事件给监听器,之后就可以调用 ApplicationContext.publishEvent(事件对象) 来发布事件。
-
这一步会从多种途径找到事件监听器,并添加至 applicationEventMulticaster
- 事件监听器顾名思义,用来接收事件广播器发布的事件,有如下来源
- 事先编程添加的、来自容器中的 bean、来自于 @EventListener 的解析
- 要实现事件监听器,只需要实现 ApplicationListener 接口,重写其中 onApplicationEvent(E e) 方法即可
- 事件监听器顾名思义,用来接收事件广播器发布的事件,有如下来源
11_初始化所有非延迟单例bean
- 这一步会将 beanFactory 的成员补充完毕,并初始化所有非延迟单例 bean
- 利用类型转换器、内嵌值解析器
- conversionService 也是一套转换机制,作为对 PropertyEditor 的补充
- embeddedValueResolvers 即内嵌值解析器,用来解析 @Value 中的 ${ },借用的是 Environment 的功能
- singletonObjects 即单例池,缓存所有单例对象
- 对象的创建都分三个阶段,每一阶段都有不同的 bean 后处理器参与进来,扩展功能
12_完成初始化-添加 lifecycleProcessor 成员
- 这一步会为 ApplicationContext 添加 lifecycleProcessor 成员,用来控制容器内需要生命周期管理的 bean
- 调用 context 的 start,即可触发所有实现 LifeCycle 接口 bean 的 start
- 调用 context 的 stop,即可触发所有实现 LifeCycle 接口 bean 的 stop
- 发布 ContextRefreshed 事件,整个 refresh 执行完成
Spring Bean
Bean 代指的就是那些被 IoC 容器所管理的对象。
我们需要告诉 IoC 容器帮助我们管理哪些对象,这个是通过配置元数据来定义的。配置元数据可以是 XML 文件、注解或者 Java 配置类。
将一个类声明为 Bean 的注解有哪些?
@Component
:通用的注解,可标注任意类为Spring
组件。如果一个 Bean 不知道属于哪个层,可以使用@Component
注解标注。@Repository
: 对应持久层即 Dao 层,主要用于数据库相关操作。@Service
: 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。@Controller
: 对应 Spring MVC 控制层,主要用户接受用户请求并调用 Service 层返回数据给前端页面。
@Component 和 @Bean 的区别是什么?
@Component
注解作用于类,而@Bean
注解作用于方法。@Component
通常是通过类路径扫描来自动侦测以及自动装配到 Spring 容器中(我们可以使用@ComponentScan
注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中)。@Bean
注解通常是我们在标有该注解的方法中定义产生这个 bean,@Bean
告诉了 Spring 这是某个类的实例,当我需要用它的时候还给我。@Bean
注解比@Component
注解的自定义性更强,而且很多地方我们只能通过@Bean
注解来注册 bean。比如当我们引用第三方库中的类需要装配到Spring
容器时,则只能通过@Bean
来实现。
// bean注解使用示例
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
// 上面的代码相当于下面的 xml 配置
<beans>
<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
// 下面这个例子是通过 @Component 无法实现的
@Bean
public OneService getService(status) {
case (status) {
when 1:
return new serviceImpl1();
when 2:
return new serviceImpl2();
when 3:
return new serviceImpl3();
}
}
注入bean的注解有哪些?
Spring 内置的 @Autowired
以及 JDK 内置的 @Resource
和 @Inject
都可以用于注入 Bean。
@Autowired 和 @Resource 的区别是什么?
Autowired
属于 Spring 内置的注解,默认的注入方式为byType
(根据类型进行匹配),也就是说会优先根据接口类型去匹配并注入 Bean (接口的实现类)。
这会有什么问题呢? 当一个接口存在多个实现类的话,byType
这种方式就无法正确注入对象了,因为这个时候 Spring 会同时找到多个满足条件的选择,默认情况下它自己不知道选择哪一个。
这种情况下,注入方式会变为 byName
(根据名称进行匹配),这个名称通常就是类名(首字母小写)。就比如说下面代码中的 smsService
就是我这里所说的名称,这样应该比较好理解了吧。我们还是建议通过 @Qualifier
注解来组合使用显式指定名称而不是依赖变量的名称。
@Resource
属于 JDK 提供的注解,默认注入方式为 byName
。如果无法通过名称匹配到对应的 Bean 的话,注入方式会变为byType
。
@Resource
有两个比较重要且日常开发常用的属性:name
(名称)、type
(类型)。
简单总结一下:
@Autowired
是 Spring 提供的注解,@Resource
是 JDK 提供的注解。
Autowired
默认的注入方式为byType
(根据类型进行匹配),@Resource
默认注入方式为 byName
(根据名称进行匹配)。
当一个接口存在多个实现类的情况下,@Autowired
和@Resource
都需要通过名称才能正确匹配到对应的 Bean。Autowired
可以通过 @Qualifier
注解来显式指定名称,@Resource
可以通过 name
属性来显式指定名称。
Tomcat中的 Filter和Servlet 与 SpringMVC Interceptor
Filter
Filter
可以拦截请求与响应
- 实现URL级别的权限访问控制、压缩响应信息等。
- 针对传入的request提前设置一些参数,或response过滤掉一些信息。
多个Filter对同一个资源进行了拦截,会先按顺序走每一个Filter。
Servlet
Tomcat 是Web应用服务器,是一个Servlet容器。jetty jboss weblogic都是servlet容器。这些servlet把网络相关的问题已经全部处理好,我们写servlet只需要关注业务逻辑即可。
Tomcat 作为 Servlet 容器,负责处理客户请求,把请求传送给 Servlet,并将 Servlet 的响应传送回给客户,而 Servlet 是一种运行在支持 Java 语言的服务器上的组件。
Interceptor
拦截器是spring容器的,是spring支持的。
面向切面编程AOP(Aspect-Oriented Programming
)中拦截器用于在某个方法或字段被访问之前,进行拦截,然后在之前或之后加入某些操作。
比如动态代理
就是拦截器的简单实现,而动态代理基于java的反射机制的。