關於@Configurable
的用法,Spring文檔有詳細的描述,不過因爲看得比較粗略,後面實際使用的時候踩了很多坑。這個註解有如下幾個用途:html
非Spring管理的對象,這裏指的是咱們本身new的對象,好比Dog dog = new Dog()
,這個dog對象的生命週期不是由Spring管理的,而是咱們本身建立的對象,根據文檔的說法,咱們只要在類上面加上@Configurable
註解,就可讓Spring來配置這些非Spring管理的對象了,即爲它們注入須要的依賴(實際上還有不少額外的工做要作)。下面有個例子java
// Account類,使用new操做符號手動建立,不交由Spring Container管理 @Configurable(autowire = Autowire.BY_TYPE) public class Account { @Autowired Dog dog; public void output(){ System.out.println(dog); } } |
上面的Account
類使用的是@Configurable
,而不是@Configuration
,@Configuration
相似於XML配置裏面的<beans></beans>
,咱們能夠在<beans></beans>
裏面聲明要交由Spring Cotainer建立和管理的Bean,所以咱們能夠在@Configuration
註解的類裏面使用@Bean
註解達到一樣的效果,注意被@Configuration
標註的類也會被Spring Container建立和管理,所以它也是一個Bean。spring
被@Configuration
標註的類,可以被Spring配置,而後當咱們手動建立Account
對象的時候(Account acc = new Account()
),Spring將會用建立一個Account
的Bean,而後這個Bean能被Spring正常地注入須要的屬性,接着Spring使用這個Bean來設置咱們剛剛建立的
Account
對象(acc)的屬性,最後返回的對象的屬性就和Bean的同樣了。編程
// 配置類,使用註解的方式建立Bean @Configuration @EnableLoadTimeWeaving @EnableSpringConfigured public class Config { // 這個Bean將會被注入到Account的屬性中 @Bean Dog dog(){ Dog d = new Dog(); d.setId(1); d.setName("dog"); return d; } } |
public class Dog { private int id; private String name; // set,get和toString方法就不貼了 } |
// 啓動類 @ComponentScan public class App { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(App.class); Account account = new Account(); account.output(); } } |
程序輸入結果以下:jvm
能夠看到,咱們本身建立的Account
對象,輸出了Dog
對象,而不是null。由於Spring使用AspectJ的LTW(LoadTimeWeaving)技術爲咱們本身建立的Account
對象注入了Dog
對象。maven
要使上面的例子正常運行,須要知足幾個要求:單元測試
spring-core, spring-beans, spring-context, spring-instrument, spring-aspects, aspectjweaver, spring-tx等幾個依賴要有,其中spring-tx是可選的,沒有的話會輸出一些警告信息。測試
@EnableLoadTimeWeaving
和@EnableSpringConfigured
兩個註解必須有,能夠註解在任意被@Configuration
註解的類上面spa
運行前加上-javaagent:/path/to/spring-instrument.jar
這個jvm參數,/path/to/spring-instrument.jar
爲你的spring-instrument
jar包的路徑debug
若是用的maven管理依賴,而且IDE爲Intellij IDEA,能夠打開Project Structure的Libraries選項查看jar包的路徑,而後在run/debug裏面配置jvm參數。
首先,若是僅僅有@Configuration
註解,是不起任何做用的,所以咱們這時候手動建立Account
對象的話,將會輸出null。由於起到關鍵做用的時候AnnotationBeanConfigurerAspect
這個類(它是使用aspect定義的,而不是class,由於我還沒用過aspectj編程,因此還不太瞭解),而這個類的實例在添加@EnableSpringConfigured
註解後將會被Spring建立,而後Spring將會結合LWT技術調用這個對象裏面的方法對咱們手動建立的Account
對象的屬性進行處理。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(SpringConfiguredConfiguration.class) //而後咱們去看看SpringConfiguredConfiguration幹了什麼 public @interface EnableSpringConfigured { } |
@Configuration public class SpringConfiguredConfiguration { /** * The bean name used for the configurer aspect. */ public static final String BEAN_CONFIGURER_ASPECT_BEAN_NAME = "org.springframework.context.config.internalBeanConfigurerAspect"; @Bean(name = BEAN_CONFIGURER_ASPECT_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public AnnotationBeanConfigurerAspect beanConfigurerAspect() { return AnnotationBeanConfigurerAspect.aspectOf(); } } |
從上能夠看到,一個AnnotationBeanConfigurerAspect類型的Bean將會被Spring Container建立和管理,所以它就是讓@Configurable
註解起做用的核心。
public aspect AnnotationBeanConfigurerAspect extends AbstractInterfaceDrivenDependencyInjectionAspect implements BeanFactoryAware, InitializingBean, DisposableBean{ //類的實現就不貼了,從它實現的接口也能夠猜到一些東西 } |
文檔上面還提到,不單單是使用new建立的時候,在反序列化的時候,被@Configurable
註解的類和使用new建立的時候同樣,會被攔截而後注入屬性
當類沒有被AspectJ動態織入(LoadTimeWeaving)的時候,@Configuration
是不起任何做用的,所以對單元測試不會產生影響