Spring @Configurable基本用法

關於@Configurable的用法,Spring文檔有詳細的描述,不過因爲看得比較粗略,後面實際使用的時候踩了很多坑。這個註解有如下幾個用途:html

爲非Spring管理的對象注入Spring Bean

非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

Imgur

能夠看到,咱們本身建立的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-instrumentjar包的路徑debug

若是用的maven管理依賴,而且IDE爲Intellij IDEA,能夠打開Project Structure的Libraries選項查看jar包的路徑,而後在run/debug裏面配置jvm參數。
Imgur
Imgur

How it works?

首先,若是僅僅有@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是不起任何做用的,所以對單元測試不會產生影響

相關文章
相關標籤/搜索