Spring Boot 2. 走向自动装配

走向自动装配
PDF 版本:第二节 走向自动装配.pdf

Spring Framework 手动装配

Spring 模式注解装配

定义: 一种用于声明在应用中扮演”组件”角色的注解

举例: @Component , @Service , @Configuration

装配: context:component-scan@ComponentScan

模式注解(Stereotype Annotations)

A stereotype annotation is an annotation that is used to declare the role that a component plays within the application. For example, the @Repository annotation in the Spring Framework is a marker for any class that fulfills the role or stereotype of a repository (also known as Data Access Object or DAO).

@Component is a generic stereotype for any Spring-managed component. Any component annotated with @Component is a candidate for component scanning. Similarly, any component annotated with an annotation that is itself meta-annotated with @Component is also a candidate for component scanning. For example, @Service is meta-annotated with @Component.

Core Spring provides several stereotype annotations out of the box, including but not limited to: @Component, @Service, @Repository, @Controller, @RestController, and @Configuration. @Repository, @Service, etc. are specializations of @Component.

原型注释是用来声明组件在应用程序中扮演的角色的注释。例如,Spring 框架中的 @Repository 注释是满足存储库角色或构造型的任何类的标记(也称为数据访问对象或 DAO)。

@Component 是任何 spring 管理组件的通用原型。任何用 @Component 注解的组件都是组件扫描的候选组件。类似地,任何用注释注释的组件(其本身是用@Component 元注释的)也是组件扫描的候选对象。例如,@Service 是用 @Component 进行元注释的。

Core Spring 提供了几个开箱即用的原型注释,包括但不限于:@Component@Service@Repository@Controller@RestController@Configuration@Repository@Service等是@Component的专门化。

模式注解是一种用于 声明在应用中扮演’组件’角色的注解。如 Spring Framework 中的 @Repository 标注在任何类上, 用于扮演仓储角色的模式注解。

@Component 作为一种由 Spring 容器托管的通用模式组件,任何被 @Component 标准的组件均为组件扫描的候选对象。类似地,凡是被

@Component 元标注( meta-annotated )的注解,如 @Service  , 当任何组件标注它时,也被视作组件扫描的候选对象

模式注解举例

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

装配方式

<context:component-scan>方式
1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 激活注解驱动特性 -->
<context:annotation-config/>
<!--找寻被 @Component 或者其派生Annotation标记的类(Class), 将它们注册为Spring Bean -->
<context:component-scan base-package="com.imooc.dive.in.spring.boot"/>
</beans>
@ComponentScan 方式
1
2
3
4
@ComponentScan(basePackages="com.imooc.dive.in.spring.boot")
public class SpringConfiguration{
...
}

自定义模式注解

@Component “派生性”
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import org.springframework.stereotype.Repository;

import java.lang.annotation.*;

/**
* 一级 {@link org.springframework.stereotype.Repository @Repository }
*
* @author <a href ='jxh98@foxmail.com'>Josway</a>
* @date 2020/9/2
* @since JDK 1.8
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repository
public @interface FirstLevelRepository {
String value() default "";
}
  • @Component
    • @Repository
      • FirstLevelRepository
@Component “层次性”
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import org.springframework.stereotype.Repository;

import java.lang.annotation.*;

/**
* 二级 {@link org.springframework.stereotype.Repository @Repository }
*
* @author <a href ='jxh98@foxmail.com'>Josway</a>
* @date 2020/9/2
* @since JDK 1.8
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@FirstLevelRepository
public @interface SecondLevelRepository {
String value() default "";
}
  • @Component
    • @Repository
      • FirstLevelRepository
        • SecondLevelRepository

Spring @Enable 模块装配

  • 定义 : 具备相同领域的功能组件集合 , 组合所形成一个独立的单元 .
  • 举例 : @EnableWebMvc , @EnableAutoConfiguration 等
  • 实现 : 注解方式 , 编程方式

Spring Framework 3.1 开始支持”@Enable 模块驱动”。所谓”模块”是指具备相同领域的功能组件集合,
组合所形成一个独立的元。如 Web MVC 模块、Aspect 代理模块、Caching (缓存)模块、JMX (Java 管
理扩展)模块、Async (异步处理)模块等。

@Enable 注解模块举例

| 框架实现 | @Enable

注解模块 | 激活模块 |
| — | — | — |
| Spring Framework | @EnableWebMvc | Web Mvc 模块 |
| | @EnableTransactionManagement | 事务管理模块 |
| | @EnableCaching | Caching 模块 |
| | @EnableMBeanExport | JMX 模块 |
| | @EnableAsync | 异步处理模块 |
| | @EnableWebFlux | Web Flux 模块 |
| | @EnableAspectJAutoProxy | AspectJ 代理模块 |
| | | |
| Spring Boot | @EnableAutoConfiguration | 自动装配模块 |
| | @EnableManagementContext | Actuator 管理模块 |
| | @EnableConfigurationProperties | 配置属性绑定模块 |
| | @EnableOAuth2Sso | OAuth2 单点登录模块 |
| | | |
| Spring Cloud | @EnableEurekaServer | Eureka 服务器模块 |
| | @EnableConfigServer | 配置服务器模块 |
| | @EnableFeignClients | Feign 客户端模块 |
| | @EnableZuulProxy | 服务网关 Zuul 模块 |
| | @EnableCircuitBreaker | 服务熔断模块 |

实现方式

注解驱动方式
1
2
3
4
5
6
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
1
2
3
4
@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
...
}
接口编程方式
1
2
3
4
5
6
7
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {
...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> {

/**
* Returns {@link ProxyCachingConfiguration} or {@code AspectJCachingConfiguration}
* for {@code PROXY} and {@code ASPECTJ} values of {@link EnableCaching#mode()},
* respectively. Potentially includes corresponding JCache configuration as well.
*/
@Override
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return getProxyImports();
case ASPECTJ:
return getAspectJImports();
default:
return null;
}
}

}

自定义 @Enable 注解

基于注解驱动实现 - @EnableHelloWorld

TODO

基于接口驱动实现 - @EnableServer

HelloWorldImportSelector->HelloWorldConfiguration->helloWorld

Spring 条件装配

从 Spring Framework 3.1 开始 , 允许在 Bean 装配时增加前置判断条件.

条件注解举例

Spring 注解 场景说明 起始版本
@Profile 配置化条件装配 3.1
@Conditional 编程条件装配 4.0

实现方式

配置方式 - @Profile
编程方式 - @Conditional
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package org.springframework.boot.autoconfigure.condition;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.context.annotation.Conditional;

/**
* {@link Conditional @Conditional} that only matches when the specified classes are on
* the classpath.
* <p>
* A {@link #value()} can be safely specified on {@code @Configuration} classes as the
* annotation metadata is parsed by using ASM before the class is loaded. Extra care is
* required when placed on {@code @Bean} methods, consider isolating the condition in a
* separate {@code Configuration} class, in particular if the return type of the method
* matches the {@link #value target of the condition}.
*
* @author Phillip Webb
* @since 1.0.0
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {

/**
* The classes that must be present. Since this annotation is parsed by loading class
* bytecode, it is safe to specify classes here that may ultimately not be on the
* classpath, only if this annotation is directly on the affected component and
* <b>not</b> if this annotation is used as a composed, meta-annotation. In order to
* use this annotation as a meta-annotation, only use the {@link #name} attribute.
* @return the classes that must be present
*/
Class<?>[] value() default {};

/**
* The classes names that must be present.
* @return the class names that must be present.
*/
String[] name() default {};

}

自定义条件装配

基于配置方式实现 - @Profile

计算服务 , 多整数求和 sum

@Profile(“Java7”) : for 循环 实现

@Profile(“Java8”) : lambda 表达式   实现

基于编程方式实现 - @ConditionalOnSystemProperty

Spring Boot 自动装配

在 Spring Boot 场景下 , 基于约定大于配置的原则 , 实现 Spring 组件自动装配的目的 . 其中使用了

底层装配技术

  • Spring 模式注解装配
  • Spring @Enable 模块装配
  • Spring 条件装配
  • Spring 工厂加载机制
    • 实现类 : SpringFactoriesLoader
    • 配置资源 : META-INF/spring.factories

自动装配举例

参考 META-INF/spring.factories

实现方式

  1. 激活自动装配 - @EnableAutoConfiguration
  2. 实现自动装配 - XXXAutoConfiguration
  3. 配置自动装配实现 - META-INF/spring.factories

自定义自动装配

HelloWorldAutoConfiguration

  • 条件判断 : user.name == "josway"
  • 模式注解 : @Configuration
  • @Enable 模块 : @EnableHelloWorld -> HelloWorldImportSelector -> HelloWorldConfiguration -> helloWorld

Spring Boot 2. 走向自动装配
https://blog.josway.cc/2022/04/25/yuque/Spring Boot 2. 走向自动装配/
作者
JOSWAY
发布于
2022年4月25日
许可协议