Spring中bean的生命周期及扩展点分析

https://my.oschina.net/u/2819035/blog/736658 (待参考)

spring中bean的生命周期.png

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

BeanFactoryPostProcessor.png
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

InstantiationAwareBeanPostProcessor继承关系图.png

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 方法之前, 做一些容器初始化之前的工作");
    }
}
spring.factories文件.png

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
ClassPathBeanDefinitionScanner类图.png
主要功能:
>> 初始化扫描注册需要的组件,如,初始化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

NamespaceHandler继承关系图.png

综合应用参考下文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实现类中。
HttpMessageConverter在SpringMVC中的作用过程.png

3.11 MessageSource (国际化配置)

3.11.1 基于 SpringBoot 的小 demo

application.yml.png
controller.png
exception_en.properties.png

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注册过程

基本结构.png
启动类.png
# 基本解析流程: 详细流程可参看 
# 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: 发布事件

发布事件.png

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)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,125评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,293评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,054评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,077评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,096评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,062评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,988评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,817评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,266评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,486评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,646评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,375评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,974评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,621评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,642评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,538评论 2 352

推荐阅读更多精彩内容