零白热、本文綱要
- 一、容器接口
1粗卜、BeanFactory與ApplicationContext
2屋确、BeanFactory接口
① DefaultListableBeanFactory類
3、ApplicationContext接口
① 實(shí)現(xiàn)MessageSource接口
② 實(shí)現(xiàn)ResourcePatternResolver接口
③ 實(shí)現(xiàn)EnvironmentCapable接口
④ 實(shí)現(xiàn)ApplicationEventPublisher接口 - 二续扔、容器實(shí)現(xiàn)
1攻臀、BeanFactory實(shí)現(xiàn)
① DefaultListableBeanFactory類
② 注冊(cè)各類后置處理器
③ beanFactory后置處理器
④ bean后置處理器
⑤ preInstantiateSingletons初始化
補(bǔ)充:AnnotationConfigUtils類
2、ApplicationContext實(shí)現(xiàn)
① ClassPathXmlApplicationContext類
② FileSystemXmlApplicationContext類
補(bǔ)充:XmlBeanDefinitionReader類
③ AnnotationConfigApplicationContext類
補(bǔ)充:<context:annotation-config/>
④ AnnotationConfigServletWebServerApplicationContext類
一纱昧、容器接口
0刨啸、基礎(chǔ)準(zhǔn)備
添加最基礎(chǔ)的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啟動(dòng)類
SpringApplication.run(A01.class, args);
的返回值為ConfigurableApplicationContext
對(duì)象设联,如下:
@SpringBootApplication
public class SpringOriginDemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringOriginDemoApplication.class, args);
}
}
BeanFactory是:
Ⅰ ApplicationContext 的父接口善已;
Ⅱ 是 Spring 的核心容器, 主要的 ApplicationContext 實(shí)現(xiàn)都【組合】了它的功能。
2离例、BeanFactory接口
BeanFactory接口换团,最基礎(chǔ)的待實(shí)現(xiàn)方法是getBean();
方法。
另外宫蛆,像控制反轉(zhuǎn)
艘包、基本的依賴注入
、直至 Bean 的生命周期的各種功能
耀盗,都是依賴于BeanFactory接口實(shí)現(xiàn)的想虎。
- ① DefaultListableBeanFactory類
是Spring項(xiàng)目中BeanFactory功能最完善的實(shí)現(xiàn)類,關(guān)系圖如下:
- ② 通過(guò)反射獲取singletonObjects成員變量案例
DefaultSingletonBeanRegistry類袍冷,如下:
通過(guò)反射獲取beanFactory中的singletonObjects磷醋,如下:
// 通過(guò)反射獲取 Field 對(duì)象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()));
3胡诗、ApplicationContext接口
- ① 實(shí)現(xiàn)MessageSource接口
Ⅰ 在/resources目錄下編寫配置文件
語(yǔ)言代碼表:Language Code Table (lingoes.cn)邓线,此處設(shè)置配置文件地區(qū)可以省略不寫,一般寫成zh煌恢、ja骇陈、en就可以(推薦)。
Ⅱ 編寫測(cè)試代碼
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));
- ② 實(shí)現(xiàn)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");
- ③ 實(shí)現(xiàn)EnvironmentCapable接口
Ⅰ 獲取變量鍵值
該方法可以獲取配置文件
(如:application.properties)內(nèi)的鍵值瑰抵,也可以拿到環(huán)境變量
的鍵值你雌,如下:
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
- ④ 實(shí)現(xiàn)ApplicationEventPublisher接口
Ⅰ 編寫事件類(實(shí)現(xiàn)ApplicationEvent )
public class UserRegisterEvent extends ApplicationEvent {
public UserRegisterEvent(Object source) {
super(source);
}
}
Ⅱ 編寫事件發(fā)布類
@Component
public class Component1 {
private static final Logger log = LoggerFactory.getLogger(Component1.class);
@Autowired
private ApplicationEventPublisher context;
public void register() {
log.debug("用戶注冊(cè)");
context.publishEvent(new UserRegisteredEvent(this));
}
}
Ⅲ 編寫事件監(jiān)聽類
@Component
public class Component2 {
private static final Logger log = LoggerFactory.getLogger(Component2.class);
@EventListener
public void aaa(UserRegisteredEvent event) {
log.debug("{}", event);
log.debug("發(fā)送短信");
}
}
此處我們是debug輸出的,在application.properties中自定義一下日志輸出:logging.level.com.stone=debug
二汛。
Ⅳ 測(cè)試
context.getBean(Component1.class).register();
實(shí)際使用場(chǎng)景中的作用:解耦合婿崭。
- ⑤ 總結(jié)
ApplicationContext接口拓展功能:
Ⅰ 國(guó)際化支持(MessageSource);
Ⅱ 通配符獲取資源(ResourcePatternResolver)肴颊;
Ⅲ 獲取環(huán)境變量(EnvironmentCapable)氓栈;
Ⅳ 發(fā)送事件(ApplicationEventPublisher)。
二婿着、容器實(shí)現(xiàn)
1授瘦、BeanFactory實(shí)現(xiàn)
- ① DefaultListableBeanFactory類
Ⅰ 準(zhǔn)備測(cè)試類
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("構(gòu)造 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("構(gòu)造 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注解的配置類并沒(méi)有被掃描,所以@Bean相關(guān)的Bean沒(méi)有輸出丘侠,如下:
至此徒欣,我們可以看出基礎(chǔ)的DefaultListableBeanFactory類并不直接支持此類注解掃描。
- ② 注冊(cè)各類后置處理器
// 給 beanFactory 添加常用的后置處理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
可以看到蜗字,此時(shí)bean1和bean2仍然沒(méi)有被掃描放入容器帚称。以為還沒(méi)有調(diào)用beanFactory的后置處理器官研。添加的后置處理器:
a秽澳、ConfigurationAnnotationProcessor闯睹;(BeanFactory后置處理器)
b、AutowiredAnnotationProcessor担神;
c楼吃、CommonAnnotationProcessor;
d妄讯、EventListenerProcessor孩锡;
e、EventListenerFactory亥贸。
- ③ beanFactory后置處理器
獲取beanFactory的后置處理器躬窜,并調(diào)用,如下:
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values()
.forEach(beanFactoryPostProcessor -> {
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
});
至此炕置,我們知道BeanFactoryPostProcessor是對(duì)BeanDifinition的一些補(bǔ)充荣挨。另外,我們嘗試獲取一下bean1朴摊、bean2默垄,如下:
System.out.println(beanFactory.getBean(Bean1.class).getBean2());
Creating shared instance of singleton bean 'bean1'
可以看到Bean對(duì)象在我們獲取的時(shí)候才去創(chuàng)建,并且bean1對(duì)象內(nèi)部并沒(méi)有注入構(gòu)造好的bean2甚纲。此時(shí)我們就需要用到Bean后置處理器口锭。
- ④ bean后置處理器
其作用是對(duì)bean生命周期的各個(gè)階段提供拓展,例如:@Autowired...
beanFactory.getBeansOfType(BeanPostProcessor.class).values()
.forEach(beanFactory::addBeanPostProcessor);
此時(shí)可以看到介杆,裝配bean1時(shí)需要bean2鹃操,容器就去溝通bean2完成注入。但是春哨,實(shí)際使用Spring的時(shí)候荆隘,其實(shí)bean都是在我們使用前已經(jīng)創(chuàng)建好的,我們還需處理悲靴。
- ⑤ preInstantiateSingletons初始化
beanFactory.preInstantiateSingletons();
System.out.println("==== before we get these beans ====");
System.out.println(beanFactory.getBean(Bean1.class).getBean2());
- ⑥ 總結(jié)
Ⅰ 不會(huì)主動(dòng)調(diào)用 BeanFactory 后處理器臭胜;
Ⅱ 不會(huì)主動(dòng)添加 Bean 后處理器;
Ⅲ 不會(huì)主動(dòng)初始化單例癞尚;
Ⅳ 不會(huì)解析beanFactory 還不會(huì)解析 ${ } 與 #{ }(如:@Value注解內(nèi)使用)耸三。
補(bǔ)充:AnnotationConfigUtils類
- ① 基礎(chǔ)準(zhǔn)備
Ⅰ Config類添加:
@Bean
public Bean3 bean3(){
return new Bean3();
}
@Bean
public Bean4 bean4(){
return new Bean4();
}
Ⅱ 編寫接口&內(nèi)部類:
interface Inter{}
static class Bean3 implements Inter{}
static class Bean4 implements Inter{}
Ⅲ Bean1內(nèi)注入:
a、同一接口不同實(shí)現(xiàn)浇揩,@Autowired按照字段名注入仪壮,如下:
@Autowired
private Inter bean3;
b、同一接口不同實(shí)現(xiàn)胳徽,@Resource(name = "bean4")指定注入积锅,如下:
c爽彤、同時(shí)開啟,按照@Autowired注入缚陷,如下:
d适篙、通過(guò)比較器修改注入,如下:
Ⅰ 修改前
beanFactory.getBeansOfType(BeanPostProcessor.class).values()
.forEach(beanPostProcessor -> {
System.out.println("[!!!LOOK HERE!!!] Added BeanPostProcessor is ---> " + beanPostProcessor);
beanFactory.addBeanPostProcessor(beanPostProcessor);
});
Ⅱ 修改后
添加了.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);
});
2嚷节、ApplicationContext實(shí)現(xiàn)
- ① ClassPathXmlApplicationContext類
Ⅰ 編寫內(nèi)部類
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>
Ⅲ 編寫測(cè)試方法
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());
}
- ② FileSystemXmlApplicationContext類
public static void testFileSystemXmlApplicationContext(){
FileSystemXmlApplicationContext context
// 絕對(duì)路徑 D:\JavaStudy\Level1\spring_origin_demo\src\main\resources\b01.xml
// 相對(duì)路徑 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());
}
補(bǔ)充: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);
}
- ③ 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;
}
}
Ⅱ 編寫測(cè)試方法
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());
}
補(bǔ)充:<context:annotation-config/>
可以看到AnnotationConfigApplicationContext類自動(dòng)添加了后置處理器,其作用類似于配置<context:annotation-config/>虎锚。
<!--添加注解驅(qū)動(dòng)-->
<context:annotation-config/>
- ④ 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;
};
}
}
Ⅱ 編寫測(cè)試方法
public static void testAnnotationConfigServletWebServerApplicationContext(){
AnnotationConfigServletWebServerApplicationContext context
= new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
for (String definitionName : context.getBeanDefinitionNames()) {
System.out.println(definitionName);
}
}
三硫痰、結(jié)尾
以上即為Spring原理分析-容器&Bean(一)的全部?jī)?nèi)容,感謝閱讀窜护。