SpringBoot_2 Annotation

1. @ComponentScan

     @ComponentScan 对应 XML 配置形式中的 <context:component-scan> 元素,用于配合一些元信息 Java Annotation,比如 @Component 和 @Repository 等,将标注了这些元信息

Annotation 的 bean 定义类批量采集到 Spring 的 IoC 容器中。

     我们可以通过 basePackages 等属性来细粒度地定制 @ComponentScan 自动扫描的范围,如果不指定,则默认 Spring 框架实现会从声明 @ComponentScan 所在类的 package 进行扫描。

     @ComponentScan 是 SpringBoot 框架魔法得以实现的一个关键组件,大家可以重点关注,我们后面还会遇到它。

2. @PropertySource 与 @PropertySources

     @PropertySource 用于从某些地方加载 *.properties 文件内容,并将其中的属性加载到 IoC 容器中,便于填充一些 bean 定义属性的占位符(placeholder),当然,这需

要 PropertySourcesPlaceholderConfigurer 的配合。如果我们使用 Java 8 或者更高版本开发,那么,我们可以并行声明多个

@Setter
@Getter
@ToString
@AllArgsConstructor
public class DataSource {

    private String username;
    private String password;
    private String url;
}

db.username=root
db.password=123456
db.url=jdbc:mysql://127.0.0.1:3306/test

@Configuration
@PropertySource("classpath:db.properties")
public class AppConfig {
    // @Value相当于 <property name="username" value="${db.username}"/>
    @Value("${db.username}")
    private String username;
    @Value("${db.password}")
    private String password;
    @Value("${db.url}")
    private String url;

    // 该bean是为了解析@Value中SpringEL表达式
    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }

    @Bean
    public DataSource dataSource() {
        return new DataSource(username, password, url);
    }
}

     如果我们使用低于 Java 8 版本的 Java 开发 Spring 应用,又想声明多个 @PropertySource,则需要借助 @PropertySources 的帮助了,代码如下所示:

@PropertySources({ @PropertySource("classpath:1.properties"), @PropertySource("classpath:2.properties"), ...})
public class XConfiguration{
    ...
}

3. @Import 与 @ImportResource

     在 XML 形式的配置中,我们通过 <import resource="XXX.xml"/> 的形式将多个分开的容器配置合到一个配置中,在 JavaConfig 形式的配置中,我们则使用 @Import 这个 Annotation 完成同

样目的:

public class Test1 {
	public void printMsg(String msg) {
		System.out.println("test1 : " + msg);
	}
}
public class Test2 {
	public void printMsg(String msg) {
		System.out.println("test2 : " + msg);
	}
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class Test1Config {
	@Bean(name = "test1")
	public Test1 test1() {
		return new Test1();
	}
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class Test2Config {
	@Bean(name = "test2")
	public Test2 test2() {
		return new Test2();
	}
}

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import({ Test1Config.class, Test2Config.class })
public class AppConfig {}

ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Test1 test1 = (Test1) context.getBean("test1");
test1.printMsg("Hello test1");
		
Test2 test2 = (Test2) context.getBean("test2");
test2.printMsg("Hello test2");

     @Import 只负责引入 JavaConfig 形式定义的 IoC 容器配置,如果有一些遗留的配置或者遗留系统需要以 XML 形式来配置(比如 dubbo 框架),我们依然可以通过 @ImportResource 将它

们一起合并到当前 JavaConfig 配置的容器中。

4.解析pom文件

     4.1.父项目

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.4.0</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

        上面的配置是我们自己项目需要依赖的父项目【spring‐boot‐starter‐parent】

        我们现在新建的SpringBoot项目是【spring‐boot‐starter‐parent】项目下的一个子项目,我们的SpringBoot项目继承了【spring‐boot‐starter‐parent】父项目。

       【spring‐boot‐starter‐parent】父项目继承了【spring-boot-dependencies】

        spring‐boot‐starter‐parent-2.4.0.pom

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-dependencies</artifactId>
  <version>2.4.0</version>
</parent>
<properties>
  <activemq.version>5.16.0</activemq.version>
  <antlr2.version>2.7.7</antlr2.version>
  <appengine-sdk.version>1.9.83</appengine-sdk.version>
  <artemis.version>2.15.0</artemis.version>
  <aspectj.version>1.9.6</aspectj.version>
  <assertj.version>3.18.1</assertj.version>
  <atomikos.version>4.0.6</atomikos.version>
  <awaitility.version>4.0.3</awaitility.version>
  <bitronix.version>2.1.4</bitronix.version>
......
</properties>

          上面的<properties>元素中配置的是我们所创建的项目的所有依赖包版本

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.apache.activemq</groupId>
      <artifactId>activemq-amqp</artifactId>
      <version>${activemq.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.activemq</groupId>
      <artifactId>activemq-blueprint</artifactId>
      <version>${activemq.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.activemq</groupId>
      <artifactId>activemq-broker</artifactId>
      <version>${activemq.version}</version>
    </dependency>
.......
</dependencies>
</dependencyManagement>

          上面的<dependencies>元素中配置的是我们所创建的项目的所有依赖包的导入

          这个【spring‐boot‐dependencies】项目实际是用来真正管理Spring Boot应用里面的所有依赖包。有了【spring‐boot‐dependencies】项目以后我们新建的SpringBoot项目所需要的依赖包

导入就不要在再配置版本和<dependencies>元素导入依赖,从【spring‐boot‐dependencies】项目中继承就可以了。

     4.2SpringBoot启动器

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

         命名:spring-boot-starter-xxxx  [SpingBoot的启动器]

         spring-boot-starter:帮助我们导入springboot基础依赖包

         spring-boot-starter-web:帮我们导入了web模块正常运行所依赖包。

         spring-boot-starter-test:帮我们导入了单元测试模块正常运行所依赖包。

        Spring Boot将所有的功能的依赖包都抽取出来,做成一个个的starters(启动器),只需要在项目里面引入这些starter 那么实现相关功能的所有依赖都会自动导入进来,且没有版本冲突。

要用什么功能就导入什么场景的启动器就可以了 。

        基本的启动器:Spring Boot应用启动器基本的一共有44种,具体如下:     

1)spring-boot-starter:这是Spring Boot的核心启动器,包含了自动配置、日志和YAML。

2)spring-boot-starter-actuator:帮助监控和管理应用。

3)spring-boot-starter-amqp:通过spring-rabbit来支持AMQP协议(Advanced Message Queuing Protocol)。

4)spring-boot-starter-aop:支持面向方面的编程即AOP,包括spring-aop和AspectJ。

5)spring-boot-starter-artemis:通过Apache Artemis支持JMS的API(Java Message Service API)。

6)spring-boot-starter-batch:支持Spring Batch,包括HSQLDB数据库。

7)spring-boot-starter-cache:支持Spring的Cache抽象。

8)spring-boot-starter-cloud-connectors:支持Spring Cloud Connectors,简化了在像Cloud Foundry或Heroku这样的云平台上连接服务。

9)spring-boot-starter-data-elasticsearch:支持ElasticSearch搜索和分析引擎,包括spring-data-elasticsearch。

10)spring-boot-starter-data-gemfire:支持GemFire分布式数据存储,包括spring-data-gemfire。

11)spring-boot-starter-data-jpa:支持JPA(Java Persistence API),包括spring-data-jpa、spring-orm、Hibernate。

12)spring-boot-starter-data-mongodb:支持MongoDB数据,包括spring-data-mongodb。

13)spring-boot-starter-data-rest:通过spring-data-rest-webmvc,支持通过REST暴露Spring Data数据仓库。

14)spring-boot-starter-data-solr:支持Apache Solr搜索平台,包括spring-data-solr。

15)spring-boot-starter-freemarker:支持FreeMarker模板引擎。

16)spring-boot-starter-groovy-templates:支持Groovy模板引擎。

17)spring-boot-starter-hateoas:通过spring-hateoas支持基于HATEOAS的RESTful Web服务。

18)spring-boot-starter-hornetq:通过HornetQ支持JMS。

19)spring-boot-starter-integration:支持通用的spring-integration模块。

20)spring-boot-starter-jdbc:支持JDBC数据库。

21)spring-boot-starter-jersey:支持Jersey RESTful Web服务框架。

22)spring-boot-starter-jta-atomikos:通过Atomikos支持JTA分布式事务处理。

23)spring-boot-starter-jta-bitronix:通过Bitronix支持JTA分布式事务处理。

24)spring-boot-starter-mail:支持javax.mail模块。

25)spring-boot-starter-mobile:支持spring-mobile。

26)spring-boot-starter-mustache:支持Mustache模板引擎。

27)spring-boot-starter-redis:支持Redis键值存储数据库,包括spring-redis。

28)spring-boot-starter-security:支持spring-security。

29)spring-boot-starter-social-facebook:支持spring-social-facebook

30)spring-boot-starter-social-linkedin:支持pring-social-linkedin

31)spring-boot-starter-social-twitter:支持pring-social-twitter

32)spring-boot-starter-test:支持常规的测试依赖,包括JUnit、Hamcrest、Mockito以及spring-test模块。

33)spring-boot-starter-thymeleaf:支持Thymeleaf模板引擎,包括与Spring的集成。

34)spring-boot-starter-velocity:支持Velocity模板引擎。

35)spring-boot-starter-web:支持全栈式Web开发,包括Tomcat和spring-webmvc。

36)spring-boot-starter-websocket:支持WebSocket开发。

37)spring-boot-starter-ws:支持Spring Web Services。

Spring Boot应用启动器面向生产环境的还有2种,具体如下:

1)spring-boot-starter-actuator:增加了面向产品上线相关的功能,比如测量和监控。

2)spring-boot-starter-remote-shell:增加了远程ssh shell的支持。

最后,Spring Boot应用启动器还有一些替换技术的启动器,具体如下:

1)spring-boot-starter-jetty:引入了Jetty HTTP引擎(用于替换Tomcat)。

2)spring-boot-starter-log4j:支持Log4J日志框架。

3)spring-boot-starter-logging:引入了Spring Boot默认的日志框架Logback。

4)spring-boot-starter-tomcat:引入了Spring Boot默认的HTTP引擎Tomcat。

5)spring-boot-starter-undertow:引入了Undertow HTTP引擎(用于替换Tomcat)。

5.SpringBoot的主类

package com.wangxing.springboot.springbootdemo1;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages = "com.wangxing.springboot")
public class Springbootdemo1Application {
    public static void main(String[] args) {
        SpringApplication.run(Springbootdemo1Application.class, args);
    }
}

     之所以说Springbootdemo1Application 是主类是因为它里面包含了主方法

      public static void main(String[] args) {}

      在这个主方法中有:

     SpringApplication.run(Springbootdemo1Application.class, args);

     SpringApplication 将一个典型的 Spring 应用启动的流程“模板化”(这里是动词),在没有特殊需求的情况下,默认模板化后的执行流程就可以满足需求了但有特殊需求也没关系, 

     SpringApplication 在合适的流程结点开放了一系列不同类型的扩展点,我们可以通过这些扩展点对 SpringBoot 程序的启动和关闭过程进行扩展。

     通过SpringApplication设置修改启动图标:

     1.在resources文件夹中创建banner.txt

         banner.txt中的内容

'##::::'##:'########:'##:::::::'##::::::::'#######::
 ##:::: ##: ##.....:: ##::::::: ##:::::::'##.... ##:
 ##:::: ##: ##::::::: ##::::::: ##::::::: ##:::: ##:
 #########: ######::: ##::::::: ##::::::: ##:::: ##:
 ##.... ##: ##...:::: ##::::::: ##::::::: ##:::: ##:
 ##:::: ##: ##::::::: ##::::::: ##::::::: ##:::: ##:
 ##:::: ##: ########: ########: ########:. #######::
..:::::..::........::........::........:::.......:::

     2.在主类中通过SpringApplication来设置启动图标

package com.wangxing.springboot.springbootdemo1;

import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.env.Environment;

import java.io.PrintStream;

@SpringBootApplication
@ComponentScan(basePackages = "com.wangxing.springboot")
public class Springbootdemo1Application {
    public static void main(String[] args) {
        SpringApplication bootstrap = new SpringApplication(Springbootdemo1Application.class);
        bootstrap.setBannerMode(Banner.Mode.CONSOLE);
        bootstrap.run(args);
    }
}

6.SpringApplication的run方法的执行流程

     SpringApplication 的 run 方法的实现是我们本次运行的主要线路,该方法的主要流程大体可以归纳如下:

     1)如果我们使用的是 SpringApplication 的静态 run 方法,那么,这个方法里面首先需要创建一个 SpringApplication 对象实例,然后调用这个创建好的 SpringApplication 的实例 run方 法。

在 SpringApplication 实例初始化的时候,它会提前做几件事情:

       根据 classpath 里面是否存在某个特征类(org.springframework.web.context.ConfigurableWebApplicationContext)来决定是否应该创建一个为 Web 应用使用的 ApplicationContext 类型,

还是应该创建一个标准 Standalone 应用使用的 ApplicationContext 类型。

       使用 SpringFactoriesLoader 在应用的 classpath 中查找并加载所有可用的 ApplicationContextInitializer。

       使用 SpringFactoriesLoader 在应用的 classpath 中查找并加载所有可用的 ApplicationListener。推断并设置 main 方法的定义类。

     2)SpringApplication 实例初始化完成并且完成设置后,就开始执行 run 方法的逻辑 了,方法执行伊始,首先遍历执行所有通过 SpringFactoriesLoader 可以查找到并加载的

SpringApplicationRunListener,调用它们的 started() 方法,告诉这些 SpringApplicationRunListener,“嘿,SpringBoot 应用要开始执行咯!”。

     3)创建并配置当前 SpringBoot 应用将要使用的 Environment(包括配置要使用的 PropertySource 以及 Profile)。

     4)遍历调用所有 SpringApplicationRunListener 的 environmentPrepared()的方法,告诉它们:“当前 SpringBoot 应用使用的 Environment 准备好咯!”。

     5)如果 SpringApplication的showBanner 属性被设置为 true,则打印 banner(SpringBoot 1.3.x版本,这里应该是基于 Banner.Mode 决定 banner 的打印行为)。这一步的逻辑其实可以不

关心,我认为唯一的用途就是“好玩”(Just For Fun)。

     6)根据用户是否明确设置了applicationContextClass 类型以及初始化阶段的推断结果,决定该为当前 SpringBoot 应用创建什么类型的 ApplicationContext 并创建完成,然后根据条件决定是

否添加 ShutdownHook,决定是否使用自定义的 BeanNameGenerator,决定是否使用自定义的 ResourceLoader,当然,最重要的,将之前准备好的 Environment 设置给创建好的

ApplicationContext 使用.

     7)ApplicationContext 创建好之后,SpringApplication 会再次借助 Spring-FactoriesLoader,查找并加载 classpath 中所有可用的 ApplicationContext-Initializer,然后遍历调用这些

ApplicationContextInitializer 的 initialize(applicationContext)方法来对已经创建好的 ApplicationContext 进行进一步的处理。

     8)遍历调用所有 SpringApplicationRunListener 的 contextPrepared()方法,通知它们:“SpringBoot 应用使用的 ApplicationContext 准备好啦!”

     9)最核心的一步,将之前通过 @EnableAutoConfiguration 获取的所有配置以及其他形式的 IoC 容器配置加载到已经准备完毕的 ApplicationContext。

     10)遍历调用所有 SpringApplicationRunListener 的 contextLoaded() 方法,告知所有 SpringApplicationRunListener,ApplicationContext "装填完毕"!

     11)调用 ApplicationContext 的 refresh() 方法,完成 IoC 容器可用的最后一道工序。

     12)查找当前 ApplicationContext 中是否注册有 CommandLineRunner,如果有,则遍历执行它们。

     13)正常情况下,遍历执行 SpringApplicationRunListener 的 finished() 方法,告知它们:“搞定!”。(如果整个过程出现异常,则依然调用所有 SpringApplicationRunListener 的 finished()

方法,只不过这种情况下会将异常信息一并传入处理)。

     至此,一个完整的 SpringBoot 应用启动完毕!

     整个过程看起来冗长无比,但其实很多都是一些事件通知的扩展点,如果我们将这些逻辑暂时忽略,那么,其实整个 SpringBoot 应用启动的逻辑就可以压缩到极其精简的几步,如图 1 所

示。

     前后对比我们就可以发现,其实 SpringApplication 提供的这些各类扩展点近乎“喧宾夺主”,占据了一个 Spring 应用启动逻辑的大部分“江山”,除了初始化并准备好ApplicationContext,剩下

的大部分工作都是通过这些扩展点完成的,所以,我们接下来对各类扩展点进行逐一剖析。

     SpringApplication 的静态 run 方法运行 SpringApplication 的静态 run 方法,那么,这个方法里面首先需要创建一个 SpringApplication 对象实例,收集各种条件和回调接口,创建

ApplicationContextInitializer 、ApplicationContextListener,创建SpringApplicationRunListeners。

     调用它的started()方法,告诉SpringBoot 应用要开始执行,创建并配置当前 SpringBoot 应用将要使用的 Environment(包括配置要使用的 PropertySource 以及 Profile),利用

SpringApplicationRunListener调用environmentPrepared()的方法,告诉SpringBoot 应用使用的 Environment 准备好,创建并初始化ApplicationContext,利用 SpringApplicationRunListener

的contextPrepared()方法,通知SpringBoot 应用使用的 ApplicationContext 准备好,将之前通过 @EnableAutoConfiguration 获取的所有配置以及其他形式的 IoC 容器配置加载到已经准备完

毕的 ApplicationContext,利用SpringApplicationRunListener 的 contextLoaded() 方法,告知所有 SpringApplicationRunListener,ApplicationContext "装填完毕",调用 ApplicationContext 的

refresh() 方法,一切准备就绪,当前 ApplicationContext 中是否注册有 CommandLineRunner,如果有,则遍历执行它们。遍历执行 SpringApplicationRunListener 的 finished() 方法,至此,一

个完整的 SpringBoot 应用启动完毕。

7.@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 {
........
}

     @SpringBootApplication实际上它是一个复合注解。虽然它的定义使用了多个注解,但实际上对于 SpringBoot 应用来说,重要的只有三个 注解,这三个注解:

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

     所以,如果我们使用上面三个主解作用在主类上整个 SpringBoot 应用依然可以与之前的启动类功能对等。

package com.wangxing.springboot.springbootdemo2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurationExcludeFilter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @ComponentScan.Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public class Springbootdemo2Application {
    public static void main(String[] args) {
        SpringApplication.run(Springbootdemo2Application.class, args);
    }
}

     但每次都写三个注解显然过于繁琐,所以写一个 @SpringBootApplication 这样的一站式复合注解显然更方便些。

     1.@SpringBootConfiguration注解 [创建配置类从而代替配置文件]

        @SpringBootConfiguration注解中包含了@Configuration。

        @Configuration--是 Spring JavaConfig 形式的 Spring IoC 容器的配置类使用的注解。既然 SpringBoot 应用骨子里就是一个 Spring 应用,那么,自然也需要加载某个 IoC 容器的配置,而

        SpringBoot 社区推荐使用基于 JavaConfig 的配置形式,所以,很明显,这里的启动类标注了 @Configuration 之后,本身其实也是一个 IoC 容器的配置类。

        Spring JavaConfig 形式的 Spring IoC 容器的配置类:配置方式---->

         1.基于 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"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
<!-- bean定义 -->

</beans>

        2.基于 JavaConfig 的配置方式

@Configuration
public class MyConfiguration{
// bean定义
} 

           任何一个标注了 @Configuration 的 Java 类定义都是一个 JavaConfig 配置类。

        注册 bean 定义:

          1.基于 XML 的注册 bean 定义方式。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
<!-- bean定义 -->
<bean id=””  class=””></bean>
</beans>

          2.基于JavaConfig 的注册 bean 定义方式。

@Configuration
public class MyConfiguration{
// bean定义
 	@Bean
    public StudentService  studentService() {
        return new StudentServiceImpl();
    }
} 

             任何一个标注了 @Bean 的方法,其返回值将作为一个 bean 定义注册到 Spring 的 IoC 容器,方法名将默认成为该 bean 定义的 id。

        依赖注入的配置方式:

          1.基于 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"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
<!-- bean定义 -->
<bean id=””  class=””>
<property name="studentDao" ref="studentDao" />
</bean>
</beans>

       2.基于JavaConfig 的依赖注入方式。

@Configuration
public class MyConfiguration{
// bean定义
 	@Bean
    public StudentService  studentService() {
        return new StudentServiceImpl(studentDao() );
}

	@Bean
    public  StudentDao  studentDao() {
        return new StudentDaoImpl();
}
} 

        如果一个 bean 的定义依赖其他 bean,则直接调用对应 JavaConfig 类中依赖 bean 的创建方法就可以了。

       @SpringBootApplication中包含了@SpringBootConfiguration注解,@SpringBootConfiguration注解中包含了@Configuration注解,@Configuration注解标注在哪一个java类上,那么这个

java类就是一个 JavaConfig 配置类,这个JavaConfig 配置类可以代替掉Spring配置文件【applicationContext.xml】,当我们在主类上标注@SpringBootApplication时,意味着主类是一个

JavaConfig 配置类,因此我们在创建SptingBoot项目的时候才不用去编写Spring配置文件【applicationContext.xml】。

     2.@EnableAutoConfiguration  [“智能”的完成自动配置]

        1.@EnableAutoConfiguration 中包含了@Import注解

           @Import注解将bean对象注册到javaConfig配置类中。

           @EnableAutoConfiguration注解利用@Import注解收集和注册特定模块相关的 bean 定义

        源码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
......
}

              @Import(AutoConfigurationImportSelector.class).借助EnableAutoConfigurationImportSelector,@EnableAutoConfiguration 可以帮助 SpringBoot 应用将所有符合条件的

@Configuration 配置都加载到当前 SpringBoot 创建并使用的 IoC 容器。

          借助于 Spring 框架原有的一个工具类:SpringFactoriesLoader 的支持,@EnableAutoConfiguration 可以“智能”地自动配置功效才得以大功告成!

在@Import(AutoConfigurationImportSelector.class)的AutoConfigurationImportSelector.class中的
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
   List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
         getBeanClassLoader());
   Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
         + "are using a custom packaging, make sure that file is correct.");
   return configurations;
}

8.SpringFactoriesLoader

public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
   Assert.notNull(factoryType, "'factoryType' must not be null");
   ClassLoader classLoaderToUse = classLoader;
   if (classLoaderToUse == null) {
      classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
   }
   List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
   if (logger.isTraceEnabled()) {
      logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
   }
   List<T> result = new ArrayList<>(factoryImplementationNames.size());
   for (String factoryImplementationName : factoryImplementationNames) {
      result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
   }
   AnnotationAwareOrderComparator.sort(result);
   return result;
}

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
   ClassLoader classLoaderToUse = classLoader;
   if (classLoaderToUse == null) {
      classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
   }
   String factoryTypeName = factoryType.getName();
   return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
   Map<String, List<String>> result = cache.get(classLoader);
   if (result != null) {
      return result;
   }

   result = new HashMap<>();
   try {
      Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
      while (urls.hasMoreElements()) {
         URL url = urls.nextElement();
         UrlResource resource = new UrlResource(url);
         Properties properties = PropertiesLoaderUtils.loadProperties(resource);
         for (Map.Entry<?, ?> entry : properties.entrySet()) {
            String factoryTypeName = ((String) entry.getKey()).trim();
            String[] factoryImplementationNames =
                  StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
            for (String factoryImplementationName : factoryImplementationNames) {
               result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                     .add(factoryImplementationName.trim());
            }
         }
      }
      // Replace all lists with unmodifiable lists containing unique elements
      result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
            .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
      cache.put(classLoader, result);
   }
   catch (IOException ex) {
      throw new IllegalArgumentException("Unable to load factories from location [" +
            FACTORIES_RESOURCE_LOCATION + "]", ex);
   }
   return result;
}

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
spring-boot-autoconfigure-2.4.0.jar/META-INF/spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
...........

9.SpringBoot核心自动配置原理

     1.SpringBoot启动的时候加载主配置类,开启了自动配置功能@EnableAutoConfiguration

        主类添加了@SpringBootConfiguration注解,@SpringBootConfiguration注解包含了@EnableAutoConfiguration注解

     2.@EnableAutoConfiguration注解利用@Import(AutoConfigurationImportSelector.class)中的参数EnableAutoConfigurationImporttSelector类给SpringIOC容器中导入一些组件;

        通过EnableAutoConfigurationImporttSelector类中的selectImports()这个方法中找到AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);找到

        getAutoConfigurationEntry()方法,

        List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);

        getCandidateConfigurations()得到  

 List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),

getBeanClassLoader());得到SpringFactoriesLoader类,通过SpringFactoriesLoader类中的loadFactoryNames方法找到loadSpringFactories()有一句话Properties properties =

PropertiesLoaderUtils.loadProperties(resource);

         通过这句得到public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

         扫描所有jar包类路径下,META-INF/spring.factories把扫描到的这些文件的内容包装成properties对象,从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把他们添

加在容器中,将类路径下META-INF/spring.factories里面配置的所有EnableAutoConfiguration的值加入到了容器中

     3.每一个自动配置类进行自动配置功能;

     4.根据当前不同的条件判断,决定这个配置类是否生效?一旦这个配置类生效,这个配置类就会给容器中添加各种组件,这些组件的属性是从对应的properties类中获取的,这些类里面的每一

个属性又是和配置文件绑定的.

     以HttpEncodingAutoConfiguration (Http编码自动配置)为例解释自动配置原理;

//表示这是一个配置类没以前编写的配置文件一样,也可以给容器中添加组件
@Configuration(proxyBeanMethods = false)
//启动指定类的ConfigurationProperties功能,将配置文件中对应的值和HttpEncodingAutoConfigurationProperties绑定起来;
@EnableConfigurationProperties(ServerProperties.class)
//Spring底层@Conditional注解,根据不同的条件,如果满足指定的条件,整个配置类里面的配置就会生效(即判断当前应用是否是web应用)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
//判断当前项目有没有这个类CharacterEncodingFilter;SpringMVC中进行乱码处理的过滤器
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {

   private final Encoding properties;

   public HttpEncodingAutoConfiguration(ServerProperties properties) {
      this.properties = properties.getServlet().getEncoding();
   }

   @Bean
   @ConditionalOnMissingBean
   public CharacterEncodingFilter characterEncodingFilter() {
      CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
      filter.setEncoding(this.properties.getCharset().name());
      filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
      filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
      return filter;
   }

   @Bean
   public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
      return new LocaleCharsetMappingsCustomizer(this.properties);
   }

   static class LocaleCharsetMappingsCustomizer
         implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {

      private final Encoding properties;

      LocaleCharsetMappingsCustomizer(Encoding properties) {
         this.properties = properties;
      }

      @Override
      public void customize(ConfigurableServletWebServerFactory factory) {
         if (this.properties.getMapping() != null) {
            factory.setLocaleCharsetMappings(this.properties.getMapping());
         }
      }

      @Override
      public int getOrder() {
         return 0;
      }
   }
}

      5.所有在配置文件中能配置的属性都是在xxxxPropertites类中封装着,配置文件能配置什么就可以参照某个功能对应的这个属性类;

    @ComponentScan 【配置自动扫描包,就可以让类上带有@Component 和 @Repository,@Service注解的java类创建出对象】

     @ComponentScan 对应 XML 配置形式中的 <context:component-scan> 元素,用于配合一些元信息注解,比如 @Component 和 @Repository 等,将标注了这些元信息注解的 bean 定义

类批量采集到 Spring 的 IoC 容器中。我们可以通过 basePackages 等属性来细粒度地定制 @ComponentScan 自动扫描的范围,如果不指定,则默认 Spring 框架实现会从声@ComponentScan

所在类的 package 进行扫描。如果我们当前应用没有任何 bean 定义需要通过 @ComponentScan 加载到当前 SpringBoot 应用对应使用的 IoC 容器,那么,除去 @ComponentScan 的声明,当

前 SpringBoot 应用依然可以照常运行,功能对等。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值