在對 Spring 進行顯示配置的時候,有兩種可選方案:Java 和 XML。Java 配置是更好的選擇,因爲它更強大,類型安全且對重構友好。因爲它就是 Java 代碼,就像程序中的其他 Java 代碼一樣。
同時,JavaConfig與其他的Java代碼又有所區別,儘管它與其他的組件一樣都使用相同的語言進行表述,但JavaConfig是 配置代碼。這意味着它不應該包含任何業務邏輯,JavaConfig也不應該侵入到業務邏輯代碼 之中。
接下來,讓我們看一下如何通過JavaConfig顯式配置Spring
基本概念:@Configuration 和 @Bean
@Configuration 被用於類上,指明這是一個配置類,該類應該包含在Spring應用上下文中如何創建bean的細節。@Bean 用於方法上,這個方法返回一個被裝配的對象,方法體中包含了最終產生bean實例的邏輯。
看一個例子:
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
上面的 AppConfig 類將和下面的 XML 配置有一樣的效果
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>
默認情況下,bean 的 ID 與帶有 @Bean 註解的方法名是一樣的。在本例中,bean的名字將會是 myService。如果你想爲其設置成一個不同的名字的話,那麼可以重命名該方法,也可以通過 name 屬性指定一個不同的名字:
@Bean(name = "hisService")
public MyService myService() {
return new MyServiceImpl();
}
開啓自動裝配也是很簡單的
@Configuration
@ComponentScan(basePackages = "com.acme")
public class AppConfig {
...
}
使用 @ComponentScan 註解即可,它等同於如下 XML 配置
<beans>
<context:component-scan base-package="com.acme"/>
</beans>
藉助 JavaConfig 實現注入
我們前面所聲明的 myService bean 是非常簡單的,它自身沒有其他的依賴。但現在,我 們需要聲明 MyApp bean,它依賴於 myService 。在 JavaConfig 中,要如何將它們裝配在一起呢?
在 JavaConfig 中裝配 bean 的最簡單方式就是引用創建 bean 的方法。例如,下面就是一種聲明 myApp 的可行方案:
@Bean
public MyApp myApp() {
return new MyApp(myService());
}
可以看到,通過調用方法來引用 bean 的方式有點令人困惑。其實還有一種理解起來更爲簡單的方式:
@Bean
public MyApp myApp(MyService myService) {
return new MyApp(myService);
}
在這裏,myApp() 方法請求一個 MyService 作爲參數。當 Spring 調用myApp() 創建 MyApp bean 的時候,它會自動裝配一個 MyService 到配置方法之中。然後,方法體就可以按照合適的方式來使用它。
通過這種方式引用其他的 bean 通常是最佳的選擇,因爲它不會要求將MyService 聲明到 同一個配置類之中。在這裏甚至沒有要求MyService 必須要在 JavaConfig 中聲明,實際上 它可以通過組件掃描功能自動發現或者通過 XML 來進行配置。
使用 JavaConfig 配置 Java Web
配置DispatcherServlet
DispatcherServlet 是 Spring MVC的核心。在這裏請求會第一次接觸到框架,它要負責將請求路由到其他的組件之中。
按照傳統的方式,像 DispatcherServlet 這樣的 Servlet 會配置在 web.xml 文件中,這個文件會放到應用的 WAR 包裏面。當然,這是配置 DispatcherServlet 的方法之一。但是,藉助於 Servlet 3 規範和 Spring 3.1的功能增強,這種方式已經不是唯一的方案了。
我們會使用 Java 將 DispatcherServlet 配置在 Servlet 容器中,而不會再使用 web.xml 文 件。如下的程序清單展示了所需的Java類。
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] {AppConfig.class};
}
//指定配置類
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] {WebConfig.class};
}
//將 DispatcherServlet 映射到 "/"
@Override
protected String[] getServletMappings() {
return new String[] {"/"};
}
}
要理解上面的程序清單是如何工作的,我們可能只需要知道擴 展AbstractAnnotationConfigDispatcherServletInitializer的任意類都會自 動地配置DispatcherServlet和Spring應用上下文,Spring的應用上下文會位於應用程序 的Servlet上下文之中。
儘管它的名字很長,但是 AbstractAnnotationConfigDispatcherServletInitializer 使用起來很簡 便。在程序清單中,WebAppInitializer 重寫了三個方法。
第一個方法是 getServletMappings(),它會將一個或多個路徑映射 到DispatcherServlet上。在本例中,它映射的是“/”,這表示它會是應用的默認 Servlet。它會處理進入應用的所有請求。
爲了理解其他的兩個方法,我們首先要理解DispatcherServlet和一個Servlet監聽器(也 就是ContextLoaderListener)的關係。
當 DispatcherServlet 啓動的時候,它會創建 Spring 應用上下文,並加載配置文件或配置 類中所聲明的bean。在程序清單的 getServletConfigClasses() 方法中,我們要求 DispatcherServlet 加載應用上下文時,使用定義在 WebConfig 配置類(使用Java配置)中的 bean。
但是在Spring Web應用中,通常還會有另外一個應用上下文。另外的這個應用上下文是 由ContextLoaderListener創建的。
getServletConfigClasses() 方法返回的帶有 @Configuration 註解的類將會用來定義 DispatcherServlet 應用上下文中的 bean。getRootConfigClasses() 方法返回的帶有 @Configuration 註解的類將會用來配置 ContextLoaderListener 創建的應用上下文中的bean。
在本例中,根配置定義在 AppConfig 中,DispatcherServlet 的配置聲明在 WebConfig 中。
啓用Spring MVC
我們有多種方式來配置 DispatcherServlet,與之類似,啓用Spring MVC組件的方法也不僅一種。以前,Spring 是使用 XML 進行配置的,你可以使用 <mvc:annotationdriven>
啓用註解驅動的Spring MVC。
我們所能創建的最簡單的 Java Spring MVC 配置就是一個帶有 @EnableWebMvc 註解的類
@Configuration
@EnableWebMvc
public class WebConfig {
}
這可以運行起來,它的確能夠啓用Spring MVC,但還有不少問題要解決:
- 沒有配置視圖解析器。如果這樣的話,Spring 默認會使用 BeanNameViewResolver,這個視圖解析器會查找 ID 與視圖名稱匹配的 bean,並且查找的 bean 要實現 View 接口,它以這樣的方式來解析視圖。
- 沒有啓用組件掃描。這樣的結果就是,Spring只能找到顯式聲明在配置類中的控制器。
- 這樣配置的話,DispatcherServlet 會映射爲應用的默認 Servlet,所以它會處理所有的請求,包括對靜態資源的請求,如圖片和樣式表(在大多數情況下,這可能並不是你想要的效果)。
因此,我們需要在 WebConfig 這個最小的 Spring MVC 配置上再加一些內容,從而讓它變得真正有用。如下程序清單中的 WebConfig 解決了上面所述的問題。
@Configuration
@EnableWebMvc //啓用 Spring MVC
@ComponentScan("com.web") //啓用組件掃描
public class WebConfig extends WebMvcConfigurerAdapter {
//配置視圖解析器
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setExposeContextBeansAsAttributes(true);
return resolver;
}
//配置靜態資源處理
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}