本案例中没有用SpringApplication.run
来直接启动Spring,而是通过自己new一个Bean工厂,逐步添加后置处理器来实现解析Bean对象的功能,大体上包括如下内容:
ServletWebServerFactory
、接口路由DispatcherServlet
、路由注册到web环境DispatcherServletRegistrationBean
)其实这个BeanFactory
(接口)才是真正的Spring IOC容器(但是像控制反转,依赖注入,Bean生命周期的各种功能,具体都是由DefaultListableBeanFactory
实现的),而ApplicationContext
(接口)是对其组合、横向扩展(例如如何去读取注解、配置文件等);
ApplicationContext
(接口)下又有很多的实现类,且他们用的都是这个唯一的DefaultListableBeanFactory
(类对象,保存的IOC中的bean,实现了BeanFactory);
ApplicationContext
下的抽象实现类AbstractApplicationContext
(其下有很多实现类)可以调用getBean(String name),且底层是先获取BeanFactory再getBean
这玩意是Spring中实际的IOC容器,我们可以自己new一个DefaultListableBeanFactory
@Configuration//本案例中可以省略(这个注解本身也只是为了被发现)
public class MyConfig {
@Bean//需要用BeanFactoryPostProcessors来解析
public Bean2 bean2(){//Bean2中用@Autowired注入了Bean1,需要用BeanPostProcessors解析
return new Bean2();
}
@Bean
public Bean1 bean1(){
return new Bean1();
}
}
public class Bean2 {
@Autowired
Bean1 bean1;
public Bean2() {
System.out.println("bean2初始化");
}
@Component及其下面的几个注解本身只是为了被Spring启动的时候发现(本案例中因为是手动,即便是不写@Component也可以正常注入),且BeanFactory本身没有提供解析@Bean、@Autowired等注解的功能(需要后置处理器BeanFactoryPostProcessors、BeanPostProcessors
来实现解析)
BeanDefinitionBuilder.genericBeanDefinition( MyConfig.class)...getBeanDefinition()
获取Bean的定义信息defaultListableBeanFactory.registerBeanDefinition("myConfig",beanDefinition)
通过bean定义信息注册到bean工厂(IOC容器)中此时只能解析这个myConfig,而不能解析其下的@Bean
public static void main(String[] args) {
//1.创建一个bean工厂(实际的)
DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
//2.生成bean定义信息:读取MyConfig本身,设置为singleton(创建一个单例myConfig对象)
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
.genericBeanDefinition(MyConfig.class)
.setScope("singleton").getBeanDefinition();
defaultListableBeanFactory.registerBeanDefinition("myConfig",beanDefinition);
使用Spring提供的工具包来注入到容器,用于解析不同的Bean生命周期AnnotationConfigUtils.registerAnnotationConfigProcessors(defaultListableBeanFactory)
这个工具一共注入5个bean,其中两个是BeanFactoryPostProcessors的实现类,通过Debug可以发现这两个分别是ConfigurationClassPostProcessor
(用于解析@Configuration和@Bean) 和EventListenerMethodProcessor
在XML中通过标签注入,效果相同
//4. 给 BeanFactory添加一些常用的工厂后置处理器,让它具备解析@Configuration、@Bean等注解的能力(还需要再注册)
//IOC容器中会多出来5个Bean,包括两个BeanFactoryPostProcessor的实现类
AnnotationConfigUtils.registerAnnotationConfigProcessors(defaultListableBeanFactory);
看下源码部分
关于先后顺序的order值相差1(这是一个Spring源码写的不规范的地方,二者用了不同的表示方法)
在这个工具类中AnnotationConfigUtils
,通过order设置了解析顺序(例如会影响@Autowired和@Resource谁生效)
因为上一步已经注入了ConfigurationClassPostProcessor
,但是只是作为bean注入的,没有执行解析的步骤
//5. 从bean工厂中取出BeanFactory的后处理器,并且执行这些后处理器
// BeanFactory 后处理器BeanFactoryPostProcessor主要功能,补充了一些 bean 的定义
defaultListableBeanFactory.getBeansOfType(BeanFactoryPostProcessor.class)
.values().forEach(beanFactoryPostProcessor -> {
//一共2种bean工厂后置处理器,注册到bean工厂去
beanFactoryPostProcessor.postProcessBeanFactory(defaultListableBeanFactory);
});
Bean2 bean2 = defaultListableBeanFactory.getBean(Bean2.class);//默认的懒汉式,所以需要手动获取一下
此时解析到了@Bean,但@Autowired还没有被解析,bean2中的bean1=null
//7.要想@Autowired、@Resource等注解被解析,还要添加Bean的后处理器(非Bean工厂后置处理器),
defaultListableBeanFactory
.getBeansOfType(BeanPostProcessor.class)
.values()
.forEach(defaultListableBeanFactory::addBeanPostProcessor);
Bean2 bean2 = defaultListableBeanFactory.getBean(Bean2.class);
可以看到@Autowired解析成功
interface Inter { }
static class Bean3 implements Inter {
public Bean3() {
System.out.println("构造 Bean3()");
}
}
static class Bean4 implements Inter {
public Bean4() {
System.out.println("构造 Bean4()");
}
}
然后再Bean5中注入Inter
static class Bean5{
//@Autowired、@Resource(不指定名字的话)都是根据参数名匹配bean对象
// @Autowired
// @Resource
// Inter bean3;
}
MyConfig注入@Bean
@Bean
public Bean5 bean5(){
return new Bean5();
}
@Autowired、@Resource(不指定名字的话)都是根据参数名匹配bean对象
@Resource(“beanName”)可以指定注入
默认情况下:二者同时存在时@Autowired生效,例如
static class Bean5{
//注入的是bean3
@Autowired
@Resource(name = "bean4")
Inter bean3;
}
方法1:反转后手动添加
ArrayList list = new ArrayList<>(beanFactory
.getBeansOfType(BeanPostProcessor.class).values());
Collections.reverse(list);
beanFactory.addBeanPostProcessors(list);
方法2:Stream利用原生order顺序
beanFactory.addBeanPostProcessors(beanFactory
.getBeansOfType(BeanPostProcessor.class).values()
.stream()
.sorted(beanFactory.getDependencyComparator())
.collect(Collectors.toCollection(ArrayList::new)));
方法3:直接利用order顺序
beanFactory.getBeansOfType(BeanPostProcessor.class).values()
.forEach(beanFactory::addBeanPostProcessor);
或者
beanFactory.addBeanPostProcessors
(beanFactory.getBeansOfType(BeanPostProcessor.class).values());
先看继承图,可以看到有如下几个功能(见名知意)
context.getMessage("hi",null,Local.CHINA);
就会打印"你好" //1. :是只在类路径下找 [classpath:application.properties]
Resource[] resources = context.getResources("classpath:application.properties");
for (Resource res : resources) {
if(res.exists())
System.out.println(res);
}
//2. *:可以找到引入依赖的jar包中,通配符匹配多个
// URL [jar:file:/D:/apache-maven-3.8.3/maven-repo/org/springframework/boot/spring-boot/2.7.3/spring-boot-2.7.3.jar!/META-INF/spring.factories]
// URL [jar:file:/D:/apache-maven-3.8.3/maven-repo/org/springframework/boot/spring-boot-autoconfigure/2.7.3/spring-boot-autoconfigure-2.7.3.jar!/META-INF/spring.factories]
// URL [jar:file:/D:/apache-maven-3.8.3/maven-repo/org/springframework/spring-beans/5.3.22/spring-beans-5.3.22.jar!/META-INF/spring.factories]
Resource[] resources1 = context.getResources("classpath*:META-INF/spring.factories");
for (Resource res : resources1) {
if(res.exists())
System.out.println(res);
}
//在application.properties中配过hi=hello
System.out.println(context.getEnvironment().getProperty("hi"));
//系统变量,直接读取
System.out.println(context.getEnvironment().getProperty("java_home"));
ApplicationEvent
public class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {//source就是事件源(谁发的这个事件,我们可以传入一个context参数)
super(source);
}
}
//context发布事件MyEvent,其中发布源是:context
context.publishEvent(new MyEvent(context));
API使用上类似于Spring整合RabbitMQ
@Component
public class MyListener {
@EventListener
public void listen(MyEvent event){
System.out.println("收到了消息"+new Date());
}
}
XmlWebApplicationContext
举例:
这个容器中就有5个后置处理器 和 两个bean
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring_bean.xml");
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext
("C:\Users\Administrator\IdeaProjects\demo23\src\main\resources\spring_bean.xml");
也可以简化为
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext
("src\main\resources\spring_bean.xml");
这一步包括了注入MyConfig,和其下的所有@Bean、相关的@Autowired等;
相比XML配置法会多出一个myConfig的bean对象
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
因为要支持Servlet,所以要在WebConfig中至少配置这三个:
ServletWebServerFactory
:WebServer工厂(Spring的web容器,可以选Tomcat、Jetty等)DispatcherServlet
:web项目必备的DispatcherServlet(所有的请求路由分发)DispatcherServletRegistrationBean
:将DispatcherServlet注册到WebServer上还需要提供一个接口用于测试:实现Controller接口的Bean
(是 org.springframework.web.servlet.mvc.Controller接口,不是注解,需要实现handleRequest()
方法)
@Configuration
class WebConfig {
@Bean
// 1. WebServer工厂,这里用Tomcat
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
}
@Bean
// 2. web项目必备的DispatcherServlet
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
@Bean
// 3. 将DispatcherServlet注册到WebServer上
public DispatcherServletRegistrationBean dispatcherServletRegistrationBean(DispatcherServlet dispatcherServlet) {
return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
}
@Bean("/hello")//必须以/开头才能被识别为路径
public Controller controller1() {
//重写匿名
return (request, response) -> {
response.getWriter().println("hello");
return null;
};
}
}
AbstractApplicationContext
中的refresh()方法对bean生命周期的各个阶段进行了增强,本文不细讲