springboot学习总结(个人总结)

一:加密算法

这里浏览器与网站互相发送加密的握手消息并验证,目的是为了保证双方都获得了一致的密码,并且可以正常的加密解密数据,为后续真正数据的传输做一次测试。另外,HTTPS一般使用的加密与HASH算法如下:

非对称加密算法:RSA,DSA/DSS
对称加密算法:AES,RC4,3DES
HASH算法:MD5,SHA1,SHA256

img

HTTPS协议和HTTP协议的区别:
https协议需要到ca申请证书,一般免费证书很少,需要交费。
http是超文本传输协议,信息是明文传输,https 则是具有安全性的ssl加密传输协议。
http和https使用的是完全不同的连接方式用的端口也不一样,前者是80,后者是443。
http的连接很简单,是无状态的 。
HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议, 要比http协议安全。

SSL 证书
从前面我们可以了解到HTTPS核心的一个部分是数据传输之前的握手,握手过程中确定了数据加密的密码。在握手过程中,网站会向浏览器发送SSL证书,SSL证书和我们日常用的身份证类似,是一个支持HTTPS网站的身份证明,SSL证书里面包含了网站的域名,证书有效期,证书的颁发机构以及用于加密传输密码的公钥等信息,由于公钥加密的密码只能被在申请证书时生成的私钥解密,因此浏览器在生成密码之前需要先核对当前访问的域名与证书上绑定的域名是否一致,同时还要对证书的颁发机构进行验证,如果验证失败浏览器会给出证书错误的提示。在这一部分我将对SSL证书的验证过程以及个人用户在访问HTTPS网站时,对SSL证书的使用需要注意哪些安全方面的问题进行描述。

二:SpringBoot2 | SpringBoot启动流程源码分析(一)

搭建源码环境

mvn clean install -DskipTests -Pfast
<properties>
        <revision>2.2.9.RELEASE</revision>
        <main.basedir>${basedir}</main.basedir>
        <disable-check>true</disable-check>
</properties>

首选是启动类

public static void main(String[] args) {
    SpringApplication.run(MarsApplication.class, args);
}
Spring Boot通常有一个名为*Application的入口类,在入口类里有一个main方法,这个main方法其实就是一个标准的java应用的入口方法。
在main方法中使用SpringApplication.run方法启动SpringBoot应用项目。


其中@SpringBootApplication是Spring Boot的核心注解,它是一个组合注解:

这里写图片描述

组合注解

其中@SpringBootApplication注解主要组合了@Configuration、@EnableAutoConfiguration、@ComponentScan。

如果不使用@SpringBootApplication注解,则可以使用在入口类上直接使用@Configuration、

@EnableAutoConfiguration、@ComponentScan也能达到相同效果。


其中几个注解的作用大致说一下:

@Configuration:是做类似于spring xml 工作的注解 标注在类上,类似与以前的**.xml配置文件。

@EnableAutoConfiguration:spring boot自动配置时需要的注解,会让Spring Boot根据类路径中的jar包依赖为当前项目进行自动配置。同时,它也是一个组合注解。
@SpringBootConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
}
@SpringBootConfiguration 也是一个组合注解,实际也可以用@Configuration替代

看到这里-----2022/1/4

@EnableAutoConfiguration的介绍
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 主要注解
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
在@EnableAutoConfiguration中用了@Import注解导入EnableAutoConfigurationImportSelector类,而EnableAutoConfigurationImportSelector就是自动配置关键。

@Import:Spring4.2之前只支持导入配置类
        Spring4.2之后支持导入普通的java类,并将其声明成一个bean

@ComponentScan:告诉Spring 哪个packages 的用注解标识的类 会被spring自动扫描并且装入bean容器。

SpringBoot的自动配置:SpringBoot的一大特色就是自动配置,例如:添加了spring-boot-starter-web依赖,会自动添加Tomcat和SpringMVC的依赖,SpringBoot会对Tomcat和SpringMVC进行自动配置。
又例如:添加了spring-boot-starter-data-jpa依赖,SpringBoot会自动进行JPA相关的配置。

SpringBoot会自动扫描@SpringBootApplication所在类的同级包以及下级包的Bean(如果为JPA项目还可以扫描标注@Entity的实体类),所以建议入口类放置在最外层包下。

spring-boot启动过程:

这里写图片描述

简单说就是:

在这里调用了SpringApplication的静态run方法,并将Application类对象和main方法的参数args作为参数传递了进去。

SpringApplication run方法:

/**
Static helper that can be used to run a SpringApplication from the specified source using default settings.
Params:
primarySource – the primary source to load
args – the application arguments (usually passed from a Java main method)
Returns:
*/
the running ApplicationContext
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        return run(new Class<?>[] { primarySource }, args);
    }
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return new SpringApplication(primarySources).run(args);
    }
在这个静态方法中,创建并构造了SpringApplication对象,并调用该对象的run方法。

构造SpringApplication对象:
构造SpringApplication对象:

这里写图片描述

本地版本

@SuppressWarnings({ "unchecked", "rawtypes" })
    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
        // 初始化器 该方法从springboot MATE-INFO 下spring.factories配置的
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        this.mainApplicationClass = deduceMainApplicationClass();
    }
    把上述initializers提前到构造函数中

getSpringFactoriesInstances

方法,该方法入参是class类型,是从mate-inf下加载所有classType的配置。

下面这两个类的作用:

ApplicationContextInitializer
ApplicationListener

主要是对一些属性附上初始值,关键还在与SpringApplication对象的initialize方法。

SpringApplication类中的构造函数中调用initialize方法,初始化SpringApplication对象的成员变量sources,webEnvironment,initializers,listeners,mainApplicationClass。

这里写图片描述

SpringApplication类中的构造函数中调用initialize方法,初始化SpringApplication对象的成员变量sources,webEnvironment,initializers,listeners,mainApplicationClass。

sources: 我们传给SpringApplication.run方法的参数

webEnvironment:

这里写图片描述

可以看到webEnvironment是一个boolean,该成员变量用来表示当前应用程序是不是一个Web应用程序。通过在classpath中查看是否存在WEB_ENVIRONMENT_CLASSES这个数组中所包含的类,如果存在那么当前程序即是一个Web应用程序,反之则不是。

​ 这里关键是调用SpringApplication对象中的getSpringFactoriesInstances方法,来获取ApplicationContextInitializer类型对象的列表。

这里写图片描述

在该方法中,首先通过调用SpringFactoriesLoader.loadFactoryNames(type, classLoader)来获取所有Spring Factories的名字(在包spring-boot-版本.jar下)。

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

这里写图片描述

# Logging Systems
org.springframework.boot.logging.LoggingSystemFactory=\
org.springframework.boot.logging.logback.LogbackLoggingSystem.Factory,\
org.springframework.boot.logging.log4j2.Log4J2LoggingSystem.Factory,\
org.springframework.boot.logging.java.JavaLoggingSystem.Factory

# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader

# ConfigData Location Resolvers
org.springframework.boot.context.config.ConfigDataLocationResolver=\
org.springframework.boot.context.config.ConfigTreeConfigDataLocationResolver,\
org.springframework.boot.context.config.StandardConfigDataLocationResolver

# ConfigData Loaders
org.springframework.boot.context.config.ConfigDataLoader=\
org.springframework.boot.context.config.ConfigTreeConfigDataLoader,\
org.springframework.boot.context.config.StandardConfigDataLoader

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers

# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener

# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor,\
org.springframework.boot.env.RandomValuePropertySourceEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\
org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor

# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.context.config.ConfigDataNotFoundFailureAnalyzer,\
org.springframework.boot.context.properties.IncompatibleConfigurationFailureAnalyzer,\
org.springframework.boot.context.properties.NotConstructorBoundInjectionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanDefinitionOverrideFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoSuchMethodFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.PatternParseFailureAnalyzer,\
org.springframework.boot.liquibase.LiquibaseChangelogMissingFailureAnalyzer

# Failure Analysis Reporters
org.springframework.boot.diagnostics.FailureAnalysisReporter=\
org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter

# Database Initializer Detectors
org.springframework.boot.sql.init.dependency.DatabaseInitializerDetector=\
org.springframework.boot.flyway.FlywayDatabaseInitializerDetector,\
org.springframework.boot.jdbc.AbstractDataSourceInitializerDatabaseInitializerDetector,\
org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializerDetector,\
org.springframework.boot.liquibase.LiquibaseDatabaseInitializerDetector,\
org.springframework.boot.orm.jpa.JpaDatabaseInitializerDetector,\
org.springframework.boot.r2dbc.init.R2dbcScriptDatabaseInitializerDetector

# Depends On Database Initialization Detectors
org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector=\
org.springframework.boot.sql.init.dependency.AnnotationDependsOnDatabaseInitializationDetector,\
org.springframework.boot.jdbc.SpringJdbcDependsOnDatabaseInitializationDetector,\
org.springframework.boot.jooq.JooqDependsOnDatabaseInitializationDetector,\
org.springframework.boot.orm.jpa.JpaDependsOnDatabaseInitializationDetector
我们从上面的spring.factories的资源文件中可以看到,得到的是     ConfigurationWarningsApplicationContextInitializer
ContextIdApplicationContextInitializer
DelegatingApplicationContextInitializer
ServerPortInfoApplicationContextInitializer这四个类的名字。

然后调用createSpringFactoriesInstances方法根据读取到的名字创建ApplicationContextInitializer实例(框起来的两行代码执行创建ApplicationContextInitializer实例)。

这里写图片描述

!

推断构造函数,创建实例

最后会将创建好的对象列表排序并返回。

SpringApplication对象的成员变量initalizers就被初始化为,ConfigurationWarningsApplicationContextInitializer,ContextIdApplicationContextInitializer,DelegatingApplicationContextInitializer,ServerPortInfoApplicationContextInitializer这四个类的对象组成的list(这是默认的成员变量initalizers初始化)。

下面是几个类的作用,这里不做详细介绍,以后会有详解介绍:

这里写图片描述

listeners:

listeners成员变量,是一个ApplicationListener<?>类型对象的集合。可以看到获取该成员变量内容使用的是跟成员变量initializers一样的方法,只不过传入的类型从ApplicationContextInitializer.class变成了ApplicationListener.class。

所以重点看spring.factories中取key为org.springframework.context.ApplicationListener的value有那些类:

这里写图片描述

也就是说,listener最终会被初始化为
ClearCachesApplicationListener
ParentContextCloserApplicationListener  FileEncodingApplicationListener
AnsiOutputApplicationListener           ConfigFileApplicationListener
DelegatingApplicationListener           LiquibaseServiceLocatorApplicationListener
ClasspathLoggingApplicationListener     LoggingApplicationListener
这几个类的对象组成的list。

何时触发,这里不做详解,等事件出现时,做详细说明(这里有一张网上的图,可以了解):

这里写图片描述

mainApplicationClass:

在deduceMainApplicationClass方法中,通过获取当前调用栈,找到入口方法main所在的类,并将其复制给SpringApplication对象的成员变量mainApplicationClass。在我们的例子中mainApplicationClass即是我们自己编写的Application类。

这里写图片描述

现在已经构造完SpringApplication对象了,之后就是调用该对象的run方法来运行spring boot项目。

理解:ApplicationContextInitializer与ApplicationListener

run方法:

6步骤

这里写图片描述

    /**
     * Run the Spring application, creating and refreshing a new
     * {@link ApplicationContext}.
     * @param args the application arguments (usually passed from a Java main method)
     * @return a running {@link ApplicationContext}
     */
    public ConfigurableApplicationContext run(String... args) {
        //StopWatch是来自org.springframework.util的工具类,可以用来方便的记录程序的运行时间
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        DefaultBootstrapContext bootstrapContext = createBootstrapContext();
        ConfigurableApplicationContext context = null;
        //设置系统属性java.awt.headless,在我们的例子中该属性会被设置为true,因为我们开发的是服务器程序,一般运行在没有显示器和键盘的环境,但是还是需要相关一些数据,这样我们就可以这样设置系统属性为headless模式。
        configureHeadlessProperty();
        //run方法中,加载了一系列SpringApplicationRunListener对象,在创建和更新ApplicationContext方法前后分别调用了listeners对象的started方法和finished方法, 并在创建和刷新ApplicationContext时,将listeners作为参数传递到了createAndRefreshContext方法中,以便在创建和刷新ApplicationContext的不同阶段,调用listeners的相应方法以执行操作。所以,所谓的SpringApplicationRunListeners实际上就是在SpringApplication对象的run方法执行的不同阶段,去执行一些操作,并且这些操作是可配置的.
        SpringApplicationRunListeners listeners = getRunListeners(args);
        //1. 创建ApplicationContext时调用startint方法
        listeners.starting(bootstrapContext, this.mainApplicationClass);
        try {
             //2. 创建并刷新ApplicationContext
            //这里是把main函数的args参数当做一个PropertySource来解析,默认情况下,args的长度是0,所以这里创建的DefaultApplicationArguments也没有实际的内容。
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            //ConfigurableEnvironment:创建并配置ApplicationConext的Environment
            //3. 在默认情况下,执行到此处时,environment成员变量为null,而webEnvironment成员变量的值为true,所以会创建一个StandardServletEnvironment对象并返回。之后会调用ConfigurableEnvironment类型的对象的configureEnvironment方法来配置上一步获取到的Environment对象。
            ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
            //
            configureIgnoreBeanInfo(environment);
            //打印banner
            Banner printedBanner = printBanner(environment);
            //4. 创建ApplicationContext:
            context = createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);
            prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
            //5. 最后刷新上下文
            refreshContext(context);
            //6. 结束刷新容器之后执行的afterRefresh()方法:
            afterRefresh(context, applicationArguments);
            //结束计时:
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
            }
            //加载SpringApplicationRunListener,方式和initializers,listeners相同,所以我们可以在spring.factories中取key为org.springframework.boot.SpringApplicationRunListener的value可以看出加载了哪些类:
            listeners.started(context);
            //实际应用中,我们会有在项目服务启动的时候就去加载一些数据或做一些事情这样的需求。比如读取配置文件,数据库连接之类的。SpringBoot给我们提供了两个接口来帮助我们实现这种需求。这两个接口分别为CommandLineRunner和ApplicationRunner。他们的执行时机为容器启动完成的时候。
//这两个接口中有一个run方法,我们只需要实现这个方法即可。这两个接口的不同之处在于:ApplicationRunner中run方法的参数为ApplicationArguments,而CommandLineRunner接口中run方法的参数为String数组。
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, listeners);
            throw new IllegalStateException(ex);
        }

        try {
            listeners.running(context);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }

StopWatch是来自org.springframework.util的工具类,可以用来方便的记录程序的运行时间。

这里写图片描述

设置系统属性java.awt.headless,在我们的例子中该属性会被设置为true,因为我们开发的是服务器程序,一般运行在没有显示器和键盘的环境,但是还是需要相关一些数据,这样我们就可以这样设置系统属性为headless模式。

这里写图片描述

代码

private void configureHeadlessProperty() {
        System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
                System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
    }
ConfigurableApplicationContext
run方法中,加载了一系列SpringApplicationRunListener对象,在创建和更新ApplicationContext方法前后分别调用了listeners对象的started方法和finished方法, 并在创建和刷新ApplicationContext时,将listeners作为参数传递到了createAndRefreshContext方法中,以便在创建和刷新ApplicationContext的不同阶段,调用listeners的相应方法以执行操作。所以,所谓的SpringApplicationRunListeners实际上就是在SpringApplication对象的run方法执行的不同阶段,去执行一些操作,并且这些操作是可配置的.

创建ApplicationContext时调用startint方法

这里写图片描述

更新ApplicationContext方法时调用finished方法:

这里写图片描述

加载SpringApplicationRunListener,方式和initializers,listeners相同,所以我们可以在spring.factories中取key为org.springframework.boot.SpringApplicationRunListener的value可以看出加载了哪些类:

这里写图片描述

EventPublishingRunListener:
可以看出加载的是org.springframework.boot.context.event.EventPublishingRunListener类
发布容器的一些事件

这里写图片描述

//EventPublishingRunListener对象在初始化时候将SpringApplication对象的成员变量listeners全都保存下来。
//等到自己的public方法被调用时,发布相应的事件,或执行相应的操作。
//RunListener是在SpringApplication对象的run方法执行到不同的阶段时,发布相应的event给SpringApplication对象的成员变量listeners中记录的事件监听器。
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

    private final SpringApplication application;

    private final String[] args;

    private final SimpleApplicationEventMulticaster initialMulticaster;

    public EventPublishingRunListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
        this.initialMulticaster = new SimpleApplicationEventMulticaster();
        for (ApplicationListener<?> listener : application.getListeners()) {
            this.initialMulticaster.addApplicationListener(listener);
        }
    }

    //---
}
EventPublishingRunListener对象在初始化时候将SpringApplication对象的成员变量listeners全都保存下来。

等到自己的public方法被调用时,发布相应的事件,或执行相应的操作。

RunListener是在SpringApplication对象的run方法执行到不同的阶段时,发布相应的event给SpringApplication对象的成员变量listeners中记录的事件监听器。


SpringApplicationRunListeners相关的类结构:

这里写图片描述

默认情况下,调用listeners的started方法,发布了ApplicationStartedEvent时,我们已经加载的事件监听器都做了什么操作,(实现了ApplicationListener接口的类,并且事件类型是ApplicationStartingEvent类型)。

这里写图片描述

在默认情况下,classpath中不存在liquibase,所以不执行任何操作。

LoggingApplicationListener监听ApplicationStartedEvent,会根据classpath中的类情况创建相应的日志系统对象,并执行一些初始化之前的操作;

这里写图片描述

默认情况下,创建的是org.springframework.boot.logging.logback.LogbackLoggingSystem类的对象,Logback是SpringBoot默认采用的日志系统。

到这里,ApplicationStartedEvent事件的处理这样就结束了。

创建并刷新 ApplicationContext:

这里写图片描述

DefaultApplicationArguments:

这里写图片描述

这里写图片描述

这里是把main函数的args参数当做一个PropertySource来解析,默认情况下,args的长度是0,所以这里创建的DefaultApplicationArguments也没有实际的内容。

ConfigurableEnvironment:创建并配置ApplicationConext的Environment

这里写图片描述

首先要调用getOrCreateEnvironment方法获取一个Environment对象。

这里写图片描述

在默认情况下,执行到此处时,environment成员变量为null,而webEnvironment成员变量的值为true,所以会创建一个StandardServletEnvironment对象并返回。

之后会调用ConfigurableEnvironment类型的对象的configureEnvironment方法来配置上一步获取到的Environment对象。

这里写图片描述

configureEnvironment方法先是调用configurePropertySources来配置properties,然后调用configureProfiles来配置profiles。

这里写图片描述

这里,configurePropertySources首先查看SpringApplication对象的成员变量defaultProperties,如果该变量非null且内容非空,则将其加入到Environment的PropertySource列表的最后。

之后查看SpringApplication对象的成员变量addCommandLineProperties和main函数的参数args,如果设置了addCommandLineProperties=true,且args个数大于0,那么就构造一个由main函数的参数组成的PropertySource放到Environment的PropertySource列表的最前面(这就能保证,我们通过main函数的参数来做的配置是最优先的,可以覆盖其他配置)。

在默认情况下,由于没有配置defaultProperties且main函数的参数args个数为0,所以这个函数什么也不做。

调用configureProfiles来配置profiles:

这里写图片描述

configureProfiles首先会读取Properties中key为spring.profiles.active的配置项,配置到Environment。

这里写图片描述

再将SpringApplication对象的成员变量additionalProfiles加入到Environment的active profiles配置中。

默认情况下,配置文件里没有spring.profiles.active的配置项,而SpringApplication对象的成员变量additionalProfiles也是一个空的集合,所以这个函数没有配置任何active profile。

调用SpringApplicationRunListeners类的对象listeners发布ApplicationEnvironmentPreparedEvent事件:

到这一步时,Environment就算是配置完成了。接下来调用SpringApplicationRunListeners类的对象listeners发布ApplicationEnvironmentPreparedEvent事件。

这里写图片描述

这里写图片描述

等到发布完事件之后,我们就可以看看,加载的ApplicationListener对象都有哪些响应了这个事件,做了什么操作:

FileEncodingApplicationListener响应该事件:

这里写图片描述

检查file.encoding配置是否与spring.mandatory_file_encoding一致
在默认情况下,因为没有spring.mandatory_file_encoding的配置,所以这个响应方法什么都不做。


AnsiOutputApplicationListener响应该事件:

这里写图片描述

根据spring.output.ansi.enabled和spring.output.ansi.console-available对AnsiOutput类做相应配置。在默认情况下,因为没有做配置,所以这个响应方法什么都不做。


ConfigFileApplicationListener响应该事件:

这里写图片描述

这里写图片描述

可以看到,ConfigFileApplicationListener从META-INF/spring.factories文件中读取EnvironmentPostProcessor配置,加载相应的EnvironmentPostProcessor类的对象,并调用其postProcessEnvironment方法。在我们的例子中,会加载CloudFoundryVcapEnvironmentPostProcessor和SpringApplicationJsonEnvironmentPostProcessor并执行,由于我们的例子中没有CloudFoundry和Json的配置,所以这个响应,不会加载任何的配置文件到Environment中来。

这里写图片描述

这里写图片描述

DelegatingApplicationListener响应该事件:将配置文件中key为context.listener.classes的配置项,加载在成员变量multicaster中

这里写图片描述

这里写图片描述

将配置文件中key为context.listener.classes的配置项,加载在成员变量multicaster中,因为在默认情况下没有key为context.listener.classes的Property,所以不会加载任何listener到该监听器中。

LoggingApplicationListener响应事件:对在ApplicationStarted时加载的LoggingSystem做一些初始化工作

这里写图片描述

这里写图片描述

这里写图片描述

默认情况下,是对加载的LogbackLoggingSystem做一些初始化工作。

打印Banner

这里写图片描述

这里写图片描述

printBanner方法中,首先会调用selectBanner方法得到一个banner对象,然后判断bannerMode的类型,如果是Banner.Mode.LOG,那么将banner对象转换为字符串,打印一条info日志,否则的话,调用banner对象的printbanner方法,将banner打印到标准输出System.out。

默认情况下,bannerMode是Banner.Mode.Console,而且也不曾提供过banner.txt这样的资源文件。所以selectBanner方法中得到到便是默认的banner对象,即SpringBootBanner类的对象:

这里写图片描述

创建ApplicationContext:

这里写图片描述

SpringApplication中调用createApplicationContext获取创建ApplicationContext(IOC容器),可以看到,当检测到本次程序是一个web应用程序(成员变量webEnvironment为true)的时候,就加载类
DEFAULT_WEB_CONTEXT_CLASS,否则的话加载DEFAULT_CONTEXT_CLASS。我们的例子是一个web应用程序,所以会加载DEFAULT_WEB_CONTEXT_CLASS,也就是org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext。
AnnotationConfigEmbeddedWebApplicationContext类的作用(功能):

这里写图片描述

可以看到我们加载的这个AnnotationConfigEmbeddedWebApplicationContext类,从名字就可以看出来,首先是一个WebApplicationContext实现了WebApplicationContext接口,然后是一个EmbeddedWebApplicationContext,这意味着它会自动创建并初始化一个EmbeddedServletContainer,同时还支持AnnotationConfig,会将使用注解标注的bean注册到ApplicationContext中。

总结起来就是:指定了容器的类名,最后通过Spring的工具类初始化容器类bean (BeanUtils.instantiate(contextClass))

这里写图片描述

通过调用Class对象的newInstance()方法来实例化对象,这等同于直接调用类的空的构造方法,所以我们来看AnnotationConfigEmbeddedWebApplicationContext类的构造方法:

这里写图片描述

构造方法中初始化了两个成员变量,类型分别为AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner用以加载使用注解的bean定义。
这样ApplicationContext对象就创建出来了,在createAndRefreshContext方法中创建了ApplicationContext对象之后会紧接着调用其setEnvironment将我们之前准备好的Environment对象赋值进去。之后分别调用postProcessApplicationContext和applyInitializers做一些处理和初始化的操作。

这里写图片描述

如果成员变量beanNameGenerator不为Null,那么为ApplicationContext对象注册beanNameGenerator bean。如果成员变量resourceLoader不为null,则为ApplicationContext对象设置ResourceLoader。默认情况下,这两个成员变量都为Null,所以什么都不做。

这里写图片描述

这里写图片描述

最后刷新上下文:

这里写图片描述

这里调用的是AbstractApplicationContext的refresh()方法刷新上下文。
1、this.prepareRefresh();
准备启动spring容器,设置容器的启动日期和活动标志

2、ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
主要是创建beanFactory,同时加载配置文件.xml中的beanDefinition  
通过String[] configLocations = getConfigLocations()获取资源路径,然后加载beanDefinition  

3、this.prepareBeanFactory(beanFactory);
给beanFactory注册一些标准组件,如ClassLoader,StandardEnvironment,BeanProcess  

4、this.postProcessBeanFactory(beanFactory);
提供给子类实现一些postProcess的注册,如AbstractRefreshableWebApplicationContext注册一些Servlet相关的postProcess,真对web进行生命周期管理的Scope,通过registerResolvableDependency()方法注册指定ServletRequest,HttpSession,WebRequest对象的工厂方法。

5、this.invokeBeanFactoryPostProcessors(beanFactory);
调用所有BeanFactoryProcessor的postProcessBeanFactory()方法  

6、this.registerBeanPostProcessors(beanFactory);
注册BeanPostProcessor,BeanPostProcessor作用是用于拦截Bean的创建  

7、this.initMessageSource();
初始化消息Bean  

8、this.initApplicationEventMulticaster();
初始化上下文的事件多播组建,ApplicationEvent触发时由multicaster通知给ApplicationListener  

9、this.onRefresh();
ApplicationContext初始化一些特殊的bean 

10、this.registerListeners();
注册事件监听器,事件监听Bean统一注册到multicaster里头,ApplicationEvent事件触发后会由multicaster广播  

11、this.finishBeanFactoryInitialization(beanFactory);
非延迟加载的单例Bean实例化

12、this.finishRefresh();
结束刷新
结束刷新容器之后执行的afterRefresh()方法:

这里写图片描述

这里写图片描述

实际应用中,我们会有在项目服务启动的时候就去加载一些数据或做一些事情这样的需求。比如读取配置文件,数据库连接之类的。SpringBoot给我们提供了两个接口来帮助我们实现这种需求。这两个接口分别为CommandLineRunner和ApplicationRunner。他们的执行时机为容器启动完成的时候。
这两个接口中有一个run方法,我们只需要实现这个方法即可。这两个接口的不同之处在于:ApplicationRunner中run方法的参数为ApplicationArguments,而CommandLineRunner接口中run方法的参数为String数组。
结束监听器:

这里写图片描述

这里写图片描述

这里写图片描述

从上述代码中可以看出,finished()方法是通过发布**结束监听**这个事件,当实现onApplication(相应事件)的监听类监听到这个事件时,就会结束相应的监听。
结束计时:

stopWatch.stop();

开始打印Log信息:

这里写图片描述

找到你的入口函数(xxApplication.class),开始打印Log信息

org.springframework.context.annotation;

自动装配

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8B25OiJv-1667359030555)(C:\Users\zhang\AppData\Roaming\Typora\typora-user-images\image-20220419085150015.png)]

总结:SpringBoot启动的时候通过@EnableAutoConfiguration注解找到META-INF/spring.factories文件中的所有自动配置类,并对其加载,这些自动配置类都是以AutoConfiguration结尾来命名的。它实际上就是一个JavaConfig形式的IOC容器配置类,通过以Properties结尾命名的类中取得在全局配置文件中配置的属性,如server.port。
*Properties类的含义:封装配置文件的相关属性。
*AutoConfiguration类的含义:自动配置类,添加到IOC容器中。

mate-spring.factories

key是:

org.springframework.boot.autoconfigure.EnableAutoConfiguration

三:注解

bean初始化步骤

 * ValueService -》无参构造-(推断构造)》普通对象-》依赖注入-》初始化前(@PostConstruct)
 -》初始化(InitializingBean的接口方法与)-》初始化后-》代理对象-》bean

bean找bean的过程

/**
 * 如果没有无参,有两个有参,则会报错,若在其中一个有参构造函数,上添加@Autowired,
 * 则会加载这个
 *  在appconfig中有多个bean,则如果名字对应不上,则不会加载
 *  在容器中是以beanName做为key map<beanName,bean对象》
 *  bean找的方式,是先以type,在以name
 *  开始以类型找,然后找到了多个,在以名字匹配。如果只有一个,那么名字就随便写
 *  orderService123
  * @param orderService123
 *      *  目前加载了3个,orderService orderService1 orderService2
 *      *  入参是orderService123 则会报错,是因为有3个,我不知道用哪个
 *      *  若指定一个其中一个名字,则用其中一个,就是名字对应不上,也会用
 * @param nameService4
 *       该nameService只有一个,所以不会多用
 */
@Autowired
public ValueService(OrderService orderService123,NameService nameService4) {
    this.orderService = orderService123;
    this.nameService = nameService4;
    System.out.println(orderService.getClass().getName());
    System.out.println(nameService.getClass().getName());
    System.out.println(22222222);
}
在springboot中如何应用

在类中注入

//注入
Map<String,orderService> userServiceMap;
//使用 
userServiceMap.get("orderService1");
如果只有两个的处理

@service与@Configuration+@Bean

@Primary

aop

springboot使用cglib进行动态代理

因此导致,拿到这个对象,是没有该对象的实例

当执行方法时候,就先执行target实例对象,

然后在执行test()

userService 需不需要aop,就是要看是否有aop

  1. 找出切面bean。遍历所有的切面bean 的方法
  2. 缓存这些方法
class UserService extends UserService{
    UserService target;
    public void test(){
        //执行切面逻辑
        //执行代理对象的test
        target.test();
    }
}

spring事务

@Configration

加了之后,事务才起作用

@SpringbootApplication 是组合

事务属性

never

never
执行该sql。如果有事务,则抛异常

springboot执行事务流程

1.判断方法有没有@Transa
2.创建数据库连接
3.conn.setautoCommit = false
4.执行代理方法  target.test() sq1.sq2
5.conn.commit

如果执行方法,需要自己注入自己,那么代理时候,就会用到sql定义事务的属性或者方法

@Bean

autoWrite

no 不会自动注入
by_name
by_type

@bean 不仅能写在@Configuration 还能写在@Component上

@Configuration+@Bean

用法

@Component+@Bean

创建的bean是不同的

wenxian00001com.huayun.service.Hudie@3c0e00a8
wenxian00001com.huayun.service.Hudie@1860f3be
hudie002: com.huayun.service.Hudie@7730ef88

@Configuration+@Bean

创建的是同一个

wenxian00002: com.huayun.service.Hudie@6b6eae52
wenxian00001com.huayun.service.Hudie@6b6eae52
wenxian00001com.huayun.service.Hudie@6b6eae52

配置bean与普通的bean

配置bean
有@Configuration
porxyBeanMethods 默认为true,表示是fullbean
porxyBeanMethods 默认为fasle,表示是lite配置 bean
lite配置bean的
@Component
@ComponentScan
@Import
@ImportSource
@Bean
配置bean作用
对于配置bean,不仅仅是放入到spring容器,spring还会解析配置Bean
porxyBeanMethods 默认为true,对应的类就是代理对象
porxyBeanMethods 默认为fasle,对应类型就是普通对象

@ComponentScan

@ComponentScan:表示将该类自动发现扫描组件。个人理解相当于,如果扫描到有@Component、@Controller、@Service等这些注解的类,并注册为Bean,可以自动收集所有的Spring组件,包括@Configuration类。我们经常使用@ComponentScan注解搜索beans,并结合@Autowired注解导入。可以自动收集所有的Spring组件,包括@Configuration类。我们经常使用@ComponentScan注解搜索beans,并结合@Autowired注解导入。如果没有配置的话,Spring Boot会扫描启动类所在包下以及子包下的使用了@Service,@Repository等注解的类。

@Import

用来导入其他配置类

@ImportResource

用来加载xml配置文件

springboot注入bean的顺序

如果有个一个类,如果有子类,就注入子类,没有子类,就注入本身

就是在父类注入bean的时候限制条件,就是如果有子类,就不加载,没有子类在注入

使用@Configuration+@Bean

自动注入

自动注入使用spel表达式,调用类与方法

该类一定要有自己的默认的构造方法

  1. Springboot加载bean时,默认按照无参构造
  2. 如果有有参构造的话,会覆盖无参构造,只需要再写一个无参构造就行了

补充知识:Spring Boot - Spring Beans之依赖构造器注入

使用所有Spring Framework技术定义的beans以及他们的依赖注入都是免费的。简单起见,我们通常使用@CompnentScan查找beans,结合@Autowired构造注入效果比较好。

如果你的代码结构是按之前建议的结构(将应用类放到根包里),你可以添加@ComponentScan,不需要任何参数。这样你所有的应用组件(@Component,@Service,@Repository,@Controller等等)都将会注册为Spring Beans。

@Configuration 和 @Component 区别

连接

https://blog.csdn.net/isea533/article/details/78072133?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164128626016781683962573%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=164128626016781683962573&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_positive~default-1-78072133.first_rank_v2_pc_rank_v29&utm_term=%40configuration&spm=1018.2226.3001.4187

一句话概括就是 @Configuration 中所有带 @Bean 注解的方法都会被动态代理,因此调用该方法返回的都是同一个实例。

下面看看实现的细节。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    String value() default "";
}

从定义来看, @Configuration 注解本质上还是 @Component,因此 context:component-scan/ 或者 @ComponentScan 都能处理@Configuration 注解的类。

@Configuration 标记的类必须符合下面的要求:

配置类必须以类的形式提供(不能是工厂方法返回的实例),允许通过生成子类在运行时增强(cglib 动态代理)。
配置类不能是 final 类(没法动态代理)。
配置注解通常为了通过 @Bean 注解生成 Spring 容器管理的类,
配置类必须是非本地的(即不能在方法中声明,不能是 private)。
任何嵌套配置类都必须声明为static。
@Bean 方法可能不会反过来创建进一步的配置类(也就是返回的 bean 如果带有 @Configuration,也不会被特殊处理,只会作为普通的 bean)。

加载过程

Spring 容器在启动时,会加载默认的一些 PostPRocessor,其中就有 ConfigurationClassPostProcessor,这个后置处理程序专门处理带有 @Configuration 注解的类,这个程序会在 bean 定义加载完成后,在 bean 初始化前进行处理。主要处理的过程就是使用 cglib 动态代理增强类,而且是对其中带有 @Bean 注解的方法进行处理。

@Component 注意

@Component 注解并没有通过 cglib 来代理@Bean 方法的调用,因此像下面这样配置时,就是两个不同的 country。

按照下面就是写得同一个实例

@Component
public class MyBeanConfig {

    @Bean
    public Country country(){
        return new Country();
    }

    @Bean
    public UserInfo userInfo(){
        return new UserInfo(country());
    }

}

1.注解分类

1.1 spring注解分类

spring注解场景说明版本
@Repository数据仓储模式注解2.0
@Component通用组件模式注解2.5
@Service服务模式注解2.5
@ControllerWeb 控制器模式注解2.5
@Configuration配置类模式注解3.0

1.2 装配注解

spring注解场景说明版本
@ImportResource替换 XML 元素 2.5
@Import导入 Configuration 类2.5
@ComponentScan扫描指定 package 下标注 Spring 模式注解的类3.1

1.3依赖注入

spring注解场景说明版本
@AutowiredBean 依赖注入,支持多种依赖查找方式2.5
@Qualifier细粒度的 @Autowired 依赖查找2.5

2.spring 注解编程模型

  • 元注解
  • 模式注解
  • 组合注解
  • 注释属性别名和覆盖

2.1 元注解

元注解是用于修饰其它注解的注解。JDK5.0提供了四种元注解:

  • @Retention
  • @Target
  • @Documented
  • @Inherited

一定程度上可以说 :框架=注解+反射+设计模式。

/**
 * 元注解(meta-annotation)一共有四种
 * 常用的元注解有以下两种:
 * @Target  用在什么地方: 指明了修饰的这个注解的使用范围,即被描述的注解可以用在哪里。
 * ElementType.TYPE:接口、类、枚举、注解
 * ElementType.FIELD:字段、枚举的常量
 * ElementType.METHOD:方法
 * ElementType.PARAMETER:方法参数
 * ElementType.CONSTRUCTOR:构造函数
 * ElementType.LOCAL_VARIABLE:局部变量
 * ElementType.ANNOTATION_TYPE:注解
 * ElementType.PACKAGE:包
 *
 * @Retention 注解的生命周期: 指明修饰的注解的生存周期,即会保留到哪个阶段。
 * RetentionPolicy.SOURCE:这种类型的Annotations只在源代码级别保留,编译时就会被忽略
 * RetentionPolicy.CLASS —— 这种类型的Annotations编译时被保留,在class文件中存在,但JVM将会忽略
 * RetentionPolicy.RUNTIME —— 这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用.
 *
 * @Documented注解   功能:指明修饰的注解,可以被例如javadoc此类的工具文档化,只负责标记,没有成员取值。
 *
 * @Inherited 允许子类继承父类中的注解。
 * 自己实现一个注解
 * 1、创建一个注解
 * 2、利用aop实现注解功能
 */
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String name() default "";

    String value() default "";
}

@interface 准确的说它不是一个接口,而是一个新的注释类型-注释类,它的类名就是注释名

@Target
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value();
}

它的意思是说,用了@Target 注解的注解,可以被用在哪些作用域中,有哪些作用域需要到

@Retention

@Retention 是定义该注解的生命周期有多久 ,而决定它的生命周期在一个枚举类型的RetentionPolicy里

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}

  • SOURCE:在源文件中有效(即源文件保留),编译器直接丢弃这种策略的注释,在.class文件中不会保留注解信息
  • CLASS:在class文件中有效(即class保留),保留在.class文件中,但是当运行Java程序时,他就不会继续加载了,不会保留在内存中,JVM不会保留注解。如果注解没有加Retention元注解,那么相当于默认的注解就是这种状态。
  • RUNTIME:在运行时有效(即运行时保留),当运行 Java程序时,JVM会保留注释,加载在内存中了,那么程序可以通过反射获取该注释
@Documented

用于指定被该元注解修饰的注解类将被javadoc工具提取成文档。默认情况下,javadoc是 不包括注解的,但是加上了这个注解生成的文档中就会带着注解了。

@Documented 注释只是用来生成文档的,不重要

@Inherited

被它修饰的Annotation将具有继承性。如果某个类使用了被@Inherited修饰的Annotation,则其子类将自动具有该注解。

2.2 spring 模式注解

理解 @Component “派⽣性”
元标注 @Component 的注解在 XML 元素 context:component-scan 或注解 @ComponentScan 扫描中“派
生”了 @Component 的特性,并且从 Spring Framework 4.0 开始支持多层次“派⽣性”。

举例说明
• @Repository
• @Service
• @Controller
• @Configuration
• @SpringBootConfiguration(Spring Boot)

基本定义
Spring 组合注解(Composed Annotations)中的元注允许是 Spring 模式注解(Stereotype Annotation)与其
他 Spring 功能性注解的任意组合。

Controller与Service与Component

@Controller 、@Service和@Component这三个注解的关系和区别。网上很多人对这三个注解进行了详细的解释,但是仅仅局限于理论,个人对于没有经过自己验证的结果总是持怀疑态度,所有花时间研究了一下,也对这三个注解理解的更加透彻。

@component

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
	String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // 关键注解
public @interface Controller {
	@AliasFor(annotation = Component.class)
	String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // 关键注解
public @interface Service {
	@AliasFor(annotation = Component.class)
	String value() default "";
}
注解扫描

首先说说这三个注解的关系,从源码中可以看出,@Controller和@Service都派生于@Component,所以三者的使用方式基本没什么差别。(ps:既然这么设计,那一定是有区别的)

平时的开发中,我们通常在控制层采用注解@Controller,在业务层采用注解@Service。spring在启动时,有一个非常核心的类ConfigurationClassPostProcessor会对类路径下的所以类进行扫描,将符合条件的bean扫描出来添加到beanDefinitionMap集合中,方便接下来的实例化。具体的扫描过程比较复杂,仅仅贴出核心判断逻辑代码

protected boolean matchSelf(MetadataReader metadataReader) {
	AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
	return metadata.hasAnnotation(this.annotationType.getName()) ||
			(this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));
}

代码解释:

1)this.annotationType.getName():获取的是注解@Component的全路径名org.springframework.stereotype.Component。

(2)metadata.hasAnnotation(this.annotationType.getName()):判断当前的类是否直接采用注解@Component。

(3)metadata.hasMetaAnnotation(this.annotationType.getName()):如果当前的类没有直接采用@Component,而是采用了类组合注解@Controller,判断组合注解@Controller中是否包含@Component。

至此,所有添加了注解@Controller、@Service和@Component都被spring扫描出来了。(ps:这就说明了其实在扫描的时候spring其实将这三个注解都按照@Component进行扫描的)

@Controller分析

如果不使用springMVC时,三者使用其实是没有什么差别的,但如果使用了springMVC,@Controller就被赋予了特殊的含义。

spring会遍历上面扫描出来的所有bean,过滤出那些添加了注解@Controller的bean,将Controller中所有添加了注解@RequestMapping的方法解析出来封装成RequestMappingInfo存储到RequestMappingHandlerMapping中的mappingRegistry。后续请求到达时,会从mappingRegistry中查找能够处理该请求的方法。

部分核心代码如下

org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping

protected boolean isHandler(Class<?> beanType) {
	// 判断扫描出来的bean是否包含注解@Controller,
	// 如果包含,springMVC会将其封装为RequestMappingInfo
	return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
			AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
	// 判断传递进来的方法是否包含@RequestMapping,
	// 如果包含,就将其封装成RequestMappingInfo
	RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
	RequestCondition<?> condition = (element instanceof Class ?
			getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
	return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}

RequestMappingHandlerMapping作用

参考:

RequestMappingHandlerMapping作用

使用SpringMVC开发的人都知道最常用的定义接口的方式就是借助于@Controller和@RequestMapping等注解实现。而RequestMappingHandlerMapping类就是用于扫描注解和生成URL映射,处理请求时的URL映射匹配等关键功能的。

接口是如何注册的

RequestMappingHandlerMapping通过继承AbstractHandlerMethodMapping抽象类间接的实现了InitializingBean接口,在启动时,会执行afterPropertiesSet方法。在afterPropertiesSet方法中,初始化了config对象,以及调用父类AbstractHandlerMethodMapping的afterPropertiesSet,父类方法afterPropertiesSet 逻辑是 initHandlerMethods,也就是父类AbstractHandlerMethodMapping 的 initHandlerMethods方法是SpringMvc初始化寻找Controller以及映射加载的核心逻辑。

@Override
    public void afterPropertiesSet() {
        this.config = new RequestMappingInfo.BuilderConfiguration();
        this.config.setPathHelper(getUrlPathHelper());
        this.config.setPathMatcher(getPathMatcher());
        this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
        this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
        this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
        this.config.setContentNegotiationManager(getContentNegotiationManager());
     //调用父类 afterPropertiesSet 方法,这是SpringMvc映射关系加载的核心;
        super.afterPropertiesSet();
    }
 
    //AbstractHandlerMethodMapping#afterPropertiesSet()方法代码
	@Override
	public void afterPropertiesSet() {
		initHandlerMethods();
	}

AbstractHandlerMethodMapping类

这个类是RequestMappingHandlerMapping类的父类。具体看下AbstractHandlerMethodMapping#initHandlerMethods()方法代码,这里为了看起来代码逻辑清晰就把一些内部方法拆解开了

1和2处的代码是关键,通过isHandler方法判断然后通过detectHandlerMethods方法执行方法注册的处理逻辑,这个方法就是处理Controller中得各个接口方法的。
————————————————

protected void initHandlerMethods() {
        if (logger.isDebugEnabled()) {
            logger.debug("Looking for request mappings in application context: " + getApplicationContext());
        }
        String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
                BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
                getApplicationContext().getBeanNamesForType(Object.class));   
      // detectHandlerMethodsInAncestorContexts 默认为false,代表不会检测SpringMvc父容器中的bean的映射关系    
                                               
        for (String beanName : beanNames) {                     
    //遍历容器中的beanName, 代理的对象跳过,获取当前bean的类型,调用isHandler判断是否是处理器(handler\controller)
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                Class<?> beanType = null;
                try {
                    beanType = getApplicationContext().getType(beanName);
                }
                catch (Throwable ex) {
                    // An unresolvable bean type, probably from a lazy bean - let's ignore it.
                    if (logger.isDebugEnabled()) {
                        logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
                    }
                }
                // 1----------如果是RequestMappingHandlerMapping,此处调用的是RequestMappingHandlerMapping类的isHandler方法判断是否是controller
                if (beanType != null && isHandler(beanType)) { 
                // 2------如果isHandler为True,加载Controller和请求映射关系 
                    detectHandlerMethods(beanName); 
                }
            }
        }
        handlerMethodsInitialized(getHandlerMethods()); // 该方法是个空实现
    }

RequestMappingHandlerMapping类的isHandler方法逻辑

判断有没有标注@Controller或者@RequestMapping注解,如果有就返回true。

protected boolean isHandler(Class<?> beanType) {
        return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
                AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
    }

解析Controller注册接口方法的核心逻辑在detectHandlerMethods方法中。

  • 通过 MethodIntrospector.selectMethods方法拿到当前Controller类下的所有的方法存储为Map<Method, T>类型的 methods对象里。
  • 遍历methods,调用registerHandlerMethod方法,通过MappingRegistry#register方法把Controller类下符合要求的接口方法注册到MappingRegistry类型的全局对象中。

registerHandlerMethod方法调用的是MappingRegistry类的register方法

protected void registerHandlerMethod(Object handler, Method method, T mapping) {
		this.mappingRegistry.register(mapping, handler, method);
	}

MappingRegistry类

  • MappingRegistry#register方法是加锁的

register方法中解析controller的逻辑中就涉及到HandlerMapping类的问题,例如默认的是RequestMappingHandlerMapping,但是如果我们想要要做一些自定义的实现,比如加版本号实现接口版本控制,那就需要继承默认的重写isHandler,getCustomTypeCondition和getCustomMethodCondition方法,实现自定义的Condition逻辑,,这几个方法的自定义逻辑会在解析controller的方法这一步通过默认的RequestMappingHandlerMapping中得getMappingForMethod方法逻辑中调用到,具体在RequestMappingHandlerMapping#createRequestMappingInfo方法中根据Method的类型选择调用getCustomTypeCondition方法或者getCustomMethodCondition方法获取到自定义逻辑中的Condition信息
————————————————

请求时如何到达对应的方法的

在DispatchServlet中,doDispatch方法会历各种HandlerMapping。当遍历到RequestMappingHandlerMapping时,RequestMappingHandlerMapping中的MappingRegistry中有一个urlLookup的map对象,key就是@RequestMapping注解定义的URL,Value就是对应的类和方法信息,所以在查找时以请求的地址lookupPath作为key就能拿到对应的类和方法信息,也就找到了这个请求对应的方法。

Spring @Enable 模块驱动

@Enable 模块驱动
@Enable 模块驱动是以 @Enable 为前缀的注解驱动编程模型。所谓“模块”是指具备相同领域的功能组件集合
,组合所形成⼀个独⽴的单元。⽐如 Web MVC 模块、AspectJ代理模块、Caching(缓存)模块、JMX(Java 管
理扩展)模块、Async(异步处理)模块等。
• 举例说明
• @EnableWebMvc
• @EnableTransactionManagement
• @EnableCaching
• @EnableMBeanExport

@Enable 模块驱动编程模式
• 驱动注解:@EnableXXX
• 导入注解:@Import 具体实现
• 具体实现
• 基于 Configuration Class
• 基于 ImportSelector 接口实现
• 基于 ImportBeanDefinitionRegistrar 接口实现

Spring 条件注解

基于配置条件注解 - @org.springframework.context.annotation.Profile
• 关联对象 - org.springframework.core.env.Environment 中的 Profiles
• 实现变化:从 Spring 4.0 开始,@Profile 基于 @Conditional 实现
• 基于编程条件注解 - @org.springframework.context.annotation.Conditional
• 关联对象 - org.springframework.context.annotation.Condition 具体实现

@Conditional 实现原理
• 上下文对象 - org.springframework.context.annotation.ConditionContext
• 条件判断 - org.springframework.context.annotation.ConditionEvaluator
• 配置阶段 -
org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase
• 判断入口 - org.springframework.context.annotation.ConfigurationClassPostProcessor
• org.springframework.context.annotation.ConfigurationClassParser

spring组合注解

@SpringBootApplication

@SpringBootApplication 是一个组合注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
}

四:设计模式

1.简单记法

里赖隔法复

双工单建原

时装桥外代组享

车模观迭责任命

悲壮房中解释器

2.六大基本原则

口诀:里赖隔法复

2.1 开闭原则

2.2 里氏替换原则

2.3 依赖倒置

2.4 接口隔离原则

2.5 迪米特法则

2.6 合成复用原则

3. 23种设计模式

3.1 分类

创建型 5

口诀:双工单建原

工厂模式,抽象工厂,单例模式,建造者模式,原型模式

结构型 7

口诀:适装桥外代组享

适配器模式,装饰模式,桥接模式,外观模式,代理模式,组和模式,享元模式

行为型模式 11

口诀:策模观迭责任命,备状访中解释器

策略模式,模板方法模式,观察者模式,迭代器模式,责任链模式,命令模式,备忘录模式,状态模式,访问者模式,中介模式,解释器模式。

五:Springboot小马哥课程

1依赖查找

1.1 名称

1.2 类型

查找的是全部,可通过@Primary

1.3注解

代码参考:

/**
 * 依赖查找示例
 * 1. 通过名称的方式来查找
 *
 * @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
 * @since
 */
public class DependencyLookupDemo {

    public static void main(String[] args) {
        // 配置 XML 配置文件
        // 启动 Spring 应用上下文
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-lookup-context.xml");
        // 按照类型查找
        lookupByType(beanFactory);
        // 按照类型查找结合对象
        lookupCollectionByType(beanFactory);
        // 通过注解查找对象
        lookupByAnnotationType(beanFactory);

//        lookupInRealTime(beanFactory);
//        lookupInLazy(beanFactory);
    }

    private static void lookupByAnnotationType(BeanFactory beanFactory) {
        if (beanFactory instanceof ListableBeanFactory) {
            ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
            Map<String, User> users = (Map) listableBeanFactory.getBeansWithAnnotation(Super.class);
            System.out.println("查找标注 @Super 所有的 User 集合对象:" + users);
        }
    }

    private static void lookupCollectionByType(BeanFactory beanFactory) {
        if (beanFactory instanceof ListableBeanFactory) {
            ListableBeanFactory listableBeanFactory = (ListableBeanFactory) beanFactory;
            Map<String, User> users = listableBeanFactory.getBeansOfType(User.class);
            System.out.println("查找到的所有的 User 集合对象:" + users);
        }
    }

    private static void lookupByType(BeanFactory beanFactory) {
        User user = beanFactory.getBean(User.class);
        System.out.println(":" + user);
    }

    private static void lookupInLazy(BeanFactory beanFactory) {
        ObjectFactory<User> objectFactory = (ObjectFactory<User>) beanFactory.getBean("objectFactory");
        User user = objectFactory.getObject();
        System.out.println("延迟查找:" + user);
    }

    private static void lookupInRealTime(BeanFactory beanFactory) {
        User user = (User) beanFactory.getBean("user");
        System.out.println("实时查找:" + user);
    }
}

2.依赖注入

2.1 根据BeanName注入

依靠xml au

2.2 根据Bean类型注入

2.2.1 单个bean对象
2.2.2 集合bean对象
<!---->依赖注入</!---->
<bean id="userRepository" class="org.geekbang.thinking.in.spring.ioc.overview.repository.UserRepository"
          autowire="byType"> <!-- Auto-Wiring -->
        <!-- 手动配置 -->
        <!--        <property name="users">-->
        <!--            <util:list>-->
        <!--                <ref bean="superUser" />-->
        <!--                <ref bean="user" />-->
        <!--            </util:list>-->
        <!--        </property>-->

   </bean>
<!--以上也可以称之为自定义bean-->

2.3自定义bean

<!---->依赖注入</!---->
<bean id="userRepository" class="org.geekbang.thinking.in.spring.ioc.overview.repository.UserRepository"
          autowire="byType"> <!-- Auto-Wiring -->
        <!-- 手动配置 -->
        <!--        <property name="users">-->
        <!--            <util:list>-->
        <!--                <ref bean="superUser" />-->
        <!--                <ref bean="user" />-->
        <!--            </util:list>-->
        <!--        </property>-->

   </bean>
<!--以上也可以称之为自定义bean-->

获取

UserRepository userRepository = applicationContext.getBean("userRepository", UserRepository.class);

2.4容器内建的bean

Environment environment = applicationContext.getBean(Environment.class);
// 配置 XML 配置文件
        // 启动 Spring 应用上下文
//        BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-injection-context.xml");
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/dependency-injection-context.xml");
// 依赖来源一:自定义 Bean
UserRepository userRepository = applicationContext.getBean("userRepository", UserRepository.class);

// 依赖来源二:依赖注入(內建依赖)
System.out.println(userRepository.getBeanFactory());
ObjectFactory userFactory = userRepository.getObjectFactory();
System.out.println(userFactory.getObject() == applicationContext);
// 依赖来源三:容器內建 Bean
Environment environment = applicationContext.getBean(Environment.class);
System.out.println("获取 Environment 类型的 Bean:" + environment);

3.SpringIOC配置元信息:

Bean定义配置:

  • 基于xml

    上面的自定义bean

  • 基于Properties

  • 基于java注解

    @Configuration+@Bean

    @Component+@Bean

  • 基于javaAPI

ApplicationContext 与BeanFactory

// ConfigurableApplicationContext <- ApplicationContext <- BeanFactory

ApplicationContext 除了IOC容器之外,还有以下:

  1. 面向切面
  2. 配置元信息
  3. 资源管理
  4. 事件
  5. 国际化
  6. 注解
  7. Environment抽象化

BeanFactory是Spring底层的ioc容器

ApplicationContext是包括BeanFactory超类

BeanDefintion元信息

跟xml配置bean同样效果

BeanDefintion.addProperties();

public static void main(String[] args) {

    // 1.通过 BeanDefinitionBuilder 构建
    BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
    // 通过属性设置
    beanDefinitionBuilder
        .addPropertyValue("id", 1)
        .addPropertyValue("name", "小马哥");
    // 获取 BeanDefinition 实例
    BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
    // BeanDefinition 并非 Bean 终态,可以自定义修改

    // 2. 通过 AbstractBeanDefinition 以及派生类
    GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
    // 设置 Bean 类型
    genericBeanDefinition.setBeanClass(User.class);
    // 通过 MutablePropertyValues 批量操作属性
    MutablePropertyValues propertyValues = new MutablePropertyValues();
    //        propertyValues.addPropertyValue("id", 1);
    //        propertyValues.addPropertyValue("name", "小马哥");
    propertyValues
        .add("id", 1)
        .add("name", "小马哥");
    // 通过 set MutablePropertyValues 批量操作属性
    genericBeanDefinition.setPropertyValues(propertyValues);
}

命名springbean

默认就是BeanNameGenerator

根据我们id,name默认生成。

一个是我们自定义。

注解一般都是默认命名,xml方式一般都是自定义。

Bean为什么需要别名

  • 复用BeanDefinition
  • 更具场景化的

导入第三方springxml配置

   <!-- 导入第三方 Spring XML 配置文件 -->
    <import resource="classpath:/META-INF/dependency-lookup-context.xml" />

    <!-- 将 Spring 容器中 "user" Bean 关联/建立别名 - "xiaomage-user" -->
    <alias name="user" alias="xiaomage-user" />
 // 配置 XML 配置文件
        // 启动 Spring 应用上下文
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-definitions-context.xml");
        // 通过别名 xiaomage-user 获取曾用名 user 的 bean
        User user = beanFactory.getBean("user", User.class);
        User xiaomageUser = beanFactory.getBean("xiaomage-user", User.class);
        System.out.println("xiaomage-user 是否与 user Bean 相同:" + (user == xiaomageUser));
// 结果是true

如何将BeaDefinition注册到ioc容器

BeanDefinition注册方式

  • XMl 注册

  • java注解

    @Bean @Component @Import @Service @conctroller @Configuration

  • java API

代码

/**
 * 注解 BeanDefinition 示例
 *
 * @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
 * @since
 */
// 3. 通过 @Import 来进行导入
@Import(AnnotationBeanDefinitionDemo.Config.class)
public class AnnotationBeanDefinitionDemo {

    public static void main(String[] args) {
        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 注册 Configuration Class(配置类)
        applicationContext.register(AnnotationBeanDefinitionDemo.class);

        // 通过 BeanDefinition 注册 API 实现
        // 1.命名 Bean 的注册方式
        registerUserBeanDefinition(applicationContext, "mercyblitz-user");
        // 2. 非命名 Bean 的注册方法
        registerUserBeanDefinition(applicationContext);

        // 启动 Spring 应用上下文
        applicationContext.refresh();
        // 按照类型依赖查找
        System.out.println("Config 类型的所有 Beans" + applicationContext.getBeansOfType(Config.class));
        System.out.println("User 类型的所有 Beans" + applicationContext.getBeansOfType(User.class));
        // 显示地关闭 Spring 应用上下文
        applicationContext.close();
    }

    public static void registerUserBeanDefinition(BeanDefinitionRegistry registry, String beanName) {
        BeanDefinitionBuilder beanDefinitionBuilder = genericBeanDefinition(User.class);
        beanDefinitionBuilder
                .addPropertyValue("id", 1L)
                .addPropertyValue("name", "小马哥");

        // 判断如果 beanName 参数存在时
        if (StringUtils.hasText(beanName)) {
            // 注册 BeanDefinition
            registry.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition());
        } else {
            // 非命名 Bean 注册方法
            BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinitionBuilder.getBeanDefinition(), registry);
        }
    }

    public static void registerUserBeanDefinition(BeanDefinitionRegistry registry) {
        registerUserBeanDefinition(registry, null);
    }

    // 2. 通过 @Component 方式
    @Component // 定义当前类作为 Spring Bean(组件)
    public static class Config {

        // 1. 通过 @Bean 方式定义

        /**
         * 通过 Java 注解的方式,定义了一个 Bean
         */
        @Bean(name = {"user", "xiaomage-user"})
        public User user() {
            User user = new User();
            user.setId(1L);
            user.setName("小马哥");
            return user;
        }
    }


}

Bean的实例化

bean常规方式:

  • 通过构造器(配置元信息:XML,java注解,javaAPI)

构造器静态方法

//类构造方法
public class User implements BeanNameAware {
    public static User createUser() {
        User user = new User();
        user.setId(1L);
        user.setName("小马哥");
        return user;
    }
}    

xml配置

<!-- 静态方法实例化 Bean -->
    <bean id="user-by-static-method" class="org.geekbang.thinking.in.spring.ioc.overview.domain.User"
          factory-method="createUser"/>

获取

 BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-instantiation-context.xml");
        User user = beanFactory.getBean("user-by-static-method", User.class);

其他

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

   <!-- 静态方法实例化 Bean -->
    <bean id="user-by-static-method" class="org.geekbang.thinking.in.spring.ioc.overview.domain.User"
          factory-method="createUser"/>

   <!-- 实例(Bean)方法实例化 Bean -->
   <bean id="user-by-instance-method" factory-bean="userFactory" factory-method="createUser"/>

   <!-- FactoryBean实例化 Bean -->
   <bean id="user-by-factory-bean" class="org.geekbang.thinking.in.spring.bean.factory.UserFactoryBean" />

    <bean id="userFactory" class="org.geekbang.thinking.in.spring.bean.factory.DefaultUserFactory"/>
</beans>

如何获取实例

/**
 * Bean 实例化示例
 *
 * @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
 * @since
 */
public class BeanInstantiationDemo {

    public static void main(String[] args) {
        // 配置 XML 配置文件
        // 启动 Spring 应用上下文
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-instantiation-context.xml");
        User user = beanFactory.getBean("user-by-static-method", User.class);
        User userByInstanceMethod = beanFactory.getBean("user-by-instance-method", User.class);
        User userByFactoryBean = beanFactory.getBean("user-by-factory-bean", User.class);
        System.out.println(user);
        System.out.println(userByInstanceMethod);
        System.out.println(userByFactoryBean);

        System.out.println(user == userByInstanceMethod);//false
        System.out.println(user == userByFactoryBean);//fakse

    }
}
  • 通过静态方法
  • 通过bean工厂方法
  • 通过factoryBean

bean的延迟加载

  • xml

java 注解

@Lazy

@PostConstruct

Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)

@PreDestroy
@Configuration // Configuration Class
public class BeanInitializationDemo {

    public static void main(String[] args) {
        // 创建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 注册 Configuration Class(配置类)
        applicationContext.register(BeanInitializationDemo.class);
        // 启动 Spring 应用上下文
        applicationContext.refresh();
        // 非延迟初始化在 Spring 应用上下文启动完成后,被初始化
        System.out.println("Spring 应用上下文已启动...");
        // 依赖查找 UserFactory
        UserFactory userFactory = applicationContext.getBean(UserFactory.class);
        System.out.println(userFactory);
        System.out.println("Spring 应用上下文准备关闭...");
        // 关闭 Spring 应用上下文
        applicationContext.close();
        System.out.println("Spring 应用上下文已关闭...");
    }

    @Bean(initMethod = "initUserFactory", destroyMethod = "doDestroy")
    @Lazy(value = false)
    public UserFactory userFactory() {
        return new DefaultUserFactory();
    }
}
//运行结果
PostConstruct : UserFactory 初始化中...
InitializingBean#afterPropertiesSet() : UserFactory 初始化中...
自定义初始化方法 initUserFactory() : UserFactory 初始化中...
Spring 应用上下文已启动...
org.geekbang.thinking.in.spring.bean.factory.DefaultUserFactory@709ba3fb
Spring 应用上下文准备关闭...
@PreDestroy : UserFactory 销毁中...
DisposableBean#destroy() : UserFactory 销毁中...
自定义销毁方法 doDestroy() : UserFactory 销毁中...
Spring 应用上下文已关闭...

Process finished with exit code 0
public class DefaultUserFactory implements UserFactory, InitializingBean, DisposableBean {

    // 1. 基于 @PostConstruct 注解
    //Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)
    @PostConstruct
    public void init() {
        System.out.println("@PostConstruct : UserFactory 初始化中...");
    }

    public void initUserFactory() {
        System.out.println("自定义初始化方法 initUserFactory() : UserFactory 初始化中...");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean#afterPropertiesSet() : UserFactory 初始化中...");
    }

    //顺序:
// @PreDestroy -> @Override-> doDestroy() 
    @PreDestroy
    public void preDestroy() {
        System.out.println("@PreDestroy : UserFactory 销毁中...");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("DisposableBean#destroy() : UserFactory 销毁中...");
    }

    public void doDestroy() {
        System.out.println("自定义销毁方法 doDestroy() : UserFactory 销毁中...");
    }

    @Override
    public void finalize() throws Throwable {
        System.out.println("当前 DefaultUserFactory 对象正在被垃圾回收...");
    }
}

初始化顺序,都是java的注解是第一顺序,spring的注解第二位,自己定义的方法是第三位

SpringBean垃圾回收

  1. 关闭spring容器
  2. 执行GC
  3. springBean覆盖的finalize()方法回调

依赖查找

单一类型依赖查找

根据bean名词查找

Object getBean(String name) throws BeansException

安装bean类型查找

public <T> T getBean(Class<T> requiredType) throws BeansException {
        this.assertBeanFactoryActive();
        return this.getBeanFactory().getBean(requiredType);
    }
 public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
        this.assertBeanFactoryActive();
        return this.getBeanFactory().getBean(name, requiredType);
    }
//覆盖默认参数
public Object getBean(String name, Object... args) throws BeansException {
        this.assertBeanFactoryActive();
        return this.getBeanFactory().getBean(name, args);
    }
//覆盖默认参数
    public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException {
        this.assertBeanFactoryActive();
        return this.getBeanFactory().getBean(requiredType, args);
    }
集合类型依赖查找

根据bean类型查找

//ListableBeanFactory
//根据类型查找所有的
 public String[] getBeanNamesForType(ResolvableType type) {
        return this.doGetBeanNamesForType(type, true, true);
    }

根据注解查找

String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType) 

层次性依赖查找

延迟查找:

延迟查找接口:

rg.springframework.beans.factory.ObjectFactory

依赖注入

\1. 依赖注入的模式和类型

  1. 自动绑定(Autowiring)
  2. 自动绑定(Autowiring)模式
  3. 自动绑定(Autowiring)限制和不足
  4. Setter 方法依赖注入 -
  5. 构造器依赖注入 —
  6. 字段注入
  7. 方法注入
  8. 回调注入
  9. 依赖注入类型选择

set注入

手动方式:

  • xml配置元信息
  • java注解
  • api配置

自动模式

  • byName

  • byType

Aware回调注入

ConfigurationProperties

基本使用

在 SpringBoot 中,当想需要获取到配置文件数据时,除了可以用 Spring 自带的 @Value 注解外,SpringBoot 还提供了一种更加方便的方式:@ConfigurationProperties。只要在 Bean 上添加上了这个注解,指定好配置文件的前缀,那么对应的配置文件数据就会自动填充到 Bean 中。

#比如在application.properties文件中有如下配置文件
config.username=jay.zhou
config.password=3333

那么按照如下注解配置,SpringBoot项目中使用@ConfigurationProperties的Bean,它的username与password就会被自动注入值了。就像下面展示的那样

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
 
@Component
@ConfigurationProperties(prefix = "config")
public class TestBean{
 
    private String username;
    
    private String password;
}

源码的探索及启发

SpringBoot为什么能够获取到它的值呢?如果让我从无到有设计,我应该怎么设计呢?

首先通过源码发现,SpringBoot主要帮助我们做了两件事情。

第一件事情就是获取到使用@ConfigurationProperties的类。

第二件事就是解析配置文件,并把对应的值设置到我们的Bean中

按照源码提供的实现思路,其核心就是对Bean的声明周期的管理。主要涉及一个叫做 BeanPostProcessor 的接口,可以在Bean初始化的时候,我们做一些文章。下面的两个方法,很简单,大致意思就是,在Spring的Bean初始化之前与之后执行。

public interface BeanPostProcessor {
 
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
 
    
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
 
}

1.@ConfigurationProperties的源码

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface ConfigurationProperties {
    @AliasFor("prefix")
    String value() default "";

    @AliasFor("value")
    String prefix() default "";

    boolean ignoreInvalidFields() default false;

    boolean ignoreUnknownFields() default true;
}

2.@ConfigurationProperties的参数解释

参数一:value:
前缀,用于识别properties/yml文件里需要映射的属性的统一前缀
参数二:prefix:
前缀,用于识别properties/yml文件里需要映射的属性的统一前缀
参数三:ignoreInvalidFields:false
是否忽略非法值,比如将一个字符串 “foo” 赋值给 bool 值,不忽略的话会报启动异常。
参数四:ignoreUnknownFields:true
对于多余的配置是否会报异常。例如:当配置文件中有一个或多个属性未绑定到实体类时或属性名字出错或已被删除了,这时候对于实体类,properties里的属性是未知的。这种情况我们希望程序启动失败,这时候就将此参数设置为false即可

3.@ConfigurationProperties的使用

  • JavaBean要映射的属性需要有public的setter方法 比如: @Data
  • JavaBean需要交给容器管理 比如:@Component
  • JavaBean中静态成员变量的不支持映射/绑定
@Data
@ConfigurationProperties("service")
@Component
public class MyProperties {
    /**
     * 静态属性
     */
    private static String global;

    /**
     * 简单类型
     */
    private String name;
    /**
     * 简单类型 映射
     */
    private boolean enabled;

    /**
     * 引用类型 映射
     */
    private InetAddress remoteAddress;

    /**
     * 集合类型映射
     */
    private List<Security> securities;

    @Data
    public static class Security {
        private String username;
        private String password;
    }
}

3.2 @ConstructorBinding构造函数映射/绑定

  • JavaBean 依然需要增加注解@ConfigurationProperties

  • JavaBean 需要提供 getter 方法。

  • 使用构造函数绑定的方式,只能 @EnableConfigurationProperties 或者 @ConfigurationPropertiesScan 的方式注入 Bean。不能使用 @Component、@Bean 或者 @Import 的方式。

  • 使用构造函数绑定的方式,只有被注解的构造器的参数被映射,其他属性不会被映射

@Getter
@ConfigurationProperties("service")
//@Data
//@Component
public class MyProperties {
    /**
     * 静态属性
     */
    private static String global;

    /**
     * 宽松绑定原则
     */
    private String contextPath;

    /**
     * 简单类型
     */
    private String name;
    /**
     * 简单类型 映射
     */
    private boolean enabled;

    /**
     * 引用类型 映射
     */
    private InetAddress remoteAddress;

    /**
     * 集合类型映射
     */
    private List<Security> securities;

    @Data
    public static class Security {
        private String username;
        private String password;
    }

    @ConstructorBinding
    MyProperties(boolean enabled, InetAddress remoteAddress, List<Security> securities) {
        this.enabled = enabled;
        this.remoteAddress = remoteAddress;
        this.securities = securities;
    }
}

支持宽松绑定原则

宽松绑定:属性的命名语法

  • 语法1、acme.my-project.person.first-name,建议在.properties和.yml文件中使用。

  • 语法2、acme.myProject.person.firstName:标准驼峰式语法。

  • 语法3、acme.my_project.person.first_name:下划线表示法,是在.properties和.yml文件中使用的另一种格式。

  • 语法4:ACME_MYPROJECT_PERSON_FIRSTNAME:大写格式,使用系统环境变量时建议使用。

注:如果确实想使用@Value,建议您使用规范形式来引用属性名称(kebab-case仅使用小写字母)。

例如,@Value(“{demo.item-price}”)将会把demo.item-price或demo.itemPrice从application.properties配件文件查找。

元注解

六:springBean的生命周期

元信息配置阶段

元信息解析阶段

注册阶段

BeanDefinition合并阶段

Bean Class加载阶段

Bean实例化前阶段

Bean实例化阶段

Bean实例化后阶段

Bean属性赋值前阶段

SpringAware回调阶段

Bean初始化前阶段

初始化阶段

初始化后阶段

初始化完成阶段

销毁前阶段

销毁阶段

垃圾收集

Bean的生命周期

BeanDefinition的概述及使用

BeanDefinition的属性

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

    //单例作用域 属性
    String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;

    //原型作用域 属性
    String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;


    // 这三个属性 用于标识Bean的类别或来源
    //代表是用户自己定义的Bean
    int ROLE_APPLICATION = 0;
    //代表Bean来自于配置文件
    int ROLE_SUPPORT = 1;
    //代表Bean来自于Spring内部
    int ROLE_INFRASTRUCTURE = 2;


    // 设置parentName,bd允许有层级 因此可以指定父bd的名称
    void setParentName(@Nullable String parentName);

    //获取parentName
    @Nullable
    String getParentName();

    // 这个比较重要 设置BeanClassName,代表bd是属于哪个类型的
    void setBeanClassName(@Nullable String beanClassName);

    //获取BeanClassName
    @Nullable
    String getBeanClassName();

    // 设置Bean的作用域 是单例、原型还是其他
    void setScope(@Nullable String scope);


    @Nullable
    String getScope();

    //设置是否懒加载,与@Lazy相似
    void setLazyInit(boolean lazyInit);

    boolean isLazyInit();

    //设置依赖的Bean,即加载当前bd之前,需要先加载依赖的bd 才会有当前bd。这与@DependsOn注解效果类似
    void setDependsOn(@Nullable String... dependsOn);

    @Nullable
    String[] getDependsOn();

    //设置bean是否可以依赖注入,可参考@AutoWired
    void setAutowireCandidate(boolean autowireCandidate);
    boolean isAutowireCandidate();

    //设置当前bean是否为主要候选Bean,一般同一类型的Bean存在多个实例时,这个字段很有效
    void setPrimary(boolean primary);
    boolean isPrimary();

    // 设置工厂BeanName,这可以跟FactoryMethod一起来用 帮忙创建Bean
    void setFactoryBeanName(@Nullable String factoryBeanName);
    @Nullable
    String getFactoryBeanName();

    //设置FactoryMethod
    void setFactoryMethodName(@Nullable String factoryMethodName);
    @Nullable
    String getFactoryMethodName();

    //获取构造函数参数
    ConstructorArgumentValues getConstructorArgumentValues();

    /**
     * Return if there are constructor argument values defined for this bean.
     * @since 5.0.2
     */
    default boolean hasConstructorArgumentValues() {
        return !getConstructorArgumentValues().isEmpty();
    }

    //获取属性参数
    MutablePropertyValues getPropertyValues();

    default boolean hasPropertyValues() {
        return !getPropertyValues().isEmpty();
    }

    //设置Bean的初始化方法,这与@InitMethod类似
    void setInitMethodName(@Nullable String initMethodName);
    @Nullable
    String getInitMethodName();

    //设置Bean销毁前方法,这与@DestroyMethod类似
    void setDestroyMethodName(@Nullable String destroyMethodName);
    @Nullable
    String getDestroyMethodName();

    //设置Bean的类别 具体取值 在前面属性部分介绍了
    void setRole(int role);
    int getRole();

    //设置bd的描述信息
    void setDescription(@Nullable String description);
    @Nullable
    String getDescription();


    // Read-only attributes

    //这个暂时不太清楚其用途,有知道的小伙伴也可以分享一下 
    ResolvableType getResolvableType();

    //是否为单例bean 
    boolean isSingleton();

    // 是否为原型bean
    boolean isPrototype();

    //当前Bean是否为抽象类
    boolean isAbstract();

    @Nullable
    String getResourceDescription();

    @Nullable
    BeanDefinition getOriginatingBeanDefinition();
}

1、Spring Bean生命周期: Bean元信息的配置与解析阶段

2、Spring Bean生命周期: Bean的注册

BeanDefinitionReaderUtils#registerBeanDefinition展开分析下Bean注册过程

public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {

        // Register bean definition under primary name.
        String beanName = definitionHolder.getBeanName();
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

        // Register aliases for bean name, if any.
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                registry.registerAlias(beanName, alias);
            }
        }
    }

上面无论是注册bd还是建立alias-beanName之间的关系,均用到了BeanDefinitionRegistry,因此我们就以它为突破口来展开

BeanFactory的继承体系

img

对图中常用接口或类进行说明:

ListableBeanFactory 集合类型BeanFactory 提供一种可以查找所有Bean实例的能力

getBeanNamesForType(Class) 根据类型去查找Bean名称列表不会强制Bean的初始化,可从源码中看出来
getBeansOfType(Class) 根据类型去查找Bean实例列表,会强制Bean的初始化,可从源码中看出来
getBeanNamesForAnnotation(Class<? extends Annotation>) 根据注解类型获取Bean名称列表
getBeansWithAnnotation(Class<? extends Annotation>) 根据注解类型获取Bean实例列表
findAnnotationOnBean(String,Class<? extends Annotation>) 根据指定名称+标注类型获取Bean实例
Hierarchical([ˌhaɪəˈrɑːkɪkl])BeanFactory 层次性BeanFactory,有父子容器的概念,可在ConfigurableListableBeanFactory设置其父容器

getParentBeanFactory() 获取父容器
boolean containsLocalBean(String name) 在当前容器中查找是否存在该名称的Bean实例
SingletonBeanRegistry 单实例BeanFactory,与单实例有关

ConfigurableBeanFactory 可配置的BeanFactory,这个一般不用于应用程序,是给其他BeanFactory扩展用的。的确,定义了很多配置方法

ConfigurableListableBeanFactory 可配置的集合类型的BeanFactory

AutowireCapableBeanFactory 提供具有自动装配能力的BeanFactory

总结:

透过继承体系可以看出,BeanDefinitionRegistry的实现类是DefaultListableBeanFactory,该类同时实现了诸多接口,可谓是BeanFactory中集大成者,因此我们到DefaultListableBeanFactory中阅读下bd注册及别名注册的源码。

Bean的注册

先来分析下DefaultListableBeanFactory的几个重要的成员属性

// 这个实质上就是IoC容器中Bean的载体,没错 它很重要,但它是无序的
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
//它代表了bd名称的集合,它是有序的 遵循bd注册的顺序
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
// 这是已创建bd名称的集合,在doGetBean方法根据beanName创建Bean时,beanName会被加到此集合中
private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));

上面两个属性都比较重要,两者结合使用的话可以实现bd的顺序访问(其实就是遍历beanDefinitionNames集合时,使用beanDefinitionMap去获取bd)

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {
        //对beanName、bd进行非空验证
        Assert.hasText(beanName, "Bean name must not be empty");
        Assert.notNull(beanDefinition, "BeanDefinition must not be null");
        //如果bd是AbstractBeanDefinition类型,则对bd进行验证(一般情况下 我们场景的bd都是继承自AbstractBeanDefinition的)
        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
               //bd验证
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            catch (BeanDefinitionValidationException ex) {
                //省略异常代码
            }
        }
        //从beanDefinitionMap根据beanName取bd
        BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
        //如果beanName名称的bd已经存在
        if (existingDefinition != null) {
            //如果不允许Bean被重新注册 则抛出异常,这里默认值是true
            if (!isAllowBeanDefinitionOverriding()) {
                throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
            }
            //如果已被注册bd的角色值小于当前待注册bd的角色值 
            else if (existingDefinition.getRole() < beanDefinition.getRole()) {
                // 省略日志输出
            }
            //如果已注册的同名bd 与本次注册的bd不相同
            else if (!beanDefinition.equals(existingDefinition)) {
                //省略日志输出
            }
            else {
                //省略日志输出
            }
            //将beanName-bd键值对放入beanDefinitionMap集合
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        else {
            //流程走到这里 说明在beanDefinitionMap中不存在同名bd
            //条件成立 说明alreadyCreated不为空 即有bd已被创建
            if (hasBeanCreationStarted()) {
                // 如果在此之间 有bean正在被创建 则这里进行加锁处理
                synchronized (this.beanDefinitionMap) {
                    //将beanName-bd键值对放入beanDefinitionMap集合
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    //将beanName添加到beanDefinitionNames集合中
                    List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    //如果beanName是手动注册的单例Bean名称,则更新manualSingletonNames
                    if (this.manualSingletonNames.contains(beanName)) {
                        Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                        //这里从集合中删除的原因个人理解:
                        //manualSingletonNames记录的是在registerSingleton时被添加的单实例beanName,而这里注入的不是单实例Bean。因为manualSingletonNames包含了此beanName,因此需要剔除
                        updatedSingletons.remove(beanName);
                        this.manualSingletonNames = updatedSingletons;
                    }
                }
            }
            else {
                //如果没有bean在被创建
                //将beanName-bd键值对放入beanDefinitionMap集合
                this.beanDefinitionMap.put(beanName, beanDefinition);
                //将beanName添加到集合中
                this.beanDefinitionNames.add(beanName);
                //这里从manualSingletonNames中剔除,个人理解为拖地操作,毕竟若集合中没有此beanName 也remove不了
                this.manualSingletonNames.remove(beanName);
            }
            //这个集合表示冻结配置时缓存的beanName集合,暂时未理解透此集合的用途
            this.frozenBeanDefinitionNames = null;
        }
        //如果已存在同名bd或已存在同名的单例对象,则重置所有已被缓存的同名的bd数据,因此这里bd注册成功后,肯定后续还会再生成Bean的
        if (existingDefinition != null || containsSingleton(beanName)) {
            resetBeanDefinition(beanName);
        }
    }

其实分析下来发现Bean注册的过程还是比较容易理解的,下面试着总结一下:

  • 若bd未被注册过,则将bd信息存入BeanDefinitionMap等集合中

  • 若bd已被注册过,允许覆盖注册的情况下,将bd信息存入BeanDefinitionMap等集合中,并清除已被缓存的同名bd信息

    下面看一下清除bd信息的代码逻辑

    protected void resetBeanDefinition(String beanName) {
            // 如果此bd属于被合并的BeanDefinition,则这里将其从MergeBeanDefinition集合中剔除
            clearMergedBeanDefinition(beanName);
    
            // 如果已存在同名的单例对象 则销毁,具体细节先不展开
            destroySingleton(beanName);
    
            // 这里for循环逻辑与MergeBeanDefinition相关,如果存在MergedBeanDefinitionPostProcessor,则重置此bd
            for (BeanPostProcessor processor : getBeanPostProcessors()) {
                if (processor instanceof MergedBeanDefinitionPostProcessor) {
                    ((MergedBeanDefinitionPostProcessor) processor).resetBeanDefinition(beanName);
                }
            }
    
            // BeanDefinition运行有层级的,如果此bd拥有多个父级bd,那么这里递归地重置其父bd
            for (String bdName : this.beanDefinitionNames) {
                if (!beanName.equals(bdName)) {
                    BeanDefinition bd = this.beanDefinitionMap.get(bdName);
                    if (beanName.equals(bd.getParentName())) {
                        resetBeanDefinition(bdName);
                    }
                }
            }
        }
    
alias别名的注册
//注意 这里的name 不要具象为beanName,虽然我们是从建立beanName--alias关系出发追溯到这里的
public void registerAlias(String name, String alias) {
        //对name、alias进行断言验证
        Assert.hasText(name, "'name' must not be empty");
        Assert.hasText(alias, "'alias' must not be empty");
        synchronized (this.aliasMap) {
            //如果别名与beanName相同,那别名就没有必要存在了,因此选择直接从this.aliasMap中移除此别名
            if (alias.equals(name)) {
                this.aliasMap.remove(alias);
                //省略日志输出
            }
            else {
               //从aliasMap中根据别名获取name
                String registeredName = this.aliasMap.get(alias);
                if (registeredName != null) {
                    //如果已存在的registeredName与此此要注册的name一致,那就没必要注册了
                    if (registeredName.equals(name)) {
                        return;
                    }
                    //流程走到这里,说明同一个别名,对应两个name,如果不允许alias覆盖 则抛出异常
                    if (!allowAliasOverriding()) {
                        //省略异常及日志输出
                }
                //这里对alias进行循环检查,避免出现A的别名是B,B的别名是A的情况
                checkForAliasCircle(name, alias);
                //将alias--name 放入aliasMap
                this.aliasMap.put(alias, name);
                //省略日志输出
            }
        }
    }

alias与beanName的映射关系,为根据名称查找Bean又提供了一种思路。就是说除了根据beanName外,也可以根据alias去查找Bean。

//name可以是beanName,也可以是alias
public String canonicalName(String name) {
        //局部变量赋值
        String canonicalName = name;
        // Handle aliasing...
        String resolvedName;
        do {
            //如果从aliasMap中能根据alias分析出beanName
            resolvedName = this.aliasMap.get(canonicalName);
            if (resolvedName != null) {
                canonicalName = resolvedName;
            }
        }
        while (resolvedName != null);
        // 无论入参name是beanName还是alias,这里返回的都应该是beanName了
        return canonicalName;
    }

3、Spring Bean生命周期: BeanDefinition的合并过程

BeanDefinition注册到IoC容器后,紧接着就是要使用Bean了,要使用必须先要获取Bean,这里我们就以DefaultListableBeanFactory#getBean方法来引出本次讨论的内容:BeanDefinition的合并

@Data
public class SuperUser implements Serializable {

    private String address;

    public SuperUser(String address) {
        this.address = address;
    }

    public SuperUser() {
    }
}


@Data
@ToString(callSuper = true)
public class User extends SuperUser {

    private String name;

    private Integer age;


    public User() {
    }

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

}

基于GenericBeanDefinition注册有层次的Bean

public class GenericBeanDefinitionDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

        //父BeanDefinition
        GenericBeanDefinition rootBeanDefinition = new GenericBeanDefinition();
        rootBeanDefinition.setBeanClass(SuperUser.class);
        //设置参数
        MutablePropertyValues propertyValues = new MutablePropertyValues();
        propertyValues.addPropertyValue("address", "地址");

        rootBeanDefinition.setPropertyValues(propertyValues);

        //子BeanDefinition
        GenericBeanDefinition childBeanDefinition = new GenericBeanDefinition();
        childBeanDefinition.setBeanClass(User.class);
        //设置构造参数
        ConstructorArgumentValues argumentValues = new ConstructorArgumentValues();
        argumentValues.addIndexedArgumentValue(0, "我就是我");
        argumentValues.addIndexedArgumentValue(1, 18);

        childBeanDefinition.setConstructorArgumentValues(argumentValues);
        childBeanDefinition.setParentName("superUser");
        //类型相同时 以子类为主
        childBeanDefinition.setPrimary(true);

        context.registerBeanDefinition("superUser", rootBeanDefinition);
        context.registerBeanDefinition("user", childBeanDefinition);

        context.refresh();

        User user = context.getBean("user", User.class);
        System.out.println(user);

        SuperUser superUser = context.getBean("superUser", SuperUser.class);
        System.out.println(superUser);
        context.close();
    }

}

在分析源码时我们要有侧重点,这里会将不太相关的逻辑一带而过。

AbstractBeanFactory#doGetBean

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
            @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
        //将name解析为beanName,如果传入的是alias,根据aliasMap进行转换,我们在前面介绍过了
        final String beanName = transformedBeanName(name);
        Object bean;

        // 如果是单例Bean
        Object sharedInstance = getSingleton(beanName);
        if (sharedInstance != null && args == null) {
            //省略日志输出
            // 这里的逻辑是根据beanName判断是否为FactoryBea,并采用相应方式去处理
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
        }
        //如果不是单例对象
        else {
            // 对原型对象进行验证,如果当前beanName已经在创建中了 抛出异常
            if (isPrototypeCurrentlyInCreation(beanName)) {
                throw new BeanCurrentlyInCreationException(beanName);
            }

            // 获取父BeanFactory,前面我们介绍过了 BeanFactory允许有层级,可已存在父BeanFactory
            BeanFactory parentBeanFactory = getParentBeanFactory();
            //如果存在父BeanFactory 去父BeanFactory中查找bean
            if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
                //省略去父BeanFactory查找Bean的过程
            }
            //typeCheckOnly默认为false ,这里将beanName放到alreadyCreated集合中 表示该Bean正在创建中
            if (!typeCheckOnly) {
                markBeanAsCreated(beanName);
            }

            try {
                // 这里来到了我们要重点关注的地方了,bd的合并 ⭐️
                final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
                checkMergedBeanDefinition(mbd, beanName, args);

                //如果存在依赖Bean,需要进行依赖查找    
                String[] dependsOn = mbd.getDependsOn();
                if (dependsOn != null) {
                    // 省略dependsOn 依赖查找代码
                }

                // 这里的if..else if .. else 是根据scope取值来的
                //scope=singleton时
                if (mbd.isSingleton()) {
                    //省略单实例Bean创建过程
                }
                //scope=prototype时
                else if (mbd.isPrototype()) {
                    //省略Prototype Bean创建过程
                }
                //scope=request、application、session时
                else {
                    // 省略其他Scope Bean的创建过程
        }

        if (requiredType != null && !requiredType.isInstance(bean)) {
            //省略类型转换代码
        }
        // 返回创建的Bean
        return (T) bean;
    }

上面的方法实现比较长、比较复杂,这里只对重要的地方进行些注释说明并将与本次讨论无关的代码先行进行注释。

下面就进入到BeanDefinition的合并逻辑了

//假设beanName=user
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
        // 检查缓存,若存在直接返回
        RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
        if (mbd != null) {
            return mbd;
        }
    //getBeanDefinition(beanName)==>实际上去DefaultListableBeanFactory.beanDefinitionMap中根据key查找BeanDefinition,这在注册阶段已经放到beanDefinitionMap了。
        return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
    }
protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd)
            throws BeanDefinitionStoreException {

        return getMergedBeanDefinition(beanName, bd, null);
    }

//根据上面的举例可知beanName=user,bd是User类的BeanDefinition,containingBd=null
protected RootBeanDefinition getMergedBeanDefinition(
            String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
            throws BeanDefinitionStoreException {

        synchronized (this.mergedBeanDefinitions) {
            RootBeanDefinition mbd = null;

            // 尝试从缓存中拿
            if (containingBd == null) {
                mbd = this.mergedBeanDefinitions.get(beanName);
            }

            if (mbd == null) {
        //如果当前BeanDefinition没有指定parentName,说明其不存在父BeanDefinition,不需要合并。以RootBeanDefinition形式展现
                if (bd.getParentName() == null) {
                    // 如果bd是RootBeanDefinition类型,直接类型转换
                    if (bd instanceof RootBeanDefinition) {
                        mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
                    }
                    else {
            //通过bd属性构造RootBeanDefinition
                        mbd = new RootBeanDefinition(bd);
                    }
                }
                else {
                    // 走到这里说明存在parentName,当前bd需要与其父bd合并
                    BeanDefinition pbd;
                    try {
            //得到父BeanName
                        String parentBeanName = transformedBeanName(bd.getParentName());
            //!beanName.equals(parentBeanName) 条件成立 说明当前beanName属于子bd
                        if (!beanName.equals(parentBeanName)) {
              //递归地以父bd名称 查找父BeanDefinition。之所以递归地查找,是因为 可能此时的parentBeanName还有父,实体类存在多重继承关系
                            pbd = getMergedBeanDefinition(parentBeanName);
                        }
                        else {
              //走到这里,说明beanName.equals(parentBeanName),很有可能是父bd查找BeanDefinition时走来的。
              //获取父BeanFactory,BeanFactory也是有层次的,有父子关系的,可参见ConfigurableBeanFactory#setParentBeanFactory
                            BeanFactory parent = getParentBeanFactory();
                            if (parent instanceof ConfigurableBeanFactory) {
                                pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
                            }
                            else {
                                throw new NoSuchBeanDefinitionException(parentBeanName,
                                        "Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +
                                        "': cannot be resolved without an AbstractBeanFactory parent");
                            }
                        }
                    }
                    catch (NoSuchBeanDefinitionException ex) {
                        throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,
                                "Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
                    }
                    // pbd是父BeanDefinition,由其构造为RootBeanDefinition
                    mbd = new RootBeanDefinition(pbd);
          //bd是子BeanDefinition,主要是继承父类的属性,并覆盖与父类同名的属性,有兴趣的可以看一下overrideFrom方法实现
                    mbd.overrideFrom(bd);
                }

                // 如果父bd未指定scope,则设置默认值
                if (!StringUtils.hasLength(mbd.getScope())) {
                    mbd.setScope(RootBeanDefinition.SCOPE_SINGLETON);
                }

                //由于containingBd=null 这里就不看了
                if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
                    mbd.setScope(containingBd.getScope());
                }

                if (containingBd == null && isCacheBeanMetadata()) {
                    this.mergedBeanDefinitions.put(beanName, mbd);
                }
            }
            //最终返回根据当前beanName找到的bd
            return mbd;
        }
    }

分析了上面的源码,我们试着总结一下:

1、如果不存在parentName,即不需要被合并,直接将bd转为RootBeanDefinition 返回即可

2、如果存在parentName

先根据parentName 找到父bd,若实体存在多级继承关系,则需要递归地查找。
将父bd转为RootBeanDefinition,并将子bd与父bd进行合并
设置一些其他属性
————————————————

4、Spring Bean生命周期: Bean的实例化

根据AbstractAutowireCapableBeanFactory#createBean源码逻辑 可将实例化过程分为实例化前阶段实例化过程实例化后阶段

实例化前阶段
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
            throws BeanCreationException {

        //省略无关代码

        try {
           // 这里就是我们分析的重点了 ⭐️
            Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
            //⭐️ 注意这个逻辑:如果postProcessBeforeInstantiation方法返回非null 则将返回值作为创建的Bean。并中断正常的创建流程
            if (bean != null) {
                return bean;
            }
        }
        catch (Throwable ex) {
            //省略异常信息
        }

        try {
            //真正创建Bean的逻辑 实例化Bean对象,为Bean属性赋值等,这里暂不展开
            Object beanInstance = doCreateBean(beanName, mbdToUse, args);
            //省略日志输出
            return beanInstance;
        }
        catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {//省略异常信息
    }

如果InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation 方法返回null,那么将不会中断正常Bean创建过程。
下面就来到的Bean实例化部分了。

实例化阶段
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
            throws BeanCreationException {

        // Instantiate the bean.
        BeanWrapper instanceWrapper = null;
        if (mbd.isSingleton()) {
            instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
        }
        if (instanceWrapper == null) {
            //Bean实例化
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }

        //省略其他代码 
        try {
            //Bean实例化后属性赋值
            populateBean(beanName, mbd, instanceWrapper);
            //Bean的初始化
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
        catch (Throwable ex) {
            //省略异常信息
        }

        //省略其他代码

        return exposedObject;
    }

上面将doCreateBean精简一下,只暴露出我们比较关系的部分。一目了然,Bean的实例化过程就藏在createBeanInstance方法中。

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
        //解析BeanClass,在BeanDefinition中类信息是以字符串形式展现,这里解析到字符串后 会将其加载为Class
        Class<?> beanClass = resolveBeanClass(mbd, beanName);

        if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                    "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
        }
        //如果设置了 Supplier 回调,则使用给定的回调方法初始化策略,通过获取Supplier#get得到实例化对象 
        Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
        if (instanceSupplier != null) {
            return obtainFromSupplier(instanceSupplier, beanName);
        }

         //如果工厂方法不为空,则使用工厂方法初始化
        if (mbd.getFactoryMethodName() != null) {
            return instantiateUsingFactoryMethod(beanName, mbd, args);
        }


        boolean resolved = false;
        boolean autowireNecessary = false;
        if (args == null) {
           //加锁
            synchronized (mbd.constructorArgumentLock) {
            //条件成立 说明构造函数或FactoryMethod已经被解析并被缓存,可直接利用构造函数解析
           //与后面的SimpleInstantiationStrategy#instantiate呼应
                if (mbd.resolvedConstructorOrFactoryMethod != null) {
                    resolved = true;
                    autowireNecessary = mbd.constructorArgumentsResolved;
                }
            }
        }
    //如果已经被解析过
        if (resolved) {
          //条件成立 使用构造函数注入
            if (autowireNecessary) {
                return autowireConstructor(beanName, mbd, null, null);
            }
            else {
        //使用默认构造函数解析
                return instantiateBean(beanName, mbd);
            }
        }

    // 使用SmartInstantiationAwareBeanPostProcessor 找到候选的构造函数 用于注入
        Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
    // AutowireMode==AUTOWIRE_CONSTRUCTOR 条件成立 说明使用基于构造函数的注入方式 (默认是AUTOWIRE_NO,需要动态检测)
    // mbd.hasConstructorArgumentValues() 条件成立 说明构造函数中拥有参数
        if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
                mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
      //基于构造函数自动注入
            return autowireConstructor(beanName, mbd, ctors, args);
        }

        // 如果mbd中配置了构造函数 则使用它进行注入
        ctors = mbd.getPreferredConstructors();
        if (ctors != null) {
            return autowireConstructor(beanName, mbd, ctors, null);
        }

        // 使用默认构造函数实例化
        return instantiateBean(beanName, mbd);
    }

分析了上面的源码之后,我们试着总结一下上面代码主要完成的事情:

1、如果mbd配置了instanceSupplier回调,则使用instanceSupplier去初始化BeanDefinition

2、如果mbd配置了工厂方法,则使用工厂方法区初始化BeanDefinition

3、实例化BeanDefinition

如果mbd已经被解析过了,则根据缓存 选择使用有参构造函数注入还是默认构造函数注入
如果mbd没有被解析过,找到mbd中候选的构造函数(一个类可能有多个构造函数),再根据一些限定条件 选择是基于有参构造函数初始化还是默认构造函数初始化
针对第1点,其实就是lambda8 supplier接口的使用,不再介绍。
针对第3点,其实就是通过反射机制 创建实例对象,最终调用了SimpleInstantiationStrategy#instantiate方法
针对第2点 举例说明下 工厂方法与静态工厂生成Bean的两种形式,再来展开说下instantiateUsingFactoryMethod源码。
————————————————
版权声明:本文为CSDN博主「码农的进阶之路」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zyxwvuuvwxyz/article/details/123253043

配置Xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="peopleFactory" class="com.wojiushiwo.factorymethod.PeopleFactory"/>

    <!--实例方法-->
    <bean id="instanceMethod" factory-bean="peopleFactory" factory-method="createPeople"/>

        <!--静态方法-->
    <bean id="staticFactoryMethod" class="com.wojiushiwo.factorymethod.People" factory-method="createPeople"/>
</beans>
//实体对象
@Data
public class People implements Serializable {

    private String name;

    private Integer age;

    public People() {
    }

    public static People createPeople() {
        People people = new People();
        people.setAge(18);
        people.setName("我就是我");
        return people;
    }
}
//People工厂类
public class PeopleFactory {

    public People createPeople() {
        return People.createPeople();
    }
}

public class FactoryMethodDemo {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("META-INF/spring.xml");

        context.refresh();

        People people = (People) context.getBean("staticFactoryMethod");
        System.out.println(people);
        People people = (People) context.getBean("instanceMethod");
        System.out.println(people);        
        context.close();
    }
}
public BeanWrapper instantiateUsingFactoryMethod(
            String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {

        BeanWrapperImpl bw = new BeanWrapperImpl();
        this.beanFactory.initBeanWrapper(bw);

        Object factoryBean;
        Class<?> factoryClass;
        boolean isStatic;
        //获取FactoryBeanName,实例方法与静态工厂方法的区别就在于有没有FactoryBeanName
        String factoryBeanName = mbd.getFactoryBeanName();
        if (factoryBeanName != null) {
      //如果存在FactoryBeanName,则说明是实例方法
            if (factoryBeanName.equals(beanName)) {
                throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
                        "factory-bean reference points back to the same bean definition");
            }
      //获取当前factoryBeanName名称的Bean
            factoryBean = this.beanFactory.getBean(factoryBeanName);
            if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) {
                throw new ImplicitlyAppearedSingletonException();
            }
      //获取工厂类Class
            factoryClass = factoryBean.getClass();
      //标记为非静态
            isStatic = false;
        }
        else {
            // 走到这里,说明是静态工厂方法
            if (!mbd.hasBeanClass()) {
                throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
                        "bean definition declares neither a bean class nor a factory-bean reference");
            }
      //factoryBean设置为null
            factoryBean = null;
      //获取工厂类Class,这里使用BeanDefinition作为其工厂类
            factoryClass = mbd.getBeanClass();
      //标记为非静态
            isStatic = true;
        }

        Method factoryMethodToUse = null;
        ArgumentsHolder argsHolderToUse = null;
        Object[] argsToUse = null;
        //explicitArgs 这个是getBean方法传递过来的,一般为null
        if (explicitArgs != null) {
            argsToUse = explicitArgs;
        }
        else {
            Object[] argsToResolve = null;
      //加锁
            synchronized (mbd.constructorArgumentLock) {
        //获取被解析的工厂方法
                factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod;
        //条件成立 说明工厂方法已经被解析过了,并存到了mbd中缓存起来了
                if (factoryMethodToUse != null && mbd.constructorArgumentsResolved) {
                    // Found a cached factory method...
                    argsToUse = mbd.resolvedConstructorArguments;
                    if (argsToUse == null) {
                        argsToResolve = mbd.preparedConstructorArguments;
                    }
                }
            }
            if (argsToResolve != null) {
                argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve, true);
            }
        }

        if (factoryMethodToUse == null || argsToUse == null) {
      //获取工厂类
            factoryClass = ClassUtils.getUserClass(factoryClass);
            //获取类中的方法
            Method[] rawCandidates = getCandidateMethods(factoryClass, mbd);
            List<Method> candidateList = new ArrayList<>();
            for (Method candidate : rawCandidates) {
        //如果方法修饰符包含static,并且方法名称是配置的FactoryMethod,则添加到候选集合中
                if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)) {
                    candidateList.add(candidate);
                }
            }
          //如果候选集合有1个元素 并且BeanDefinition中未设置构造参数 (explicitArgs一般都为null )
            if (candidateList.size() == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
        //获取方法
                Method uniqueCandidate = candidateList.get(0);
        //如果方法参数为空
                if (uniqueCandidate.getParameterCount() == 0) {
                    mbd.factoryMethodToIntrospect = uniqueCandidate;
                    synchronized (mbd.constructorArgumentLock) {
            //将下面这些全缓存到mbd中,下次直接用(与createBeanInstance方法呼应上了)
                        mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
                        mbd.constructorArgumentsResolved = true;
                        mbd.resolvedConstructorArguments = EMPTY_ARGS;
                    }
          //实例化bd,设置到BeanWrapper中
                    bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, uniqueCandidate, EMPTY_ARGS));
                    return bw;
                }
            }
            //程序走到这里 大概率是BeanDefinition中设置了构造参数
            Method[] candidates = candidateList.toArray(new Method[0]);
      //按照修饰符及方法参数 进行排序
            AutowireUtils.sortFactoryMethods(candidates);

            ConstructorArgumentValues resolvedValues = null;
      //是否构造函数注入
            boolean autowiring = (mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
            int minTypeDiffWeight = Integer.MAX_VALUE;
            Set<Method> ambiguousFactoryMethods = null;

      //最小参数数量 默认是0
            int minNrOfArgs;
            if (explicitArgs != null) {
                minNrOfArgs = explicitArgs.length;
            }
            else {
        //走到这里 说明explicitArgs未被设置参数
        //如果bd设置了构造参数,则从bd中解析参数
                if (mbd.hasConstructorArgumentValues()) {
                    ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
                    resolvedValues = new ConstructorArgumentValues();
          //得到解析的最小参数数量
                    minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
                }
                else {
                    minNrOfArgs = 0;
                }
            }

            LinkedList<UnsatisfiedDependencyException> causes = null;
            //下面主要是推断参数、FactoryMethod,代码比较长 就先不分析了
            for (Method candidate : candidates) {
                Class<?>[] paramTypes = candidate.getParameterTypes();

                if (paramTypes.length >= minNrOfArgs) {
                    ArgumentsHolder argsHolder;

                    if (explicitArgs != null) {
                        // Explicit arguments given -> arguments length must match exactly.
                        if (paramTypes.length != explicitArgs.length) {
                            continue;
                        }
                        argsHolder = new ArgumentsHolder(explicitArgs);
                    }
                    else {
                        // Resolved constructor arguments: type conversion and/or autowiring necessary.
                        try {
                            String[] paramNames = null;
                            ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
                            if (pnd != null) {
                                paramNames = pnd.getParameterNames(candidate);
                            }
                            argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw,
                                    paramTypes, paramNames, candidate, autowiring, candidates.length == 1);
                        }
                        catch (UnsatisfiedDependencyException ex) {
                            if (logger.isTraceEnabled()) {
                                logger.trace("Ignoring factory method [" + candidate + "] of bean '" + beanName + "': " + ex);
                            }
                            // Swallow and try next overloaded factory method.
                            if (causes == null) {
                                causes = new LinkedList<>();
                            }
                            causes.add(ex);
                            continue;
                        }
                    }

                    int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
                            argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
                    // Choose this factory method if it represents the closest match.
                    if (typeDiffWeight < minTypeDiffWeight) {
                        factoryMethodToUse = candidate;
                        argsHolderToUse = argsHolder;
                        argsToUse = argsHolder.arguments;
                        minTypeDiffWeight = typeDiffWeight;
                        ambiguousFactoryMethods = null;
                    }
                    // Find out about ambiguity: In case of the same type difference weight
                    // for methods with the same number of parameters, collect such candidates
                    // and eventually raise an ambiguity exception.
                    // However, only perform that check in non-lenient constructor resolution mode,
                    // and explicitly ignore overridden methods (with the same parameter signature).
                    else if (factoryMethodToUse != null && typeDiffWeight == minTypeDiffWeight &&
                            !mbd.isLenientConstructorResolution() &&
                            paramTypes.length == factoryMethodToUse.getParameterCount() &&
                            !Arrays.equals(paramTypes, factoryMethodToUse.getParameterTypes())) {
                        if (ambiguousFactoryMethods == null) {
                            ambiguousFactoryMethods = new LinkedHashSet<>();
                            ambiguousFactoryMethods.add(factoryMethodToUse);
                        }
                        ambiguousFactoryMethods.add(candidate);
                    }
                }
            }

            if (factoryMethodToUse == null) {
                if (causes != null) {
                    UnsatisfiedDependencyException ex = causes.removeLast();
                    for (Exception cause : causes) {
                        this.beanFactory.onSuppressedException(cause);
                    }
                    throw ex;
                }
                List<String> argTypes = new ArrayList<>(minNrOfArgs);
                if (explicitArgs != null) {
                    for (Object arg : explicitArgs) {
                        argTypes.add(arg != null ? arg.getClass().getSimpleName() : "null");
                    }
                }
                else if (resolvedValues != null) {
                    Set<ValueHolder> valueHolders = new LinkedHashSet<>(resolvedValues.getArgumentCount());
                    valueHolders.addAll(resolvedValues.getIndexedArgumentValues().values());
                    valueHolders.addAll(resolvedValues.getGenericArgumentValues());
                    for (ValueHolder value : valueHolders) {
                        String argType = (value.getType() != null ? ClassUtils.getShortName(value.getType()) :
                                (value.getValue() != null ? value.getValue().getClass().getSimpleName() : "null"));
                        argTypes.add(argType);
                    }
                }
                String argDesc = StringUtils.collectionToCommaDelimitedString(argTypes);
                //抛出异常
            }
            else if (void.class == factoryMethodToUse.getReturnType()) {
                //抛出异常
            }
            else if (ambiguousFactoryMethods != null) {
                //抛出异常
            }

            if (explicitArgs == null && argsHolderToUse != null) {
                mbd.factoryMethodToIntrospect = factoryMethodToUse;
                argsHolderToUse.storeCache(mbd, factoryMethodToUse);
            }
        }

        Assert.state(argsToUse != null, "Unresolved factory method arguments");
    //实例化bd 设置到BeanWrapper中
        bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, factoryMethodToUse, argsToUse));
        return bw;
    }

至此经过createBeanInstance方法 就为我们创建了一个实例对象,但是现在这个对象属性还未被赋值

实例化后阶段

实例对象创建之后,就来到了对象属性赋值过程了,我们大致看一下populateBean方法,观察下InstantiationAwareBeanPostProcessor对属性赋值过程的影响

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
        //省略无关代码

        boolean continueWithPropertyPopulation = true;
        //条件一 synthetic默认值是false 一般都会成立
        //条件二 成立的话 说明存在InstantiationAwareBeanPostProcessor
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof InstantiationAwareBeanPostProcessor) {
                    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                    //在BeanPostProcessor浅析中分析到此方法时说过,若Bean实例化后回调不返回true 则对属性赋值过程产生影响。以下代码就是说明
                    if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                        continueWithPropertyPopulation = false;
                        break;
                    }
                }
            }
        }
        //ibp.postProcessAfterInstantiation=false时 属性赋值过程终止
        if (!continueWithPropertyPopulation) {
            return;
        }

5、Spring Bean生命周期:属性赋值阶段

节在谈论Bean的实例化过程时,在说明实例化后阶段时只是粗略地看了一下populateBean,并未展开分析。本节接着populateBean开始分析对象赋值阶段的事情。
populateBean其实主要做了以下几件事:

  • Bean实例化后回调,来决定是否进行属性赋值 (上节分析过了)
  • 对属性进行自动装配
  • InstantiationAwareBeanPostProcessor属性赋值前回调
  • 属性的真正赋值
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
        //省略无关代码

        // 1、 Bean实例化后回调,来决定是否进行属性赋值 
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof InstantiationAwareBeanPostProcessor) {
                    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                    if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                        return;
                    }
                }
            }
        }

        PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
        //2、对属性进行自动装配
        int resolvedAutowireMode = mbd.getResolvedAutowireMode();
        if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
            MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
            // Add property values based on autowire by name if applicable.
            if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
                autowireByName(beanName, mbd, bw, newPvs);
            }
            // Add property values based on autowire by type if applicable.
            if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
                autowireByType(beanName, mbd, bw, newPvs);
            }
            pvs = newPvs;
        }

        boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
        boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
        //3、InstantiationAwareBeanPostProcessor属性赋值前回调
        PropertyDescriptor[] filteredPds = null;
        if (hasInstAwareBpps) {
            if (pvs == null) {
                pvs = mbd.getPropertyValues();
            }
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof InstantiationAwareBeanPostProcessor) {
                    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                    PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
                    if (pvsToUse == null) {
                        if (filteredPds == null) {
                            filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
                        }
                        pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                        if (pvsToUse == null) {
                            return;
                        }
                    }
                    pvs = pvsToUse;
                }
            }
        }
        //省略无关代码

        if (pvs != null) {
          //属性的赋值
            applyPropertyValues(beanName, mbd, bw, pvs);
        }
    }
属性自动装配

PropertyValues 对bd中属性的封装,可以理解为bd中属性键值均由其保存,其常用实现类为MutablePropertyValues,在BeanDefinition的概述及使用 有介绍其使用,可点击查看

        //这里的bd是已经执行过合并BeanDefinition操作了
        //如果bd存在属性 则获取
        PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
        //获取bd的自动注入模式 
        //注入模式有四种:
        //1.构造函数注入 2、按照名称注入 3、按照类型注入 4、不注入(默认,依然可能会被注解驱动注入)
        int resolvedAutowireMode = mbd.getResolvedAutowireMode();
        //如果是按名称注入或类型注入时
        if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
            MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
            //按名称注入
            if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
                autowireByName(beanName, mbd, bw, newPvs);
            }
            // Add property values based on autowire by type if applicable.
            if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
                //按类型注入,基本上这种比较常用
                autowireByType(beanName, mbd, bw, newPvs);
            }
            pvs = newPvs;
        }

面我们分别来大致看下autowireByNameautowireByType 熟悉下实现原理

autowireByName

protected void autowireByName(
            String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
        //获取属性名称
        String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
        //遍历属性名称
        for (String propertyName : propertyNames) {
            //如果属性名称已在beanDefinitionMap中,说明其是bd 并已被注册待IoC容器
            if (containsBean(propertyName)) {
                //根据名称获取其bean对象
                Object bean = getBean(propertyName);
                //以键值方法赋值到pvs
                pvs.add(propertyName, bean);
                // 这里是维护dependentBeanMap、dependenciesForBeanMap两个集合,
                // 这里不再展开 在说到LifecycleProcessor时再展开
                registerDependentBean(propertyName, beanName);
                //省略日志输出
            }
            else {
                //省略日志输出
            }
        }
    }

autowireByType

按类型注入稍显复杂些,但流程上与按名称注入类似

protected void autowireByType(
            String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
        //类型转换器
        TypeConverter converter = getCustomTypeConverter();
        if (converter == null) {
            converter = bw;
        }

        Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
        //依然是获取属性名称
        String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
        //遍历属性名称
        for (String propertyName : propertyNames) {
            try {
                //获取属性描述对象
                PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
                //不对Object类型做注入,因此这里判断条件如下
                if (Object.class != pd.getPropertyType()) {
                    MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
                    boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);
                    DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
                    //解析依赖
                    Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
                    if (autowiredArgument != null) {
                    //以键值方法赋值到pvs
                        pvs.add(propertyName, autowiredArgument);
                    }
                    for (String autowiredBeanName : autowiredBeanNames) {
                     // 这里是维护dependentBeanMap、dependenciesForBeanMap两个集合,
                     // 这里不再展开 在说到LifecycleProcessor时再展开
                        registerDependentBean(autowiredBeanName, beanName);
                        //省略日志输出
                    }
                    autowiredBeanNames.clear();
                }
            }
            catch (BeansException ex) {
                //省略异常信息
        }
    }

接下来我们进入到resolveDependency,大致分析下解析依赖的主要流程

DefaultListableBeanFactory#resolveDependency

public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
            @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

        descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
    //如果依赖类型是Optional
        if (Optional.class == descriptor.getDependencyType()) {
            return createOptionalDependency(descriptor, requestingBeanName);
        }
  //如果依赖类型是ObjectFactory或ObjectProvider
        else if (ObjectFactory.class == descriptor.getDependencyType() ||
                ObjectProvider.class == descriptor.getDependencyType()) {
            return new DependencyObjectProvider(descriptor, requestingBeanName);
        }
   //如果依赖类型是Inject
        else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
            return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
        }
        else {
            Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
                    descriptor, requestingBeanName);
            if (result == null) {
        //实际执行解析依赖的逻辑代码  
                result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
            }
            return result;
        }
    }

    @Nullable
    public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
            @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

        InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
        try {
            Object shortcut = descriptor.resolveShortcut(this);
            if (shortcut != null) {
                return shortcut;
            }
            //获取依赖类型
            Class<?> type = descriptor.getDependencyType();
      //获取依赖类型的默认值,如@Value注解 可提供默认值
            Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
            if (value != null) {
        //如果默认值是String类型
                if (value instanceof String) {
          //从配置文件中解析出指定key的数据
                    String strVal = resolveEmbeddedValue((String) value);
                    BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);
                    value = evaluateBeanDefinitionString(strVal, bd);
                }
        //类型转换器 用于转换类型,如配置文件中声明的是字符串类型的数字,而java中使用Integer接收,则类型转换器就派上用场了
                TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
                return (descriptor.getField() != null ?
                        converter.convertIfNecessary(value, type, descriptor.getField()) :
                        converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
            }
            //解析出类型是Stream、Map、数组、Collection等集合类型的依赖。解析的思路很类似 即去IoC容器中 查找集合类实际泛型对应的Bean
            Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
            if (multipleBeans != null) {
                return multipleBeans;
            }
            //这里主要是查找单实例Bean的,如果某个类型的Bean有多个,这里会被全部查找出来,因此使用Map接收
            Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
            if (matchingBeans.isEmpty()) {
                if (isRequired(descriptor)) {
                    raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
                }
                return null;
            }

            String autowiredBeanName;
            Object instanceCandidate;

      //如果查找出的Bean有多个,
            if (matchingBeans.size() > 1) {
        //找出标注了@Primary的那个Bean名称,作为查找出的Bean
                autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
                if (autowiredBeanName == null) {
                    if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
            //如果没有@Primary注解标注,那么抛出NoUniqueBeanDefinitionException
                        return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
                    }
                    else {
                        return null;
                    }
                }
                instanceCandidate = matchingBeans.get(autowiredBeanName);
            }
            else {
        //如果查找出的Bean只有1个 那么说明找到了。
                Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
                autowiredBeanName = entry.getKey();
                instanceCandidate = entry.getValue();
            }

            if (autowiredBeanNames != null) {
                autowiredBeanNames.add(autowiredBeanName);
            }
            if (instanceCandidate instanceof Class) {
                instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
            }
            Object result = instanceCandidate;
            if (result instanceof NullBean) {
                if (isRequired(descriptor)) {
                    raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
                }
                result = null;
            }
            if (!ClassUtils.isAssignableValue(type, result)) {
                throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
            }
            return result;
        }
        finally {
            ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
        }
    }

可以看出resolveDependency方法还是很强大的,无论是单一类型对象还是集合类型对象,无论是Optional类型还是延迟加载ObjectFactory类型 其均可以解析出来。

属性赋值前回调
         //boolean值 判断有没有InstantiationAwareBeanPostProcessor存在
         boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
         // 这是 是否依赖检查的标记 不是我们此次的重点
        boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);

        PropertyDescriptor[] filteredPds = null;
        //IoC容器中如果存在InstantiationAwareBeanPostProcessor
        if (hasInstAwareBpps) {
            if (pvs == null) {
                pvs = mbd.getPropertyValues();
            }
            //遍历BeanPostProcessor,找到InstantiationAwareBeanPostProcessor类型
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof InstantiationAwareBeanPostProcessor) {
                    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                    //postProcessProperties、postProcessPropertyValues两个方法含义类似。如果postProcessProperties未被重写 则执行postProcessPropertyValues方法
                    PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
                    if (pvsToUse == null) {
                        if (filteredPds == null) {
                            filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
                        }
                        pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                        if (pvsToUse == null) {
                            return;
                        }
                    }
                    pvs = pvsToUse;
                }
            }
        }

关于InstantiationAwareBeanPostProcessor的描述,可参见BeanPostProcessor浅析

这里针对一个小案例说明下postProcessPropertyValues和postProcessProperties的使用

需求:将注入的user对象中name属性由wojiushiwo修改为abc
————————————————
版权声明:本文为CSDN博主「码农的进阶之路」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zyxwvuuvwxyz/article/details/123271627

实体对象User

@Data
@ToString
public class User {
    private String name;
    private Integer age;
    public User() {
    }
    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}
public class MyInstantiationBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        if(ObjectUtils.nullSafeEquals("user",beanName) && User.class.equals(bean.getClass())){

            final MutablePropertyValues propertyValues;

            if(pvs instanceof MutablePropertyValues){
                propertyValues= (MutablePropertyValues) pvs;
            }else{
                propertyValues=new MutablePropertyValues();
            }

            if(propertyValues.contains("name")){
                propertyValues.removePropertyValue("name");
                propertyValues.addPropertyValue("name","abcd");
            }
            return propertyValues;

        }

        return null;
    }
}
public class BeanPostProcessDemo {

    public static void main(String[] args) {
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
        beanDefinitionBuilder.addPropertyValue("name", "wojiushiwo");
        beanDefinitionBuilder.addPropertyValue("age", 20);
        // 获取 AbstractBeanDefinition
        AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
        // 附加属性(
        beanDefinition.setAttribute("name", "我是附加属性");
        // 当前 BeanDefinition 来自哪里(辅助作用)
        beanDefinition.setSource(BeanPostProcessDemo.class);

        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

        beanFactory.addBeanPostProcessor(new MyInstantiationBeanPostProcessor());
        // 注册 User 的 BeanDefinition
        beanFactory.registerBeanDefinition("user", beanDefinition);

        User user = beanFactory.getBean("user", User.class);

        System.out.println(user);

    }
}
public class BeanPostProcessDemo {

    public static void main(String[] args) {
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(User.class);
        beanDefinitionBuilder.addPropertyValue("name", "wojiushiwo");
        beanDefinitionBuilder.addPropertyValue("age", 20);
        // 获取 AbstractBeanDefinition
        AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
        // 附加属性(
        beanDefinition.setAttribute("name", "我是附加属性");
        // 当前 BeanDefinition 来自哪里(辅助作用)
        beanDefinition.setSource(BeanPostProcessDemo.class);

        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

        beanFactory.addBeanPostProcessor(new MyInstantiationBeanPostProcessor());
        // 注册 User 的 BeanDefinition
        beanFactory.registerBeanDefinition("user", beanDefinition);

        User user = beanFactory.getBean("user", User.class);

        System.out.println(user);

    }
}

输出结果:

User(name=abcd, age=20)

属性的真正赋值
    if (pvs != null) {
            //将从前面步骤得到的pvs 赋值到beanWrapper中以实现属性赋值,这部分具体源码这里不展开了
            applyPropertyValues(beanName, mbd, bw, pvs);
        }

6、Spring Bean生命周期:Bean的初始化阶段

接着上节继续分析,上节说了Bean属性赋值的过程,这节就聊一下初始化Bean的过程
initializeBean方法主要有四块内容

  1. Bean Aware方法回调
  2. BeanPostProcessor 初始化前回调
  3. 执行初始化方法
  4. BeanPostProcessor 初始化后回调

下面我们就按照这个顺序跟着源码逐块分析

Bean Aware方法回调
 private void invokeAwareMethods(final String beanName, final Object bean) {
        if (bean instanceof Aware) {
            //如果当前Bean实现了BeanNameAware,则将beanName通过回调传给当前Bean
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
            //如果当前Bean实现了BeanClassLoaderAware,则将BeanClassLoader通过回调传给当前Bean
            if (bean instanceof BeanClassLoaderAware) {
                ClassLoader bcl = getBeanClassLoader();
                if (bcl != null) {
                    ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
                }
            }
            //如果当前Bean实现了BeanFactoryAware,则将BeanFactory通过回调传给当前Bean
            if (bean instanceof BeanFactoryAware) {
                ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
            }
        }
    }

这个方法主要实现了BeanNameAwareBeanClassLoaderAwareBeanFactoryAware三个Aware接口的回调。

下面就举例演示下

public class BeanAwareDemo implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(BeanAwareDemo.class);
        context.refresh();
        BeanAwareDemo bean = context.getBean(BeanAwareDemo.class);

        System.out.println("beanFactory==>" + bean.beanFactory);
        System.out.println("beanName==>" + bean.beanName);

        System.out.println("classLoader==>" + bean.classLoader);
        context.close();
    }

    private ClassLoader classLoader;

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    private String beanName;

    @Override
    public void setBeanName(String name) {
        this.beanName = name;
    }
}

输出结果:

beanFactory==>org.springframework.beans.factory.support.DefaultListableBeanFactory@458c1321
beanName==>beanAwareDemo
classLoader==>sun.misc.Launcher$AppClassLoader@18b4aac2

看到这可能有同学会想:“那我平时经常用的ApplicationContextAware是什么时候,在哪里被回调的呢?”

我:别着急,剩下的一些常用Aware接口的回调时机、在哪里回调,我们先不再这里展开了,以后有时间了单独拿出来讨论。

Bean初始化前后阶段
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
        //省略分析过的代码

        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
            //初始化前回调
            wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }

        try {
            //执行初始化方法
            invokeInitMethods(beanName, wrappedBean, mbd);
        }
        catch (Throwable ex) {
            //省略异常信息
        }
        if (mbd == null || !mbd.isSynthetic()) {
          //初始化后回调
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }

        return wrappedBean;
    }

里呢,为了方便说明,将Bean初始化前后放到一起来分析了。

经过我们前面章节的分析,我们对BeanPostProcessor这个后置处理器也有了一些认识了。主要是通过postProcessBeforeInitialization、postProcessAfterInitialization两个方法在Bean初始化前与后对Bean进行些修饰。
一起来看下这两个方法吧!

 //初始化前回调
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
            throws BeansException {

        //将原始Bean对象存一份
        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessBeforeInitialization(result, beanName);  
            // 如果有回调函数 返回null了,那么依然使用原始的Bean对象
            if (current == null) {
                return result;
            }
            //如果不返回null,则将返回值作为当前Bean对象,接着进行循环处理
            result = current;
        }
        return result;
    }

    //初始化后回调
    @Override
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException {
        //将原始Bean对象存一份
        Object result = existingBean;
        for (BeanPostProcessor processor : getBeanPostProcessors()) {

            Object current = processor.postProcessAfterInitialization(result, beanName);
            // 如果有回调函数 返回null了,那么依然使用原始的Bean对象
            if (current == null) {
                return result;
            }
            //如果不返回null,则将返回值作为当前Bean对象,接着进行循环处理
            result = current;
        }
        return result;
    }

这里不再给出栗子了,有兴趣的小伙伴可以去BeanPostProcessor浅析看一下

Bean 初始化方法
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
            throws Throwable {
        //判断当前Bean是否实现了InitializingBean
        boolean isInitializingBean = (bean instanceof InitializingBean);
        if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
            //省略日志输出
            //if条件成立 是Java安全代码逻辑可忽略,与else代码段实质操作一致
            if (System.getSecurityManager() != null) {
                try {
                    AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
                        ((InitializingBean) bean).afterPropertiesSet();
                        return null;
                    }, getAccessControlContext());
                }
                catch (PrivilegedActionException pae) {
                    throw pae.getException();
                }
            }
            else {
                // 如果Bean实现了InitializingBean,那么这里回调afterPropertiesSet方法
                ((InitializingBean) bean).afterPropertiesSet();
            }
        }
        //如果mbd不为空 且bean类型不是NullBean
        if (mbd != null && bean.getClass() != NullBean.class) {
           //获取InitMethod方法名
            String initMethodName = mbd.getInitMethodName();
            //条件成立:1、存在initMethod方法 2、当前Bean不是InitializingBean或initMetodName不等于afterPropertiesSet 3、不是外部管理的initMethods
            if (StringUtils.hasLength(initMethodName) &&
                    !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                    !mbd.isExternallyManagedInitMethod(initMethodName)) {
                    // 那么执行自定义InitMethod方法,根据方法名获取Method 反射调用
                invokeCustomInitMethod(beanName, bean, mbd);
            }
        }
    }

上面给出了afterPropertiesSet、initMethod两个初始化方法的调用过程及调用顺序,这里的initMethod与@Bean中initMethod属性是一致的。

有的小伙伴可能会说了:“那我平时还经常使用一种别的初始化方法呢?是用@PostConstruct”标注的,它不是在这里被调用吗?那它是在哪里被调用的?"

我: 还记得BeanPostProcessor浅析章节吗? 我们在那个章节介绍了一些常用的BeanPostProcessor,其中@PostConstruct就是在CommonAnnotationBeanPostProcessor被调用的。

其实CommonAnnotationBeanPostProcessor继承自InitDestroyAnnotationBeanPostProcessor,确切地说是两个类联合起来完成对@PostConstruct的解析和方法调用

还记得上面👆 初始化前回调函数的调用吗?没错,无论是对@PostConstruct的解析和方法调用均发生在postProcessBeforeInitialization。我们简单地梳理下

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        //这里的元信息主要收集目标Bean中标注了@PostConstruct和@PreDestroy注解的方法
        LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
        try {
            // 这里以反射方式直线初始化方法的调用
            metadata.invokeInitMethods(bean, beanName);
        }
        catch (InvocationTargetException ex) {
            throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
        }
        catch (Throwable ex) {
            throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
        }
        return bean;
    }

上面分析了@PostConstruct、afterPropertiesSet及initMethod初始化方法的调用过程,那么小伙伴 如果同一个Bean中同时有着三个初始化方法,那它们的调用顺序是怎样的呢?
答:其实通过我们的分析就可以推断出了,@PostConstruct标注的初始化方法发生在初始化前回调函数阶段,afterPropertiesSet和initMethod则发生在初始化阶段,并且afterPropertiesSet的调用先于initMethod。因此三者的调用顺序是@PostConstruct>afterPropertiesSet>initMethod

bean的生命周末

BeanFactory

 * <p>Bean factory implementations should support the standard bean lifecycle interfaces
 * as far as possible. The full set of initialization methods and their standard order is:
 * <ol>
 * <li>BeanNameAware's {@code setBeanName}
 * <li>BeanClassLoaderAware's {@code setBeanClassLoader}
 * <li>BeanFactoryAware's {@code setBeanFactory}
 * <li>EnvironmentAware's {@code setEnvironment}
 * <li>EmbeddedValueResolverAware's {@code setEmbeddedValueResolver}
 * <li>ResourceLoaderAware's {@code setResourceLoader}
 * (only applicable when running in an application context)
 * <li>ApplicationEventPublisherAware's {@code setApplicationEventPublisher}
 * (only applicable when running in an application context)
 * <li>MessageSourceAware's {@code setMessageSource}
 * (only applicable when running in an application context)
 * <li>ApplicationContextAware's {@code setApplicationContext}
 * (only applicable when running in an application context)
 * <li>ServletContextAware's {@code setServletContext}
 * (only applicable when running in a web application context)
 * <li>{@code postProcessBeforeInitialization} methods of BeanPostProcessors
 * <li>InitializingBean's {@code afterPropertiesSet}
 * <li>a custom init-method definition
 * <li>{@code postProcessAfterInitialization} methods of BeanPostProcessors
 * </ol>
 *
 * <p>On shutdown of a bean factory, the following lifecycle methods apply:
 * <ol>
 * <li>{@code postProcessBeforeDestruction} methods of DestructionAwareBeanPostProcessors
 * <li>DisposableBean's {@code destroy}
 * <li>a custom destroy-method definition
 * </ol>

     bean的生命周期
     1. 准备工作
     2. 创建容器对象
     3. 读取配置文件,加载beanDefine对象
     4. 执行beanfactoryPostprocessor扩展工作
       4.1 准备工作
        注册beanpostprocessor
        注册广播器
        初始化国际化配置
        注册监听器
     5. 对象实例化操作
     6. 对象初始化操作
      6.1 自定义属性赋值
      6.2 容器对象赋值
      6.3 调用beanpostprocessor的前置处理器进行扩展
      6.4 调用initMethod方法进行初始化
      6.5 调用beanpostprocessor后置处理器进行扩展

AbstractApplicationContext

/**
*    1. 准备工作
     2. 创建容器对象
     3. 读取配置文件,加载beanDefine对象
     4. 执行beanfactoryPostprocessor扩展工作
       4.1 准备工作
        注册beanpostprocessor
        注册广播器
        初始化国际化配置
        注册监听器
     5. 对象实例化操作
     6. 对象初始化操作
      6.1 自定义属性赋值
      6.2 容器对象赋值
      6.3 调用beanpostprocessor的前置处理器进行扩展
      6.4 调用initMethod方法进行初始化
      6.5 调用beanpostprocessor后置处理器进行扩展
*/
@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
        // 1. 准备工作
        // Prepare this context for refreshing.
        prepareRefresh();
        //2. 创建容器对象
        // Tell the subclass to refresh the internal bean factory.
        // 加载xml配置文件的属性值到当前工厂,最重要就是beanDefine
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        // beanFactory的准备工作,对各种属性进行填充
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            // 子类覆盖方法额外处理,到此我们一般不处理任何扩展工作,但是可以查看web中的代码,有具体实现
            postProcessBeanFactory(beanFactory);

            StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
            // Invoke factory processors registered as beans in the context.
            // 调用各种beanfactory处理器
            invokeBeanFactoryPostProcessors(beanFactory);
           // 注册bean处理器,这里只是注册功能,真正调用getbean方法
            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);
            beanPostProcess.end();
            // 为上下文初始化message消息,即不同语言的消息体,国际化处理,
            // Initialize message source for this context.
            initMessageSource();
            // 初始化事件监听
            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();
            // 留给子类来初始化其他bean
            // Initialize other special beans in specific context subclasses.
            onRefresh();
            // 在所有注册的bean中,查找listener bean,注册到消息广播中
            // Check for listener beans and register them.
            registerListeners();
            // 完成刷新过程,通知生命周期处理器lifecycleprocessor刷新过程,同时发出ContextRefreshEvent通知别人
            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            finishRefresh();
        }

        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
            }

            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();

            // Reset 'active' flag.
            cancelRefresh(ex);

            // Propagate exception to caller.
            throw ex;
        }

        finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
            contextRefresh.end();
        }
    }
}

BeanDefinition的合并源码分析

1、如果不存在parentName,即不需要被合并,直接将bd转为RootBeanDefinition 返回即可

2、如果存在parentName

先根据parentName 找到父bd,若实体存在多级继承关系,则需要递归地查找。
将父bd转为RootBeanDefinition,并将子bd与父bd进行合并
设置一些其他属性

循环依赖

三级缓存,提前暴露自己。

/** Cache of singleton objects: bean name to bean instance. 
* 一级缓存  
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
//三级缓存  
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

//二级缓存  
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

三级缓存。

ObjectFactory 函数式表达式。

加载,首先去读取一级缓存,

@Component
public class A {

    @Autowired
    B b;
}

@Component
public class B {

    @Autowired
    A a;
}

Spring Bean对象从创建到初始化大致会经过四个流程

getSingleton()、doCreateBean()、populateBean()、addSingleton()

getSingleton:从单例池中获取bean对象,如果没有,则进行创建
doCreateBean():创建bean对象
populateBean():填充依赖,如果被填充的对象不存在于单例池,则进行创建等四个流程
addSingleton():将初始化完成的对象加入到单例池

循环依赖的对象在三级缓存中的迁移过程

  • A在实例化过程中,需要B的实例,先从一级缓存中找,没有,在从二级缓存中找,也没有,在从三级缓存中,也没有,于是创建A,把A放在三级缓存中。去实例化B
  • B在实例化过程中,先从一级缓存找,没有,则从二级缓存找,没有,在从三级缓存中,也没有找到,则在三级缓存创建B,然后再找A的实例,先从一级缓存找,没有找到,在从二级缓存找,也没有找,则从三级缓存找,好到了A,找到了,然后把三级缓存的A放入到二级缓存,并删除三级缓存A。
  • B顺利初始化,将自己放入到一级缓存,把二级缓存的B删除,三级缓存的B删除。
  • 再试创建A,A先B的实例,在一级缓存中找,找到了B,则完成A的创建,删除二级缓存中的A,在删除三级缓存中的A。

img

七:Aware回调

SpringBeanAware接口回调阶段

Spring中定义了Aware接口,该接口并未定义方法。该接口更多的是一个标记的作用,具体方法由子类去定义和实现。

BeanNameAware

BeanNameAware
BeanClassLoaderAware
BeanFactoryAware

ApplicationContextAware

ApplicationContextAware接口定义了一个设置应用上下文的方法。该接口的实现类实现该方法,可以做对上下文处理的逻辑。该aware还要对应一个BeanPostProcessor才能生效。
Spring中内置了一个ApplicationContextAwareProcessor的后置处理器来处理,在BeanPostProcessor中会检查bean是否实现Aware接口,然后调用回调方法:

八:HandlerInterceptor简介

这里我分三篇博客来介绍HandlerInterceptor的使用,从基本的使用、到自定义注解、最后到读取body中的流解决无法多次读取的问题。

1、定义实现类

定义一个Interceptor 非常简单方式也有几种,我这里简单列举两种

1、类要实现Spring 的HandlerInterceptor 接口

2、类继承实现了HandlerInterceptor 接口的类,例如 已经提供的实现了HandlerInterceptor 接口的抽象类HandlerInterceptorAdapter

这里博主用的是第二种方法继承HandlerInterceptorAdapter

2、HandlerInterceptor方法介绍

    boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception;

    void postHandle(
            HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
            throws Exception;

    void afterCompletion(
            HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception;

九:Spring整合mybatis

mybatis干什么的

十:springboot使用restTemplate

一、概述

  1. RestTemplate很多请求都需要用到相似或者相同的Http Header;
  2. 如果在每次请求之前都把Header填入HttpEntity/RequestEntity,这样的代码会显得十分冗余。

二、实现

1. 原理

Spring提供了ClientHttpRequestInterceptor接口,可以对RestTemplate请求进行拦截,并在其被发送至服务端之前修改请求或是增强相应的信息。

2. 代码
实现ClientHttpRequestInterceptor接口
@Component
public class ActionTrackInterceptor implements ClientHttpRequestInterceptor {
  @Autowired
  ActionIdGenerator actionIdGenerator;

  @Override
  public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
      throws IOException {
    HttpHeaders headers = request.getHeaders();

    // 加入自定义字段
    headers.add("actionId", actionIdGenerator.generate());

    // 保证请求继续被执行
    return execution.execute(request, body);
  }
}
将自定义拦截器添加到RestTemplate实例
@Configuration
public class ClientConfig {

  // 注入拦截器。拦截器也可以不声明为Bean, 直接在这里新建实例
  @Autowired
  ActionTrackInterceptor actionTrackInterceptor;

  // 声明为Bean,方便应用内使用同一实例
  @Bean
  public RestTemplate restTemplate() {
    RestTemplate restTemplate = new RestTemplate();

    // 把自定义的ClientHttpRequestInterceptor添加到RestTemplate,可添加多个
    restTemplate.setInterceptors(Collections.singletonList(actionTrackInterceptor));
    return restTemplate;
  }
}

具体使用

前期的工作已经完成了,现在使用这个RestTemplate实例发送请求,就会在Header中带上“actionId”这个字段了,当然你可以配置更多的诸如Accept, Content-Type等通用的字段

// 客户端代码
restTemplate.getForObject(SERVER_URL, Example.class);

// 服务端代码
// 如果服务端也是用Spring RestController/MVC 实现,利用@RequestHeader注解,即可获取之前添加的actionId字段了
@RequestMapping(value = "/example")
public Example example(@RequestHeader("actionId") String actionId) {
    //业务逻辑
}

序列化与反序列化

错误日志:

*com.google.gson.internal.LinkedTreeMap cannot be cast to xx(entity)*

解释

LinkedTreeMap 得到的对象无法被映射到实体类

原因

泛型的map被转成gson后,对象被擦除,变为无属性的Object。所以无法被还原。

错误代码

public <K, V> Map<K, V> toMap2(String json, Class<K> kClazz, Class<V> vClazz) {
    return sGson.fromJson(json, new TypeToken<Map<K, V>>() {}.getType());
}

解决方法

    /**
     * @param json map的序列化结果
     * @param <K>   k类型
     * @param <V>   v类型
     * @return Map<K       ,       V>
     */
    public <K, V> Map<K, V> toMap(String json, Class<K> kClazz, Class<V> vClazz) {
        return sGson.fromJson(json,TypeToken.getParameterized(Map.class,kClazz,vClazz).getType());
    }

其他几种解决思路

当Map的Key为String类型时

为什么要特别说明为key为String呢,因为不管你的Key为什么类型,通过Gson还原时得到的LinkedTreeMap 的 key都为String类型。

此时通过JsonObject即可解决

    public <V> Map<String, V> toStringKeyMap(String json, Class<V> vClazz) {
        Map<String, V> map = new HashMap<>();
        try {
            JsonObject obj = new JsonParser().parse(json).getAsJsonObject();
            Set<Map.Entry<String, JsonElement>> entrySet = obj.entrySet();
            for (Map.Entry<String, JsonElement> entry : entrySet) {
                String entryKey = entry.getKey();
                JsonObject object = (JsonObject) entry.getValue();
                V value = sGson.fromJson(object, vClazz);
                map.put(entryKey, value);
            }
        } catch (Exception e) {
            return null;
        }
        return map;
    }

能会问,那怎么不用JsonObject转化 JsonObject kobject = (JsonObject) entry.getKey() , 那样不就解决问题了

因为Key被映射为String类型了,目前还没找到解决办法,也不打算在这中地方深追下去了,太费时间了。

通过 由Map的key组成的List,和有value组成的List解决

**思路:**先得到由Map的Key组成的keyList,再得到由value组成的valueList;之后分别序列化keyList、valueList。反序列化时,也是分别对keyList和valueList反序列化,在依次添加到map中去

1)得到keyList和vList

/**
     * @param map map对象
     * @param <K> k类型
     * @param <V> v类型
     * @return keyList
     */
    public static <K, V> List<K> getMapKeyList(Map<K, V> map, Class<K> kClazz, Class<V> vClazz) {
        List<K> keyList = new ArrayList<>();
        for (Map.Entry<K, V> entry : map.entrySet()) {
            keyList.add(entry.getKey());
        }
        return keyList;
    }

    /**
     * @param map     map对象
     * @param keyList 由map的key组成的keyList
     * @param <K>     k类型
     * @param <V>     v类型
     * @return valueList
     */
    public static <K, V> List<V> getMapValueListByKey(Map<K, V> map, List<K> keyList, Class<K> kClazz, Class<V> vClazz) {
        List<V> valueList = new ArrayList<>();

        for (K k : keyList) {
            V v = map.get(k);
            valueList.add(v);
        }

        return valueList;
    }

(2)序列化keyList和valueList

    private Gson sGson = new Gson();

    public String toJson(Object object) {
        return sGson.toJson(object);
    }

(3)反序列化keyList和valueList,得到map

    /**
     * @param kJson keyList的序列化结果
     * @param vJson valueList的序列化结果
     * @param <K>   k类型
     * @param <V>   v类型
     * @return Map<K   ,   V>
     */
    public <K, V> Map<K, V> toMap(String kJson, String vJson, Class<K> kClazz, Class<V> vClazz) {
        Map<K, V> map = new HashMap<>();
        try {
            List<K> kList = toList(kJson, kClazz);
            List<V> vList = toList(vJson, vClazz);
            for (int i = 0; i < kList.size(); i++) {
                K k = kList.get(i);
                V v = vList.get(i);
                map.put(k, v);
            }
        } catch (Exception e) {
            return null;
        }
        return map;
    }

    /**
     * @param json list的序列化字符串
     * @param <T>  T类型
     * @return List<T>
     */
    public <T> List<T> toList(String json, Class<T> clazz) {
        List<T> list = new ArrayList<>();
        try {
            JsonArray array = new JsonParser().parse(json).getAsJsonArray();
            for (final JsonElement elem : array) {
                list.add(new Gson().fromJson(elem, clazz));
            }
        } catch (Exception e) {
            return null;
        }
        return list;
    }

ParameterizedType接口顾名思义就是参数化类型。例如List、String这种不是参数化类型,凡是这种带有泛型的类型如Collection、Map<String, Object>就是参数化类型。

ParameterizedType type = TypeUtils.parameterize(List.class,UserVo.class);

List<UserVo> users = restTemplate.getForObject(url,param, type);

十一:泛型

泛型在java中有很重要的地位,无论是开源框架还是JDK源码都能看到它。

毫不夸张的说,泛型是通用设计上必不可少的元素,所以真正理解与正确使用泛型,是一门必修课。

1.泛型本质

泛型的本质是参数化类型,即给类型指定一个参数,然后再使用的时再指定此参数的具体值,那么这个类型就可以在使用时候决定了。这种类型可以用在类,接口和方法中,分别为泛型类,泛型接口,泛型方法。

2.为什么使用泛型

泛型的好处是编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高了代码的重用率。

1.安全

2.消除转换

3.提升性能

4.重用性

2.1 保证安全性

没有使用泛型之前,从集合中读取的每一个对象都必须进行类型转换,如果不小心插入了错误的类型,在运行时候就会出错

没有使用泛型

public static void noGeneric() {
ArrayList names = new ArrayList();
names.add("mikechen的互联网架构");
names.add(123); //编译正常
}

使用了泛型

public static void useGeneric() {
ArrayList<String> names = new ArrayList<>();
names.add("mikechen的互联网架构");
names.add(123); //编译不通过
}

想当于告诉了编译器,每个集合接受的对象是什么,编译期间就会做类型校验,告知是否插错了类型,使得程序更加安全,增强了程序的健壮性。

2.2消除了类型转换

没有使用泛型,需要强制转换,使用了自动转换。

List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);

List<String> list = new ArrayList<String>();
list.add("hello");
String s = list.get(0); // no cast

2.3避免了不必要的装箱、拆箱操作,提高程序的性能

泛型固定了类型,使用的时候就已经知道了值得类型,避免了不必要的拆箱与装箱,提高性能。

3.如何使用泛型

泛型三种类型:泛型类,泛型接口,泛型方法

3.1泛型类

定义格式:

public class className {

}

public class GenericClass<ab,a,c> {}

T:任意类型 type
E:集合中元素的类型 element
K:key-value形式 key
V: key-value形式 value
示例代码:

public class 类名 <泛型类型1,...> {
    
}

注意类型:泛型类必须是引用类型,不能是基本类型。

定义泛型类,在类名后添加一对尖括号,在尖括号内添加参数类型,可多个

3.2 泛型接口

定义格式:

public <泛型类型> 返回类型 方法名(泛型类型 变量名)

public interface GenericInterface <T>{
    void show(T value);
}

3.3泛型方法

public T method (){

}

    public <T> T getT(T t) {
        return t;
    }

3.4 泛型通配符

java通配符是用于解决泛型之间引用传递问题的特殊语法,

无界通配符

<?>

类型是任何类型

固定上界通配符

<? extend E> 只能是E或者是E的之类

固定下界通配符

<? super E> 只能是E或者是E的父类(超类)

4.泛型中KTVE的含义

果点开JDK中一些泛型类的源码,我们会看到下面这些代码:

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
...
}
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {
...
}

常见泛型参数名称有如下:

E: Element (在集合中使用,因为集合中存放的是元素)
T:Type(Java 类)
K: Key(键)
V: Value(值)
N: Number(数值类型)
?: 表示不确定的java类型

4.泛型原理

泛型的本质是将数据类型参数化,它通过擦除的方式来实现,即在编译期间擦除泛型语法并作出一些相应类型转换动作。

例如:

public class Caculate<T> {
private T num;
}

们定义了一个泛型类,定义了一个属性成员,该成员的类型是一个泛型类型,这个 T 具体是什么类型,我们也不知道,它只是用于限定类型的。

反编译一下这个 Caculate 类:

public class Caculate{
public Caculate(){}
private Object num;
}

发现编译器擦除 Caculate 类后面的两个尖括号,并且将 num 的类型定义为 Object 类型。

那么是不是所有的泛型类型都以 Object 进行擦除呢?大部分情况下,泛型类型都会以 Object 进行替换,而有一种情况则不是。那就是使用到了extends和super语法的有界类型,如:

public class Caculate<T extends String> {
private T num;
}

num会被String替换,而不是object。

5.泛型类介绍

Java类型Type 之

ParameterizedType

GenericArrayType,

TypeVariabl

WildcardType

反射+泛型有关的接口类型

ava.lang.reflect.Type:java语言中所有类型的公共父接口
java.lang.reflect.ParameterizedType
java.lang.reflect.GenericArrayType
java.lang.reflect.WildcardType

1. Type直接子接口

ParameterizedType,GenericArrayType,TypeVariable和WildcardType四种类型的接口

ParameterizedType: 表示一种参数化的类型,比如Collection

GenericArrayType: 表示一种元素类型是参数化类型或者类型变量的数组类型

TypeVariable: 是各种类型变量的公共父接口

WildcardType: 代表一种通配符类型表达式,比如?, ? extends Number, ? super Integer【wildcard是一个单词:就是“通配符”

2. Type直接实现子类 :Class类
3. java.lang.reflect.Type接口

Type所有类型指代的有:
原始类型 (raw types)【对应Class】,
参数化类型 (parameterizedtypes)【对应ParameterizedType】,
数组类型 (array types)【对应GenericArrayType】,
类型变量 (type variables)【对应TypeVariable】,
基本数据类型(primitivetypes)【仍然对应Class】

4. java.lang.reflect.ParameterizedType接口

ParameterizedType接口类型的含义
表示参数化类型。比如:Map这种参数化类型
获取参数化类型<>中的实际类型
源码声明:Type[] getActualTypeArguments();
【注意】无论<>中有几层<>嵌套,这个方法仅仅脱去最外层的<>之后剩下的内容就作为这个方法的返回值。

public E methodIV(ArrayList<ArrayList<String>> al1,   
ArrayList<E> al2,
ArrayList<String> al3,
ArrayList<? extends Number> al4,
ArrayList<E[]> al5){} 
//那么他的每一参数总体上看都是参数化类型的。
1. 对于ArrayList<Arraylist<String>>,通过getActualTypeArguments()返回之后,脱去最外层的<>之后,剩余的类型是ArrayList<String>。因此对这个参数的返回类型是ParameterizedType2. 对于ArrayList<E>,通过getActualTypeArguments()返回之后,脱去最外层的<>之后,剩余的类型是E。因此对这个参数的返回类型是TypeVariable3. 对于ArrayList<String>,通过getActualTypeArguments()返回之后,脱去最外层的<>之后,剩余的类型是String。因此对这个参数的返回类型是Class4. 对于ArrayList<? extends Number>,通过getActualTypeArguments()返回之后,脱去最外层的<>之后,剩余的类型是? Extends Number。因此对这个参数的返回类型是WildcardType5. 对于ArrayList<E[]>,通过getActualTypeArguments()返回之后,脱去最外层的<>之后,剩余的类型是E[]。因此对这个参数的返回类型是GenericArrayType
5. java.lang.reflect. GenericArrayType接口

GenericArrayType接口类型的含义
表示泛型数组类型。比如:void method(ArrayList[] al){…}
【注意】<>不能出现在数组的初始化中,即new数组之后不能出现<>,否则javac无法通过。但是作为引用变量或者方法的某个参数是完全可以的。
获取泛型数组中元素的类型
源码声明:Type getGenericComponentType();
【注意】无论从左向右有几个[]并列,这个方法仅仅脱去最右边的[]之后剩下的内容就作为这个方法的返回值。
为什么返回值类型是Type?

public static  E methodV(
String[] p1,
E[] p2,
ArrayList[] p3,
E[][] p4){}
//
1. 对于String[],通过getGenericComponentType()返回之后,脱去最右边的[]之后,剩余的类型是String。因此对这个参数的返回类型是Class
2. 对于E[],通过getGenericComponentType()返回之后,脱去最右边的[]之后,剩余的类型是E。因此对这个参数的返回类型是TypeVariable
3. 对于ArrayList[],通过getGenericComponentType()返回之后,脱去最右边的[]之后,剩余的类型是ArrayList。因此对这个参数的返回类型是ParameterizedType
ArrayList<String>[] 作为引用变量或参数是可以的。
4. 对于E[][],通过getGenericComponentType()返回之后,脱去最右边的[]之后,剩余的类型是E[]。因此对这个参数的返回类型是GenericArrayType

6. java.lang.reflect.TypeVariable接口

TypeVariable接口类型的含义
表示类型参数或者又叫做类型变量。比如:void method(E e){}中的E就是类型变量

获取类型变量的泛型限定的上边界的类型
源码声明:Type[] getActualTypeArguments();

【注意】这里面仅仅是上边界。原因就是类型变量在定义的时候只能使用extends进行(多)边界限定。不能使用super,否则编译无法通过。同时extends给出的都是类型变量的上边界。
为什么是返回类型是数组?因为类型变量可以通过&进行多个上边界限定,因此上边界有多个,因此返回值类型是数组类型[ ]。
例如下面的方法:

public static <e extends Map & Cloneable & Serializable> E methodVI(E e){}
E的第一个上边界是Map,ParameterizedType类型
E的第二个上边界是Cloneable,是Class类型
因此,为统一,返回值的数组的元素类型就是Type

7. java.lang.reflect.WildcardType接口

WildcardType接口类型的含义
表示通配符类型的表达式。

比如 void printColl(ArrayList<? extends Number> al); 中的 ? extends Number

【注意】根据上面API的注释提示:现阶段通配符表达式仅仅接受一个上边界或者下边界,这个和定义类型变量时候可以指定多个上边界是不一样。但是API说了,为了保持扩展性,这里返回值类型写成了数组形式。实际上现在返回的数组的大小就是1
获取通配符表达式对象的泛型限定的上边界的类型
源码声明:Type[] getUpperBounds();
【注意】上面说了,现阶段返回的Type[ ]中的数组大小就是1个。写成Type[ ]是为了语言的升级而进行的扩展。
例如下面的方法

1. public static voidprintColl(ArrayList<? extends ArrayList> al){}
通配符表达式是:? extends ArrayList,这样 extends后面是?的上边界,这个上边界是ParameterizedType类型。
2. public static  voidprintColl(ArrayList<? extends E> al){}
通配符表达式是:? extends E,这样 extends后面是?的上边界,这个上边界是TypeVariable类型
3.public static  voidprintColl(ArrayList <? extends E[]> al){}
通配符表达式是:? extends E[],这样 extends后面是?的上边界,这个上边界是GenericArrayType类型
4.public static  voidprintColl(ArrayList<? extends Number> al){}
通配符表达式是:? extends Number,这样 extends后面是?的上边界,这个上边界是Class类型
最终统一成Type作为数组的元素类型。

8. Type及其子接口的来历

一. 泛型出现之前的类型
没有泛型的时候,只有所谓的原始类型。此时,所有的原始类型都通过字节码文件类Class类进行抽象。Class类的一个具体对象就代表一个指定的原始类型。
二. 泛型出现之后的类型
泛型出现之后,扩充了数据类型。从只有原始类型扩充了参数化类型、类型变量类型、泛型限定的的参数化类型 (含通配符+通配符限定表达式)、泛型数组类型。
三. 与泛型有关的类型不能和原始类型统一到Class的原因
[1]. 【产生泛型擦除的原因】
本来新产生的类型+原始类型都应该统一成各自的字节码文件类型对象。但是由于泛型不是最初Java中的成分。如果真的加入了泛型,涉及到JVM指令集的修改,这是非常致命的。
[2]. 【Java中如何引入泛型】
为了使用泛型的优势又不真正引入泛型,Java采用泛型擦除的机制来引入泛型。Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的麻烦。但是,一旦编译完成,所有的和泛型有关的类型全部擦除。
[3]. 【Class不能表达与泛型有关的类型】
因此,与泛型有关的参数化类型、类型变量类型、泛型限定的的参数化类型 (含通配符+通配符限定表达式)、泛型数组类型这些类型全部被打回原形,在字节码文件中全部都是泛型被擦除后的原始类型,并不存在和自身类型一致的字节码文件。所以和泛型相关的新扩充进来的类型不能被统一到Class类中。
(4). 与泛型有关的类型在Java中的表示
为了通过反射操作这些类型以迎合实际开发的需要,Java就新增了ParameterizedType,GenericArrayType,TypeVariable 和WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。
(5). Type的引入:统一与泛型有关的类型和原始类型Class
【引入Type的原因】
为了程序的扩展性,最终引入了Type接口作为Class,ParameterizedType,GenericArrayType,TypeVariable和WildcardType这几种类型的总的父接口。这样实现了Type类型参数接受以上五种子类的实参或者返回值类型就是Type类型的参数。
【Type接口中没有方法的原因】
从上面看到,Type的出现仅仅起到了通过多态来达到程序扩展性提高的作用,没有其他的作用。因此Type接口的源码中没有任何方法。

ParameterizedType用法与简介

ParameterizedType 意为参数化类型

何为参数化类型

public class ReflectTestBean {
    List<String> list1;
    List list2;
    Map<String, Integer> map1;
    Map map2;
}
@Test
public void test() {
    Class<ReflectTestBean> cls = ReflectTestBean.class;
    Field[] fields = cls.getDeclaredFields();
    for (Field field : fields) {
        System.out.println(field.getName() + " 是否是泛型:" + (field.getGenericType() instanceof ParameterizedType));
    }
}
// list1 是否是泛型:true
// list2 是否是泛型:false
// map1 是否是泛型:true
// map2 是否是泛型:false

@Test
public void test() {
    Class<ReflectTestBean> cls = ReflectTestBean.class;
    Field[] fields = cls.getDeclaredFields();
    for (Field field : fields) {
        // 先判断字段是否带有泛型
        if (field.getGenericType() instanceof ParameterizedType) {
            ParameterizedType type = (ParameterizedType) field.getGenericType();
            System.out.print(field.getName() + " 的属性为:" + type.getTypeName());
            Type[] types = type.getActualTypeArguments();
            for (Type t : types) {
                System.out.println(", 真实的泛型的类型为:" + t.getTypeName());
            }
        }
    }
}
// list1 的属性为:java.util.List<java.lang.String>, 真实的泛型的类型为:java.lang.String
// map1 的属性为:java.util.Map<java.lang.String, java.lang.Integer>, 真实的泛型的类型为:java.lang.String, java.lang.Integer

  • Type Field.getGenericType();

    获取类字段的完整数据类型,可以注意到,带了泛型的属性,将完整的属性类型及泛型类型都标识出来了,而没有加泛型的属性则显示的是interface,意为接口类型

  • Type[] ParameterizedType.getActualTypeArguments()

    获取类字段中 <泛型> 的类型 下列例子中,通过获取ParameterizedType.getActualTypeArguments(),可以获取该字段的所有泛型类型,以后在反射工具类中,可以通过这个方法读取到所有的字段的参数类型

十二:自定义校验

spring boot通过Constraint和ConstraintValidator自定义参数校验注解

在spring boot进行开发的时候,有时需要对请求的参数进行校验, 而@Constraint可以实现自定义的校验注解。

十三:Profiles多配置详解

环境是最常见的配置隔离方式之一,可以根据不同的运行环境提供不同的配置信息来应对不同的业务场景,在SpringBoot内支持了多种配置隔离的方式,可以激活单个或者多个配置文件。

优先级

命令行方式 > Java系统属性方式 > 系统变量方式 > 配置文件方式

第一种:命令行方式

命令行方式是一种外部配置的方式,在执行java -jar命令时可以通过–spring.profiles.active=test的方式进行激活指定的profiles列表。

使用方式如下所示:

java -jar order-service-v1.0.jar --spring.profiles.active=dev &> order-service.log &

健康检查

1.什么是Spring  Boot的健康检查,有什么用?

Spring Boot提供了多项组件的健康检查,有利于监控各组件运行状况,但是有时开发者因此会启动不成功,报错等,需要合理配置。

2.Spring Boot项目中都有哪些检查,如何配置相关检查:

2.1首先健康检查引入的包是

<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-actuator</artifactid>
</dependency>

2.2相关健康检查相关Indicator

CassandraHealthIndicator 检查Cassandra是否可用

DiskSpaceHealthIndicator 检查磁盘空间是否不足

DataSourceHealthIndicator 检查能否从DataSource获取链接

ElasticsearchHealthIndicator 检查Elasticsearch cluste是否可用

JmsHealthIndicator 检查JMS broker是否可用

MailHealthIndicator 检查mail server是否可用

MongoHealthIndicator 检查Mongo database是否可用

RabbitHealthIndicator 检查Rabbit server是否可用

RedisHealthIndicator 检查Redis server是否可用

SolrHealthIndicator 检查Solr server是否可用

可以看到,有各项外部服务的检查,具体的请浏览官方文档,这里不再赘余

一、基础使用

查看健康状况
http://localhost:8080/actuator/health

查看各种监控指标jvm的请求的等等
http://localhost:8080/actuator/metrics 

查看配置信息
http://localhost:8080/actuator/configprops

查看日志
http://localhost:8080/actuator/loggers


查看内存使用情况
https://cunchu.51240.com/ 转换网址
http://localhost:8080/actuator/metrics/jvm.memory.used

2.3如何关闭/开启健康检查

application.properties里显式设定

//如禁止es的健康检查如下,默认均为开启状态
management.health.elasticsearch.enabled=false

也可以使用*全部禁止

management.health.*.enabled=false

根据需要配置自己需要的,加权限

application.properties

1、开启关闭监控点
management.endpoint.<NAME>.enable=true
management.endpoint.<NAME>.enable=false

2、不想全开启想开启某些的的话可以设置
actuator默认时全开启的
方式一
1)关闭默认
management.endpoints.enabled-by-default=false
2)设置自己想要看的部分
management.endpoint.health.enabled=true
management.endpoint.loggers.enabled=true
management.endpoint.env.enabled=true

方式二 
management.endpoints.web.exposure.include= beans,metrics,info,health,loggers
方式三 的作用大于方式二 二三一起结合访问不了loggers
management.endpoint.loggers.enabled=false

3、暴露访问的接口

# 包含那些协议
management.endpoint.<PROTOCOL>.expose.exclude=
management.endpoint.<PROTOCOL>.expose.include=

# 监控标准支持那些 included http endpoints
management.endpoint.http.expose.include=metrics

#exclude jmx endpoints
management.endpoints.jmx.exposure.exclude=beans


# security 默认密码 random string
#spring.security.user.name=<username>
#spring.security.user.password=<password>
spring.security.user.name=ADMIN
spring.security.user.password=123456

假如希望查看更多actuator选项,可以在spring boot中配置文件

application.properties中添加如下语句:

management.endpoints.web.exposure.include=*

{
    "_links": {
        "self": {
            "href": "http://localhost:9091/actuator",
            "templated": false
        },
        "beans": {
            "href": "http://localhost:9091/actuator/beans",//bean加载
            "templated": false
        },
        "caches-cache": {//缓存加载
            "href": "http://localhost:9091/actuator/caches/{cache}",
            "templated": true
        },
        "caches": {
            "href": "http://localhost:9091/actuator/caches",
            "templated": false
        },
        "health": {
            "href": "http://localhost:9091/actuator/health",
            "templated": false
        },
        "health-path": {
            "href": "http://localhost:9091/actuator/health/{*path}",
            "templated": true
        },
        "info": {
            "href": "http://localhost:9091/actuator/info",
            "templated": false
        },
        "conditions": {
            "href": "http://localhost:9091/actuator/conditions",
            "templated": false
        },
        "configprops": {
            "href": "http://localhost:9091/actuator/configprops",
            "templated": false
        },
        "configprops-prefix": {
            "href": "http://localhost:9091/actuator/configprops/{prefix}",
            "templated": true
        },
        "env": {
            "href": "http://localhost:9091/actuator/env",
            "templated": false
        },
        "env-toMatch": {
            "href": "http://localhost:9091/actuator/env/{toMatch}",
            "templated": true
        },
        "loggers": {
            "href": "http://localhost:9091/actuator/loggers",
            "templated": false
        },
        "loggers-name": {
            "href": "http://localhost:9091/actuator/loggers/{name}",
            "templated": true
        },
        "heapdump": {
            "href": "http://localhost:9091/actuator/heapdump",
            "templated": false
        },
        "threaddump": {
            "href": "http://localhost:9091/actuator/threaddump",
            "templated": false
        },
        "metrics": {
            "href": "http://localhost:9091/actuator/metrics",
            "templated": false
        },
        "metrics-requiredMetricName": {
            "href": "http://localhost:9091/actuator/metrics/{requiredMetricName}",
            "templated": true
        },
        "scheduledtasks": {//任务加载
            "href": "http://localhost:9091/actuator/scheduledtasks",
            "templated": false
        },
        "mappings": {
            "href": "http://localhost:9091/actuator/mappings",
            "templated": false
        }
    }
}

1 /health端点

/health端点会聚合你程序的健康指标,来检查程序的健康情况。端点公开的应用健康信息取决于:

#显示健康具体信息 默认不会显示详细信息

management.endpoint.health.show-details=always

该属性可以使用以下值之一进行配置:

NameDescription
never不展示详细信息,up或者down的状态,默认配置
when-authorized详细信息将会展示给通过认证的用户。授权的角色可以通过management.endpoint.health.roles配置
always对所有用户暴露详细信息

/health端点有很多自动配置的健康指示器:如redis、rabbitmq、db等组件。当你的项目有依赖对应组件的时候,这些健康指示器就会被自动装配,继而采集对应的信息。如上面的 diskSpace 节点信息就是DiskSpaceHealthIndicator 在起作用

当如上的组件有一个状态异常,应用服务的整体状态即为down。我们也可以通过配置禁用某个组件的健康监测

management.health.mongo.enabled: false

或者禁用所有自动配置的健康指示器:

management.health.defaults.enabled: false

{
    "status": "UP",
    "components": {
        "db": {
            "status": "UP",
            "details": {
                "database": "MySQL",
                "validationQuery": "isValid()"
            }
        },
        "diskSpace": {
            "status": "UP",
            "details": {
                "total": 296022437888,
                "free": 225813389312,
                "threshold": 10485760,
                "exists": true
            }
        },
        "ping": {
            "status": "UP"
        }
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Oz8482dq-1667359030557)(D:\study\总结\图片\health.png)]

status 取值有四种取值。第一种 up 表示正常。第二种 DOWN 表示遇到了问题,不正常。第三种 OUT_OF_SERVICE 这表达的是什么意思呢,资源未在使用或者不该使用。第四种 UNKNOWN 不知道,Actuator 不知道状态是什么。

2 /metrics端点

/metrics端点用来返回当前应用的各类重要度量指标,比如:内存信息、线程信息、垃圾回收信息、tomcat、数据库连接池等。

{
    "names": [
        "tomcat.threads.busy",
        "jvm.threads.states",
        "jdbc.connections.active",
        "jvm.gc.memory.promoted",
        "http.server.requests",
        "hikaricp.connections.max",
        "hikaricp.connections.min",
        "jvm.memory.used",
        "jvm.gc.max.data.size",
        "jdbc.connections.max",
         ....
    ]
}

**不同于1.x,Actuator在这个界面看不到具体的指标信息,只是展示了一个指标列表。**为了获取到某个指标的详细信息,我们可以请求具体的指标信息,像这样:

http://localhost:8080/actuator/metrics/{MetricName}

http://localhost:8084/actuator/metrics/system.cpu.count

3/loggers端点

/loggers 端点暴露了我们程序内部配置的所有logger的信息。我们访问**/actuator/loggers**可以看到,

http://localhost:8080/actuator/loggers/{name}

http://localhost:8084/actuator/loggers

http://localhost:8084/actuator/loggers/_org

http://localhost:8084/actuator/loggers/ROOT

{
 "configuredLevel":"INFO",
 "effectiveLevel":"INFO"
}
⭐改变运行时的日志等级

/loggers端点我最想提的就是这个功能,能够动态修改你的日志等级。

比如,我们可以通过下述方式来修改 root logger的日志等级。我们只需要发起一个URL 为

4 /info端点

/info端点可以用来展示你程序的信息。我理解过来就是一些程序的基础信息。并且你可以按照自己的需求在配置文件application.properties中个性化配置(默认情况下,该端点只会返回一个空的json内容。):

nfo 端点用于暴露 Spring Boot 应用的自身信息。在 Spring Boot 内部,它把这部分工作委托给了一系列 InfoContributor 对象,而 Info 端点会暴露所有 InfoContributor 对象所收集的各种信息

访问 http://localhost:8080/actuator/info 端点后,返回了一个空。它是做什么用的呢?info 端点是 Actuator 里面的一个例外,它并不是一个监控端点,而是一个描述性的端点。它是用来描述应用的使用方式非常的简单。它的格式 key-value 形式,主要遵守这个格式,随便怎么写

#info
info.app.encoding=UTF-8
info.app.java.source=11
info.app.java.target=11
{
    "app": {
        "encoding": "UTF-8",
        "java": {
            "source": "11",
            "target": "11"
        }
    }
}

同时我们还可以这么配置 info 端点,而不是硬编码这些值。我们就可以按照如下所示的配置,重写前面的示例,重启我们应用,最后得到同样的效果。

info.app.encoding=@project.build.sourceEncoding@
info.app.java.source=@java.version@
info.app.java.target=@java.version@

效果

{
    "app": {
        "encoding": "UTF-8",
        "java": {
            "source": "11.0.11",
            "target": "11.0.11"
        }
    }
}

info 一般的使用是建议描述应用。什么意思呢?比如我可以写上应用的名称。项目的开发者是谁,开发者的邮箱,一旦我们的应用出现问题,我们就可以知道是谁开发的,找到相应的人解决问题。

十四spring事件

注册ApplicationListener

方法一:

ApplicationListener作为spring Bean的注册

方法二:

ConfigurableApplicationContext API注册

1.Spring内置事件

Spring内置事件是Spring在内部帮我们写好的事件,在使用时,不需要重写他们,只需在容器中注入重写后的监听器即可。

ApplicationContextEvent

事件说明
ContextRefreshedEvent所有单例bean都已被实例化, 所有的容器对象都已准备好可使用,如果容器支持热重载,则refresh可以被触发多次(XmlWebApplicatonContext支持热刷新,而 GenericApplicationContext则不支持)
ContextStartedEvent当容器启动时发布,即调用start()方法, 已启用意味着所有的Lifecycle bean都已显式接收到了start 信号
ContextStoppedEvent当容器停止时发布,即调用stop()方法, 即所有的Lifecycle bean都已显式接收到了stop信号 , 关闭 的容器可以通过start()方法重启
ContextClosedEvent当容器关闭时发布,即调用close方法, 关闭意味着所有的单例bean都已被销毁.关闭的容器不能被重启 或refresh

2.自定义事件

自定义事件类需要继承ApplicationEvent,这个类有一个构造方法需要调用父类super()

//自定义事件   不能加@Component注解,否则会报错
public class MyEvent extends ApplicationEvent{

	private String name ;

	public MyEvent(Object source,String name ) {
		//需要调用父类构造器
		super(source);
		System.out.println("我的自定义事件:"+name);
		this.name = name;
	}
public MyEvent(Object source) {
		super(source);
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}  

3.事件监听器

3.1 事件监听器-基于接口

事件监听器需要实现ApplicationListener接口,这是个泛型接口,泛型类类型就是事件类型,其次需要是spring容器托 管的bean,所以这里加了@component,只有一个方法,就是onApplicationEvent。

//自定义事件监听器
@Component
public class MyListener implements ApplicationListener<MyEvent> {
	@Override
	public void onApplicationEvent(MyEvent event) {

		String name = event.getName();
		if ("I love you".equals(name)){
			System.out.println("我的自定义监听器: I love you too!");
		}
	}
}

3.2事件监听器-基于注解

//自定义事件监听器
@Component
public class MyListener {

	@EventListener(MyEvent.class)
	public void onApplicationEvent(MyEvent event) {

		String name = event.getName();
		if ("I love you".equals(name)){
			System.out.println("我的自定义监听器: I love you too!");
		}
	}
}

4.事件发布 publishEvent

@RestController
@RequestMapping("/event")
public class EventController {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;

@GetMapping("/notice/{message}")
    public void notice(@PathVariable(name = "message") String message) {
    
       	Object o = new Object();
        applicationEventPublisher.publishEvent(new MyEvent(o,"I love you"));
        
    }
}

十五 java加载顺序

加载结果

java加载类的顺序为:父类的static代码块-------> 子类的static代码块 ------> 父类的代码块 ------> 父类的构造方法 ------> 子类的代码块 ------> 子类的构造方法

其中,static代码块在类中只加载一次,如果再次需要加载带有static代码块的类时,第二次不加载,且如果有多个static代码块,按照它们在类中所写的顺序来加载。

由此我们可以对上面的结论进行进一步的优化,类在加载的时候初始化过程为:

  1. 初始化父类中的静态成员变量和静态代码块 ;
  2. 初始化子类中的静态成员变量和静态代码块 ;
  3. 初始化父类的普通成员变量和代码块,再执行父类的构造方法;
  4. 初始化子类的普通成员变量和代码块,再执行子类的构造方法;

类加载器的作用

类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换为方法区的运行时数据结构,然后再堆中生成一个代表这个类的java.lang.Class对象,作为方法区数据访问入口。

类缓存:标准的JavaSE 类加载器可以按照要求查找类,但一旦某个类被加载到类加载器中,它将维持加载一段时间,不过jvm垃圾回收器可以回收Class对象。

类加载器分类

引导类加载器:Bootstap ClassLoader

扩展类加载器:ExtenSion Classloader

系统类加载器:System ClassLoader

//获取系统类加载器
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
        System.out.println(classLoader);
        //获取扩展类加载器
        ClassLoader extClassLoader = classLoader.getParent();
        System.out.println(extClassLoader);
        //bootstap
        ClassLoader rootClassLoader = extClassLoader.getParent();
        System.out.println(rootClassLoader);
//结果
jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dc
jdk.internal.loader.ClassLoaders$PlatformClassLoader@340f438e
null

十六Spring总结——SpEL表达式

1、什么是SpEL表达式
SpEL:(spring expression language) 是一种表达式语言,是一种强大,简洁的装配Bean的方式。
他可以通过运行期间执行的表达式将值装配到我们的属性或构造函数当中,也可以调用JDK中提供的静态常量,获取外部Properties文件中的的配置。
2、SpEL表达式的作用
能够更加简单,多元的装配Bean,弥补了XML静态注入的不足。
简化开发,减少了一些逻辑、配置的编写,使代码更加简洁。
SpEL表达式的格式:#{表达式}

#{表达式:}

十七:单元测试

SpringMVC单元测试-MockMvc

一 简介

MockMvc实现对Http请求的模拟,可以方便对Controller进行测试,使得测试速度快、不依赖网络环境,而且提供验证的工具,使得请求的验证统一而且很方便。

二 常见使用方式

1 MockMvcBuilder构造MockMvc的构造器

2 MockMvcRequestBuilders创建请求request

3 mockMvc调用perform,执行一个request请求,调用controller的业务处理逻辑,返回ResultActions

4 可以通过ResultActions, MockMvcResultMatchers对结果进行验证

@RunWith(SpringRunner.class)

@SpringBootTest

@TestExecutionListeners({ MockitoTestExecutionListener.class}) // 使用@MockBean, @SpyBean

public abstract class AbstractBaseTest extends AbstractJUnit4SpringContextTests {

    protected static final Logger LOG = LogUtils.get();

}

   

public class MockMvcDemo extends AbstractBaseTest {

    private MockMvc mvc;

  

    @Before

    public void setUp() {

        mvc = MockMvcBuilders.standaloneSetup(new AccountController()).build();

    }

  

    @Test

    public void test() throws Exception {

        // 构建请求

        MockHttpServletRequestBuilder request = MockMvcRequestBuilders.get("/account/info")

                .contentType("text/html")

                .accept(MediaType.APPLICATION_JSON);

  

        // 发送请求,获取请求结果

        ResultActions perform = mvc.perform(request);

  

        // 请求结果校验

        perform.andExpect(MockMvcResultMatchers.status().isOk());

  

        MvcResult mvcResult = perform.andReturn();

        MockHttpServletResponse response = mvcResult.getResponse();

  

        // 校验类:org.springframework.test.web.servlet.result.MockMvcResultMatchers

    }

}
//

@WebMvcTest
public class MockMvcDemo extends AbstractTestController {

    private MockMvc mvc;

  

    @Before

    public void setUp() {

        mvc = MockMvcBuilders.standaloneSetup(new AccountController()).build();

    }

  

    @Test

    public void test() throws Exception {

        // 构建请求

        MockHttpServletRequestBuilder request = MockMvcRequestBuilders.get("/account/info")

                .contentType("text/html")

                .accept(MediaType.APPLICATION_JSON);

  

        // 发送请求,获取请求结果

        ResultActions perform = mvc.perform(request);

  

        // 请求结果校验

        perform.andExpect(MockMvcResultMatchers.status().isOk());

  

        MvcResult mvcResult = perform.andReturn();

        MockHttpServletResponse response = mvcResult.getResponse();

  

        // 校验类:org.springframework.test.web.servlet.result.MockMvcResultMatchers

    }

}

1 MockMvcBuilder

MockMvcBuilder是MockMvc的构造器,主要有两个实现:StandaloneMockMvcBuilder和DefaultMockMvcBuilder。

① MockMvcBuilders.webAppContextSetup(WebApplicationContext context):集成Web环境方式,指定WebApplicationContext,将会从该上下文获取相应的控制器并得到相应的MockMvc;

② MockMvcBuilders.standaloneSetup(Object… controllers):独立测试方式,通过参数指定一组控制器,这样就不需要从上下文获取了,比如this.mockMvc =MockMvcBuilders.standaloneSetup(new AccountController()).build();

结果验证

十八:SpringBoot用户鉴权以及统一获取用户信息

问题

1.关于前端收不到后端throw的自定义异常message信息问题解决

问题描述:

自定义了个异常类继承RuntimeException, 然后在自定义throw给前端抛异常message的时候, 在IDEA中任何问题没有,打成jar包后部署,前端vue项目总是收不到,我抛的异常信息. 总是显示messgae未空字符串.

原因分析:

经过多方查资料才发现原来是使用了如下框架依赖, 这个thymeleaf框架默认是不给前端传异常信息的,(下面的/)

org.springframework.boot
spring-boot-starter-thymeleaf

解决方案:

在propertis配置文件中加上这两行即可放开拦截:

server.error.include-exception=true
server.error.include-message=always

2.修改banner

3.yaml引入

application.properties引入其他文件

#config.location=classpath:mybatis.yaml

4.NotEmpty NotBlank NotNull 区别

@NotEmpty、@NotBlank、@NotNull 区别和使用

通过注解的方式我们可以更加优雅的对参数的格式进行校验,但是在使用 @NotEmpty、@NotBlank、@NotNull 的过程中,由于含义有点类似,所以导致使用的时候会有一些迷茫,下面,对这三个注解逐一进行使用上的说明:

@NotNull
适用于基本数据类型(Integer,Long,Double等等),当 @NotNull 注解被使用在 String 类型的数据上,则表示该数据不能为 Null(但是可以为 Empty)
@NotBlank
适用于 String 类型的数据上,加了@NotBlank 注解的参数不能为 Null 且 trim() 之后 size > 0
@NotEmpty
适用于 String、Collection集合、Map、数组等等,加了@NotEmpty 注解的参数不能为 Null 或者 长度为 0
————————————————
在使用这些注解的时候,还需要注意一点(注:否则会造成注解无效哦!!),那就是在Controller 层定义方法的时候在参数位置上加上 @Valid(javax.validation.Valid) 注解!!!如下图:

5.controller接受参数方式

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值