快捷键
Ctrl+Alt+U 类图, Ctrl+Alt+B 跳转到方法的实现,Ctrl+Shift+T 根据文件名搜索文件,Ctrl+O 显示一个类的所有方法
Ctrl+H 根据文件内容搜索文件
1. 容器接口
BeanFactory能做哪些事情?
ApplicationContext有哪些扩展功能?
事件解耦(ApplicationContext提供的)
到底什么是BeanFactory
它是ApplicationContext的父接口
它才是Spring的核心容器,主要的ApplicationContext实现都组合了它的功能。换句话说,ApplicationContext包含了一个BeanFactory变量,默认的实现类是DefaultListableBeanFactory,单例Bean(singletonObjects)
在SpringBoot Web中,context使用的是AnnotationConfigServletWebServerApplicationContext
BeanFactory能干点啥
表面上只有getBean
实际上控制反转,基本的依赖注入,直至Bean的生命周期的各种功能,都有它的实现类提供
主要实现类DefaultListableBeanFactory
单例Bean:DefaultSingletonBeanRegistry,重要属性 singletonObjects,包含所有的单例Bean,key是Bean的名字,value是Bean实例
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
获取所有单例对象
先建2个类
package com.gjy.spring.a01;
import org.springframework.stereotype.Component;
@Component
public class Component1 {
}
package com.gjy.spring.a01;
import org.springframework.stereotype.Component;
@Component
public class Component2 {
}
package com.gjy.spring.a01;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import java.lang.reflect.Field;
import java.util.Map;
@SpringBootApplication
public class A01App {
private static final Logger log = LoggerFactory.getLogger(A01App.class);
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = SpringApplication.run(A01App.class, args);
System.out.println(context);
// 主要是以下代码
Field field = DefaultSingletonBeanRegistry.class.getDeclaredField("singletonObjects");
field.setAccessible(true);
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
Map<String, Object> map = (Map<String, Object>) field.get(beanFactory);
map.entrySet().stream().filter(e -> e.getKey().startsWith("component"))
.forEach(e -> System.out.println(e.getKey() + "=" + e.getValue()));
}
}
输出结果,省略掉其他内容了。
component1=com.gjy.spring.a01.Component1@1a891add
component2=com.gjy.spring.a01.Component2@5176d279
ApplicationContext比BeanFactory多了点啥
MessageSource:国际化资源能力
在 resources 目录下新建三个文件,注意文件名必须一致
// messages.properties
hi=hi
// messages_en.properties
hi=Hello
// messages_zh.properties
hi=你好
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = SpringApplication.run(A01App.class, args);
System.out.println(context.getMessage("hi", null, Locale.getDefault()));
System.out.println(context.getMessage("hi", null, Locale.CHINESE));
System.out.println(context.getMessage("hi", null, Locale.ENGLISH));
}
ResourcePatternResolver:通配符匹配资源能力,(磁盘路径、类路径)
多个资源:context.getResources(""); 单个资源:context.getResource("");
在 resources 目录下新建application.yml
spring:
application:
name: spring-theroy
// 类路径 classpath: 包含jar包 classpath*:
// 磁盘路径 file:
Resource[] resources = context.getResources("classpath:application.yml"); // "classpath*:META-INF/spring.factories"
System.out.println(Arrays.toString(resources));
ApplicationEventPublisher:发布事件对象
一种解耦方式,分离变与不变。如发短信,邮件,APP功能,不在代码层面写死,功能完成时,调用publishEvent方法。
会自动调用参数为自定义的Event类,可以编写多个@EventListener,实现不同的功能。
编写一个类实现ApplicationEvent类
package com.gjy.spring.a01;
import org.springframework.context.ApplicationEvent;
public class UserRegisterEvent extends ApplicationEvent {
public UserRegisterEvent(Object source) {
super(source);
}
}
发送的代码
@SpringBootApplication
public class A01App {
private static final Logger log = LoggerFactory.getLogger(A01App.class);
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext context = SpringApplication.run(A01App.class, args);
//UserRegisterEvent里的值任意
context.publishEvent(new UserRegisterEvent(context));
}
}
接受的代码
@Component
public class Component2 {
private static final Logger log = LoggerFactory.getLogger(Component2.class);
@EventListener
public void listener(UserRegisterEvent event) {
log.info("event {}", event);
}
}
EnvironmentCapable:Spring环境信息,(系统环境变量、.properties、.yml、环境信息)
// 不区分大小写
System.out.println(context.getEnvironment().getProperty("java_home"));
System.out.println(context.getEnvironment().getProperty("spring.application.name"));
学到了什么
BeanFactory和ApplicationContext不仅仅是简单的继承关系,ApplicationContext组合并扩展了BeanFactory功能。
一种新的功能解耦方式,注入ApplicationEventPublisher对象,完成事件发送功能,继承ApplicationEvent类,完成功能,给方法添加@EventListener完成事件的接受。
2. 容器实现
BeanFactory实现的特点
ApplicationContext常见实现和方法
内嵌容器和DispatcherServlet
BeanFactory实现
beanFactory不会做的事
- 不会主动调用BeanFactory后处理器
- 不会主动添加Bean后处理器
- 不会主动初始化单例
- 不会解析beanFactory,还不会解析 ${} 与 #{}
package com.gjy.spring.a01;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
public class BeanFactoryTest {
public static void main(String[] args) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 添加一些 Bean 的定义(class,scope,初始化,销毁)
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class)
.setScope("singleton").getBeanDefinition();
beanFactory.registerBeanDefinition("config", beanDefinition);
// 给 beanFactory 添加一些常用的后处理器,扩展。只是添加到Bean工厂
// ConfigurationAnnotationProcessor,AutowiredAnnotationProcessor,CommonAnnotationProcessor,EventListenerProcessor,EventListenerFactory
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
// 执行上面注册的后处理器。
// BeanFactory后处理器主要补充了一些bean定义 ConfigurationAnnotationProcessor
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values()
.forEach(processor -> processor.postProcessBeanFactory(beanFactory));
// bean后处理器, 针对bean生命周期的各个阶段提供扩展, 例如 @Autowired @Resource
// AutowiredAnnotationProcessor,CommonAnnotationProcessor 建立与BeanFactory的联系
beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
// 准备好所有单例
beanFactory.preInstantiateSingletons();
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
System.out.println(beanFactory.getBean(Bean1.class).getBean2());
}
@Configuration
static class Config {
@Bean
public Bean1 bean1() {
return new Bean1();
}
@Bean
public Bean2 bean2() {
return new Bean2();
}
}
static class Bean1 {
public Bean1() {
System.out.println("构造器 Bean1");
}
@Autowired
private Bean2 bean2;
public Bean2 getBean2() {
return bean2;
}
}
static class Bean2 {
public Bean2() {
System.out.println("构造器 Bean2");
}
}
}
bean后处理器会有排序的逻辑
同一个对象既加了@Autowired又加了@Resource,@Autowired会生效。
与加入后处理器的顺序有关
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}
ApplicationContext实现
classPathXmlApplicationContext
fileSystemXmlApplicationContext
annotationConfigApplicationContext
annotationConfigServletWebServerApplicationContext
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 name="bean1" class="com.gjy.spring.a02.A02App.Bean1"/>
<bean name="bean2" class="com.gjy.spring.a02.A02App.Bean2">
<property name="bean1" ref="bean1"/>
</bean>
</beans>
package com.gjy.spring.a02;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.Controller;
public class A02App {
private static final Logger log = LoggerFactory.getLogger(A02App.class);
public static void main(String[] args) {
// classPathXmlApplicationContext();
// fileSystemXmlApplicationContext();
/*DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
// reader.loadBeanDefinitions(new FileSystemResource(""));
reader.loadBeanDefinitions(new ClassPathResource("b01.xml"));
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}*/
// annotationConfigApplicationContext();
annotationConfigServletWebServerApplicationContext();
}
private static void classPathXmlApplicationContext() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("b01.xml");
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
}
private static void fileSystemXmlApplicationContext() {
String s = "D:\\code\\java\\microservice_spc\\spring5_theroy\\src\\main\\resources\\b01.xml";
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext(s);
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
}
private static void annotationConfigApplicationContext() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
}
private static void annotationConfigServletWebServerApplicationContext() {
AnnotationConfigServletWebServerApplicationContext context =
new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
}
@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 c() {
return ((request, response) -> {
response.getWriter().println("hello spring");
return null;
});
}
}
@Configuration
static class Config {
@Bean
public Bean1 bean1() {
return new Bean1();
}
}
static class Bean1 {
}
static class Bean2 {
private Bean1 bean1;
public Bean1 getBean1() {
return bean1;
}
public void setBean1(Bean1 bean1) {
this.bean1 = bean1;
}
}
}