spring3.0使用annotation徹底代替XML
好久以前寫過兩篇博客:
spring3.0使用annotation徹底代替XML
spring3.0使用annotation徹底代替XML(續)
用java config來代替XML,當時還遺留下一些問題:
<tx:annotation-driven />聲明性事務等配置沒法用簡單代碼來實現
web.xml沒法去掉
隨着servlet 3.0規範以及spring3.1.M2的發佈,如今以上的問題也解決了。
先來講說web.xml,有兩種方法來替代
(一)annotation
@WebServlet (urlPatterns="/hello" )
public class HelloServlet extends HttpServlet {}
servlet3.0增長了@WebServlet, @WebFilter, @WebListener等註解,servlet容器會在classpath掃描並註冊全部的標註好的servlet, filter和listener。這種方法只針對你能訪問源代碼的狀況,對於像spring_mvc用到的DispatcherServlet,沒法在源碼上加annotation,能夠用第二種方法來實現bootstrap
(二)ServletContainerInitializer
這是servlet3的一個接口,咱們來看看spring-web提供的實現
@HandlesTypes (WebApplicationInitializer.class )
public class SpringServletContainerInitializer implements ServletContainerInitializer {
public void onStartup(Set<Class<?>> webAppInitializerClasses,
ServletContext servletContext) throws ServletException {
//implemention omitted
}
}
@HandlesTypes也是servlet3中的註解,這裏它處理的是WebApplicationInitializer,也就是說servlet容器會掃描classpath,將全部實現了WebApplicationInitializer接口的類傳給onStartup方法中的webAppInitializerClasses,並調用onStartup方法來註冊servlet。具體的註冊代碼能夠這樣寫:
public class WebInit implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext sc) throws ServletException {
sc.addFilter("hibernateFilter" , OpenSessionInViewFilter.class ).addMappingForUrlPatterns(null , false , "/*" );
// Create the 'root' Spring application context
AnnotationConfigWebApplicationContext root = new AnnotationConfigWebApplicationContext();
root.scan("septem.config.app" );
// Manages the lifecycle of the root application context
sc.addListener(new ContextLoaderListener(root));
AnnotationConfigWebApplicationContext webContext = new AnnotationConfigWebApplicationContext();
webContext.setConfigLocation("septem.config.web" );
ServletRegistration.Dynamic appServlet = sc.addServlet("appServlet" , new DispatcherServlet(webContext));
appServlet.setLoadOnStartup(1 );
appServlet.addMapping("/" );
}
}
以上的代碼分別調用了sc.addFilter, sc.addListener, sc.addServlet來註冊filter, listener和servlet.
用以上的方法就能將WEB-INF/web.xml刪除了.spring3.1.M2開始增長了一系列annotation來實現聲明性事務及簡化spring_mvc配置。WebInit中註冊的DispatcherServlet所對應的配置在septem.config.web包裏面:
@Configuration
@ComponentScan (basePackages="septem.controller" )
@EnableWebMvc
public class WebConfig {
}
一行@EnableWebMvc就導入了spring_mvc須要的諸多bean,再配合@ComponentScan掃描septem.controller包裏面全部的@Controller,基本的mvc配置就完成了。
聲明性事務也是相似,經過spring root application context掃描包septem.config.app:
@Configuration
@EnableTransactionManagement
public class DataConfig {
@Bean public AnnotationSessionFactoryBean sessionFactory() {
AnnotationSessionFactoryBean sessionFactoryBean = new AnnotationSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource());
sessionFactoryBean.setNamingStrategy(new ImprovedNamingStrategy());
sessionFactoryBean.setPackagesToScan("septem.model" );
sessionFactoryBean.setHibernateProperties(hProps());
return sessionFactoryBean;
}
private DataSource dataSource() {
BasicDataSource source = new BasicDataSource();
source.setDriverClassName("org.hsqldb.jdbcDriver" );
source.setUrl("jdbc:hsqldb:mem:s3demo_db" );
source.setUsername("sa" );
source.setPassword("" );
return source;
}
@Bean public HibernateTransactionManager transactionManager() {
HibernateTransactionManager hibernateTransactionManager = new HibernateTransactionManager();
hibernateTransactionManager.setSessionFactory(sessionFactory().getObject());
return hibernateTransactionManager;
}
private Properties hProps() {
Properties p = new Properties();
p.put("hibernate.dialect" , "org.hibernate.dialect.HSQLDialect" );
p.put("hibernate.cache.use_second_level_cache" , "true" );
p.put("hibernate.cache.use_query_cache" , "true" );
p.put("hibernate.cache.provider_class" ,
"org.hibernate.cache.EhCacheProvider" );
p.put("hibernate.cache.provider_configuration_file_resource_path" ,
"ehcache.xml" );
p.put("hibernate.show_sql" , "true" );
p.put("hibernate.hbm2ddl.auto" , "update" );
p.put("hibernate.generate_statistics" , "true" );
p.put("hibernate.cache.use_structured_entries" , "true" );
return p;
}
}
DataConfig定義了全部與數據庫和hibernate相關的bean,經過@EnableTransactionManagement實現聲明性事務。
service是如何註冊的呢?
@Configuration
@ComponentScan (basePackages="septem.service" )
public class AppConfig {
}
經過@ComponentScan掃描包septem.service裏定義的全部service,一個簡單service實現以下:
@Service @Transactional
public class GreetingService {
@Autowired
private SessionFactory sessionFactory;
@Transactional (readOnly=true )
public String greeting() {
return "spring without xml works!" ;
}
@Transactional (readOnly=true )
public Book getBook(Long id) {
return (Book) getSession().get(Book.class , id);
}
@Transactional (readOnly=true )
public Author getAuthor(Long id){
return (Author) getSession().get(Author.class , id);
}
public Book newBook() {
Book book = new Book();
book.setTitle("java" );
getSession().save(book);
return book;
}
public Author newAuthor() {
Book book = newBook();
Author author = new Author();
author.setName("septem" );
author.addBook(book);
getSession().save(author);
return author;
}
private Session getSession() {
return sessionFactory.getCurrentSession();
}
}
這樣整個項目中就沒有XML文件了。在寫這些代碼的過程當中也碰到很多問題,紀錄以下:
(一)項目沒有web.xml,maven的war插件要加上failOnMissingWebXml=false
< plugin >
< groupId > org.apache.maven.plugins</ groupId >
< artifactId > maven-war-plugin</ artifactId >
< version > 2.1.1</ version >
< configuration >
< failOnMissingWebXml > false</ failOnMissingWebXml >
</ configuration >
</ plugin >
(二) tomcat-embeded7.0.16還有點小BUG,不能把DispatcherServlet映射爲"/",因此代碼裏把它映射爲"/s3/"
appServlet.addMapping("/s3/" );
(三) 若是要使用spring提供的OpenSessionInViewFilter,在定義Hibernate SessionFactory的時候,不能直接new SessionFactory出來,即如下代碼是不能實現聲明性事務的:
@Bean public SessionFactory sessionFactory() {
org.hibernate.cfg.Configuration config = new org.hibernate.cfg.Configuration();
config.setProperties(hProps());
config.addAnnotatedClass(Book.class );
return config.buildSessionFactory();
}
必須使用spring提供的FactoryBean:
@Bean public AnnotationSessionFactoryBean sessionFactory() {
AnnotationSessionFactoryBean sessionFactoryBean = new AnnotationSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource());
sessionFactoryBean.setNamingStrategy(new ImprovedNamingStrategy());
sessionFactoryBean.setPackagesToScan("septem.model" );
sessionFactoryBean.setHibernateProperties(hProps());
return sessionFactoryBean;
}
後記:在spring3.1以servlet3中annotation已是一等公民了,能夠實現任何原先只能在xml文件中配置的功能,並具備簡潔,靜態檢查及重構友好等優勢。整體上來說spring提供的「魔法」仍是太多了,尤爲是跟hibernate,事務,open session in view等機制結合在一塊兒的時候,簡潔代碼的背後隱藏着太多的依賴關係,若是程序出了問題,排除這些魔法,一層一層地還原程序的原本面目,將是一件很須要耐心的事情
代碼提交到google code了,有興趣的同窗能夠checkout,運行集成測試類HomeControllerIT
svn checkout http://spring-no-xml.googlecode.com/svn/trunk/ spring_no_xml
cd spring_no_xml
mvn integration-test
歡迎關注本站公眾號,獲取更多信息