隨筆 - 41  文章 - 7  trackbacks - 0
          <2016年8月>
          31123456
          78910111213
          14151617181920
          21222324252627
          28293031123
          45678910

          常用鏈接

          留言簿

          隨筆分類

          隨筆檔案

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          原文:
          7.12.1 基本概念: @Bean 和 @Configuration
          在Spring新的Java配置支持中,其核心構件是@Configuration注解類和@Bean注解方法.
          @Bean 注解用來表示方法實例化,配置以及初始化由Spring IoC容器管理的新對象.
          對于那些熟悉Spring <beans/> XML配置的人來說,@Bean 注解扮演了與<bean/> 元素相同的角色.你可以在任何Spring@Component 上使用@Bean 注解方法,但通常情況下,它們經常與@Configuration beans一起使用.
          @Configuration 注解類表示其主要目的作為bean定義的來源(source).
          此外, @Configuration 類允許在同一個類中調用其它的@Bean 方法來定義它們之間的依賴關系.
          可能最簡單的@Configuration 類可以像下面這樣讀取:
          @Configuration
          public class AppConfig {    
          @Bean    
          public MyService myService() {        
          return new MyServiceImpl();    
          }
          }
          AppConfig 類等價于下面的Spring <beans/> XML:
          <beans>    
          <bean id="myService" class="com.acme.services.MyServiceImpl"/>
          </beans>
          完整(full)@Configuration vs 精簡(lite) @Beans 模式?
          @Bean 方法聲明在未用@Configuration 注解的類中時,他們被稱為“精簡”模式處理.
          例如,bean 方法聲明在@Component 中或普通類(plain old class)中將被認為是精簡的('lite').
          不像完整@Configuration, 精簡@Bean 方法不能容易地聲明bean之間的依賴關系.通常情況下,當處于精簡模式時,一個@Bean 方法不能調用另一個@Bean方法.
          只有在@Configuration 類中使用@Bean 方法才是一種可確保總是處于完整模式下的推薦方法.
          這可以阻止同一個@Bean方法不小心被調用多次,有助于減少細微的bug(當運行在精簡模式下時,這些bug很難追查)。
          @Bean 和 @Configuration 注解會在下面的章節中詳細討論. 但首先,我們要講解基于Java配置來創建Spring 容器的各種方法.
          7.12.2 使用AnnotationConfigApplicationContext來實例化Spring 容器
          下面的章節記錄了Spring的AnnotationConfigApplicationContext, 它是Spring 3.0中新引入的.
          這個多用途的 ApplicationContext 實現不僅有能力接受@Configuration 類作為它的輸入,也可以接受@Component 類和使用JSR-330 元數據注解類作為它的輸入.
          @Configuration 類作為輸入時, @Configuration 類自身也將作為bean定義進行注冊,并且這個類中所有聲明了@Bean 方法也會以bean的定義進行注冊.
          當提供@Component 和JSR-330時,它們也是按bean定義進行注冊, 并會假設在那些類的必要地方使用DI元數據,如@Autowired 或 @Injectare.
          簡單構造
          與實例化ClassPathXmlApplicationContext時需要Spring XML文件方式相同, 當實例化AnnotationConfigApplicationContext時,也需要@Configuration 類作為它的輸入.
          這允許在Spring容器中完全自由地使用XML:
          public static void main(String[] args) {    
          ApplicationContext ctx =
          new AnnotationConfigApplicationContext(AppConfig.class);    
          MyService myService = ctx.getBean(MyService.
          class);    
          myService.doStuff();
          }
          正如上面所講的, AnnotationConfigApplicationContext 不限制于只同@Configuration 類工作. 任何@Component 或JSR-330 注解類都可以作為其構造器的輸入.例如:
          public static void main(String[] args) {    
          ApplicationContext ctx =
          new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class);    
          MyService myService = ctx.getBean(MyService.
          class);    
          myService.doStuff();
          }
          上面的代碼會假設MyServiceImpl, Dependency1 和 Dependency2 使用的是Spring 依賴注入,如@Autowired.
          通過編程使用register(Class<?>…​)來構建容器
          AnnotationConfigApplicationContext 可以使用無參構造器來實例化,然后再調用register() 方法來配置. 當通過編程來構建AnnotationConfigApplicationContext時,這種方法是特別有用的.
          public static void main(String[] args) {    
          AnnotationConfigApplicationContext ctx =
          new AnnotationConfigApplicationContext();    
          ctx.register(AppConfig.
          class, OtherConfig.class);    
          ctx.register(AdditionalConfig.
          class);    
          ctx.refresh();    
          MyService myService = ctx.getBean(MyService.
          class);    
          myService.doStuff();
          }
          使用scan(String…​)來啟用組件掃描
          要啟用組件掃描,只需要像這樣下面注解你的@Configuration類:
          @Configuration
          @ComponentScan(basePackages = "com.acme")
          public class AppConfig  {     ... }
          有經驗的 Spring用戶非常熟悉與它等價的來自Spring context:命名空間的XML聲明:
          <beans>    
          <context:component-scan base-package="com.acme"/>
          </beans>
          在上面的例子中,com.acme 包將掃描, 會查找所有@Component注解的類, 那些類也會以Spring bean的定義注冊在容器中. AnnotationConfigApplicationContext 暴露了 scan(String…​) 方法以允許執行相同組件掃描功能:
          public static void main(String[] args) {    
          AnnotationConfigApplicationContext ctx =
          new AnnotationConfigApplicationContext();    
          ctx.scan(
          "com.acme");    
          ctx.refresh();    
          MyService myService = ctx.getBean(MyService.
          class);
          }
          記住@Configuration 類是帶有@Component的meta-annotated, 因此它們是組件掃描的候選人!
          在上面的例子中, 假設AppConfig 聲明在com.acme 包(或它的子包)中,在調用scan()方法時,它也會挑選出來,并當refresh()的時候,它所有的 @Bean 方法將被處理,并以bean的定義注冊到容器中.
          使用 AnnotationConfigWebApplicationContext來支持Web應用程序
          AnnotationConfigApplicationContext 的WebApplicationContext變異是AnnotationConfigWebApplicationContext. 此實現可用于配置Spring ContextLoaderListener servlet 監聽器, Spring MVC DispatcherServlet等等.
          下面在web.xml中配置典型Spring MVC web application的片斷.
          注意 contextClass 和 init-param的使用:
          <web-app>    
          <!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext         instead of the default XmlWebApplicationContext -->    
          <context-param>        
          <param-name>contextClass</param-name>        
          <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext        
          </param-value>    
          </context-param>    
          <!-- Configuration locations must consist of one or more comma- or space-delimited         fully-qualified @Configuration classes. Fully-qualified packages may also be         specified for component-scanning -->    
          <context-param>        
          <param-name>contextConfigLocation</param-name>        
          <param-value>com.acme.AppConfig</param-value>    
          </context-param>    
          <!-- Bootstrap the root application context as usual using ContextLoaderListener -->    
          <listener>        
          <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>    
          </listener>    
          <!-- Declare a Spring MVC DispatcherServlet as usual -->    
          <servlet>        
          <servlet-name>dispatcher</servlet-name>        
          <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>        
          <!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext             instead of the default XmlWebApplicationContext -->        
          <init-param>            
          <param-name>contextClass</param-name>            
          <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext            
          </param-value>        
          </init-param>        
          <!-- Again, config locations must consist of one or more comma- or space-delimited             and fully-qualified @Configuration classes -->        
          <init-param>            
          <param-name>contextConfigLocation</param-name>            
          <param-value>com.acme.web.MvcConfig</param-value>        
          </init-param>    
          </servlet>    
          <!-- map all requests for /app/* to the dispatcher servlet -->    
          <servlet-mapping>        
          <servlet-name>dispatcher</servlet-name>        
          <url-pattern>/app/*</url-pattern>    
          </servlet-mapping>
          </web-app>
          7.12.3 使用@Bean 注解
          @Bean 是一個方法級注解,XML <bean/> 元素的直接模擬.
          此注解支持<bean/>元素中提供的屬性,如:init-method, destroy-method, autowiring 以及name.
          你可將 @Bean 注解用在@Configuration注解或@Component注解的類中.
          聲明bean
          要聲明一個bean,可簡單地在方法上使用@Bean 注解.你使用這種方法來在ApplicationContext中注冊bean的定義,其特定類型是方法的返回值.默認情況下,bean的名稱將與方法名稱同名.
          下面是一個@Bean 方法聲明的簡單示例:
          @Configuration
          public class AppConfig {    
          @Bean    
          public TransferService transferService() {        
          return new TransferServiceImpl();    
          }
          }
          上述的配置與下面的Spring XML配置完全等價:
          <beans>    
          <bean id="transferService" class="com.acme.TransferServiceImpl"/>
          </beans>
          兩種Bean聲明都使名為transferService 的bean在ApplicationContext中可用,并且都綁定TransferServiceImpl的對象實例上:
          transferService -> com.acme.TransferServiceImpl
          Bean 依賴
          @Bean 注解方法可以有任意數量的參數來描述構建bean時所需的依賴.例如,如果我們的TransferService需要一個AccountRepository,我們可以通過方法參數來實現那種依賴
          @Configuration
          public class AppConfig {    
          @Bean    
          public TransferService transferService(AccountRepository accountRepository) {        
          return new TransferServiceImpl(accountRepository);    
          }
          }
          這種解決機制與基于構造器的依賴注入是完全相同的, 參考the relevant section 來了解更多細節.
          接收生命周期回調
          任何使用@Bean 注解定義的類都支持正常的生命周期回調,也可以使用 JSR-250中的@PostConstruct 和 @PreDestroy 注解, 參考JSR-250 annotations 來了解詳情.
          完全支持 Spring lifecycle 回調. 如果一個bean實現了InitializingBean, DisposableBean, 或 Lifecycle, 它們各自的方法都會被容器調用.
          標準集合的 *Aware 接口,如 BeanFactoryAware, BeanNameAware, MessageSourceAware, ApplicationContextAware等等都完全支持.
          @Bean 注解支持指定任意初始化和銷毀回調方法, 非常類似于Spring XML bean元素中的
          init-methoddestroy-method屬性:
          public class Foo {    
          public void init() {         // initialization logic    
          }
          }
          public class Bar {    
          public void cleanup() {         // destruction logic    
          }
          }
          @Configuration
          public class AppConfig {    
          @Bean(initMethod = "init")    
          public Foo foo() {        
          return new Foo();    
          }    
          @Bean(destroyMethod = "cleanup")    
          public Bar bar() {        
          return new Bar();    
          }
          }
          默認情況下,使用Java配置的Bean都有一個 public close 或shutdown 方法以在銷毀回調時自動調用.
          如果你有一個public close 或shutdown 方法,并且不想在容器關閉時自動被調用,你可以簡單地在Bean定義中添加@Bean(destroyMethod="")來禁用默認(推斷)模式.
          你可能很想為那些通過JNDI獲取到的資源那樣做,因為它們的生命周期是在應用程序外被管理的.
          特別是,對于DataSource這樣的資源一定要保證這樣做,因為它們在Java EE應用程序服務器上可能會出現問題.
          @Bean(destroyMethod="")
          public DataSource dataSource() throws NamingException {    
          return (DataSource) jndiTemplate.lookup("MyDS");
          }
          此外,使用@Bean 方法,你通常會選擇使用編程來進行JNDI查找: 要么使用Spring的JndiTemplate/JndiLocatorDelegate幫助類,要么直接使用 JNDI InitialContext,但絕不是JndiObjectFactoryBean,因為它會強制你聲明FactoryBean 的返回類型,而不是實際目標類型, 這使得它很難在其它@Bean中跨引用調用.
          當然,在上面 Foo 的情況中, 其效果與直接在構造器中調用init()方法是一樣的:
          @Configuration
          public class AppConfig {    
          @Bean    
          public Foo foo() {        
          Foo foo =
          new Foo();        
          foo.init();        
          return foo;    
          }    
          // ...
          }
          指定bean范圍
          使用@Scope注解
          你可為@Bean 注解的bean指定一個scope.你可以使用 Bean Scopes任何標準的scope.
          默認scope是singleton,但你可以用@Scope 注解進行覆蓋:
          @Configuration
          public class MyConfiguration {    
          @Bean    
          @Scope("prototype")    
          public Encryptor encryptor() {        
          // ...    
          }
          }
          @Scope and scoped-proxy
          通過scoped proxiesSpring提供了一種便利的方式來與scoped依賴一起工作. 在XML配置中,創建代理的最簡單方式是使用<aop:scoped-proxy/> 元素. 在Java中,使用@Scope注解與proxyMode屬性來配置bean也可以達到同樣的效果. 默認是無代理( ScopedProxyMode.NO),但你可以指定為ScopedProxyMode.TARGET_CLASS 或ScopedProxyMode.INTERFACES.
          如果你看了XML參考文檔中的 scoped proxy 例子,再來看 @Bean ,它看起來就像下面這樣:
          // an HTTP Session-scoped bean exposed as a proxy
          @Bean
          @SessionScope
          public UserPreferences userPreferences() {    
          return new UserPreferences();
          }
          @Bean
          public Service userService() {    
          UserService service =
          new SimpleUserService();    
          // a reference to the proxied userPreferences bean    
          service.setUserPreferences(userPreferences());    
          return service;
          }
          自定義bean名稱
          默認情況下, 配置類使用@Bean 方法的名稱作為結果bean的名稱.這可以通過name 屬性來覆蓋.
          @Configuration
          public class AppConfig {    
          @Bean(name = "myFoo")    
          public Foo foo() {        
          return new Foo();    
          }
          }
          Bean 別名
          如在Section 7.3.1, “Naming beans”中講述的, 有時候期望單個bean上能指定多個名稱,即別名. @Bean 注解的name屬性可通過接受一個數組來達到這個目的.
          @Configuration
          public class AppConfig {    
          @Bean(name = { "dataSource", "subsystemA-dataSource", "subsystemB-dataSource" })    
          public DataSource dataSource() {        
          // instantiate, configure and return DataSource bean...    
          }
          }
          Bean 描述
          有時候,為bean提供更加詳盡的文本描述通常是有幫助的. 特別是bean出于監控目的暴露(可通過JMX)時,特別有用.
          To add a description to a @Bean the @Description annotation can be used:
          @Configuration
          public class AppConfig {    
          @Bean    
          @Description("Provides a basic example of a bean")    
          public Foo foo() {        
          return new Foo();    
          }
          }
          7.12.4 使用@Configuration注解
          @Configuration 是一個類級注解,用來表示bean定義的來源. @Configuration 類可通過public @Bean注解方法來聲明bean.在@Configuration中調用@Bean方法也可用來定義bean之間的依賴. 參考 Section 7.12.1, “Basic concepts: @Bean and @Configuration"一般介紹
          注入bean 依賴
          @Beans依賴另一個bean時, 表示依賴很簡單,就像一個bean的方法調用另一個:
          @Configuration
          public class AppConfig {    
          @Bean    
          public Foo foo() {        
          return new Foo(bar());    
          }    
          @Bean    
          public Bar bar() {        
          return new Bar();    
          }
          }
          在上面的例子中,通過構造器注入, foo bean 收到了bar的引用.
          聲明bean依賴的方法只能是@Bean 方法在@Configuration 類中聲明. 不能通過@Component類來聲明bean依賴關系.
          Lookup 方法注入
          如前所述,Lookup方法注入是一個高級的功能,你應該很少使用。它在一個singleton范圍bean依賴于原型范圍的bean是有用的。使用java類型進行這種配置,提供了一種自然的方式實現這一模式。
          public abstract class CommandManager {    
          public Object process(Object commandState) {        
          // grab a new instance of the appropriate Command interface        
          Command command = createCommand();        
          // set the state on the (hopefully brand new) Command instance        
          command.setState(commandState);    
          return command.execute();    
          }    
          // okay... but where is the implementation of this method?    
          protected abstract Command createCommand();
          }
          使用Java配置支持 , 你可以創建CommandManager的子類,抽象createCommand()方法可通過查找新(prototype(原型))command 對象來覆蓋
          @Bean
          @Scope("prototype")
          public AsyncCommand asyncCommand() {    
          AsyncCommand command =
          new AsyncCommand();    
          // inject dependencies here as required    
          return command;
          }


          @Bean
          public CommandManager commandManager() {    
          // return new anonymous implementation of CommandManager with command() overridden    
          // to return a new prototype Command object    
          return new CommandManager() {        
          protected Command createCommand() {            
          return asyncCommand();        
          }    
          }
          }
          關于基于Java配置內部是如何工作的更多信息
          下面的例子展示了一個@Bean注解方法被調用了兩次:
          @Configuration
          public class AppConfig {    
          @Bean    
          public ClientService clientService1() {        
          ClientServiceImpl clientService =
          new ClientServiceImpl();        
          clientService.setClientDao(clientDao());        
          return clientService;    
          }    
          @Bean    
          public ClientService clientService2() {        
          ClientServiceImpl clientService =
          new ClientServiceImpl();        
          clientService.setClientDao(clientDao());        
          return clientService;    
          }    
          @Bean    
          public ClientDao clientDao() {        
          return new ClientDaoImpl();    
          }
          }
          注:4.2.5版本上沒發現這個問題
          clientDao() 在clientService1()和clientService2()中各調用了一次.因為這個方法會創建一個新ClientDaoImpl 的實例,并將其返回,你可能正希望有2個實例(一個服務一個).
          那肯定是有問題的:在Spring中,實例bean默認有一個singleton scope . 這個魔法來自: 所有@Configuration 類在啟動期間都會使用CGLIB來子類化. 在子類中,孩子方法在調用其父類方法創建新實例前,它會為所有緩存(scoped)bean首先檢查容器.注意在 Spring 3.2中,沒有必須在類路徑中添加CGLIB,因為CGLIB的類已被重新打包放到了org.springframework.cglib之下,并且直接包含在spring-core JAR中.
          根據bean的scope不同,行為也可能不同.在這里我們談論的是singletons.
          CGLIB在啟動時動態添加特性有個限制 dynamically adds features at startup-time:
          • Configuration 類不應該是final
          • 它們必須有一個無參構造器
          7.12.5 組合基于 Java的配置
          使用@Import注解
          非常像Spring xml文件中的<import/> 元素,這樣有助于模塊化配置,@Import 注解允許從其它配置類中加載@Bean 定義:
          @Configuration public class ConfigA {     
          @Bean    
          public A a() {        
          return new A();    
          }
          }
          @Configuration
          @Import(ConfigA.class)
          public class ConfigB {    
          @Bean    
          public B b() {        
          return new B();    
          }
          }
          現在,實例化上下文時,不需要同時指定ConfigA.class 和 ConfigB.class ,只需要提供ConfigB就可以了:
          public static void main(String[] args) {    
          ApplicationContext ctx =
          new AnnotationConfigApplicationContext(ConfigB.class);    
          // now both beans A and B will be available...    
          A a = ctx.getBean(A.
          class);    
          B b = ctx.getBean(B.
          class);
          }
          這種方法簡化了容器實例化,只需要處理一個類就可以了,在構造期間,不需要開發者記住大量數目的@Configuration 類.
          Injecting dependencies on imported @Bean definitions
          上述例子可以工作,但過于簡單化。在大多數實際情況下,bean將有一個跨配置類的依賴關系。使用XML時,這本身不是一個問題,因為不涉及到編譯器,通過一個簡單聲明ref =“somebean",就可以信任Spring在容器初始化就可以完成這些工作。
          當然,當使用@Configuration類時,java編譯器限制了配置模型,在引用其它bean時必須是有效的java語法。
          幸運的是,解決這個問題很簡單。正如我們已經討論過的,@Bean可以有一個任意數量的參數來描述的bean的依賴關系。讓我們考慮一個帶有@Configuration類的真實的場景,每個依賴的bean都聲明在其它地方:
          Configuration
          public class ServiceConfig {    
          @Bean    
          public TransferService transferService(AccountRepository accountRepository) {        
          return new TransferServiceImpl(accountRepository);    
          }
          }
          @Configuration
          public class RepositoryConfig {    
          @Bean    
          public AccountRepository accountRepository(DataSource dataSource) {       
           
          return new JdbcAccountRepository(dataSource);   
            }
          }
          @Configuration @Import({ServiceConfig.class, RepositoryConfig.class})
          public class SystemTestConfig {    
          @Bean    
          public DataSource dataSource() {        
          // return new DataSource   
            }
          }
          public static void main(String[] args) {    
          ApplicationContext ctx =
          new AnnotationConfigApplicationContext(SystemTestConfig.class);    
          // everything wires up across configuration classes...    
          TransferService transferService = ctx.getBean(TransferService.
          class);    
          transferService.transfer(100.00,
          "A123", "C456");
          }
          這是達到同樣結果的另一種方式.記住 @Configuration 類最終只是容器中的另一個bean: 這意味著,它可以像其它bean一樣利用@Autowired 和 @Value 注入!
          確保你注入的依賴關系是最簡單的一種。@Configuration 類在上下文初始化時很早就被處理了,并強制注入依賴的資源(這種方式可能會導致意外的早期初始化). 無論何時,只要有可能,就要借助以參數為基礎的注入,如上面的例子。
          同時,通過@Bean來定義BeanPostProcessor 和 BeanFactoryPostProcessor 時要特別小心.那些通常應該聲明為static @Bean 方法, 不觸發其包含配置類的實例化. 否則,@Autowired 和 @Value 將不能在配置類上工作,因為創建bean實例的過程過早了.
          @Configuration
          public class ServiceConfig {    
          @Autowired    
          private AccountRepository accountRepository;    
          @Bean    
          public TransferService transferService() {        
          return new TransferServiceImpl(accountRepository);    
          }
          }
          @Configuration public class RepositoryConfig {    
          private final DataSource dataSource;    
          @Autowired    
          public RepositoryConfig(DataSource dataSource) {        
          this.dataSource = dataSource;   
            }    
          @Bean    
          public AccountRepository accountRepository() {        
          return new JdbcAccountRepository(dataSource);    
          }
          }
          @Configuration @Import({ServiceConfig.class, RepositoryConfig.class})
          public class SystemTestConfig {    
          @Bean    
          public DataSource dataSource() {        
          // return new DataSource    
          }
          }
          public static void main(String[] args) {    
          ApplicationContext ctx =
          new AnnotationConfigApplicationContext(SystemTestConfig.class);    
          // everything wires up across configuration classes...    
          TransferService transferService = ctx.getBean(TransferService.
          class);    
          transferService.transfer(100.00,
          "A123", "C456");
          }
          @Configuration 類中的構造器注入只在Spring Framework 4.3中支持. 同時也要注意到如果目標bean只定義了一個構造器,也沒有必要指定 @Autowired ;在上面的例子中, @Autowired 不必出現在RepositoryConfig 構造器上.
          在上述場景中,@Autowired 工作得很好并提供了預期的模塊化,但要明確確定自動裝配bean定義聲明的位置依然有些模糊.例如,當開發者查看ServiceConfig時, 你如何明確的知道@Autowired AccountRepository bean在哪里聲明的呢?在代碼中雖是不明確的,但這可能正好.
          記得Spring Tool Suite 提供了可以渲染組件是如何有線連接起來的圖形- 這可能正是你需要的.
          同時,你的Java IDE 也能容易地找到所有聲明和AccountRepository 類型的使用, 并且可以快速向你展示@Bean 方法返回類型的地址.
          如果這種模糊性是不可接受的,你希望能在IDE中直接從一個@Configuration 導航到另一個,那么可考慮自動裝裝配置類自身:
          @Configuration
          public class ServiceConfig {    
          @Autowired    
          private RepositoryConfig repositoryConfig;    
          @Bean    
          public TransferService transferService() {        
          // navigate 'through' the config class to the @Bean method!        
          return new TransferServiceImpl(repositoryConfig.accountRepository());   
            }
          }
          在上面的場景中,可以非常明確地知道AccountRepository 在什么位置定義的. 但現在, ServiceConfig 緊密耦合了RepositoryConfig,這是一種權衡. 通過使用基于接口的或基于抽象類的@Configuration類,這種緊密耦合可以有所緩解。考慮下面的代碼:
          @Configuration
          public class ServiceConfig {    
          @Autowired    
          private RepositoryConfig repositoryConfig;    
          @Bean    
          public TransferService transferService() {        
          return new TransferServiceImpl(repositoryConfig.accountRepository());    
          }
          }
          @Configuration public interface RepositoryConfig {    
          @Bean    
          AccountRepository accountRepository();
          }
          @Configuration
          public class DefaultRepositoryConfig implements RepositoryConfig {    
          @Bean    
          public AccountRepository accountRepository() {        
          return new JdbcAccountRepository(...);    
          }
          }
          @Configuration @Import({ServiceConfig.class, DefaultRepositoryConfig.class}) 
          // import the concrete config!
          public class SystemTestConfig {    
          @Bean    
          public DataSource dataSource() {        
          // return DataSource    
          }
          }
          public static void main(String[] args) {    
          ApplicationContext ctx =
          new AnnotationConfigApplicationContext(SystemTestConfig.class);    
          TransferService transferService = ctx.getBean(TransferService.
          class);    
          transferService.transfer(100.00,
          "A123", "C456");
          }
          現在ServiceConfig相對于具體的DefaultRepositoryConfig來說是松散耦合的,并且內置IDE工具仍然是有用的: 對于開發者來說,很容易看到RepositoryConfig實現的類型層次結構.通過這種方式, 導航@Configuration 類及其依賴與平常導航基于接口的代碼沒有什么不同.
          有條件地包含@Configuration 類或 @Bean 方法
          基于某些系統狀態有條件地啟用或禁用某個@Configuration類甚至單個@Bean方法,這通常是有用的. 這方面一個常見的例子是當某個特定profile在Spring環境中啟用了后,使用@Profile 注解來激活beans (參考Section 7.13.1, “Bean definition profiles”來了解細節).
          @Profile 注解實際上是使用更加靈活的@Conditional注解來實現的. @Conditional注解表示特定的org.springframework.context.annotation.Condition實現(在@Bean注冊應該先進行咨詢).
          Condition 接口的實現只是簡單的提供了一個返回true或false的matches(…​)方法.
          下面是@Profile的實際Condition實現:
          @Override
          public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {    
          if (context.getEnvironment() != null) {        
          // Read the @Profile annotation attributes        
          MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.
          class.getName());        
          if (attrs != null) {            
          for (Object value : attrs.get("value")) {                
          if (context.getEnvironment().acceptsProfiles(((String[]) value))) {                    
          return true;                
          }            
          }            
          return false;        
          }    
          }    
          return true;
          }
          參考@Conditional javadocs 來了解更多詳情l.
          結合Java和XML配置
          Spring@Configuration 類支持的目的不是100%完全代替Spring XML. 一些如Spring XML命名空間的設施仍然是容器中配置的理想方式. 在這種情況下,XML依然是便利的或必須的,你可以選擇:
          要么使用ClassPathXmlApplicationContext以XML為中心的方式來實例化容器,要么以Java為中心使用AnnotationConfigApplicationContext, @ImportResource注解導入必要XML的方式來實例化容器.
          "以XML為中心的”@Configuration的使用
          從XML,并以包含@Configuration類的特設方式來啟動Spring容器是更可取的方案.舉例來說,在一個大量使用Spring XML的現有代碼庫中,它可以根據需要來創建@Configuration類,并從現有XML中包含它們.
          下面你會發現在這種“XML為中心”的情況下使用@Configuration類的操作
          記住@Configuration類最終只是容器中的bean定義.在這個例子中,我們創建了名為AppConfig的@Configuration 類,并以<bean/>定義將其包含在了system-test-config.xml文件中.
          由于開啟了<context:annotation-config/> ,容器會識別@Configuration 注解并會適當地處理AppConfig中聲明的@Bean方法.
          @Configuration
          public class AppConfig {    
          @Autowired    
          private DataSource dataSource;    
          @Bean    
          public AccountRepository accountRepository() {        
          return new JdbcAccountRepository(dataSource);    
          }    
          @Bean    
          public TransferService transferService() {        
          return new TransferService(accountRepository());    
          }
          }
          system-test-config.xml:
          <beans>    
          <!-- enable processing of annotations such as @Autowired and @Configuration -->    
          <context:annotation-config/>    
          <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>    
          <bean class="com.acme.AppConfig"/>    
          <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">        
          <property name="url" value="${jdbc.url}"/>        
          <property name="username" value="${jdbc.username}"/>        
          <property name="password" value="${jdbc.password}"/>    
          </bean>
          </beans>
          jdbc.properties:
          jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
          jdbc.username=sa
          jdbc.password=
          public static void main(String[] args) {    
          ApplicationContext ctx =
          new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml");    
          TransferService transferService = ctx.getBean(TransferService.
          class);    
          // ...
          }
          在上面的system-test-config.xml文件中, AppConfig <bean/> 并沒有聲明id元素.然而這樣做是可以接受的,因為沒有其它bean會引用它, 并且顯示式通過名稱來獲取它也是不太可能發生的.
          DataSource bean也有類似的原因 -它只根據類型來自動裝配,因此明確的bean id 不是嚴格需要的.
          因為 @Configuration是@Component的元注解, @Configuration注解類將自動參與組件掃描.
          與上面場景相同,我們可以重新定義 system-test-config.xml 以利用組件掃描功能.
          注意,在這種情況中,我們不需要明確地聲明<context:annotation-config/>, 因為<context:component-scan/> 開啟了同樣的功能.
          system-test-config.xml:
          <beans>    
          <!-- picks up and registers AppConfig as a bean definition -->    
          <context:component-scan base-package="com.acme"/>    
          <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>    
          <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">        
          <property name="url" value="${jdbc.url}"/>        
          <property name="username" value="${jdbc.username}"/>        
          <property name="password" value="${jdbc.password}"/>    
          </bean>
          </beans>
          @Configuration class-centric use of XML with @ImportResource
          在@Configuration類的應用程序中,其主要機制是用于配置容器, 使用一些XML它仍然可能是必須的.在這些場景中, 只要簡單地使用@ImportResource 并定義需要的XML就可以了.這樣就實現了“java為中心”的方法來配置容器,并最低限度的保留了XML。
          @Configuration
          @ImportResource("classpath:/com/acme/properties-config.xml")
          public class AppConfig {    
          @Value("${jdbc.url}")    
          private String url;    
          @Value("${jdbc.username}")    
          private String username;    
          @Value("${jdbc.password}")    
          private String password;    
          @Bean    
          public DataSource dataSource() {        
          return new DriverManagerDataSource(url, username, password);    
          }
          }
          properties-config.xml
          <beans>    
          <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
          </beans>
          jdbc.properties
          jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
          jdbc.username=sa
          jdbc.password=
          public static void main(String[] args) {    
          ApplicationContext ctx =
          new AnnotationConfigApplicationContext(AppConfig.class);    
          TransferService transferService = ctx.getBean(TransferService.
          class);    
          // ...
          }
          posted on 2016-08-05 17:04 胡小軍 閱讀(2312) 評論(0)  編輯  收藏 所屬分類: Spring4

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 儋州市| 青岛市| 天峻县| 阳新县| 乌兰浩特市| 五家渠市| 土默特右旗| 临安市| 扎囊县| 梅州市| 满洲里市| 波密县| 布尔津县| 霍州市| 新化县| 乌鲁木齐县| 德兴市| 巴塘县| 甘德县| 康定县| 旺苍县| 汉寿县| 蕲春县| 邹平县| 林口县| 广宗县| 泸西县| 礼泉县| 砀山县| 武清区| 铜梁县| 江都市| 盖州市| 桑日县| 乌兰浩特市| 若尔盖县| 永修县| 印江| 包头市| 仙游县| 微山县|