spring容器的常见接口与功能
BeanFactory与ApplicationContext的关系
BeanFactory是 ApplicationContext 的父接口,它才是 Spring 的核心容器, 主要的 ApplicationContext 实现都【组合】了它的功能
另一方面,BeanFactory是ApplicationContext一个成员变量
BeanFactory的功能
表面上只有 getBean,实际上控制反转、基本的依赖注入、直至 Bean 的生命周期的各种功能, 都由它的实现类提供
ApplicationContext 比 BeanFactory 多点啥
applicationContext对beanfactory的功能拓展主要体现在四个扩展接口:MessageSource(国际化)、ResourcePatternResolver(通配符方式获取一组 Resource 资源 )、ApplicationEventPublisher(事件发布与监听,实现组件之间的解耦 )、EnvironmentCapable上(整合 Environment 环境(能通过它获取各种来源的配置信息))
MessageSource(国际化)
System.out.println(context.getMessage("hi", null, Locale.CHINA));
System.out.println(context.getMessage("hi", null, Locale.ENGLISH));
System.out.println(context.getMessage("hi", null, Locale.JAPANESE));
可以通过浏览器的请求头获取这个信息 ,根据用户的使用需求返回规定的语言
ResourcePatternResolver
通过通配符获取一系列资源。
Resource[] resources = context.getResources("classpath*:META-INF/spring.factories");
for (Resource resource : resources) {
System.out.println(resource);
}
EnvironmentCapable
获取来自环境变量或者properties配置文件的配置信息。
System.out.println(context.getEnvironment().getProperty("java_home"));
System.out.println(context.getEnvironment().getProperty("server.port"));
ApplicationEventPublisher
用于发布或者监听事件,spring中任何一个组件都可以作为监听器
//发布事件
context.publishEvent(new UserRegisteredEvent(context));
//监听事件
@EventListener
public void aaa(UserRegisteredEvent event) {
log.debug("{}", event);
log.debug("发送短信");
}
事件可以用于解耦功能,比如,对于一个注册功能,在注册成功之后可能需要发送短信、可能需要返回信息,可能两者都需要,不同的业务或者使用场景会有不一样的使用要求,这时候就不能在一个方法里面吧注册功能写死了,可以发事件:先获取事件发布器,然后发布事件
@Autowired
private ApplicationEventPublisher context;
public void register() {
log.debug("用户注册");
context.publishEvent(new UserRegisteredEvent(this));
}
接着另一个业务监听到事件之后就去执行相应的业务,做到注册功能和后续功能的解耦合。
@EventListener
public void aaa(UserRegisteredEvent event) {
log.debug("{}", event);
log.debug("发送短信");
}
spring容器的实现
BeanFactory的实现
BeanFactory的实现之一:DefaultListableBeanFactory,这个实现获得的对象是一个bean容器,先创建一个实例对象得到spring核心容器
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
一开始创建好里面是没有任何的bean的,所以需要给这个BeanFactory添加一些bean的定义,不是直接添加对象,因为对象是BeanFactory帮你创建的,所以你需要告诉的是bean的信息,bean 的定义(class, scope, 初始化, 销毁),再有BeanFactory根据定义创建对象。
例如,我们有如下的类和类的依赖引用关系
@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(Bean2.class);
public Bean2() {
log.debug("构造 Bean2()");
}
}
}
AbstractBeanDefinition beanDefinition =
BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
beanFactory.registerBeanDefinition("config", beanDefinition);
这一段代码是创建了一个config类的bean,并且把这个bean放到了bean容器beanFactory中,通过打印bean容器发现此时容器里只有一个config对象的信息,并没有把bean1和bean2的信息加入到容器中,即没有解析config类中的注解,是因为解析注解并不是有BeanDefinition完成,需要另外一个工具类——BeanFactory后处理器,后处理器可以扩展beanfactory功能,例如解析注解
先把常用的后处理器放入bean工厂,让bean工厂有这么些个bean,但是如果要用到这些后处理器还需要建立这些处理器与beanFactory的联系并执行。
// 给 BeanFactory 添加一些常用的后处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
添加后处理器之后需要运行该后处理器:因为处理器是beanfactory对象,所以要先把这些对象获取了,然后再去执行
// BeanFactory 后处理器主要功能,补充了一些 bean 定义
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(beanFactoryPostProcessor -> {
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
});
此时去打印beanFactory中的bean就可以发现里面是把bean1和bean2的信息也创建并加入进容器了。
这时候根据getBean(Bean1.class).getBean2()会发现返回值是一个null,即Bean1类中的@Autowired注解失效 ,没有创建出bean2对象这个依赖注解还需要Bean后处理器来解决。
// Bean 后处理器, 针对 bean 的生命周期的各个阶段提供扩展, 例如 @Autowired @Resource ...
beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream()
.sorted(beanFactory.getDependencyComparator())
.forEach(beanPostProcessor -> {
System.out.println(">>>>" + beanPostProcessor);
beanFactory.addBeanPostProcessor(beanPostProcessor);
});
因为beanFactory一开始只是在容器中存放各个bean的信息,只有当getBean的时候才会去创建bean对象,而有时候我们为了性能考虑会像提前把bean对象创建好,这时候就可以用到下面这个方法
beanFactory.preInstantiateSingletons(); // 准备好所有单例
总结:beanFactory 不会做的事
1. 不会主动调用 BeanFactory 后处理器
2. 不会主动添加 Bean 后处理器
3. 不会主动初始化单例
4. 不会解析beanFactory 还不会解析 ${ } 与 #{ }
ApplicationContext的实现
四个比较经典的实现类:
ClassPathXmlApplicationContext
到类路径下读取配置文件
// ⬇️较为经典的容器, 基于 classpath 下 xml 格式的配置文件来创建
private static void testClassPathXmlApplicationContext() {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("a02.xml");
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(context.getBean(Bean2.class).getBean1());
}
FileSystemXmlApplicationContext
// ⬇️基于磁盘路径下 xml 格式的配置文件来创建
private static void testFileSystemXmlApplicationContext() {
FileSystemXmlApplicationContext context =
new FileSystemXmlApplicationContext(
"src\\main\\resources\\a02.xml");
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println(context.getBean(Bean2.class).getBean1());
}
上面这两个类的实现原理是借助了DefaultListableBeanFactory,读取xml文件,把读取到的bean信息形成bean放入bean工厂里面
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
System.out.println("读取之前...");
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
System.out.println("读取之后...");
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions(new FileSystemResource("src\\main\\resources\\a02.xml"));
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
AnnotationConfigApplicationContext
基于注解配置的实现
private static void testAnnotationConfigServletWebServerApplicationContext() {
AnnotationConfigServletWebServerApplicationContext context =
new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
}
@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;
}
}
AnnotationConfigServletWebServerApplicationContext
较为经典的容器, 基于 java 配置类来创建, 用于 web 环境
private static void testAnnotationConfigServletWebServerApplicationContext() {
AnnotationConfigServletWebServerApplicationContext context =
new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}
}
@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 controller1() {
return (request, response) -> {
response.getWriter().print("hello");
return null;
};
}
}