Spring Import注解


今天了解了,Spring @Import的使用

先貼上Spring官方關於Spring @Import注解的文檔鏈接   https://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-java-using-import

 

一.@Import 引入一個普通java對象 適用4.2.0之后版本

二.@Import與@Configuration使用方式

三.@Import 與 ImportBeanDefinitionRegistrar 使用4.2.0之前版本

四.@Import 與 ImportSelector 使用方式

 

 

 

一。@Import直接引入一個類,將其作為Spring bean,受Spring容器管理;(Spring 4.2.0以及之后版本可以使用)

在Spring 4.2.x之前的版本,@Import注解無法引入一個沒有注解 @Configuration 或者 @Bean 且沒有實現ImportBeanDefinitionRegistry 或者 ImportSelector 的類,將其作為Spring Bean管理;

  如果你在使用的版本是4.2.0之前的版本,想要使用@Import導入一個類,方法二、三或許可行;

特地拿了Spring 4.1.9版本,測試,@Import一個普通的Java類,拋出異常:

Exception in thread "main" org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: pack1.AppConfig2 was @Import'ed but is not annotated with @Configuration nor does it declare any @Bean methods; it does not implement ImportSelector or extend ImportBeanDefinitionRegistrar. Update the class to meet one of these requirements or do not attempt to @Import it.

翻譯過來就是: AppConfig2被import導入了,但是沒有標注@Configuration或者也沒有聲明任何@Bean方法,也沒有實現ImportSelector或者繼承ImportBeanDefinitionRegistrar類;

 

不做任何改動,修改maven導入的Spring版本號,拿4.2.0測試,就成功Import該java類;  Import導入的類,默認beanId是類全限定名,可以設定@Configuration改變類的beanId;

關於為什么配置類上加@Configuration作用,查看我的理解      https://www.cnblogs.com/lvbinbin2yujie/p/10279416.html

@Configuration @Import({AppConfig2.class}) public class AppConfig1 { public static void main(String[] args) { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig1.class); String[] names = ac.getBeanDefinitionNames(); for (String string : names) { System.out.println(string+","+ac.getBean(string)); } } } public class AppConfig2 { }

 

二。@Import引入配置類@Configuration標注的類; 純屬個人看Spring官方文檔的理解,大致能看懂,Spring官方文檔鏈接:https://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-java-using-import

   2.1 先說最容易看懂的一種使用方式,每個@Configuration里的@Bean定義都很明顯,就像開發過程中寫了好多配置文件,然后在某一個XML里面<import  />其他的bean;

@Configuration public class ConfigA { @Bean public A a() { return new A(); } } public class A { }

 

 

@Import({ConfigA.class}) @Configuration public class ConfigB { @Bean public B b() { return new B(); } public static void main(String[] args) { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ConfigB.class); String[] names = ac.getBeanDefinitionNames(); for (String string : names) { System.out.println(string+" , "+ac.getBean(string)); } } }

 

測試結果如下:  印證了 1. @Import引入的類默認beanId為類全路徑名  ;  2. @Configuration標注的類是動態代理之后的類 ;

configB , pack1.ConfigB$$EnhancerBySpringCGLIB$$1acf4932@4313f5bc
pack1.ConfigA , pack1.ConfigA$$EnhancerBySpringCGLIB$$e26ca611@7f010382
a , pack1.A@1e802ef9
b , pack1.B@2b6faea6

 

 

  2.2 但是我們平常見得開發中,比如service配置文件在一個XML里,dao的配置文件在一個XML中,就會跨文件引用,但是我們都會寫 ref引用那個對象,因為Spring會幫助我們找到需要的對象;

舉個例子模擬這種跨文件的引用,Spring官方文檔里面也有這樣一個例子;

// dao類
public class AccountDao { private DataSource dataSource; public AccountDao(DataSource dataSource) { this.dataSource=dataSource; } public void saveMoney() { System.out.println("利用dataSource模擬存錢操作"); } }

 

// service類
public class AccountService { private AccountDao accountDao; public AccountService(AccountDao accountDao) { this.accountDao=accountDao; } public void saveMoney() { accountDao.saveMoney(); } }

 

下面將使用注解,來代替原來的XML配置方式,定義了兩個配置類DaoConfig  ServiceConfig 

@Configuration public class DaoConfig { @Autowired private DataSource dataSource; @Bean public AccountDao accountDao() { return new AccountDao(dataSource); } }

 

@Configuration public class ServiceConfig { @Autowired private AccountDao accountDao; @Bean public AccountService accountService() { return new AccountService(accountDao); } 

 

@Configuration @Import({ServiceConfig.class,DaoConfig.class}) public class SystemConfig { @Bean public DataSource dataSource() { return new DruidDataSource(); } public static void main(String[] args) { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SystemConfig.class); AccountService service = ac.getBean(AccountService.class); service.saveMoney();  } }

 

 

查看測試結果:

利用dataSource模擬存錢操作

 

 

簡單來講下我的分析:從bean的load講一下, 第一個加載的bean是SystemConfig,先解析Import注解,和SysemConfig一樣遞歸加載ServiceConfig以及DaoConfig這兩個類,遞歸因為沒有別的注解了,所以就是解析@Bean,也就是納入了AccountService AccountDao 最后是 DataSource 這三個Bean ,別忘了 加上SystemConfig、ServiceConfig 、DaoConfig這樣六個bean對象;然后屬性@Autowired注入,如果屬性注入的時候這個bean還沒有就回去實例化這個Bean,這樣一來就完成了。

可能看完這個方法,還是復雜啊,假如少Import一個配置類,那肯定會報錯了。

 

  2.3 此外,Spring另外一個方法可以實現上面功能,比較奇特的方式;

@Configuration public class DaoConfig { @Autowired private DataSource dataSource; @Bean public AccountDao accountDao() { return new AccountDao(dataSource); } }

 

@Configuration public class ServiceConfig2 {  @Autowired private DaoConfig daoConfig; @Bean public AccountService accountService() {  AccountDao dao1 = daoConfig.accountDao(); System.out.println("ServiceConfig中的dao:"+dao1); return new AccountService(dao1); } }

 

@Configuration @Import({ServiceConfig2.class,DaoConfig.class}) public class SystemConfig2 { @Autowired private ServiceConfig2 serviceConfig; @Bean public DataSource dataSource() { return new DruidDataSource(); } public static void main(String[] args) { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SystemConfig2.class); AccountService service = ac.getBean(AccountService.class); service.saveMoney(); } }

 

 

測試結果: 由於使用main方法測試,如果在業務方法中可以直接 serviceConfig.accountService().saveMoney();

ServiceConfig中的dao:pack2.AccountDao@3daa422a
利用dataSource模擬存錢操作

 

 

說明: 1. @Configuration注解的類本身也是個bean對象,這點不能忘;   2.@Configuration標注的類調用 同一個@Bean方法 會一直得到同一個bean對象 ;

    3. Spring官方文檔中有一句:要使用@Configuration注解需要使用 <context:component-scan /> 標簽

 

三。方法三,實現ImportBeanDefinitionRegistrar接口, 這也是 Spring 4.2.0之前版本 如果想要引入一個普通bean,沒法直接Import,實現接口並且重寫方法里注冊這個bean對象;

public class ABeanDefinitionRegistry implements ImportBeanDefinitionRegistrar{ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
     //簡單說下使用,只要將beanDefinition注冊到registry里,就算把bean對象引入了;只是簡單寫了個例子引入A類 BeanDefinitionBuilder bd
= BeanDefinitionBuilder.rootBeanDefinition(A.class); registry.registerBeanDefinition("a", bd.getBeanDefinition()); } }

 

@Import({ABeanDefinitionRegistry.class})

 

這個方法是怎么調用的呢?ConfigurationClassParser類的processImports方法,是解析類上@Import注解的;ConfigurationClassBeanDefinitionReader的loadBeanDefinitionsFromRegistrars方法,遍歷引入的實現ImportBeanDefinitionRegistrar的類,然后調用其registerBeanDefinitions方法; 

 

四。方法四,實現ImportSelector接口,這種方式相比之前方式更加容易理解,只需要返回bean的類全限定名即可;

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

 

這樣調用即可;

@Import({AImportSelector.class})

 

 三、四兩種方式都有個參數為AnnotationMetadata是類上的注解信息的,可以這樣實現很多功能,由於不是很了解;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM