SpringBoot整合原理解析

原理解析

SpringBoot的启动过程

阶段一:SpringApplication 构造

  1. 记录 BeanDefinition 源
  2. 推断应用类型
  3. 记录 ApplicationContext 初始化器
  4. 记录监听器
  5. 推断主启动类

阶段二:执行 run 方法

  1. 得到 SpringApplicationRunListeners,名字取得不好,实际是事件发布器

    • 发布 application starting 事件1
  2. 封装启动 args

  3. 准备 Environment 添加命令行参数(*)

    (准备环境变量,和配置变量)

  4. ConfigurationPropertySources 处理(*)

    (添加一个处理参数格式的处理器)

    • 发布 application environment 已准备事件2
  5. 通过 EnvironmentPostProcessorApplicationListener 进行 env 后处理(*)

    • application.properties,由 StandardConfigDataLocationResolver 解析
    • spring.application.json
  6. 绑定 配置文件中的spring.main 到 SpringApplication 对象(*)

  7. 打印 banner(*)

  8. 创建容器

  9. 准备容器

    • 发布 application context 已初始化事件3
  10. 加载 bean 定义

(有xml配置文件,包扫描,配置类3种方式加载bean定义)

  • 发布 application prepared 事件4
  1. refresh 容器

    (这一步就对应这spring调用bean后处理工厂和bean后处理器的方法)

    • 发布 application started 事件5
  2. 执行 runner

    • 发布 application ready 事件6

    • 这其中有异常,发布 application failed 事件7

带 * 的有独立的示例

演示 - 启动过程

com.itheima.a39.A39_1 对应 SpringApplication 构造

com.itheima.a39.A39_2 对应第1步,并演示 7 个事件

com.itheima.a39.A39_3 对应第2、8到12步

org.springframework.boot.Step3

org.springframework.boot.Step4

org.springframework.boot.Step5

org.springframework.boot.Step6

org.springframework.boot.Step7

收获💡
  1. SpringApplication 构造方法中所做的操作

    • 可以有多种源用来加载 bean 定义
    • 应用类型推断
    • 添加容器初始化器
    • 添加监听器
    • 演示主类推断
  2. 如何读取 spring.factories 中的配置

  3. 从配置中获取重要的事件发布器:SpringApplicationRunListeners

  4. 容器的创建、初始化器增强、加载 bean 定义等

  5. CommandLineRunner、ApplicationRunner 的作用

    (CommandLineRunner执行的是原始的参数,ApplicationRunner执行的是经过处理的参数)

  6. 环境对象

    1. 命令行 PropertySource
    2. ConfigurationPropertySources 规范环境键名称
    3. EnvironmentPostProcessor 后处理增强
      • 由 EventPublishingRunListener 通过监听事件2️⃣来调用
    4. 绑定 spring.main 前缀的 key value 至 SpringApplication
  7. Banner

内嵌容器

springboot为我们集成了tomcat服务器,下图是一个运行在tomcat上jar包的文件结构

Server
└───Service
    ├───Connector (协议, 端口)
    └───Engine
        └───Host(虚拟主机 localhost)
            ├───Context1 (应用1, 可以设置虚拟路径, / 即 url 起始路径; 项目磁盘路径, 即 docBase )
            │   │   index.html
            │   └───WEB-INF
            │       │   web.xml (servlet, filter, listener) 3.0
            │       ├───classes (servlet, controller, service ...)
            │       ├───jsp
            │       └───lib (第三方 jar 包)
            └───Context2 (应用2)
                │   index.html
                └───WEB-INF
                        web.xml

SpringBoot内嵌tomcat关键代码演示

    public static void main(String[] args) throws LifecycleException, IOException {
        // 1.创建 Tomcat 对象
        Tomcat tomcat = new Tomcat();
        tomcat.setBaseDir("tomcat");

        // 2.创建项目文件夹, 即 docBase 文件夹
        File docBase = Files.createTempDirectory("boot.").toFile();
        docBase.deleteOnExit();

        // 3.创建 Tomcat 项目, 在 Tomcat 中称为 Context
        Context context = tomcat.addContext("", docBase.getAbsolutePath());

        WebApplicationContext springContext = getApplicationContext();

        // 4.编程添加 Servlet
        context.addServletContainerInitializer(new ServletContainerInitializer() {
            @Override
            public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
                HelloServlet helloServlet = new HelloServlet();
                servletContext.addServlet("aaa", helloServlet).addMapping("/hello");

                DispatcherServlet dispatcherServlet = springContext.getBean(DispatcherServlet.class);
                servletContext.addServlet("dispatcherServlet", dispatcherServlet).addMapping("/");
            }
        }, Collections.emptySet());

        // 5.启动 Tomcat
        tomcat.start();

        // 6.创建连接器, 设置监听端口
        Connector connector = new Connector(new Http11Nio2Protocol());
        connector.setPort(8080);
        tomcat.setConnector(connector);
    }

这个是基于javaEE的servlet

public class HelloServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        resp.getWriter().print("""
                <h3>hello</h3>
                """);
    }
}

那么我们需要在编程添加 Servlet这一步添加基于MVC的Controller

关键代码

WebApplicationContext springContext = getApplicationContext();

// 4.编程添加 Servlet
context.addServletContainerInitializer(new ServletContainerInitializer() {
    @Override
    public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
        // ⬇️通过 ServletRegistrationBean 添加 DispatcherServlet 等
        for (ServletRegistrationBean registrationBean : 
             springContext.getBeansOfType(ServletRegistrationBean.class).values()) {
            registrationBean.onStartup(ctx);
        }
    }
}, Collections.emptySet());

除此之外SpringBoot还内嵌了数据库,和内嵌缓存数据库

自动配置

SpringBoot还有一个重要的特性就是帮助我们自动配置了很多经常用到的库。

如何实现的,这是还原了部分源代码

    @Configuration // 本项目的配置类
    @Import(MyImportSelector.class)
    static class Config {
        //自己定义和第三方定义,自己定义的后加载,后加载的会覆盖先加载的
        @Bean
        public Bean1 bean1() {
            return new Bean1("本项目");
        }
    }

    static class MyImportSelector implements DeferredImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            List<String> names = SpringFactoriesLoader.loadFactoryNames(MyImportSelector.class, null);
            return names.toArray(new String[0]);
        }
    }

    @Configuration // 第三方的配置类
    static class AutoConfiguration1 {
        @Bean
        @ConditionalOnMissingBean
        public Bean1 bean1() {
            return new Bean1("第三方");
        }
    }

这里有一个非常重要的方法SpringFactoriesLoader.loadFactoryNames我们尝试打印一下他的返回值names

for (String name : names) {
    System.out.println("names返回值" + name);
}

names返回值com.example.a35.A41_1.AutoConfiguration1
names返回值com.example.a35.A41_1.AutoConfiguration2

他的结果是这个,这是因为

image-20220805160656195

image-20220805160712476

在META-INF中有一个spring.factories的文件,他里面定义了需要加载的配置类而SpringFactoriesLoader.loadFactoryNames就是获取所有配置类并且进行自动配置.

image-20220805160901587

我们可以发现SpringBoot的starter包里面会有spring.factories自动配置的文件

下面我们来介绍几种常用的库的自动配置

AopAutoConfiguration

Spring Boot 是利用了自动配置类来简化了 aop 相关配置

  • AOP 自动配置类为 org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
  • 可以通过 spring.aop.auto=false 禁用 aop 自动配置
  • AOP 自动配置的本质是通过 @EnableAspectJAutoProxy 来开启了自动代理,如果在引导类上自己添加了 @EnableAspectJAutoProxy 那么以自己添加的为准
  • @EnableAspectJAutoProxy 的本质是向容器中添加了 AnnotationAwareAspectJAutoProxyCreator 这个 bean 后处理器,它能够找到容器中所有切面,并为匹配切点的目标类创建代理,创建代理的工作一般是在 bean 的初始化阶段完成的
DataSourceAutoConfiguration

Spring Boot 管理关系型数据库的自动配置类

  • 对应的自动配置类为:org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
  • @EnableConfigurationProperties({DataSourceProperties.class}) ,通过这个来配置添加配置参数如url,username,password

简单说明一下,Spring Boot 支持两大类数据源:

  • EmbeddedDatabase - 内嵌数据库连接池(测试或者是小项目使用)
  • PooledDataSource - 非内嵌数据库连接池

PooledDataSource 又支持如下数据源

  • hikari 提供的 HikariDataSource
  • tomcat-jdbc 提供的 DataSource
  • dbcp2 提供的 BasicDataSource
  • oracle 提供的 PoolDataSourceImpl
MybatisAutoConfiguration
  • MyBatis 自动配置类为 org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
  • 它主要配置了两个 bean
    • SqlSessionFactory - MyBatis 核心对象,用来创建 SqlSession
    • SqlSessionTemplate - SqlSession 的实现,此实现会与当前线程绑定
    • 用 ImportBeanDefinitionRegistrar 的方式扫描所有标注了 @Mapper 注解的接口
    • 用 AutoConfigurationPackages 来确定扫描的包
  • 还有一个相关的 bean:MybatisProperties,它会读取配置文件中带 mybatis. 前缀的配置项进行定制配置

@MapperScan 注解的作用与 MybatisAutoConfiguration 类似,会注册 MapperScannerConfigurer 有如下区别

  • @MapperScan 扫描具体包(当然也可以配置关注哪个注解)
  • @MapperScan 如果不指定扫描具体包,则会把引导类范围内,所有接口当做 Mapper 接口
  • MybatisAutoConfiguration 关注的是所有标注 @Mapper 注解的接口,会忽略掉非 @Mapper 标注的接口

这里有同学有疑问,之前介绍的都是将具体类交给 Spring 管理,怎么到了 MyBatis 这儿,接口就可以被管理呢?

  • 其实并非将接口交给 Spring 管理,而是每个接口会对应一个 MapperFactoryBean,是后者被 Spring 所管理,接口只是作为 MapperFactoryBean 的一个属性来配置
TransactionAutoConfiguration
  • 事务自动配置类有两个:

    • org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration
    • org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
  • 前者配置了 DataSourceTransactionManager 用来执行事务的提交、回滚操作

  • 后者功能上对标 @EnableTransactionManagement,包含以下三个 bean

    • BeanFactoryTransactionAttributeSourceAdvisor 事务切面类,包含通知和切点
    • TransactionInterceptor 事务通知类,由它在目标方法调用前后加入事务操作
    • AnnotationTransactionAttributeSource 会解析 @Transactional 及事务属性,也包含了切点功能
  • 如果自己配置了 DataSourceTransactionManager 或是在引导类加了 @EnableTransactionManagement,则以自己配置的为准

自动装配

前面我们介绍了很多其他第三方的自动装配的原理,如果我们写的库需要自动装配需要怎么做

在SpringBoot中,需要在META-INF的spring.factories中添加

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.a35.A41_2.AutoConfiguration1,\
com.example.a35.A41_2.AutoConfiguration2

他起到的作用和上面MyImportSelector一样,不过SpringBoot会自动import

EnableAutoConfiguration

public class A41_2 {

    public static void main(String[] args) throws IOException {
        AnnotationConfigServletWebServerApplicationContext context = new AnnotationConfigServletWebServerApplicationContext();
        StandardEnvironment env = new StandardEnvironment();
        env.getPropertySources().addLast(new SimpleCommandLinePropertySource(
                "--spring.datasource.url=jdbc:mysql://localhost:3306/eesy",
                "--spring.datasource.username=root",
                "--spring.datasource.password=123456"
        ));
        context.setEnvironment(env);
        context.registerBean("config", Config.class);
        context.refresh();

        for (String name : context.getBeanDefinitionNames()) {
            String resourceDescription = context.getBeanDefinition(name).getResourceDescription();
            if (resourceDescription != null)
                System.out.println(name + " 来源:" + resourceDescription);
        }
        context.close();
    }

    @Configuration // 本项目的配置类
    @EnableAutoConfiguration
    static class Config {
        @Bean
        public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
            return new TomcatServletWebServerFactory();
        }
    }


    @Configuration // 第三方的配置类
    static class AutoConfiguration1 {
        @Bean
        public Bean1 bean1() {
            return new Bean1();
        }
    }

    @Configuration // 第三方的配置类
    static class AutoConfiguration2 {
        @Bean
        public Bean2 bean2() {
            return new Bean2();
        }
    }

    static class Bean1 {

    }
    static class Bean2 {

    }
}
条件装配

在应用的过程中我们往往需要进行条件装配,比如在web场景下如果导入的是tomcat依赖我们就不需要Jetty和Undertow,下面代码复刻了功能

    static class MyCondition implements Condition { // 存在 Druid 依赖
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnClass.class.getName());
            String className = attributes.get("className").toString();
            boolean exists = (boolean) attributes.get("exists");
            boolean present = ClassUtils.isPresent(className, null);
            return exists ? present : !present;
        }
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD, ElementType.TYPE})
    @Conditional(MyCondition.class)
    @interface ConditionalOnClass {
        boolean exists(); // true 判断存在 false 判断不存在

        String className(); // 要判断的类名
    }

    @Configuration // 第三方的配置类
    @ConditionalOnClass(className = "com.alibaba.druid.pool.DruidDataSource", exists = false)
    static class AutoConfiguration1 {
        @Bean
        public Bean1 bean1() {
            return new Bean1();
        }
    }
@Value注入底层

概况:Spring中Environment中存放可能用到的参数context会对他进行解析比如解析 ${}

    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A39.class);
        DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory();

        ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
        resolver.setBeanFactory(beanFactory);

        test1(context, resolver, Bean1.class.getDeclaredField("home"));
        test2(context, resolver, Bean1.class.getDeclaredField("age"));
    }
    
    private static void test2(AnnotationConfigApplicationContext context, ContextAnnotationAutowireCandidateResolver resolver, Field field) {
        DependencyDescriptor dd1 = new DependencyDescriptor(field, false);
        // 获取 @Value 的内容
        String value = resolver.getSuggestedValue(dd1).toString();
        System.out.println(value);

        // 解析 ${}
        value = context.getEnvironment().resolvePlaceholders(value);
        System.out.println(value);
        System.out.println(value.getClass());
        Object age = context.getBeanFactory().getTypeConverter().convertIfNecessary(value, dd1.getDependencyType());
        System.out.println(age.getClass());
    }

    private static void test1(AnnotationConfigApplicationContext context, ContextAnnotationAutowireCandidateResolver resolver, Field field) {
        DependencyDescriptor dd1 = new DependencyDescriptor(field, false);
        // 获取 @Value 的内容
        String value = resolver.getSuggestedValue(dd1).toString();
        System.out.println(value);

        // 解析 ${}
        value = context.getEnvironment().resolvePlaceholders(value);
        System.out.println(value);
    }

    public class Bean1 {
        @Value("${JAVA_HOME}")
        private String home;
        @Value("18")
        private int age;
    }

同时@Value还可以导入Bean

private static void test3(AnnotationConfigApplicationContext context, ContextAnnotationAutowireCandidateResolver resolver, Field field) {
    DependencyDescriptor dd1 = new DependencyDescriptor(field, false);
    // 获取 @Value 的内容
    String value = resolver.getSuggestedValue(dd1).toString();
    System.out.println(value);

    // 解析 ${}
    value = context.getEnvironment().resolvePlaceholders(value);
    System.out.println(value);
    System.out.println(value.getClass());

    // 解析 #{} @bean3
    Object bean3 = context.getBeanFactory().getBeanExpressionResolver().evaluate(value, new BeanExpressionContext(context.getBeanFactory(), null));

    // 类型转换
    Object result = context.getBeanFactory().getTypeConverter().convertIfNecessary(bean3, dd1.getDependencyType());
    System.out.println(result);
}
public class Bean2 {
    @Value("#{@bean3}") // SpringEL       #{SpEL}
    private Bean3 bean3;
}
@Autowired导入底层

DependencyDescriptor在@Autowired中这个类比较关键,他负责描述了,注入bean的依赖

成员变量注入

DependencyDescriptor dd1 = new DependencyDescriptor(Bean1.class.getDeclaredField("bean2"), false);
Object bean1 = beanFactory.doResolveDependency(dd1, "bean1", null, null);
@Autowired
private Bean2 bean2;

根据参数类型注入

Method setBean2 = Bean1.class.getDeclaredMethod("setBean2", Bean2.class);
DependencyDescriptor dd2 = new DependencyDescriptor(new MethodParameter(setBean2, 0), false);
Object bean2 = beanFactory.doResolveDependency(dd2, "bean2", null, null);
@Autowired
public void setBean2(Bean2 bean2) {
    this.bean2 = bean2;
}

包装为 Optional

DependencyDescriptor dd3 = new DependencyDescriptor(Bean1.class.getDeclaredField("bean3"), false);
dd3.increaseNestingLevel();
Object bean3 = beanFactory.doResolveDependency(dd3, "bean3", null, null);

increaseNestingLevel只有我们调用了这个方法之后会获取到Bean2

@Autowired
private Optional<Bean2> bean3;

包装为 ObjectProvider,ObjectFactory

DependencyDescriptor dd4 = new DependencyDescriptor(Bean1.class.getDeclaredField("bean4"), false);
if (dd4.getDependencyType() == ObjectFactory.class) {
    dd4.increaseNestingLevel();
    Object bean4 = beanFactory.doResolveDependency(dd4, "bean1", null, null);
    System.out.println(bean4);
}
@Autowired
private ObjectFactory<Bean2> bean4;

数组类型

private static void testArray(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
    DependencyDescriptor dd1 = new DependencyDescriptor(Target.class.getDeclaredField("serviceArray"), true);
    if (dd1.getDependencyType().isArray()) {
        Class<?> componentType = dd1.getDependencyType().getComponentType();
        System.out.println(componentType);
        String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, componentType);
        List<Object> beans = new ArrayList<>();
        for (String name : names) {
            System.out.println(name);
            Object bean = dd1.resolveCandidate(name, componentType, beanFactory);
            beans.add(bean);
        }
        beanFactory.getTypeConverter().convertIfNecessary(beans, dd1.getDependencyType());
    }
}
@Autowired
private Service[] serviceArray;

list类型

与数组类型类似

DependencyDescriptor dd2 = new DependencyDescriptor(Target.class.getDeclaredField("serviceList"), true);
if (dd2.getDependencyType() == List.class) {
    Class<?> resolve = dd2.getResolvableType().getGeneric().resolve();
    System.out.println(resolve);
    String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, resolve);
    List<Object> beanList = new ArrayList<>();
    for (String name : names) {
        System.out.println(name);
        Object o = dd2.resolveCandidate(name, resolve, beanFactory);
        beanList.add(o);
    }
}
@Autowired
private List<Service> serviceList;

ApplicationContext

DependencyDescriptor dd3 = new DependencyDescriptor(Target.class.getDeclaredField("applicationContext"), true);
Field resolvableDependencies = DefaultListableBeanFactory.class.getDeclaredField("resolvableDependencies");
resolvableDependencies.setAccessible(true);
Map<Class<?>, Object> dependencies = (Map<Class<?>, Object>) resolvableDependencies.get(beanFactory);
/*dependencies.forEach((k, v) -> {
    System.out.println("key:" + k + "value:" + v);
});*/
for (Map.Entry<Class<?>, Object> entry : dependencies.entrySet()) {
    dependencies.get(ConfigurableApplicationContext.class);
    if (entry.getKey().isAssignableFrom(ConfigurableApplicationContext.class)) {
        System.out.println(entry.getValue());
        break;
    }
}
@Autowired
private ConfigurableApplicationContext applicationContext;

泛型

DependencyDescriptor dd4 = new DependencyDescriptor(Target.class.getDeclaredField("dao"), true);
ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
Class<?> type = dd4.getDependencyType();
for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {
    BeanDefinition bd = beanFactory.getMergedBeanDefinition(name);
    //对比beanDefinition与DependencyDescriptor的泛型做比较
    if (resolver.isAutowireCandidate(new BeanDefinitionHolder(bd, name), dd4)) {
        System.out.println(name);
    }
}
interface Dao<T> {
}
@Component("dao1")
static class Dao1 implements Dao<Student> {
}

@Component("dao2")
static class Dao2 implements Dao<Teacher> {
}

@Qualifier

根据名字注入

DependencyDescriptor dd5 = new DependencyDescriptor(Target.class.getDeclaredField("service"), true);
Class<?> dependencyType = dd5.getDependencyType();
ContextAnnotationAutowireCandidateResolver resolver = new ContextAnnotationAutowireCandidateResolver();
for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, dependencyType)) {
    BeanDefinition bd = beanFactory.getMergedBeanDefinition(name);
    if (resolver.isAutowireCandidate(new BeanDefinitionHolder(bd, name), dd5)) {
        System.out.println(name);
        System.out.println(dd5.resolveCandidate(name, dependencyType, beanFactory));
    }
}
@Autowired
@Qualifier("service2")
private Service service;
interface Service {

}

@Component("service1")
static class Service1 implements Service {

}

@Component("service2")
static class Service2 implements Service {

}

@Component("service3")
static class Service3 implements Service {

}

@Primary

优先查找

static class Target1 {
    @Autowired
    private Service service;
}

static class Target2 {
    @Autowired
    private Service service3;
}

interface Service {

}

@Component("service1")
static class Service1 implements Service {

}

@Component("service2")
@Primary
static class Service2 implements Service {

}

@Component("service3")
static class Service3 implements Service {

}
private static void testPrimary(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
    DependencyDescriptor dd = new DependencyDescriptor(Target1.class.getDeclaredField("service"), false);
    Class<?> type = dd.getDependencyType();
    for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {
        if (beanFactory.getMergedBeanDefinition(name).isPrimary()) {
            System.out.println(name);
            System.out.println(dd.resolveCandidate(name, type, beanFactory));
        }
    }
}

Default

默认根据名字进行查找

private static void testDefault(DefaultListableBeanFactory beanFactory) throws NoSuchFieldException {
    DependencyDescriptor dd = new DependencyDescriptor(Target2.class.getDeclaredField("service3"), false);
    Class<?> type = dd.getDependencyType();
    for (String name : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, type)) {
        if (name.equals(dd.getDependencyName())) {
            System.out.println(name);
            System.out.println(dd.resolveCandidate(name, type, beanFactory));
        }
    }
}

发布监听

当我们的系统需要异步,除了使用mq中间件以为我们还可以通过,使用springboot事件监听器和发布器来完成,下面就是一个使用它的案例,不过他的缺点就是对于分布式的支持并不是很好

@Configuration
public class A41_1 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(A41_1.class);
        context.getBean(MyService.class).doBusiness();
        context.close();
    }

    static class MyEvent extends ApplicationEvent{
        public MyEvent(Object source) {
            super(source);
        }
    }

    @Component
    @Slf4j
    static class MyService {

        @Autowired
        private ApplicationEventPublisher publisher;

        public void doBusiness(){
            log.info("主线业务");
            publisher.publishEvent(new MyEvent("myService.doBusiness"));
//            log.info("发送短信");
//            log.info("发送邮件");
        }
    }

    @Component
    @Slf4j
    static class smsApplicationListener implements ApplicationListener<MyEvent> {
        @Override
        public void onApplicationEvent(MyEvent event) {
            log.info("发送短信");
        }
    }

    @Component
    @Slf4j
    static class emailApplicationListener implements ApplicationListener<MyEvent> {
        @Override
        public void onApplicationEvent(MyEvent event) {
            log.info("发送邮件");
        }
    }
}
事件监听器

自定义一个监听器

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface MyListener {
}

配置一个SmartInitializingSingleton,使他对于listener进行解析

@Bean
public SmartInitializingSingleton smartInitializingSingleton(ConfigurableApplicationContext context) {
    return () -> {
        //获取任意类型的bean加了MyListener注解解析他
        for (String name : context.getBeanDefinitionNames()) {
            Object bean = context.getBean(name);
            for (Method method : bean.getClass().getMethods()) {
                if (method.isAnnotationPresent(MyListener.class)) {
                    ApplicationListener<ApplicationEvent> listener = event -> {
                        //监听器方法需要的类型
                        Class<?> eventType = method.getParameterTypes()[0];
                        //筛选事件类型
                        if(eventType.isAssignableFrom(event.getClass())){
                            try {
                                method.invoke(bean, event);
                            } catch (IllegalAccessException | InvocationTargetException e) {
                                throw new RuntimeException(e);
                            }
                        }
                    };
                    //加入到context
                    context.addApplicationListener(listener);
                }
            }
        }
    };
}
事件发布器
@Bean
public ApplicationEventMulticaster applicationEventMulticaster(ConfigurableApplicationContext applicationContext, ThreadPoolTaskExecutor executor) {
    return new AbstractApplicationEventMulticaster() {
        private List<GenericApplicationListener> listeners = new ArrayList<>();

        @Override
        public void addApplicationListenerBean(String listenerBeanName) {
            log.info(listenerBeanName);
            ApplicationListener bean = applicationContext.getBean(listenerBeanName, ApplicationListener.class);
            //获取事件类型
            ResolvableType type = ResolvableType.forClass(bean.getClass()).getInterfaces()[0].getGeneric();
            log.info(String.valueOf(type));

            //将原始的listener封装为支持类型检查的listener
            GenericApplicationListener genericApplicationListener = new GenericApplicationListener() {
                @Override
                public void onApplicationEvent(ApplicationEvent event) {
                    executor.submit(()-> bean.onApplicationEvent(event));
                }

                //是否支持事件类型
                @Override
                public boolean supportsEventType(ResolvableType eventType) {
                    return type.isAssignableFrom(eventType);
                }
            };
            listeners.add(genericApplicationListener);
        }

        @Override
        public void multicastEvent(ApplicationEvent event, ResolvableType eventType) {
            listeners.forEach(i -> {
                if (i.supportsEventType(ResolvableType.forClass(event.getClass()))) {
                    i.onApplicationEvent(event);
                }
            });
        }
    };
}

添加一个线程池

@Bean
public ThreadPoolTaskExecutor executor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(3);
    executor.setMaxPoolSize(10);
    executor.setQueueCapacity(100);
    return executor;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值