Spring Boot 走向自動裝配

Spring 模式注解裝配

模式注解(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 

模式注解是一種用于聲明在應用中扮演“組件”角色的注解。如 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> 方式
<?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 方式
public class SpringConfiguration {
... 
}

自定義模式注解

  • @Component "派生性"
/**
* 一級 {@link Repository @Repository}
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a> * @since 1.0.0
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repository
public @interface FirstLevelRepository {
    String value() default "";
}
* @Component
    * @Repository
        * @FirstLevelRepository
  • @Component "層次性"
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@FirstLevelRepository
public @interface SecondLevelRepository {
    String value() default "";
}
* @Component
    * @Repository
        * @FirstLevelRepository
            * @SecondLevelRepository 

Spring @Enable模塊裝配

Spring Framework 3.1 開始支持”@Enable 模塊驅動“。所謂“模塊”是指具備相同領域的功能組件集合, 組合所形成一個獨立 的單元。比如 Web MVC 模塊、AspectJ代理模塊、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 服務熔斷模塊

實現方式

注解驅動方式

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

@Configuration
public class DelegatingWebMvcConfiguration extends
WebMvcConfigurationSupport {
... 
}

注解驅動方式

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {
...
 }
public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> {
    /**
    * {@inheritDoc}
    * @return {@link ProxyCachingConfiguration} or {@code
    AspectJCacheConfiguration} for
    * {@code PROXY} and {@code ASPECTJ} values of {@link
    EnableCaching#mode()}, respectively
    */
    public String[] selectImports(AdviceMode adviceMode) {
        switch (adviceMode) {
            case PROXY:
                return new String[] {
AutoProxyRegistrar.class.getName(),ProxyCachingConfiguration.class.getName() };
        case ASPECTJ:
            return new String[] {
                AnnotationConfigUtils.CACHE_ASPECT_CONFIGURATION_CLASS_NAME };
        default:
    } 
}

自定義 @Enable 模塊

基于注解驅動實現 @EnableHelloWorld

public class HelloWorldConfiguration {
    @Bean
    public String helloWorld(){
        return "hello world 2018";
    }

}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(HelloWorldConfiguration.class) //基于注解
public @interface EnableHelloWorld {
}

基于接口驅動實現

public class HelloWorldConfiguration {
    @Bean
    public String helloWorld(){
        return "hello world 2018";
    }

}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(HelloWorldImportSelector.class)//基于接口 彈性
public @interface EnableHelloWorld {
}

public class HelloWorldImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{HelloWorldConfiguration.class.getName()};
    }
}

Spring 條件裝配

從 Spring Framework 3.1 開始,允許在 Bean 裝配時增加前置條件判斷

條件注解舉例

Spring注解 場景說明 起始版本
@Profile 配置化條件裝配 3.1
@Conditional 編程條件裝配 4.0

實現方式

  • 配置方式-@Profile
  • 編程方式-@Conditional
@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

  • 引導類
@ComponentScan(basePackages = "com.imooc.diveinspringboot.service")
public class CalculateServiceBootstrap {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = new SpringApplicationBuilder(CalculateServiceBootstrap.class)
                .web(WebApplicationType.NONE).profiles("java8")
                .run(args);
        CalculateService calculateService = context.getBean(CalculateService.class);
        System.out.println("sum :"+calculateService.sum(1,2,3,4,5,6,7,8,9,10));
        context.close();
    }
}
  • 計算接口
public interface CalculateService {
    /**
     * 多個整數求和
     * @param values v
     * @return sum
     */
     Integer sum(Integer...values);

}
  • @Profile("Java7") : for 循環(huán)
@Profile("java7")
@Service
public class Java7CalculateService implements CalculateService {

    @Override
    public Integer sum(Integer... values) {
        System.out.println("java7 sum");
        int sum = 0;
        for(Integer v :values){
            sum+=v;
        }
        return sum;
    }
}
  • @Profile("Java8") : Lambda
@Profile("java8")
@Service
public class Java8CalculateService implements CalculateService {

    @Override
    public Integer sum(Integer... values) {
        System.out.println("java8 sum");
        return Stream.of(values).reduce(0,Integer::sum);
    }
}

基于編程方式實現-@ConditionalOnSystemProperty 通過判斷系統(tǒng)配置值來裝載Bean

  • 引導類
public class SystemPropertyConditionBootstrap {

    @ConditionOnSystemProperty(name = "user.name",value = "zed")
    @Bean
    public String helloWorld(){
        return "hello zed!";
    }
    public static void main(String[] args) {
        ConfigurableApplicationContext context = new SpringApplicationBuilder(SystemPropertyConditionBootstrap.class).web(WebApplicationType.NONE).run(args);
        String helloWorld = context.getBean("helloWorld",String.class);
        System.out.println(helloWorld);
        context.close();
    }

}
  • Condition 注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnSystemPropertyCondition.class)
public @interface ConditionOnSystemProperty {
    /**
     * 名稱
     * @return name
     */
    String name();

    /**
     * 值
     * @return value
     */
    String value();
}
  • 條件實現類 implements Condition
public class OnSystemPropertyCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Map<String,Object> attributes = metadata.getAnnotationAttributes(ConditionOnSystemProperty.class.getName());
        String propertyName = String.valueOf(attributes.get("name"));
        String propertyValue = String.valueOf(attributes.get("value"));

        String javaPropertyValue = System.getProperty(propertyName);

        return javaPropertyValue.equals(propertyValue);
    }
}

Spring Boot 自動裝配

底層裝配技術

  • 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 == "zed"
* 模式注解: @Configuration
* @Enable 模塊:@EnableHelloWorld-> HelloWorldImportSelector->HelloWorldConfiguration->helloWorld

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容