https://my.oschina.net/u/2819035/blog/736658 (待参考)
1.结论:执行的先后顺序为:
step1: 构造器
step2: setter方法
step3: BeanNameAware的setBeanName方法
step4: BeanFactoryAware的setBeanFactory方法
step5: BeanPostProcessor的postProcessBeforeInitialization方法
step6: @PostConstruct注解标注的方法
step7: InitializingBean的afterPropertiesSet方法
step8: xml中的init-method方法
step9: BeanPostProcessor的postProcessAfterInitialization方法
step10: SmartInitializingSingleton 的 afterSingletonsInstantiated 方法
step11: @PreDestroy注解下的方法
step12: DisposableBean的destroy方法
step13: xml中的 destroy-method方法
2.demo
2.1pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zy</groupId>
<artifactId>java8</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>java8 Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.20</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
</dependencies>
<build>
<finalName>java8</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
2.2BeanLifeCycle类实现了InitializingBean接口,BeanNameAware接口,BeanFactoryAware接口,DisposableBean接口
package com.zy.lifecycle;
import lombok.Setter;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class BeanLifeCycle implements
InitializingBean,
BeanFactoryAware,
BeanNameAware,
DisposableBean {
private String name;
public void setName(String name) {
this.name = name;
System.out.println(">>>>>>>>>>>>>>>执行了setter方法<<<<<<<<<<<<<<<<<");
}
public BeanLifeCycle() {
System.out.println(">>>>>>>>>>>>>>>执行了空参构造方法<<<<<<<<<<<<<<<<<");
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("执行了>>>>>>>>>>>>>BeanFactoryAware的setBeanFactory方法<<<<<<<<<<<<<<<");
}
@Override
public void setBeanName(String name) {
System.out.println("执行了>>>>>>>>>>>>>BeanNameAware的setBeanName方法<<<<<<<<<<<<<<<");
}
@Override
public void destroy() throws Exception {
System.out.println("执行了>>>>>>>>>>>>>DisposableBean的destroy方法<<<<<<<<<<<<<<<");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("执行了>>>>>>>>>>>>>InitializingBean的afterPropertiesSet方法<<<<<<<<<<<<<<<");
}
public void initMethod(){
System.out.println("执行了>>>>>>>>>>>>>xml中的init-method方法<<<<<<<<<<<<<<<");
}
public void destroyMethod(){
System.out.println("执行了>>>>>>>>>>>>>xml中的 destroy-method方法<<<<<<<<<<<<<<<");
}
}
2.3BeanLifeCyclePostProcessor类,实现了BeanPostProcessor接口
package com.zy.lifecycle;
import lombok.Setter;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Component
public class BeanLifeCycle implements
InitializingBean,
BeanFactoryAware,
BeanNameAware,
SmartInitializingSingleton,
DisposableBean {
private String name;
public void setName(String name) {
this.name = name;
System.out.println(">>>>>>>>>>>>>>>执行了setter方法<<<<<<<<<<<<<<<<<");
}
public BeanLifeCycle() {
System.out.println(">>>>>>>>>>>>>>>执行了空参构造方法<<<<<<<<<<<<<<<<<");
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("执行了>>>>>>>>>>>>>BeanFactoryAware的setBeanFactory方法<<<<<<<<<<<<<<<");
}
@Override
public void setBeanName(String name) {
System.out.println("执行了>>>>>>>>>>>>>BeanNameAware的setBeanName方法<<<<<<<<<<<<<<<");
}
@Override
public void destroy() throws Exception {
System.out.println("执行了>>>>>>>>>>>>>DisposableBean的destroy方法<<<<<<<<<<<<<<<");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("执行了>>>>>>>>>>>>>InitializingBean的afterPropertiesSet方法<<<<<<<<<<<<<<<");
}
@Override
public void afterSingletonsInstantiated() {
System.out.println("执行了>>>>>>>>>>>>>实现 SmartInitializingSingleton 接口的 afterSingletonsInstantiated 方法<<<<<<<<<<<<<<<");
}
public void initMethod(){
System.out.println("执行了>>>>>>>>>>>>>xml中的init-method方法<<<<<<<<<<<<<<<");
}
public void destroyMethod(){
System.out.println("执行了>>>>>>>>>>>>>xml中的 destroy-method方法<<<<<<<<<<<<<<<");
}
@PreDestroy
public void preDestroy(){
System.out.println("执行了>>>>>>>>>>>>>@PreDestroy注解中的方法<<<<<<<<<<<<<<<");
}
@PostConstruct
public void postConstruct(){
System.out.println("执行了>>>>>>>>>>>>>@PostConstruct注解下的方法<<<<<<<<<<<<<<<");
}
}
2.4spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.zy.lifecycle"/>
<bean class="com.zy.lifecycle.BeanLifeCycle" id="beanLifeCycle"
init-method="initMethod" destroy-method="destroyMethod">
<property name="name" value="tom" />
</bean>
<bean class="com.zy.lifecycle.BeanLifeCyclePostProcessor"/>
</beans>
2.5单元测试
package com.zy.lifecycle;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
@Test
public void fn() {
ClassPathXmlApplicationContext ctx =
new ClassPathXmlApplicationContext("classpath:*applicationContext.xml");
BeanLifeCycle beanLifeCycle = (BeanLifeCycle) ctx.getBean("beanLifeCycle");
ctx.close();
}
}
2.6结果如下:
D:\JDK\bin\java.exe -Dvisualvm.id=347762319877467 -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\IntelliJIDEA\IntelliJ IDEA 2018.2.2\lib\idea_rt.jar=60242:D:\IntelliJIDEA\IntelliJ IDEA 2018.2.2\bin" -Dfile.encoding=UTF-8 -classpath "D:\IntelliJIDEA\IntelliJ IDEA 2018.2.2\lib\idea_rt.jar;D:\IntelliJIDEA\IntelliJ IDEA 2018.2.2\plugins\junit\lib\junit-rt.jar;D:\IntelliJIDEA\IntelliJ IDEA 2018.2.2\plugins\junit\lib\junit5-rt.jar;D:\JDK\jre\lib\charsets.jar;D:\JDK\jre\lib\deploy.jar;D:\JDK\jre\lib\ext\access-bridge-64.jar;D:\JDK\jre\lib\ext\cldrdata.jar;D:\JDK\jre\lib\ext\dnsns.jar;D:\JDK\jre\lib\ext\jaccess.jar;D:\JDK\jre\lib\ext\jfxrt.jar;D:\JDK\jre\lib\ext\localedata.jar;D:\JDK\jre\lib\ext\nashorn.jar;D:\JDK\jre\lib\ext\sunec.jar;D:\JDK\jre\lib\ext\sunjce_provider.jar;D:\JDK\jre\lib\ext\sunmscapi.jar;D:\JDK\jre\lib\ext\sunpkcs11.jar;D:\JDK\jre\lib\ext\zipfs.jar;D:\JDK\jre\lib\javaws.jar;D:\JDK\jre\lib\jce.jar;D:\JDK\jre\lib\jfr.jar;D:\JDK\jre\lib\jfxswt.jar;D:\JDK\jre\lib\jsse.jar;D:\JDK\jre\lib\management-agent.jar;D:\JDK\jre\lib\plugin.jar;D:\JDK\jre\lib\resources.jar;D:\JDK\jre\lib\rt.jar;E:\Programming\000_ssm_shiro_\java8\target\classes;E:\repository\junit\junit\4.11\junit-4.11.jar;E:\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;E:\repository\org\projectlombok\lombok\1.16.20\lombok-1.16.20.jar;E:\repository\redis\clients\jedis\2.9.0\jedis-2.9.0.jar;E:\repository\org\apache\commons\commons-pool2\2.4.2\commons-pool2-2.4.2.jar;E:\repository\org\springframework\spring-beans\4.2.4.RELEASE\spring-beans-4.2.4.RELEASE.jar;E:\repository\org\springframework\spring-core\4.2.4.RELEASE\spring-core-4.2.4.RELEASE.jar;E:\repository\commons-logging\commons-logging\1.2\commons-logging-1.2.jar;E:\repository\org\springframework\spring-context\4.2.4.RELEASE\spring-context-4.2.4.RELEASE.jar;E:\repository\org\springframework\spring-aop\4.2.4.RELEASE\spring-aop-4.2.4.RELEASE.jar;E:\repository\aopalliance\aopalliance\1.0\aopalliance-1.0.jar;E:\repository\org\springframework\spring-expression\4.2.4.RELEASE\spring-expression-4.2.4.RELEASE.jar" com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 -junit4 com.zy.lifecycle.App,fn
十一月 19, 2018 10:45:28 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4cc77c2e: startup date [Mon Nov 19 22:45:28 CST 2018]; root of context hierarchy
十一月 19, 2018 10:45:28 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from file [E:\Programming\000_ssm_shiro_\java8\target\classes\applicationContext.xml]
>>>>>>>>>>>>>>>执行了空参构造方法<<<<<<<<<<<<<<<<<
十一月 19, 2018 10:45:28 下午 org.springframework.context.support.ClassPathXmlApplicationContext doClose
信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@4cc77c2e: startup date [Mon Nov 19 22:45:28 CST 2018]; root of context hierarchy
>>>>>>>>>>>>>>>执行了setter方法<<<<<<<<<<<<<<<<<
执行了>>>>>>>>>>>>>BeanNameAware的setBeanName方法<<<<<<<<<<<<<<<
执行了>>>>>>>>>>>>>BeanFactoryAware的setBeanFactory方法<<<<<<<<<<<<<<<
执行了>>>>>>>>>>>>>BeanPostProcessor的postProcessBeforeInitialization方法<<<<<<<<<<<<<<<
执行了>>>>>>>>>>>>>@PostConstruct注解下的方法<<<<<<<<<<<<<<<
执行了>>>>>>>>>>>>>InitializingBean的afterPropertiesSet方法<<<<<<<<<<<<<<<
执行了>>>>>>>>>>>>>xml中的init-method方法<<<<<<<<<<<<<<<
执行了>>>>>>>>>>>>>BeanPostProcessor的postProcessAfterInitialization方法<<<<<<<<<<<<<<<
执行了>>>>>>>>>>>>>@PreDestroy注解中的方法<<<<<<<<<<<<<<<
执行了>>>>>>>>>>>>>DisposableBean的destroy方法<<<<<<<<<<<<<<<
执行了>>>>>>>>>>>>>xml中的 destroy-method方法<<<<<<<<<<<<<<<
3.Spring全家桶扩展点
3.1 Spring中bean的生命周期即为扩展点
如上文所述, Spring容器中的bean在初始化及销毁前, 如果实现了上述相关接口, 会执行对应的实现的方法, 可作为扩展点.
3.2 XXXAware
// 顶层接口: org.springframework.beans.factory.Aware
该接口主要的作用是一个标记超级接口,实现了该接口的bean是具有回调spring容器的能力.
这是一个空接口,实际方法的签名是由各个子接口来实现,通常只接受返回单个参数的setXXX方法.
// XXXAware在spring里表示对XXX可以感知,通俗点解释就是:
如果在某个类里面想要使用spring的一些东西,就可以通过实现XXXAware接口告诉spring,
spring看到后就会给你送过来,而接收的方式是通过实现接口唯一的方法setXXX。
// 常见 Aware
使用了spring Aware 你的bean将会和spring框架耦合(少用),
spring aware 的目的是为了让bean获取spring容器的服务:
1) BeanNameAware:
可以获取容器中bean的名称
2) BeanFactoryAware:
可以获取当前BeanFactory, 可以调用容器的服务
3) BeanClassLoaderAware:
可以获取当前ClassLoader
4) ApplicationContextAware:
可以当前的applicationContext,这也可以调用容器的服务
5) MessageSourceAware:
可以获得messageSource,能获取国际化文本信息
6) EnvironmentAware:
可以获取能获取当前容器的环境属性信息, 如 properties 及yml中配置信息, 激活/默认配置文件
凡注册到Spring容器内的bean,实现了EnvironmentAware接口重写setEnvironment方法后,
在工程启动时可以获得application.properties的配置文件配置的属性值。
7) ServletContextAware:
可以获取 WebApplicationContext 相关信息
8) ApplicationEventPulisherAware:
可以获取应用事件发布器,可以发布事件
9) ResourceLoaderAware:
可以获得资源加载器,可以获得外部资源文件的内容;
// 应用实例 --> Apache Dubbo
org.apache.dubbo.config.spring.beans.factory.annotation.ServiceAnnotationBeanPostProcessor
package com.zy.eureka.aware;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.context.ServletContextAware;
import javax.servlet.ServletContext;
@Component
public class MyAware implements EnvironmentAware, BeanNameAware, BeanFactoryAware, BeanClassLoaderAware, ApplicationContextAware, ApplicationEventPublisherAware, ServletContextAware, ResourceLoaderAware {
private Environment environment;
private String beanName;
private ConfigurableListableBeanFactory beanFactory;
private ClassLoader classLoader;
private ApplicationContext applicationContext;
private ApplicationEventPublisher applicationEventPublisher;
private ServletContext servletContext;
private ResourceLoader resourceLoader;
/**
* 若想读取 classpath 下, application.yml 配置文件中的配置项
* 方法1.实现了 EnvironmentAware 接口, 调用其 setEnvironment 方法即可获取 环境变量信息
* 方法2.使用 @Value 注解即可, 如:
* @Value("${psy.name}")
* private String psyName;
* @param environment
*/
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
String environmentProperty = environment.getProperty("psy.name", "zx");
System.out.println("EnvironmentAware -------------> " + environmentProperty);
}
@Override
public void setBeanName(String name) {
this.beanName = name;
System.out.println("BeanNameAware -----------> ");
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, beanFactory, "AnnotationInjectedBeanPostProcessor requires a ConfigurableListableBeanFactory");
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
System.out.println("BeanFactoryAware -----------> ");
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
System.out.println("BeanClassLoaderAware -----------> ");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
System.out.println("ApplicationContextAware -----------> ");
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
System.out.println("ApplicationEventPublisherAware -----------> ");
}
@Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
System.out.println("ServletContextAware -----------> ");
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
System.out.println("ResourceLoaderAware -----------> ");
}
public Environment getEnvironment() { return environment; }
public ClassLoader getClassLoader() { return classLoader; }
public ConfigurableListableBeanFactory getBeanFactory() { return beanFactory; }
public String getBeanName() { return beanName; }
public ApplicationContext getApplicationContext() { return applicationContext; }
public ApplicationEventPublisher getApplicationEventPublisher() { return applicationEventPublisher; }
public ServletContext getServletContext() { return servletContext; }
public ResourceLoader getResourceLoader() { return resourceLoader; }
}
3.3 BeanFactoryPostProcessor & BeanDefinitionRegistryPostProcessor
1.BeanPostProcessor
bean后置处理器,bean创建对象初始化前后进行拦截工作的
2.BeanFactoryPostProcessor
>> 定义
是beanFactory的后置处理器;
>> 调用时机:
允许使用者修改容器中的bean definitions, 在BeanFactory标准初始化之后调用,
这时所有的BeanDefinition已经保存加载到beanFactory,但是bean的实例还未创建
>> 能干什么:
来定制和修改BeanFactory的内容,如覆盖或添加BeanDefinition属性
可以与bean definitions打交道,但是千万不要进行bean实例化.
若要hack到bean实例化过程,请考虑使用BeanPostProcessor。
3.3.1 两种方式来注册 BeanDefinition
package com.zy.eureka.beanfactorypostprocessor;
import com.zy.eureka.bean.SelfBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.DefaultBeanNameGenerator;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.stereotype.Component;
/**
* 这里可以采用 实现 ImportBeanDefinitionRegistrar 接口的方式, 动态导入, 不必再加 @Component
*/
@Component
public class SelfBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
private BeanNameGenerator beanNameGenerator = new DefaultBeanNameGenerator();
/**
* 方法一: 若实现了 BeanDefinitionRegistryPostProcessor 接口, 可以考虑该方式
* 这里注册 BeanDefinition, 最终会将 Bean 注入到 BeanFactory
* @param registry
* @throws BeansException
*/
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(SelfBean.class);
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
builder.setScope(BeanDefinition.SCOPE_SINGLETON);
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
String beanName = beanNameGenerator.generateBeanName(beanDefinition, registry);
registry.registerBeanDefinition(beanName, beanDefinition);
}
/**
* 方法二: 只实现 BeanFactoryPostProcessor 接口的话, 可以考虑采用该方式
* 这里也可以注册 BeanDefinition, 最终会将 Bean 注入到 BeanFactory
* @param beanFactory
* @throws BeansException
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(SelfBean.InnerSelfBean.class);
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
builder.setScope(BeanDefinition.SCOPE_SINGLETON);
DefaultListableBeanFactory factory = (DefaultListableBeanFactory) beanFactory;
AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
String beanName = beanNameGenerator.generateBeanName(beanDefinition, factory);
factory.registerBeanDefinition(beanName, beanDefinition);
}
}
package com.zy.eureka.bean;
public class SelfBean {
public void self(String hello) {
System.out.println(hello + "----------------------");
}
public static class InnerSelfBean {
public void inner() {
System.out.println("inner self bean..........");
}
}
}
3.4 BeanPostProcessor
BeanPostProcessor的postProcessBeforeInitialization方法及postProcessAfterInitialization方法
都是在bean实例化(不是初始化)之后执行的, 具体执行时机详见本文第一部分, bean的生命周期的分析.
3.4.1 InstantiationAwareBeanPostProcessor
3.5 ImportXXX
3.5.1 ImportBeanDefinitionRegistrar
参考下文 4.1 中的 FishConfigurationRegistrar
3.6 XXXInitializer
3.6.1 ApplicationContextInitializer
ApplicationContextInitializer是一个回调接口,它会在
ConfigurableApplicationContext的refresh()方法调用之前被调用,做一些容器的初始化工作。
常用于需要对应用程序上下文进行编程初始化的web应用程序中。
ApplicationContextInitializer支持Order注解或接口,表示执行顺序,越小越早执行;
例如,根据上下文环境注册属性源或激活配置文件等。
#应用
1.Apache Dubbo
DubboApplicationContextInitializer
package com.zy.eureka.initializer;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MyApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
// 在 spring cloud 中, 注意: 这里可能初始化两次
System.out.println("这里可以在调用 refresh 方法之前, 做一些容器初始化之前的工作");
}
}
3.7 XXXListener & XXXEvent
3.7.2 XXXEvent
1.ContextRefreshedEvent
该事件在spring的上下文被初始化和刷新时触发。
这里的刷新其实就是指由ConfigurableApplicationContext定义的refresh方法,在重新加载属性文件等时调用。
2.ContextStartedEvent
Event raised when an {@code ApplicationContext} gets started.
调用地点: AbstractApplicationContext#start
3.ContextStoppedEvent
Event raised when an {@code ApplicationContext} gets stopped.
调用地点: AbstractApplicationContext#stop
4.ContextClosedEvent
Event raised when an {@code ApplicationContext} gets closed.
调用地点: AbstractApplicationContext#doClose
5.ServletRequestHandledEvent & RequestHandledEvent
Servlet-specific subclass of RequestHandledEvent, adding servlet-specific context information.
调用地点(最后未处理): FrameworkServlet#publishRequestHandledEvent
3.8 XXXScanner
3.8.1 ClassPathBeanDefinitionScanner (BeanDefinition扫描注册)
Spring提供了从classpath自动扫描Bean组建并将其对应的BeanDefinination加入到容器中的功能,
基于注解的自动扫描配置加载, 从而减轻了配置。
spring提供自动扫描功能的核心类是:ClassPathBeanDefinitionScanner,
该类根据提供个基础包名,扫描classpath下与该包名的路径下,
找到符合条件的类并注册到spring的BeanDefinition注册器中。
默认情况下,这个Scanner将会扫面所有用Spring指定了的注解标识了的类,如 @Component 系列注解。
也可以对扫描的机制进行配置,设置一些Filter,只有满足Filter的类才能被注册为Bean
主要功能:
>> 初始化扫描注册需要的组件,如,初始化Filters,设置resourceLoader,environment,registry
>> 对外提供scan方法,根据传入的包名,自动扫描加载BeanDefinition并将BeanDefinition注册到registry,
其中最终通过父类的findCandidateComponents方法完成扫描加载BeanDefinition
其中:
ClassPathBeanDefinitionScanner类主要完成扫描加载依赖组件的初始化以及将BeanDefinition注册到registry。
父类ClassPathScanningCandidateComponentProvider主要完成包扫描以及BeanDefinition加载。
下述三种方式最终都是通过ClassPathBeanDefinitionScanner类完成扫描注册工作
3.8.1.1 解析<context:component-scan/>标签后生成该Scanner
xml配置通过</context:component-scan>元素配置要扫描的包以及对应的filter,
spring默认对</context:component-scan>的处理器是ComponentScanBeanDefinitionParser
(ContextNamespaceHandler中配置的)
ComponentScanBeanDefinitionParser解析xml组装ClassPathBeanDefinitionScanner扫描注册
3.8.1.2 AnnotationConfigApplicationContext 的构造器生成Scanner
/**
* Create a new AnnotationConfigApplicationContext that needs to be populated
* through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
*/
public AnnotationConfigApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
3.8.1.3 通过@ComponentScan注解指定要扫描的包,通过ClassPathBeanDefinitionScanner自动扫描注册
注解对应生成的类是ComponentScanAnnotationParser,
ComponentScanAnnotationParser实际还是通过ClassPathBeanDefinitionScanner 来处理类的扫描注册。
3.9 NamespaceHandler
综合应用参考下文4.3
3.10 HttpMessageConverter
3.10.1 顶层接口
/**
* Strategy interface that specifies a converter that can convert from and to HTTP requests and responses.
* 转换 HTTP requests 和 responses 的策略接口
*/
public interface HttpMessageConverter<T> {
/**
* Indicates whether the given class can be read by this converter.
*/
boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
/**
* Indicates whether the given class can be written by this converter.
*/
boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
/**
* Return the list of {@link MediaType} objects supported by this converter.
*/
List<MediaType> getSupportedMediaTypes();
/**
* Read an object of the given type from the given input message, and returns it.
*/
T read(Class<? extends T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException;
/**
* Write an given object to the given output message.
*/
void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException;
}
3.10.2 概述
@RequestBody、@ResponseBody注解,可以直接将输入解析成Json、将输出解析成Json,
但HTTP 请求和响应是基于文本的,意味着浏览器和服务器通过交换原始文本进行通信,
而这里其实就是HttpMessageConverter发挥着作用。
Http请求响应报文其实都是字符串,当请求报文到java程序会被封装为一个ServletInputStream流,
开发人员再读取报文,响应报文则通过ServletOutputStream流,来输出响应报文。
从流中只能读取到原始的字符串报文,同样输出流也是。
那么在报文到达SpringMVC / SpringBoot和从SpringMVC / SpringBoot出去,
都存在一个字符串到java对象的转化问题。
这一过程,在SpringMVC / SpringBoot中,是通过HttpMessageConverter来解决的。
在Spring的处理过程中,一次请求报文和一次响应报文,
分别被抽象为一个请求消息HttpInputMessage和一个响应消息HttpOutputMessage。
处理请求时,由合适的消息转换器将请求报文绑定为方法中的形参对象,
在这里同一个对象就有可能出现多种不同的消息形式,如json、xml。
同样响应请求也是同样道理。
在Spring中,针对不同的消息形式,有不同的HttpMessageConverter实现类来处理各种消息形式,
至于各种消息解析实现的不同,则在不同的HttpMessageConverter实现类中。
3.11 MessageSource (国际化配置)
3.11.1 基于 SpringBoot 的小 demo
https://blog.csdn.net/haihui_yang/article/details/83987839 (国际化)
https://www.cnblogs.com/boshen-hzb/p/10869999.html (可参考)
4.综合应用
4.1 仿 Apache Dubbo 实现生产者向注册中心注册前的ServiceBean的BeanDefinition注册过程
# 基本解析流程: 详细流程可参看
# https://www.jianshu.com/p/49a675abcad0
-->@EnableFish
--> @FishComponentScan
--> @Import(FishConfigurationRegistrar.class)
--> FishConfigurationRegistrar#registerBeanDefinitions
--> MyBeanAnnotationBeanPostProcessor#postProcessBeanDefinitionRegistry
--> @MyBean注解的类, 如 Hello, 提取其信息, 动态生成一个新的类 (如MyDynamicBean),
将MyDynamicBean注册为 BeanDefinition, 最终生成一个或多个 MyDynamicBean 的实例.
其中MyDynamicBean可以实现相关接口, 如ApplicationListener接口,
在其实例化及初始化后, 完成其他功能, 如Dubbo中向zk中注册ServiceBean.
package com.zy.eureka.custominjection;
@MyBean(version = "1.0.0.100")
public class Hello {
}
package com.zy.eureka.custominjection;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.SingletonBeanRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.lang.annotation.Annotation;
import java.util.LinkedHashSet;
import java.util.Set;
import static org.springframework.context.annotation.AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR;
/**
* 实现 BeanDefinitionRegistryPostProcessor 接口, 实现其方法, 可以实现:
* 自定义注解, 并通过解析注解, 在 spring 容器启动的过程中, 扫描自定义注解, 注册 bean
*
* 参考: Dubbo 中
* {@link ServiceAnnotationBeanPostProcessor}
* 参考: Spring 中
* {@link ConfigurationClassPostProcessor}
* 这里 BeanDefiniton 注册过程 未完待续.............
*/
public class MyBeanAnnotationBeanPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware, ResourceLoaderAware, BeanClassLoaderAware {
private final Set<String> packagesToScan;
private Environment environment;
private ResourceLoader resourceLoader;
private ClassLoader classLoader;
public MyBeanAnnotationBeanPostProcessor(Set<String> packagesToScan) {
this.packagesToScan = packagesToScan;
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
System.out.println("这里实现解析自定义注解, 并注册 BeanDefinition 的逻辑");
Set<String> resolvePackagesToScan = resolvePackagesToScan(this.packagesToScan);
if (CollectionUtils.isEmpty(resolvePackagesToScan)) {
System.out.println("packagesToScan is empty , MyBean registry will be ignored!");
} else {
registerMyBeans(resolvePackagesToScan, registry);
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
private void registerMyBeans(Set<String> resolvePackagesToScan, BeanDefinitionRegistry registry) {
FishClassPathBeanDefinitionScanner scanner = new FishClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
scanner.setBeanNameGenerator(beanNameGenerator);
scanner.addIncludeFilter(new AnnotationTypeFilter(MyBean.class));
for (String packageToScan : resolvePackagesToScan) {
// Registers @MyBean Bean first
scanner.scan(packageToScan);
// Finds all BeanDefinitionHolders of @MyBean whether @ComponentScan scans or not.
Set<BeanDefinitionHolder> beanDefinitionHolders = findMyBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
registerMyBean(beanDefinitionHolder, registry, scanner);
// if (logger.isInfoEnabled()) {
// logger.info(beanDefinitionHolders.size() + " annotated Fish's @MyBean Components { " + beanDefinitionHolders + " } were scanned under package[" + packageToScan + "]");
// }
}
} else {
// if (logger.isWarnEnabled()) {
// logger.warn("No Spring Bean annotating Fish's @MyBean was found under package[" + packageToScan + "]");
// }
}
}
}
private void registerMyBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry, ClassPathBeanDefinitionScanner scanner) {
Class<?> beanClass = resolveClass(beanDefinitionHolder);
Annotation annotation = AnnotatedElementUtils.findMergedAnnotation(beanClass, MyBean.class);
/**
* The {@link AnnotationAttributes} of @MyBean annotation
*/
AnnotationAttributes myBeanAnnotationAttributes = AnnotationUtils.getAnnotationAttributes(annotation, false, false);
//////////////////////////////////////// 这里需要注册 BeanDefinition ////////////////////////////////////////
}
private Class<?> resolveClass(BeanDefinitionHolder beanDefinitionHolder) {
BeanDefinition beanDefinition = beanDefinitionHolder.getBeanDefinition();
String beanClassName = beanDefinition.getBeanClassName();
return ClassUtils.resolveClassName(beanClassName, classLoader);
}
private Set<BeanDefinitionHolder> findMyBeanDefinitionHolders(ClassPathBeanDefinitionScanner scanner, String packageToScan, BeanDefinitionRegistry registry, BeanNameGenerator beanNameGenerator) {
Set<BeanDefinition> beanDefinitions = scanner.findCandidateComponents(packageToScan);
Set<BeanDefinitionHolder> beanDefinitionHolders = new LinkedHashSet<>(beanDefinitions.size());
for (BeanDefinition beanDefinition : beanDefinitions) {
String beanName = beanNameGenerator.generateBeanName(beanDefinition, registry);
BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(beanDefinition, beanName);
beanDefinitionHolders.add(beanDefinitionHolder);
}
return beanDefinitionHolders;
}
/**
* It'd better to use BeanNameGenerator instance that should reference
* {@link ConfigurationClassPostProcessor#componentScanBeanNameGenerator},
* thus it maybe a potential problem on bean name generation.
*
* @param registry {@link BeanDefinitionRegistry}
* @return {@link BeanNameGenerator} instance
* @see SingletonBeanRegistry
* @see AnnotationConfigUtils#CONFIGURATION_BEAN_NAME_GENERATOR
* @see ConfigurationClassPostProcessor#processConfigBeanDefinitions
*/
private BeanNameGenerator resolveBeanNameGenerator(BeanDefinitionRegistry registry) {
BeanNameGenerator beanNameGenerator = null;
if (registry instanceof SingletonBeanRegistry) {
SingletonBeanRegistry singletonBeanRegistry = SingletonBeanRegistry.class.cast(registry);
beanNameGenerator = (BeanNameGenerator) singletonBeanRegistry.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
}
if (beanNameGenerator == null) {
// if (logger.isInfoEnabled()) {
// logger.info("BeanNameGenerator bean can't be found in BeanFactory with name [" + CONFIGURATION_BEAN_NAME_GENERATOR + "]");
// logger.info("BeanNameGenerator will be a instance of " + AnnotationBeanNameGenerator.class.getName() + " , it maybe a potential problem on bean name generation.");
// }
beanNameGenerator = new AnnotationBeanNameGenerator();
}
return beanNameGenerator;
}
private Set<String> resolvePackagesToScan(Set<String> packagesToScan) {
Set<String> resolvedPackagesToScan = new LinkedHashSet<>(packagesToScan.size());
for (String packageToScan : packagesToScan) {
if (StringUtils.hasText(packageToScan)) {
String placeholders = environment.resolvePlaceholders(packageToScan);
resolvedPackagesToScan.add(placeholders);
}
}
return resolvedPackagesToScan;
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
}
package com.zy.eureka.custominjection;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface MyBean {
/**
* @return version
*/
String version() default "";
}
package com.zy.eureka.custominjection;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ClassUtils;
import javax.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition;
/**
* Fish {@link FishComponentScan} Bean Registrar
*
* @see MyBean
* @see FishComponentScan
* @see ImportBeanDefinitionRegistrar
*/
public class FishConfigurationRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(@NotNull AnnotationMetadata importingClassMetadata, @NotNull BeanDefinitionRegistry registry) {
Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
registerMyBeanAnnotationBeanPostProcessor(packagesToScan, registry);
}
private void registerMyBeanAnnotationBeanPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
BeanDefinitionBuilder builder = rootBeanDefinition(MyBeanAnnotationBeanPostProcessor.class);
builder.addConstructorArgValue(packagesToScan);
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(), registry);
}
private Set<String> getPackagesToScan(AnnotationMetadata metadata) {
AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(FishComponentScan.class.getName()));
Class<?>[] basePackageClasses = attributes.getClassArray("basePackageClasses");
String[] basePackages = attributes.getStringArray("basePackages");
String[] value = attributes.getStringArray("value");
// Appends value array attributes
Set<String> packagesToScan = new LinkedHashSet<>(Arrays.asList(value));
packagesToScan.addAll(Arrays.asList(basePackages));
Arrays.stream(basePackageClasses).forEach(basePackage -> packagesToScan.add(ClassUtils.getPackageName(basePackage)));
if (packagesToScan.isEmpty()) {
return Collections.singleton(ClassUtils.getPackageName(metadata.getClassName()));
}
return packagesToScan;
}
}
package com.zy.eureka.custominjection;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import java.util.Set;
import static org.springframework.context.annotation.AnnotationConfigUtils.registerAnnotationConfigProcessors;
/**
* Fish {@link ClassPathBeanDefinitionScanner} that exposes some methods to be public.
*/
public class FishClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
public FishClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, Environment environment, ResourceLoader resourceLoader) {
this(registry, false, environment, resourceLoader);
}
public FishClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment, ResourceLoader resourceLoader) {
super(registry, useDefaultFilters);
setEnvironment(environment);
setResourceLoader(resourceLoader);
registerAnnotationConfigProcessors(registry);
}
@Override
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
return super.doScan(basePackages);
}
@Override
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
return super.checkCandidate(beanName, beanDefinition);
}
}
package com.zy.eureka.custominjection;
import org.springframework.context.annotation.Import;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Fish Component Scan {@link Annotation},scans the classpath for annotated components that will be auto-registered as
* Spring beans. my-annotation {@link MyBean}.
* @see MyBean
*/
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(FishConfigurationRegistrar.class)
public @interface FishComponentScan {
/**
* Alias for the {@link #basePackages()} attribute. Allows for more concise annotation
* declarations e.g.: {@code @FishComponentScan("org.my.pkg")} instead of
* {@code @FishComponentScan(basePackages="org.my.pkg")}.
*
* @return the base packages to scan
*/
String[] value() default {};
/**
* Base packages to scan for annotated @MyBean classes. {@link #value()} is an
* alias for (and mutually exclusive with) this attribute.
* <p>
* Use {@link #basePackageClasses()} for a type-safe alternative to String-based
* package names.
*
* @return the base packages to scan
*/
String[] basePackages() default {};
/**
* Type-safe alternative to {@link #basePackages()} for specifying the packages to
* scan for annotated @MyBean classes. The package of each class specified will be
* scanned.
*
* @return classes from the base packages to scan
*/
Class<?>[] basePackageClasses() default {};
}
package com.zy.eureka.custominjection;
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@FishComponentScan
@Inherited
public @interface EnableFish {
/**
* Base packages to scan for annotated @Service classes.
* <p>
* Use {@link #scanBasePackageClasses()} for a type-safe alternative to String-based
* package names.
*
* @return the base packages to scan
* @see FishComponentScan#basePackages()
*/
@AliasFor(annotation = FishComponentScan.class, attribute = "basePackages")
String[] scanBasePackages() default {};
/**
* Type-safe alternative to {@link #scanBasePackages()} for specifying the packages to
* scan for annotated @Service classes. The package of each class specified will be
* scanned.
*
* @return classes from the base packages to scan
* @see FishComponentScan#basePackageClasses
*/
@AliasFor(annotation = FishComponentScan.class, attribute = "basePackageClasses")
Class<?>[] scanBasePackageClasses() default {};
}
4.2 Spring 中的 Listener 与 Event 实现 Observer模式
可参考下述设计模式中的观察者模式
https://www.jianshu.com/p/b2a8e29a927f
step1: 实现 ApplicationEvent 接口
package com.zy.eureka.eventandlistener;
import lombok.Data;
import org.springframework.context.ApplicationEvent;
import java.io.Serializable;
public class MyEvent extends ApplicationEvent {
public MyEvent(EventBean source) {
super(source);
}
@Data
public static class EventBean implements Serializable {
private static final long serialVersionUID = 3867177398235790794L;
private Long id;
private String eventName;
private String eventLevel;
private String eventTime;
}
}
step2: 实现 ApplicationListener 接口, 定义泛型
package com.zy.eureka.eventandlistener;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
@Component
public class MyEventListener implements ApplicationListener<MyEvent> {
@Override
public void onApplicationEvent(MyEvent event) {
MyEvent.EventBean source = (MyEvent.EventBean) event.getSource();
System.out.println("eventName: --> " + source.getEventName());
System.out.println("eventLevel: --> " + source.getEventLevel());
System.out.println("eventTime: --> " + source.getEventTime());
}
}
step3: 发布事件
4.3 仿 Apache Dubbo 的 DubboNamespaceHandler 扩展Spring自定义标签
扩展Spring自定义标签大致需要如下几步:
1.创建需要扩展的组件
2.定义XSD文件描述组件内容
3.创建一个文件,实现BeanDefinitionParser接口,用来解析XSD文件中的定义和组件定义
4.创建Handler文件,扩展字NamespaceHandlerSupport,目的是将组件注册到Spring容器
5.编写Spring.handlers和Spring.schemas文件
https://www.hellojava.com/a/44944.html (自定义Spring xml标签)
参考资源
https://blog.csdn.net/lichunericli/article/details/89764662 (Spring扩展点)
https://www.cnblogs.com/hafiz/p/9155017.html (SpringBoot扩展点)
https://www.jianshu.com/p/52f96a333e9b (ClassPathBeanDefinitionScanner分析)
https://www.jianshu.com/p/333ed5ee958d (HttpMessageConverter)