今天了解了,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 與 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是類上的注解信息的,可以這樣實現很多功能,由於不是很了解;