總結
- 使用
WebApplicationInitializer
取代web.xml
- 使用
@Configuration
來載入Spring設定
瞭解web.xml中spring的用途
web.xml範例
<?xml version="1.0" encoding="utf-8" ?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!-- Spring context loader -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 共用的spring config, 如dataSource, DAO , Service -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:/applicationContext.xml</param-value>
</context-param>
<!-- Spring Request Dispatcher -->
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
可以看到web.xml裡主要做了三件事
- 載入ContextLoaderListener,用來完成下面兩件事
- 指出共用的spring component設定檔以便載入
- 指出Spring webmvc用的設定檔,以便產生Dispatcher
所以後續要做的事,就是想辦法一步步用annotation完成這三件事,
從xml到annotaion
@Configuration及@Bean
其實不久前,我們才從使用Builder Pattern的純Java程式轉到使用xml設定,沒想到沒多久我們又要轉回到純Java程式來,不過這次多了Annotation,至少可以用比較共通的方式來表達我們的意圖。
先看個範例吧
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
<property name="poolName" value="baseCP" />
<property name="connectionTestQuery" value="SELECT 1" />
<property name="driverClassName" value="org.h2.Driver" />
<property name="jdbcUrl" value="jdbc:h2:tcp://localhost/~/test" />
<property name="username" value="sa" />
<property name="password" value="" />
<property name="maximumPoolSize" value="5" />
</bean>
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<constructor-arg ref="hikariConfig" />
</bean>
</beans>
這是個產生共用dataSource的spring xml設定檔,改用annotation就兩件事
- 用@Configuration加在Class上,表示這是一個專門做spring設定用的Class,來完成原有xml裡的設定。
- 用@Bean加在有回傳物件的Method上,來完成
裡所做的事
所以轉成java程式大致就像這樣
@Configuration
public class ApplicationConfig {
@Bean
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setPoolName("baseCP");
config.setConnectionInitSql("SELECT 1");
config.setDriverClassName("org.h2.Driver");
config.setJdbcUrl("jdbc:h2:tcp://localhost/~/test");
config.setUsername("sa");
config.setPassword("");
config.setMaximumPoolSize(5);
HikariDataSource dataSource = new HikariDataSource(config);
return dataSource.getDataSource();
}
}
移除web.xml
自Servlet 3.0後,WEB-INF目錄下可以不用加入web.xml,要做initialize的工作可用下列兩種方式
- 使用@WebServlet, @WebFilter, @WebListener來標示原有的servlet,filter,listener
- 實作javax.servlet.ServletContainerInitializer,來處理各項工作。
Spring用了第二種方式,在SpringServletContainerInitializer中實作了ServletContainerInitializer,再指定了HandlesTypes。
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
....
}
}
所以在Spring Web Application中,只要是WebApplicationInitializer的subclass都會被丟進來處理,所以切入點就很明顯了;只要我們從WebApplicationInitializer下手,大概就能完成使用annotation載入的工作。
可用的WebApplicationInitializer
直接參考Spring doc中的AbstractAnnotationConfigDispatcherServletInitializer,可以看到目前Spring提供的subclass
org.springframework.web.servlet.support
Class AbstractAnnotationConfigDispatcherServletInitializer
java.lang.Object
org.springframework.web.context.AbstractContextLoaderInitializer
org.springframework.web.servlet.support.AbstractDispatcherServletInitializer
org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer
All Implemented Interfaces:
WebApplicationInitializer
WebApplicationInitializer、AbstractContextLoaderInitializer、AbstractDispatcherServletInitializer及AbstractAnnotationConfigDispatcherServletInitializer,這四個class任一個都能完成載入的必要工作,請擇一即可,沒特別需求,不要併用啊…
使用WebApplicationInitializer範例
public class MyWebInitializer implements WebApplicationInitializer {
private static final Logger logger = LoggerFactory.getLogger(MyWebInitializer.class);
public void onStartup(ServletContext servletContext) throws ServletException {
logger.info("----------------------------------");
logger.info("do startup from MyWebInitializer");
logger.info("----------------------------------");
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
applicationContext.register(ApplicationConfig.class);
servletContext.addListener(new ContextLoaderListener(applicationContext));
AnnotationConfigWebApplicationContext dispatchContext = new AnnotationConfigWebApplicationContext();
dispatchContext.register(WebMvcConfig.class);
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("spring", new DispatcherServlet(dispatchContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
再複習一下web.xml要做的三件事
- 載入ContextLoaderListener,用來完成下面兩件事
- 指出共用的spring component設定檔以便載入
- 指出Spring webmvc用的設定檔,以便產生Dispatcher
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
applicationContext.register(ApplicationConfig.class);
就是載入共用的spring component
servletContext.addListener(new ContextLoaderListener(applicationContext));
使用ContextLoaderListener
AnnotationConfigWebApplicationContext dispatchContext = new AnnotationConfigWebApplicationContext();
dispatchContext.register(WebMvcConfig.class);
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("spring", new DispatcherServlet(dispatchContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
載入Spring webmvc用的設定檔,以便產生Dispatcher
使用AbstractAnnotationConfigDispatcherServletInitializer範例
public class MyWebConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
private static final Logger logger = LoggerFactory.getLogger(MyWebConfig.class);
public MyWebConfig() {
logger.info("----------------------------------");
logger.info("do startup from MyWebConfig");
logger.info("----------------------------------");
}
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{ApplicationConfig.class};
}
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{WebMvcConfig.class};
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
一樣是完成必要的三件事,只是一堆要自己new的操作就免了,只要指出各類的configuration class即可。
上面這些只是基本,再來Spring MVC要用的adaptor、unit test及build的設定才是要多花時間的部份…
One thought to “在Spring Web Application中使用Annotation取代XML”