Spring5底层原理之BeanFactory与ApplicationContext
目录
BeanFactory与ApplicationContext
ClassPathXmlApplicationContext的实现
AnnotationConfigApplicationContext的实现
AnnotationConfigServletWebServerApplicationContext的实现
BeanFactory与ApplicationContext
BeanFactory
BeanFactory是ApplicationContext的父接口。是Spring的核心容器,ApplicationContext的主要的方法均是调用BeanFactory的方法。比如说下面获取bean对象案例中
@SpringBootApplication
public class CodeApplication {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(CodeApplication.class, args);
ctx.getBean("abc");
}
}
通过Ctrl+alt+b键可以看到具体的实现方法如下,先获取BeanFactory对象后调用BeanFactory对象的getBean()方法。
@Override
public Object getBean(String name) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(name);
}
BeanFactory表面只能getBean(),实际上控制反转,基本的依赖注入、直至Bean的生命周期的各种功能,都由它的实现类体提供。
ApplicationContext
相比较BeanFactory,ApplicationContext多了哪些功能?
由类图可以看出,ApplicationContext除了继承BeanFactory的两个接口,也继承了四个其他接口。
接下来分别用代码查看具体做了什么。
首先是翻译。不过该翻译需要自己导入文本
首先在resource包下创建messages开头的properties文件。在里面编写key=value样式的翻译
在主程序中编写代码。可以正常输出翻译后的内容。
第二个就是路径查找资源
第三个是从环境中获取配置值
第四个是发布事件
这个可以用于解耦,比如用户注册成功后,可能需要发送短信通知,又或是邮件通知。不知道如何去通知时,不能随意更改注册部分的功能,这时可以通过发布事件来处理。
首先自定义事件,需要继承ApplicationEvent类。
//自定义事件,需要继承ApplicationEvent
public class UserRegisterEvent extends ApplicationEvent {
public UserRegisterEvent(Object source) {
super(source);
}
}
@Component
public class Component1 {
@Autowired
private ApplicationEventPublisher publisher;
public void register(){
System.out.println("用户注册");
//发布者发布一个UserRegisterEvent事件,从this这个对象发出
publisher.publishEvent(new UserRegisterEvent(this));
}
}
@Component
public class Component2 {
@EventListener
public void send(UserRegisterEvent event){
System.out.println("发送信息"+event);
}
}
@SpringBootApplication
public class CodeApplication {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(CodeApplication.class, args);
Component1 publisher = ctx.getBean(Component1.class);
publisher.register();
}
}
运行结果如下?
用户注册
发送信息com.zmt.test.UserRegisterEvent[source=com.zmt.test.Component1@1d3ac898]
容器实现
BeanFactory实现
从空项目中从头实现BeanFactory
public class TestBeanFactory {
public static void main(String[] args) {
/**
* 从头实现BeanFactory
*/
//创建bean工厂
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//beanDefinition定义,存储class,scope,初始化方法,销毁方法等信息
AbstractBeanDefinition bean = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
beanFactory.registerBeanDefinition("config",bean);
//接下来获取查看哪些bean被加载
String[] names = beanFactory.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
}
@Configuration
static class Config {
@Bean
public Bean1 bean1(){
return new Bean1();
}
@Bean
public Bean2 bean2(){
return new Bean2();
}
}
static class Bean1 {
@Autowired
private Bean2 bean2;
public Bean1() {
System.out.println("构造bean1");
}
public Bean2 getBean2() {
return bean2;
}
}
static class Bean2 {
public Bean2() {
System.out.println("构造bean2");
}
}
}
运行结果如下?
config
由此可以看出,这些注解都没有被解析。只有初始时定义的bean被加载。想要解析注解,需要给beanFactory添加一个后处理器。
运行结果如下?
config
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
可以看到添加了五个处理器。接下来调用这些处理器去解析注解
运行结果如下?
config
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
bean1
bean2
这里看到,可以成功解析注解了,测试Bean1中的getBean2方法发现
bean2并没有被注入,很显然时@Autowried没有起作用。我们需要再写一个Bean后处理器(针对bean的生命周期各个阶段进行扩展)。
也从另一方面体现了,只有当只用bean对象时才会被创建,不使用时只会在beanFactory中保存bean信息。
如果需要提前加载bean,则需要调用方法beanFactory.preInstantiateSingletons()。
具体代码如下
public class TestBeanFactory {
public static void main(String[] args) {
/**
* 从头实现BeanFactory
*/
//创建bean工厂
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//beanDefinition定义,存储class,scope,初始化方法,销毁方法等信息
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
//将beanDefinition注册到beanFactory中
beanFactory.registerBeanDefinition("config", beanDefinition);
//为BeanFactory添加一些常用的Bean工厂后处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
//调用bean工厂后处理器的方法
beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().forEach(processor -> processor.postProcessBeanFactory(beanFactory));
for (String name : beanFactory.getBeanDefinitionNames()) {
System.out.println(name);
}
//将BeanDefinition中的Bean后处理器添加bean后处理器
beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);
//单例对象事先创建,而不是创建时才创建bean对象
beanFactory.preInstantiateSingletons();
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 {
@Autowired
private Bean2 bean2;
public Bean1() {
System.out.println("构造bean1");
}
public Bean2 getBean2() {
return bean2;
}
}
static class Bean2 {
public Bean2() {
System.out.println("构造bean2");
}
}
}
从上述代码中我们可以得到以下结论
- beanFactory不会主动调用Bean工厂后处理器
- beanFactory不会主动添加Bean后处理器
- beanFactory不会主动初始化单例bean
- beanFactory不会解析${}与#{}
ApplicationContext实现
ClassPathXmlApplicationContext的实现
<?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 id="bean1" class="com.zmt.test3.Bean1"/>
<bean id="bean2" class="com.zmt.test3.Bean2">
<property name="bean1" ref="bean1"/>
</bean>
</beans>
public static void testClassPathXmlApplication(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
for (String beanDefinitionName : context.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
System.out.println(context.getBean(Bean2.class).getBean1());
}
运行结果如下?
bean1
bean2
com.zmt.test3.Bean1@5f3a4b84
applicationContext读取xml中的配置具体操作如下
public static void main(String[] args){
//创建BeanFactory
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//创建xml读取器
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
System.out.println("读取配置文件前");
for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
//读取xml文件
reader.loadBeanDefinitions("application.xml");
System.out.println("读取配置文件后");
for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
}
运行结果如下??
读取配置文件前
20:52:54.539 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [application.xml]
读取配置文件后
bean1
bean2
读取xml文件的实现方式还有一种FileSystemXmlApplicationContext,这种实现方式与ClassPathXmlApplicationContext的区别在于前者可以指定配置文件在磁盘中的路径或是src下的路径,其他与后者无异。
AnnotationConfigApplicationContext的实现
@Configuration
public class Config{
@Bean
public Bean1 bean1(){
return new Bean1();
}
//也可以通过添加@Autowired注解在Bean2中注入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 beanDefinitionName : context.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
System.out.println(context.getBean(Bean2.class).getBean1());
}
运行结果如下??
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
codeApplication.Config
bean1
bean2
com.zmt.test3.Bean1@96def03
由此可以看出,相比于ClassPahtXmlApplicationContext中,AnnotationConfigApplicationContext会自动添加常用的后处理器并主动调用这些后处理器
AnnotationConfigServletWebServerApplicationContext的实现
这个是用于web环境下的容器实现,既支持Java配置类又支持servlet与servlet的web容器
public static void testAnnotationConfigServletWebServerApplicationContext(){
AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
for (String beanDefinitionName : context.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}
}
@Configuration
public class WebConfig{
//创建一个web容器
@Bean
public ServletWebServerFactory servletWebServerFactory(){
return new TomcatServletWebServerFactory();
}
//前端控制器,用来分发Servlet
@Bean
public DispatcherServlet dispatcherServlet(){
return new DispatcherServlet();
}
//将前端控制器注册到Web容器当中
@Bean
public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet){
return new DispatcherServletRegistrationBean(dispatcherServlet,"/");
}
//创建一个控制器用来处理前端请求
@Bean("/hello")//当Bean注解中以/开头时,后面的内容除了作为bean名称外,还要充当网络访问路径
public Controller controller1(){
return new Controller() {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
response.getWriter().print("hello");
return null;
}
};
}
}
运行结果如下??
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
codeApplication.WebConfig
servletWebServerFactory
dispatcherServlet
registrationBean
/hello
访问/hello路径结果如下?
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!