Spring5.0源碼深度解析之Spring核心註解

Spring核心註解原理

這篇文章主要針對Spring核心註解進行分析linux

一:@Condition註解

Condition 是在spring4.0 增長的條件註解,經過這個能夠功能能夠實現選擇性的注入Bean操做,接下來先學習下Condition是如何使用的,而後分析spring源碼瞭解其中的實現原理。spring

實現案例:設計模式

在Spring容器加載中,若是當前環境是WIN7操做系統就裝配win7實體類、其餘系統就不裝配。安全

@Configuration
public class MyConfig {
    /**
     * Import做用主要將外部的jar包注入到springioc容器中 @Import(Win7Entity.class) 等於與@Bean
     * Import註冊的bean對象 id爲當前類全路徑
     */
    @Bean
    public Win7Entity win7Entity() {
        return new Win7Entity();
    }
public class V1Spring {
    public static void main(String[] args) {

        // 1.基於註解方式實現啓動
        ApplicationContext annotationApplicationContext =
                new AnnotationConfigApplicationContext(MyConfig.class);
        Win7Entity win7Entity = (Win7Entity) annotationApplicationContext.getBean("win7Entity");
        System.out.println(win7Entity);

輸出結果工具

com.mayikt.v1.entity.Win7Entity@5606c0b學習

MyConditionui

public class MyCondition implements Condition {
    /**
     * @param context  獲取到當前的上下文
     * @param metadata 獲取當前註解的細心
     * @return
     */
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 1.獲取當前的環境
        Environment environment = context.getEnvironment();
        // win7 linux win8 win10 蘋果系統MAC
        String osName = environment.getProperty("os.name");
        if (osName.equals("Windows 10")) {
            // 能夠註冊該對象
            return true;
        }
        // 不能註冊該對象
        return false;
    }
}
@Configuration
public class MyConfig {
    /**
     * Import做用主要將外部的jar包注入到springioc容器中 @Import(Win7Entity.class) 等於與@Bean
     * Import註冊的bean對象 id爲當前類全路徑
     */
    @Bean
    @Conditional(MyCondition.class)
    public Win7Entity win7Entity() {
        return new Win7Entity();
    }

輸出:this

com.mayikt.v1.entity.Win7Entity@64d7f7e0spa

二:@Import註解

1.爲何要使用@Import註解?Import註解的主要做用的將外部的jar包注入到springioc容器中操作系統

2.@Bean註解應用場景是什麼?註冊加載外部jar包

@Configuration
@Import(Win7Entity.class)
public class MyConfig {
    /**
     * Import做用主要將外部的jar包注入到springioc容器中 @Import(Win7Entity.class) 等同於@Bean
     * Import註冊的bean對象 id爲當前類全路徑
     */
   /* @Bean
    @Conditional(MyCondition.class)
    public Win7Entity win7Entity() {
        return new Win7Entity();
    }*/
public class V1Spring {
    public static void main(String[] args) {

        // 1.基於註解方式實現啓動
        ApplicationContext annotationApplicationContext =
                new AnnotationConfigApplicationContext(MyConfig.class);
        Win7Entity win7Entity = (Win7Entity) annotationApplicationContext.getBean("com.mayikt.v1.entity.Win7Entity");//這裏當前類Win7Entity的全路徑
        System.out.println(win7Entity);

結果:

com.mayikt.v1.entity.Win7Entity@f4168b8

public class V1Spring {
    public static void main(String[] args) {

        // 1.基於註解方式實現啓動
        ApplicationContext annotationApplicationContext =
                new AnnotationConfigApplicationContext(MyConfig.class);
        Win7Entity win7Entity = (Win7Entity) annotationApplicationContext.getBean("com.mayikt.v1.entity.Win7Entity");
        System.out.println(win7Entity);
        String[] beanDefinitionNames = annotationApplicationContext.getBeanDefinitionNames();
        for (int i = 0; i < beanDefinitionNames.length; i++) {
            System.out.println(beanDefinitionNames[i]);
        }

結果

com.mayikt.v1.entity.Win7Entity@f4168b8
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
myConfig
com.mayikt.v1.entity.Win7Entity

總結下:@Bean註解與@Import註解的區別?

@Bean註解註冊的bean的id是以方法名稱來做爲beanid ,

@Import註解以當前類的完整路徑地址註冊 ,相比來講@Import注入類更加簡單

應用場景都是引入外部jar包

三@EnableXXX註解的實現原理

配合@Configuration使用,包括 @EnableAsync, @EnableScheduling, @EnableTransactionManagement, @EnableAspectJAutoProxy, @EnableWebMvc。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({PayEntity.class,MyPayEntity.class}) //引入@Import註解
public @interface EnablePayEntity {
    // 只要啓動的時候 加入該EnablePayEntity  就會將PayEntity實體類注入到spruingioc容器
    // Enable註解的話 底層 實際上在調用@Import(PayEntity.class)
}
@Configuration
@Import(Win7Entity.class)
@EnablePayEntity    //配置類加上這個註解
public class MyConfig {
    /**
     * Import做用主要將外部的jar包注入到springioc容器中 @Import(Win7Entity.class) 等於與@Bean
     * Import註冊的bean對象 id爲當前類全路徑
     */

程序輸出結果

com.mayikt.v1.entity.Win7Entity@130f889
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
myConfig
com.mayikt.v1.entity.PayEntity
com.mayikt.v1.entity.MyPayEntity
com.mayikt.v1.entity.Win7Entity

@EnableXXX註解的實現原理:底層實際上仍是調用@Import註解

四:ImportSelector類

public class MyImportSelector implements ImportSelector {
    /**
     *  註解信息
     * @param importingClassMetadata
     * @return
     */
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //注入多個對象,類的完整路徑
        return new String[]{"com.mayikt.v1.entity.MemberEntity","com.mayikt.v1.entity.MsEntity"};
    }
}
@Configuration
@Import({Win7Entity.class,MyImportSelector.class})
@EnablePayEntity
public class MyConfig {
    /**
     * Import做用主要將外部的jar包注入到springioc容器中 @Import(Win7Entity.class) 等於與@Bean
     * Import註冊的bean對象 id爲當前類全路徑
     */

輸出結果

com.mayikt.v1.entity.Win7Entity@16267862
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
myConfig
com.mayikt.v1.entity.PayEntity
com.mayikt.v1.entity.MyPayEntity
com.mayikt.v1.entity.Win7Entity
com.mayikt.v1.entity.MemberEntity
com.mayikt.v1.entity.MsEntity

五:ImportBeanDefinitionRegistrar

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    /**
     * AnnotationMetadata 註解的信息
     * @param importingClassMetadata
     * @param registry
     */
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // spring容器中 bean 的信息 Bean Definition描述   手動註冊到ioc容器中
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(SmsEntity.class);
        registry.registerBeanDefinition("smsEntity", rootBeanDefinition);
        // 底層源碼:springioc 底層經過beanDefinitionMap存放 線程是安全的
    }
    //FactoryBean (往IOC容器存儲對象 注入對象) BeanFactory(從ioc工廠獲取bean對象)
}

底層實現原理:

this.beanDefinitionMap.put(beanName, beanDefinition);
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap(256)

底層經過一個ConcurrentHashMap保存起來的。

@Configuration
@Import({Win7Entity.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
@EnablePayEntity
public class MyConfig {
    /**
     * Import做用主要將外部的jar包注入到springioc容器中 @Import(Win7Entity.class) 等於與@Bean
     * Import註冊的bean對象 id爲當前類全路徑
     */

輸出結果

myConfig
com.mayikt.v1.entity.PayEntity
com.mayikt.v1.entity.MyPayEntity
com.mayikt.v1.entity.Win7Entity
com.mayikt.v1.entity.MemberEntity
com.mayikt.v1.entity.MsEntity
smsEntity

六:FactoryBean

public class MyFactoryBean implements FactoryBean<MyEntity> {
    public MyEntity getObject() throws Exception {
        return new MyEntity();
    }
    public Class<?> getObjectType() {
        return MyEntity.class;
    }
    //往IOC容器中注入對象
    public boolean isSingleton() {
        // 默認的狀況下就是爲true true表示爲單例 false 表示爲多例
        return false;
    }
}
@Configuration
@Import({Win7Entity.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class,MyFactoryBean.class})
@EnablePayEntity
public class MyConfig {
    /**
     * Import做用主要將外部的jar包注入到springioc容器中 @Import(Win7Entity.class) 等於與@Bean
     * Import註冊的bean對象 id爲當前類全路徑
     */

輸出結果

myConfig
com.mayikt.v1.entity.PayEntity
com.mayikt.v1.entity.MyPayEntity
com.mayikt.v1.entity.Win7Entity
com.mayikt.v1.entity.MemberEntity
com.mayikt.v1.entity.MsEntity
com.mayikt.v2.config.MyFactoryBean
smsEntity

疑問:BeanFactory和FactoryBean區別?

BeanFactory是個Factory,也就是IOC容器或對象工廠,FactoryBean是個Bean。在Spring中,全部的Bean都是由BeanFactory(也就是IOC容器)來進行管理的。

但對FactoryBean而言,這個Bean不是簡單的Bean,而是一個能生產或者修飾對象生成的工廠Bean,它的實現與設計模式中的工廠模式和修飾器模式相似

疑問:@Service與@Compent註解區別?

底層仍是調用@Compont註解,主要是爲了分類,更好劃分場景,注意要加上掃包範圍

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

Spring註釋@Qualifie

在學習@Autowired的時候咱們已經接觸到了@Qualifier,這節就來詳細學習一下自定義@Qualifier

例如定義一個交通工具類:Vehicle以及它的子類Bus和Sedan。

若是用@Autowired來找Vehicle的話,會有兩個匹配的選項Bus和Sedan。爲了限定選項,能夠像下面這樣。

@Autowired
@Qualifier("car")
private Vehicle vehicle;

若是要頻繁使用@Qualifier("car")而且想讓它變得更有意義,咱們能夠自定義一個@Qualifier。

@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Car{

}
@Autowired
  @Car
  private Vehicle vehicle;

最後在Sedan類加上註釋。

@Car
public class Sedan implements Vehicle{
}

疑問:@Primary與@Qualifier區別?

在一個接口下有兩個實現類使用@Autowired獲取的時候,有什麼問題?

@Autowired默認狀況下使用類型查找,會存在問題。SpringBoot多數據源 設置默認或者優先級。

解決方案:

@Resource按照名稱查找;

@Qualifier指定實現類

@Primary指定實現類的優先級第一,默認獲取

 

本文參考

螞蟻課堂:http://www.mayikt.com/

相關文章
相關標籤/搜索