零、本文纲要
- 一、容器接口
1、BeanFactory与ApplicationContext
2、BeanFactory接口
① DefaultListableBeanFactory类
3、ApplicationContext接口
① 实现MessageSource接口
② 实现ResourcePatternResolver接口
③ 实现EnvironmentCapable接口
④ 实现ApplicationEventPublisher接口 - 二、容器实现
1、BeanFactory实现
① DefaultListableBeanFactory类
② 注册各类后置处理器
③ beanFactory后置处理器
④ bean后置处理器
⑤ preInstantiateSingletons初始化
补充:AnnotationConfigUtils类
2、ApplicationContext实现
① ClassPathXmlApplicationContext类
② FileSystemXmlApplicationContext类
补充:XmlBeanDefinitionReader类
③ AnnotationConfigApplicationContext类
补充:<context:annotation-config/>
④ AnnotationConfigServletWebServerApplicationContext类
一、容器接口
0、基础准备
添加最基础的Spring Boot依赖,如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
1、BeanFactory与ApplicationContext
- ① Spring Boot启动类
SpringApplication.run(A01.class, args);
的返回值为ConfigurableApplicationContext
对象,如下:
@SpringBootApplication
public class SpringOriginDemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringOriginDemoApplication.class, args);
}
}
BeanFactory接口.png
BeanFactory是:
Ⅰ ApplicationContext 的父接口;
Ⅱ 是 Spring 的核心容器, 主要的 ApplicationContext 实现都【组合】了它的功能。
2、BeanFactory接口
BeanFactory接口,最基础的待实现方法是getBean();
方法。
另外,像控制反转
、基本的依赖注入
、直至 Bean 的生命周期的各种功能
,都是依赖于BeanFactory接口实现的。
- ① DefaultListableBeanFactory类
是Spring项目中BeanFactory功能最完善的实现类,关系图如下:
DefaultListableBeanFactory类.png- ② 通过反射获取singletonObjects成员变量案例
DefaultSingletonBeanRegistry类,如下:
DefaultSingletonBeanRegistry类.png通过反射获取beanFactory中的singletonObjects,如下:
// 通过反射获取 Field 对象singletonObjects
Field singletonObjects = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
// 暴力反射
singletonObjects.setAccessible(true);
// 获取beanFactory中的singletonObjects,并遍历
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
Map<String, Object> map = (Map<String, Object>) singletonObjects.get(beanFactory);
map.entrySet()
.stream().filter(e -> e.getKey().startsWith("component"))
.forEach(e -> System.out.println(e.getKey() + " = " + e.getValue()));
反射输出结果.png
3、ApplicationContext接口
- ① 实现MessageSource接口
Ⅰ 在/resources目录下编写配置文件
语言代码表:Language Code Table (lingoes.cn),此处设置配置文件地区可以省略不写,一般写成zh、ja、en就可以(推荐)。
在/resources目录下编写配置文件.png 逐个配置配置文件.pngⅡ 编写测试代码
System.out.println(context.getMessage("hi", null, Locale.ENGLISH));
System.out.println(context.getMessage("hi", null, Locale.CHINA));
System.out.println(context.getMessage("hi", null, Locale.JAPAN));
输出.png
- ② 实现ResourcePatternResolver接口
Ⅰ 获取类路径下指定资源classpath:
Resource[] resources = context.getResources("classpath:application.properties");
for (Resource resource : resources) {
System.out.println(resource); // 输出 class path resource [application.properties]
}
Ⅱ 获取jar包其他资源classpath*:
Resource[] resources = context.getResources("classpath*:META-INF/spring.factories");
获取jar包其他资源.png
- ③ 实现EnvironmentCapable接口
Ⅰ 获取变量键值
该方法可以获取配置文件
(如:application.properties)内的键值,也可以拿到环境变量
的键值,如下:
System.out.println(context.getEnvironment().getProperty("java_home")); // 输出 C:\Program Files\Java\jdk1.8.0_311
System.out.println(context.getEnvironment().getProperty("server.port")); // 输出 8080
- ④ 实现ApplicationEventPublisher接口
Ⅰ 编写事件类(实现ApplicationEvent )
public class UserRegisterEvent extends ApplicationEvent {
public UserRegisterEvent(Object source) {
super(source);
}
}
Ⅱ 编写事件发布类
@Component
public class Component1 {
private static final Logger log = LoggerFactory.getLogger(Component1.class);
@Autowired
private ApplicationEventPublisher context;
public void register() {
log.debug("用户注册");
context.publishEvent(new UserRegisteredEvent(this));
}
}
Ⅲ 编写事件监听类
@Component
public class Component2 {
private static final Logger log = LoggerFactory.getLogger(Component2.class);
@EventListener
public void aaa(UserRegisteredEvent event) {
log.debug("{}", event);
log.debug("发送短信");
}
}
此处我们是debug输出的,在application.properties中自定义一下日志输出:logging.level.com.stone=debug
。
Ⅳ 测试
context.getBean(Component1.class).register();
实现ApplicationEventPublisher接口.png
实际使用场景中的作用:解耦合。
- ⑤ 总结
ApplicationContext接口拓展功能:
Ⅰ 国际化支持(MessageSource);
Ⅱ 通配符获取资源(ResourcePatternResolver);
Ⅲ 获取环境变量(EnvironmentCapable);
Ⅳ 发送事件(ApplicationEventPublisher)。
二、容器实现
1、BeanFactory实现
- ① DefaultListableBeanFactory类
Ⅰ 准备测试类
public class TestBeanFactory {
@Configuration
static class Config{
@Bean
public Bean1 bean1(){
return new Bean1();
}
@Bean
public Bean2 bean2(){
return new Bean2();
}
}
static class Bean1{
private static final Logger log = LoggerFactory.getLogger(Bean1.class);
public Bean1(){
log.debug("构造 Bean1()");
}
@Autowired
private Bean2 bean2;
public Bean2 getBean2() {
return bean2;
}
}
static class Bean2{
private static final Logger log = LoggerFactory.getLogger(Bean1.class);
public Bean2(){
log.debug("构造 Bean2()");
}
}
}
Ⅱ 编写main方法
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// Bean 的定义信息 class、scope、initMethod、destroyMethod 等
AbstractBeanDefinition bd
= BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
beanFactory.registerBeanDefinition("config", bd);
for (String definitionName : beanFactory.getBeanDefinitionNames()) {
System.out.println(definitionName);
}
}
可以看到我们@Configuration注解的配置类并没有被扫描,所以@Bean相关的Bean没有输出,如下:
控制台输出.png至此,我们可以看出基础的DefaultListableBeanFactory类并不直接支持此类注解扫描。
- ② 注册各类后置处理器
// 给 beanFactory 添加常用的后置处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
添加常用的后置处理器.png
可以看到,此时bean1和bean2仍然没有被扫描放入容器。以为还没有调用beanFactory的后置处理器。添加的后置处理器:
a、ConfigurationAnnotationProcessor;(BeanFactory后置处理器)
b、AutowiredAnnotationProcessor;
c、CommonAnnotationProcessor;
d、EventListenerProcessor;
e、EventListenerFactory。
- ③ beanFactory后置处理器
获取beanFactory的后置处理器,并调用,如下:
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values()
.forEach(beanFactoryPostProcessor -> {
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
});
获取beanFactory的后置处理器.png
至此,我们知道BeanFactoryPostProcessor是对BeanDifinition的一些补充。另外,我们尝试获取一下bean1、bean2,如下:
System.out.println(beanFactory.getBean(Bean1.class).getBean2());
尝试获取bean1.png
Creating shared instance of singleton bean 'bean1'
可以看到Bean对象在我们获取的时候才去创建,并且bean1对象内部并没有注入构造好的bean2。此时我们就需要用到Bean后置处理器。
- ④ bean后置处理器
其作用是对bean生命周期的各个阶段提供拓展,例如:@Autowired...
beanFactory.getBeansOfType(BeanPostProcessor.class).values()
.forEach(beanFactory::addBeanPostProcessor);
bean后置处理器.png
此时可以看到,装配bean1时需要bean2,容器就去沟通bean2完成注入。但是,实际使用Spring的时候,其实bean都是在我们使用前已经创建好的,我们还需处理。
- ⑤ preInstantiateSingletons初始化
beanFactory.preInstantiateSingletons();
System.out.println("==== before we get these beans ====");
System.out.println(beanFactory.getBean(Bean1.class).getBean2());
preInstantiateSingletons初始化.png
- ⑥ 总结
Ⅰ 不会主动调用 BeanFactory 后处理器;
Ⅱ 不会主动添加 Bean 后处理器;
Ⅲ 不会主动初始化单例;
Ⅳ 不会解析beanFactory 还不会解析 ${ } 与 #{ }(如:@Value注解内使用)。
补充:AnnotationConfigUtils类
- ① 基础准备
Ⅰ Config类添加:
@Bean
public Bean3 bean3(){
return new Bean3();
}
@Bean
public Bean4 bean4(){
return new Bean4();
}
Ⅱ 编写接口&内部类:
interface Inter{}
static class Bean3 implements Inter{}
static class Bean4 implements Inter{}
Ⅲ Bean1内注入:
a、同一接口不同实现,@Autowired按照字段名注入,如下:
@Autowired
private Inter bean3;
@Autowired按照字段名注入.png
b、同一接口不同实现,@Resource(name = "bean4")指定注入,如下:
@Resource(name = "bean4")指定注入.pngc、同时开启,按照@Autowired注入,如下:
同时开启按照@Autowired注入.pngd、通过比较器修改注入,如下:
Ⅰ 修改前
beanFactory.getBeansOfType(BeanPostProcessor.class).values()
.forEach(beanPostProcessor -> {
System.out.println("[!!!LOOK HERE!!!] Added BeanPostProcessor is ---> " + beanPostProcessor);
beanFactory.addBeanPostProcessor(beanPostProcessor);
});
修改前.png
Ⅱ 修改后
添加了.stream().sorted(beanFactory.getDependencyComparator())
;
beanFactory.getBeansOfType(BeanPostProcessor.class).values()
.stream().sorted(beanFactory.getDependencyComparator())
.forEach(beanPostProcessor -> {
System.out.println("[!!!LOOK HERE!!!] Added BeanPostProcessor is ---> " + beanPostProcessor);
beanFactory.addBeanPostProcessor(beanPostProcessor);
});
修改后.png
2、ApplicationContext实现
- ① ClassPathXmlApplicationContext类
Ⅰ 编写内部类
public class Demo02 {
static class Bean1{}
static class Bean2{
private Bean1 bean1;
public void setBean1(Bean1 bean1) {
this.bean1 = bean1;
}
public Bean1 getBean1() {
return bean1;
}
}
}
Ⅱ 编写b01.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置bean-->
<bean name="bean1" class="com.stone.demo02.Demo02.Bean1"/>
<bean name="bean2" class="com.stone.demo02.Demo02.Bean2">
<property name="bean1" ref="bean1"/>
</bean>
</beans>
Ⅲ 编写测试方法
public static void testClassPathXmlApplicationContext(){
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("b01.xml");
for (String definitionName : context.getBeanDefinitionNames()) {
System.out.println(definitionName);
}
System.out.println(context.getBean(Bean2.class).getBean1());
}
ClassPathXmlApplicationContext类.png
- ② FileSystemXmlApplicationContext类
public static void testFileSystemXmlApplicationContext(){
FileSystemXmlApplicationContext context
// 绝对路径 D:\JavaStudy\Level1\spring_origin_demo\src\main\resources\b01.xml
// 相对路径 src/main/resources/b01.xml
= new FileSystemXmlApplicationContext("src/main/resources/b01.xml");
for (String definitionName : context.getBeanDefinitionNames()) {
System.out.println(definitionName);
}
System.out.println(context.getBean(Bean2.class).getBean1());
}
FileSystemXmlApplicationContext类.png
补充:XmlBeanDefinitionReader类
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
System.out.println("[!!!LOOK HERE!!!] Before reader works :");
for (String definitionName : beanFactory.getBeanDefinitionNames()) {
System.out.println(definitionName);
}
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
// reader.loadBeanDefinitions(new ClassPathResource("b01.xml"));
reader.loadBeanDefinitions(new FileSystemResource("src/main/resources/b01.xml"));
System.out.println("[!!!LOOK HERE!!!] After reader worked :");
for (String definitionName : beanFactory.getBeanDefinitionNames()) {
System.out.println(definitionName);
}
XmlBeanDefinitionReader类.png
- ③ AnnotationConfigApplicationContext类
Ⅰ 编写Config类
@Configuration
static class Config{
@Bean
public Bean1 bean1(){
return new Bean1();
}
@Bean
public Bean2 bean2(Bean1 bean1){
Bean2 bean2 = new Bean2();
bean2.setBean1(bean1);
return bean2;
}
}
Ⅱ 编写测试方法
public static void testAnnotationConfigApplicationContext(){
AnnotationConfigApplicationContext context
= new AnnotationConfigApplicationContext(Config.class);
for (String definitionName : context.getBeanDefinitionNames()) {
System.out.println(definitionName);
}
System.out.println(context.getBean(Bean2.class).getBean1());
}
AnnotationConfigApplicationContext类.png
补充:<context:annotation-config/>
可以看到AnnotationConfigApplicationContext类自动添加了后置处理器,其作用类似于配置<context:annotation-config/>。
<!--添加注解驱动-->
<context:annotation-config/>
注解驱动在上.png
注解驱动在下.png
- ④ AnnotationConfigServletWebServerApplicationContext类
Ⅰ 编写WebConfig类
@Configuration
static class WebConfig{
@Bean // 必须有
public ServletWebServerFactory servletWebServerFactory(){
return new TomcatServletWebServerFactory();
}
@Bean // 必须有
public DispatcherServlet dispatcherServlet(){
return new DispatcherServlet();
}
@Bean // 必须有
public DispatcherServletRegistrationBean registrationBean(
DispatcherServlet dispatcherServlet){
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}
@Bean("/hello")
public Controller controller(){
// org.springframework.web.servlet.mvc.Controller
return (request, response) -> {
response.getWriter().println("Hello, context!");
return null;
};
}
}
Ⅱ 编写测试方法
public static void testAnnotationConfigServletWebServerApplicationContext(){
AnnotationConfigServletWebServerApplicationContext context
= new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
for (String definitionName : context.getBeanDefinitionNames()) {
System.out.println(definitionName);
}
}
AnnotationConfigServletWebServerApplicationContext类.png
三、结尾
以上即为Spring原理分析-容器&Bean(一)的全部内容,感谢阅读。
网友评论