Spring的模式註解裝配、@Enable模塊裝配、條件裝配與工廠加載機制

前言:這是在慕課網上學習Spring Boot2.0深度實踐之核心技術篇 時所做的筆記,主要供本人複習之用.

目錄

第一章 Spirng模式註解裝配

1.1 模式註解舉例

1.2 裝配方式:

1.3 自定義模式註解

第二章 Spring @Enable模塊裝配

2.1 基於註解驅動

2.2 基於接口驅動實現

第三章 Spirng條件裝配

3.1 基於配置方式實現 @Profile

3.2 基於編程方式實現

3.2.1 OnSystemPropertyCondition

3.2.2 ConditioanlOnSystemProperty

3.3.3 應用

第四章 工廠加載機制

4.1 通過EnableAutoConfiguration激活自動裝配

4.2 實現自動裝配:

4.3 配置到自動化裝配文件裏:


第一章 Spirng模式註解裝配

模式註解一種用於聲明在應用中扮演"組件"角色的註解,即標註了這個註解,表明一個類等在應用中扮演組件.

應用指的是Spring或SpringBoot應用,

1.1 模式註解舉例

@component作爲一種由Spirng容器託管的通用模式組件,任何被@Component標準的組件均爲組件掃描的候選對象.類似的,凡是被@Component原標註的註解,如@Service,任何組件標註它時,也將被是做組件掃描的候選對象.

1.2 裝配方式:

裝配方式:<context:component-scan>(Spring 2.5)或@ComponentScan(Spring 3.1)

1.3 自定義模式註解

模式註解的派生性:

@component,@Repository,@FirstLevelRepository註解,都有value簽名,保留了簽名的一致性,這就是註解的派生性.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {

	String value() default "";

}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {
	@AliasFor(annotation = Component.class)
	String value() default "";
}

 

模式註解的層次性:

我們聲明二級註解SecondLevelRepository,我們的SecondLevelRepository派生於FirstLevelRepository,這就是層次性.

@SecondLevelRepository(value = "myFirstLevelRepository") // Bean 名稱
public class MyFirstLevelRepository {
}

 我們的@SpringBootAppliacation也是模式註解.

 

第二章 Spring @Enable模塊裝配

模塊裝配的舉例:

2.1 基於註解驅動

Import可以導入多個類,只不過這裏我們只導入了一個.

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

 

public class HelloWorldConfiguration {

    @Bean
    public String helloWorld() { // 方法名即 Bean 名稱
        return "Hello,World 2018";
    }

}

 完成以上兩步後,意味着只要我們EnableHelloWorld的註解標註在某個類上時,我們的Bean就已經存在了.

@EnableHelloWorld
public class EnableHelloWorldBootstrap {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = new SpringApplicationBuilder(EnableHelloWorldBootstrap.class)
                .web(WebApplicationType.NONE)
                .run(args);

        // helloWorld Bean 是否存在
        String helloWorld =
                context.getBean("helloWorld", String.class);

        System.out.println("helloWorld Bean : " + helloWorld);

        // 關閉上下文
        context.close();
    }
}

 

2.2 基於接口驅動實現

加載過程:HelloWorldImportSelector->helloWorldConfiguration->HelloWorld

我們用下面的Selector進行一個導入,導入之後變成了helloword Bean.這樣的好處是可以通過編程來控制哪個要變成Bean.

public class HelloWorldConfiguration {

    @Bean
    public String helloWorld() { // 方法名即 Bean 名稱
        return "Hello,World 2018";
    }

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

 

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

 

第三章 Spirng條件裝配

版本:在Spring3.0推出了@Configuration,3.1推出了ImportSelector

定義:Bean裝配的前置判斷

舉例:@Profile ,@Conditional

實現:註解方式,編程方式.

3.1 基於配置方式實現 @Profile

計算服務,多整數求和sum

public interface CalculateService {

    Integer sum(Integer... values);
}

@Profile("Java7")  for循環實現

@Profile("Java7")
@Service
public class Java7CalculateService implements CalculateService {

    @Override
    public Integer sum(Integer... values) {
        System.out.println("Java 7 for 循環實現 ");
        int sum = 0;
        for (int i = 0; i < values.length; i++) {
            sum += values[i];
        }
        return sum;
    }

    public static void main(String[] args) {
        CalculateService calculateService = new Java7CalculateService();
        System.out.println(calculateService.sum(1,2,3,4,5,6,7,8,9,10));
    }

}

@Profile("Java8") Lamda表達式實現

@Profile("Java8")
@Service
public class Java8CalculateService implements CalculateService {

    @Override
    public Integer sum(Integer... values) {
        System.out.println("Java 8 Lambda 實現");
        int sum = Stream.of(values).reduce(0, Integer::sum);
        return sum;
    }

    public static void main(String[] args) {
        CalculateService calculateService = new Java8CalculateService();
        System.out.println(calculateService.sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
    }

}

Java8使用:

@SpringBootApplication(scanBasePackages = "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 Bean 是否存在
        CalculateService calculateService = context.getBean(CalculateService.class);

        System.out.println("calculateService.sum(1...10) : " +
                calculateService.sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));

        // 關閉上下文
        context.close();
    }
}

 

3.2 基於編程方式實現

Spring4之後Profile的實現是通過@Contional(ProfileCondition.class)來實現的.

AnnotatedTypeMetadata保存的是一些元信息.用來獲得註解的一些信息.

實現(模仿的是ConditionalOnProperty):

3.2.1 OnSystemPropertyCondition

如果返回true表示參數正確,建立對應的Bean.

public class OnSystemPropertyCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {

        Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnSystemProperty.class.getName());

        String propertyName = String.valueOf(attributes.get("name"));

        String propertyValue = String.valueOf(attributes.get("value"));

        String javaPropertyValue = System.getProperty(propertyName);

        return propertyValue.equals(javaPropertyValue);
    }
}

3.2.2 ConditioanlOnSystemProperty

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnSystemPropertyCondition.class)
public @interface ConditionalOnSystemProperty {

    /**
     * Java 系統屬性名稱
     * @return
     */
    String name();

    /**
     * Java 系統屬性值
     * @return
     */
    String value();
}

3.3.3 應用

在matches函數中,取出來的name是user.name,value是Mercy.就是註解在上面的值.

public class ConditionalOnSystemPropertyBootstrap {

    @Bean
    @ConditionalOnSystemProperty(name = "user.name", value = "Mercy")
    public String helloWorld() {
        return "Hello,World 小馬哥";
    }

    public static void main(String[] args) {
        ConfigurableApplicationContext context = new SpringApplicationBuilder(ConditionalOnSystemPropertyBootstrap.class)
                .web(WebApplicationType.NONE)
                .run(args);
        // 通過名稱和類型獲取 helloWorld Bean
        String helloWorld = context.getBean("helloWorld", String.class);

        System.out.println("helloWorld Bean : " + helloWorld);

        // 關閉上下文
        context.close();
    }
}

第四章 工廠加載機制

更詳細的可以看:https://blog.csdn.net/q610376681/article/details/88578155

定義:基於約定大於配置的原則,實現Spring組件自動裝配的目的.

裝配:模式註解,@Enable模塊,條件裝配,工廠加載機制

實現:激活自動裝配,實現自動裝配,配置自動裝配實現.

是SpringBoot主要使用的自動裝配機制

原理:由SpringFactoriesLoader的loadFactories進行加載,通過for循環進行迭代裝配所有依賴中META-INF/Spring.facorties裏的值.

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

實現:

4.1 通過EnableAutoConfiguration激活自動裝配

@EnableAutoConfiguration
public class EnableAutoConfigurationBootstrap {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = new SpringApplicationBuilder(EnableAutoConfigurationBootstrap.class)
                .web(WebApplicationType.NONE)
                .run(args);

        // helloWorld Bean 是否存在
        String helloWorld =
                context.getBean("helloWorld", String.class);

        System.out.println("helloWorld Bean : " + helloWorld);

        // 關閉上下文
        context.close();

    }
}

4.2 實現自動裝配:

@EnableHelloWorld的定義可以參考1.2.2節,@ConditionalOnSystemProperty可以參考1.3.1節.

@Configuration // Spring 模式註解裝配
@EnableHelloWorld // Spring @Enable 模塊裝配
@ConditionalOnSystemProperty(name = "user.name", value = "Mercy") // 條件裝配
public class HelloWorldAutoConfiguration {
}

4.3 配置到自動化裝配文件裏:

在resource中新建META-INF,在META-INF中新建Spring.factories,寫入以下內容,\用於換行.

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.imooc.diveinspringboot.configuration.HelloWorldAutoConfiguration

流程:

首先進行ConditionalOnSystemProperty的判斷,如果滿足條件就進行下一步.

然後使用模式註解配置爲Bean.

@Enable模塊:@EnableHelloword->HelloWorldImportSelector->HelloWorldConfiguration->最終生成HelloWordBean.

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章