目錄html
隨着Spring的流行,咱們經歷過基於XML-Based 的配置,隨着SpringBoot的流行,咱們逐漸使用基於註解的配置替換掉了基於XML-Based的配置,那麼你知道基於註解的配置的基礎組件都是什麼嗎?都包括哪些要素?那麼本節就來探討一下。注:本篇文章更多的是討論Spring基於註解的配置一覽,具體的技術可能沒有那麼深,請各位大佬見諒。java
探討主題:mysql
Spring中新的概念是支持@Bean註解 和 @Configuration 註解的類。@Bean 註解用來代表一個方法實例化,配置而且經過IOC容器初始化並管理一個新的對象。@Bean註解就等同於XML-Based中的<beans/>
標籤,而且扮演了相同的做用。你可使用基於註解的配置@Bean 和 @Component,然而他們都用在@Configuration配置類中。web
使用@Configuration 註解的主要做用是做爲bean定義的類,進一步來講,@Configuration註解的類容許經過調用同類中的其餘@Bean標註的方法來定義bean之間依賴關係。 以下所示:spring
新建一個maven項目(我通常都直接建立SpringBoot項目,比較省事),建立AppConfig
,MyService
,MyServiceImpl
類,代碼以下:sql
@Configuration public class AppConfig { @Bean public MyService myService(){ return new MyServiceImpl(); } } public interface MyService {} public class MyServiceImpl implements MyService {}
上述的依賴關係等同於XML-Based:數據庫
<beans> <bean id="myService",class="com.spring.annotation.service.impl.MyServiceImpl"/> </beans>
AnnotationConfigApplicationContext 基於註解的上下文是Spring3.0 新添加的註解,它是ApplicationContext
的一個具體實現,它能夠接收@Configuration
註解的類做爲輸入參數,還能接收使用JSR-330元註解的普通@Component類。數組
當提供了@Configuration 類做爲輸入參數時,@Configuration類就會註冊做爲bean的定義信息而且全部聲明@Bean的方法也都會做爲bean的定義信息。websocket
當提供@Component和JSR-330 聲明的類時,他們都會註冊做爲bean的定義信息,而且假設在必要時在這些類中使用諸如@Autowired或@Inject之類的註解session
在某些基於XML-Based的配置,咱們想獲取上下文容器使用ClassPathXmlApplicationContext
,如今你可以使用@Configuration 類來實例化AnnotationConfigApplicationContext。
在MyService
中添加一個printMessage()
方法,實現類實現對應的方法。新建測試類進行測試
public class ApplicationTests { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); MyService service = context.getBean(MyService.class); // printMessage() 輸出something... service.printMessage(); } }
如前所述,AnnotationConfigApplicationContext不只限於使用@Configuration類。 任何@Component或JSR-330帶註釋的類均可以做爲輸入提供給構造函數,以下例所示
public class ApplicationTests { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(MyServiceImpl.class,Dependency1.class,Dependency2.class); MyService myService = context.getBean(MyService.class); myService.printMessage(); } }
你能夠實例化AnnotationConfigApplicationContext
經過使用無參數的構造器而且使用register
方法進行註冊,它和AnnotationConfigApplicationContext
帶參數的構造器起到的效果相同。
public class ApplicationTests { 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); System.out.println(ctx.getBean(OtherConfig.class)); System.out.println(ctx.getBean(AdditionalConfig.class)); myService.printMessage(); } }
OtherConfig.class 和 AdditionalConfig.class 是使用@Component 標註的類。
爲了容許組件進行掃描,須要在@Configuration配置類添加@ComponentScan()
註解,改造以前的AdditionalConfig
類,以下:
@Configuration @ComponentScan(basePackages = "com.spring.annotation.config") public class AdditionalConfig {}
@ComponentScan指定了基礎掃描包位於com.spring.annotation.config下,全部位於該包範圍內的bean都會被註冊進來,交由Spring管理。它就等同於基於XML-Based的註解:
<beans> <context:component-scan base-package="com.spring.annotation.config/> </beans>
AnnotationConfigApplicationContext中的scan()方法以容許相同的組件掃描功能,如如下示例所示:
public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.scan("com.spring.annotation"); ctx.refresh(); MyService myService = ctx.getBean(MyService.class); }
爲何說@Configuration用法和@Component都可以標註配置類?由於@Configuration的元註解就是@Component。
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Configuration { String value() default ""; }
AnnotationConfigApplicationContext的一個WebApplicationContext的變化是使用AnnotationConfigWebApplicationContext
。配置Spring ContextLoaderListener的servlet監聽器,Spring MVC的DispatcherServlet等時,可使用此實現。如下web.xml代碼段配置典型的Spring MVC Web應用程序(請注意context-param和init-param的使用)
<web-app> <!-- 配置web上下文監聽器使用 AnnotationConfigWebApplicationContext 而不是默認的 XmlWebApplicationContext --> <context-param> <param-name>contextClass</param-name> <param-value> org.springframework.web.context.support.AnnotationConfigWebApplicationContext </param-value> </context-param> <!-- 配置位置必須包含一個或多個以逗號或空格分隔的徹底限定的@Configuration類。 也能夠爲組件掃描指定徹底 限定的包--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>com.spring.annotation.config.AdditionalConfig</param-value> </context-param> <!--使用ContextLoaderListener像往常同樣引導根應用程序上下文--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 定義一個SpringMVC 核心控制器 DispatcherServlet--> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 配置web上下文監聽器使用 AnnotationConfigWebApplicationContext 而不是默認的 XmlWebApplicationContext --> <init-param> <param-name>contextClass</param-name> <param-value> org.springframework.web.context.support.AnnotationConfigWebApplicationContext </param-value> </init-param> <!-- 配置位置必須包含一個或多個以逗號或空格分隔的徹底限定的@Configuration類。 也能夠爲組件掃描指定 徹底限定的包--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>com.spring.annotation.config.MvcConfig</param-value> </init-param> </servlet> <!-- 將/app/* 的全部請求映射到調度程序servlet --> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/app/*</url-pattern> </servlet-mapping> </web-app>
@Bean 註解是一個方法級別的註解,可以替換XML-Based中的
init-method
,
destroy-method
,
autowiring
。
與基礎概念中Bean的定義相同,讀者能夠參考基礎概念部分進行了解,咱們不在此再進行探討。
@Bean 註解能夠有任意數量的參數來構建其依賴項,例如
public class MyService { private final MyRepository myRepository; public MyService(MyRepository myRepository) { this.myRepository = myRepository; } public String generateSomeString() { return myRepository.findString() + "-from-MyService"; } } @Configuration class MyConfiguration { @Bean public MyService myService() { return new MyService(myRepository()); } @Bean public MyRepository myRepository() { return new MyRepository(); } } public class MyRepository { public String findString() { return "some-string"; } }
任何使用@Bean的註解都支持生命週期的回調,使用JSR-220提供的@PostConstruct
和@PreDestory
註解來實現。若是bean實現了InitializingBean
,DisposableBean
或者Lifecycle
接口,他們的方法會由IOC容器回調。一些以Aware的實現接口(像是BeanFactoryAware,BeanNameAware, MessageSourceAware, ApplicationContextAware等)也支持回調。
@Bean註解支持特定的初始化和銷燬方法,就像XML-Based中的init-method
和 destory-method
中的bean屬性,下面這個例子證明了這一點
AppConfig.java
@Configuration public class AppConfig { @Bean(initMethod = "init") public BeanOne beanOne(){ return new BeanOne(); } @Bean(destroyMethod = "cleanup") public BeanTwo beanTwo(){ return new BeanTwo(); } } class BeanOne { public void init(){} } class BeanTwo { public void cleanup(){} }
對於上面的例子,也能夠手動調用init()方法,與上面的initMethod 方法等效
@Bean public BeanOne beanOne(){ BeanOne beanOne = new BeanOne(); beanOne.init(); return beanOne; }
當你直接使用Java開發時,你可使用對象執行任何操做,而且沒必要老是依賴於容器生命週期。
Spring包括@Scope註解可以讓你指定Bean的做用範圍,Bean的Scope默認是單例的,也就是說@Bean標註的對象在IOC的容器中只有一個。你能夠重寫@Scope的做用範圍,下面的例子說明了這一點,修改OtherConfig以下
OtherConfig.java
@Configuration public class OtherConfig { @Bean @Scope("prototype") public Dependency1 dependency1(){ return new Dependency1(); } }
每次嘗試獲取dependency1這個對象的時候都會從新生成一個新的對象實例。下面是Scope的做用範圍和解釋:
Scope | Descriptionn |
---|---|
singleton | 默認單例的bean定義信息,對於每一個IOC容器來講都是單例對象 |
prototype | bean對象的定義爲任意數量的對象實例 |
request | bean對象的定義爲一次HTTP請求的生命週期,也就是說,每一個HTTP請求都有本身的bean實例,它是在單個bean定義的後面建立的。僅僅在web-aware的上下文中有效 |
session | bean對象的定義爲一次HTTP會話的生命週期。僅僅在web-aware的上下文中有效 |
application | bean對象的定義範圍在ServletContext生命週期內。僅僅在web-aware的上下文中有效 |
websocket | bean對象的定義爲WebSocket的生命週期內。僅僅在web-aware的上下文中有效 |
Spring提供了一種經過scoped proxies與scoped依賴一塊兒做用的方式。最簡單的在XML環境中建立代理的方式是經過<aop:scoped-proxy/>
標籤。使用@Scope
註解爲在Java中配置bean提供了與proxyMode屬性相同的功能。默認是不須要代理的(ScopedProxyMode.NO),可是你須要指定ScopedProxyMode.TARGET_CLASS
或者ScopedProxyMode.INTERFACES
。
默認的狀況下,配置類經過@Bean配置的默認名稱(方法名第一個字母小寫)進行註冊和使用,可是你能夠更換@Bean的name爲你想指定的名稱。修改AdditionalConfig 類
AdditionalConfig.java
@Configuration //@ComponentScan(basePackages = "com.spring.annotation.config") public class AdditionalConfig { @Bean(name = "default") public Dependency2 dependency2(){ return new Dependency2(); } }
有時候須要爲單例的bean提供多個名稱,也叫作Bean的別名。Bean註解的name屬性接收一個Array數組。下面這個例子證明了這一點:
OtherConfig.java
@Configuration public class OtherConfig { // @Bean // @Scope("prototype") // public Dependency1 dependency1(){ // return new Dependency1(); // } @Bean({"dataSource", "dataSourceA", "dataSourceB"}) public DataSource dataSource(){ return null; } }
有時,提供更詳細的bean描述信息會頗有幫助(可是開發不多使用到)。爲了增長一個對@Bean的描述,你須要使用到@Description註解
OtherConfig.java
@Configuration public class OtherConfig { // @Bean // @Scope("prototype") // public Dependency1 dependency1(){ // return new Dependency1(); // } // @Bean({"dataSource", "dataSourceA", "dataSourceB"}) // public DataSource dataSource(){ // return null; // } @Bean @Description("此方法的bean名稱爲dependency1") public Dependency1 dependency1(){ return new Dependency1(); } }
更多關於@Configuration 的詳細說明,請你參考https://mp.weixin.qq.com/s/FLJTsT2bAru-w7cF4CG8kQ
已經把@Configuration的註解說明的比較詳細了。
Spring基於註解的配置可以容許你自定義註解,同時可以下降配置的複雜性。
就像在Spring XML文件中使用
@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); A a = ctx.getBean(A.class); B b = ctx.getBean(B.class); }
這種方法簡化了容器實例化,由於只須要處理一個類,而不是要求你在構造期間記住可能大量的@Configuration類
選擇性的容許或者禁止@Configuration註解的類和@Bean註解的方法是頗有用的,基於一些任意系統狀態。一個常見的例子是隻有在Spring環境中啓用了特定的配置文件時才使用@Profile註釋激活bean。
@Profile註解也實現了更靈活的註解@Conditional,@Conditional 註解代表在註冊@Bean 以前應參考特定的Condition實現。
實現Condition接口就會提供一個matched方法返回true或者false
更多關於@Conditional 的示例,請參考
https://www.cnblogs.com/cxuanBlog/p/10960575.html
Spring @Configuration類可以100%替換XML配置,但一些工具(如XML命名空間)仍舊是配置容器的首選方法,在這種背景下,使用XML使很方便的並且使剛需了。你有兩個選擇:使用以XML配置實例化容器爲中心,例如:ClassPathXmlApplicationContext
導入XML或者實例化以Java配置爲中心的AnnotationConfigApplicationContext
並提供ImportResource
註解導入須要的XML配置。
請記住,@Configuration類存放的是容器中的bean定義信息,下面的例子中,咱們將會建立一個@Configuration類而且加載了外部xml配置。下面展現了一個普通的Java配置類
@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> <!--容許開啓 @Autowired 或者 @Configuration--> <context:annotation-config/> <!-- 讀取外部屬性文件 --> <!-- 更多關於屬性讀取的資料,參考 https://www.cnblogs.com/cxuanBlog/p/10927819.html --> <context:property-placeholder location="classpath:/com/spring/annotation/jdbc.properties"/> <bean class="com.spring.annotation.config.AppConfig"/> <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}" /> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> </beans>
引入jdbc.properties創建數據庫鏈接
jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/sys jdbc.username=root jdbc.password=123456
public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/spring/annotation/system-test-config.xml"); TransferService transferService = ctx.getBean(TransferService.class); // ... }
在
system-test-config.xml
中,AppConfig 對應的標籤沒有聲明id屬性,雖然這樣作是能夠接受的,可是沒有必要,由於沒有其餘bean引用它,而且不太可能經過名稱從容器中獲取它。一樣的,DataSource bean只是按類型自動裝配,所以不嚴格要求顯式的bean id。
由於@Configuration的原註解是@Component,因此@Configuration註解的類也能用於組件掃描,使用與前一個示例中描述的相同的方案,咱們能夠從新定義system-test-config.xml以利用組件掃描。 請注意,在這種狀況下,咱們不須要顯式聲明<context:annotation-config />
,由於<context:component-scan />
啓用相同的功能。
<beans> <context:component-scan base-package="com.spring.annotation"/> <context:property-placeholder location="classpath:/com/spring/annotation/jdbc.properties"/> <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.driverClassName}" /> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> </beans>
在基於Java註解的配置類中,仍然可使用少許的@ImportResource導入外部配置,最好的方式就是二者結合,下面展現了一下Java註解結合XML配置的示例
@Configuration @ImportResource("classpath:/com/spring/annotation/properties-config.xml") public class AppConfig { @Value("${jdbc.driverClassName}") private String driver; @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/spring/annotation/jdbc.properties"/> </beans>
jdbc.properties
jdbc.driverClassName=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/sys jdbc.username=root jdbc.password=123456
public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); TransferService transferService = ctx.getBean(TransferService.class); // ... }
相關閱讀:
PropertyPlaceholderConfigurer 基本用法
談談 ServletConfig 和 ServletContext
@Configuration所有配置一覽https://mp.weixin.qq.com/s/FLJTsT2bAru-w7cF4CG8kQ