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 
Java代碼    收藏代碼
  1. @WebServlet(urlPatterns="/hello")  
  2. public class HelloServlet extends HttpServlet {}  

servlet3.0增長了@WebServlet, @WebFilter, @WebListener等註解,servlet容器會在classpath掃描並註冊全部的標註好的servlet, filter和listener。這種方法只針對你能訪問源代碼的狀況,對於像spring_mvc用到的DispatcherServlet,沒法在源碼上加annotation,能夠用第二種方法來實現bootstrap 
(二)ServletContainerInitializer 
這是servlet3的一個接口,咱們來看看spring-web提供的實現 
Java代碼    收藏代碼
  1. @HandlesTypes(WebApplicationInitializer.class)  
  2. public class SpringServletContainerInitializer implements ServletContainerInitializer {  
  3.     public void onStartup(Set<Class<?>> webAppInitializerClasses,  
  4.                           ServletContext servletContext) throws ServletException {  
  5.         //implemention omitted  
  6.     }  
  7.   
  8. }  

@HandlesTypes也是servlet3中的註解,這裏它處理的是WebApplicationInitializer,也就是說servlet容器會掃描classpath,將全部實現了WebApplicationInitializer接口的類傳給onStartup方法中的webAppInitializerClasses,並調用onStartup方法來註冊servlet。具體的註冊代碼能夠這樣寫: 
Java代碼    收藏代碼
  1. public class WebInit implements WebApplicationInitializer {   
  2.     @Override  
  3.     public void onStartup(ServletContext sc) throws ServletException {  
  4.         sc.addFilter("hibernateFilter", OpenSessionInViewFilter.class).addMappingForUrlPatterns(nullfalse"/*");  
  5.         // Create the 'root' Spring application context  
  6.         AnnotationConfigWebApplicationContext root = new AnnotationConfigWebApplicationContext();  
  7.         root.scan("septem.config.app");  
  8.         // Manages the lifecycle of the root application context  
  9.         sc.addListener(new ContextLoaderListener(root));  
  10.         AnnotationConfigWebApplicationContext webContext = new AnnotationConfigWebApplicationContext();  
  11.         webContext.setConfigLocation("septem.config.web");  
  12.         ServletRegistration.Dynamic appServlet = sc.addServlet("appServlet"new DispatcherServlet(webContext));  
  13.         appServlet.setLoadOnStartup(1);  
  14.         appServlet.addMapping("/");  
  15.     }  
  16. }  

以上的代碼分別調用了sc.addFilter, sc.addListener, sc.addServlet來註冊filter, listener和servlet. 
用以上的方法就能將WEB-INF/web.xml刪除了.spring3.1.M2開始增長了一系列annotation來實現聲明性事務及簡化spring_mvc配置。WebInit中註冊的DispatcherServlet所對應的配置在septem.config.web包裏面: 
Java代碼    收藏代碼
  1. @Configuration  
  2. @ComponentScan(basePackages="septem.controller")  
  3. @EnableWebMvc  
  4. public class WebConfig {  
  5. }  

一行@EnableWebMvc就導入了spring_mvc須要的諸多bean,再配合@ComponentScan掃描septem.controller包裏面全部的@Controller,基本的mvc配置就完成了。 
聲明性事務也是相似,經過spring root application context掃描包septem.config.app: 
Java代碼    收藏代碼
  1. @Configuration  
  2. @EnableTransactionManagement  
  3. public class DataConfig {  
  4.       
  5.     @Bean  public AnnotationSessionFactoryBean sessionFactory() {    
  6.         AnnotationSessionFactoryBean sessionFactoryBean = new AnnotationSessionFactoryBean();    
  7.         sessionFactoryBean.setDataSource(dataSource());    
  8.         sessionFactoryBean.setNamingStrategy(new ImprovedNamingStrategy());  
  9.         sessionFactoryBean.setPackagesToScan("septem.model");  
  10.         sessionFactoryBean.setHibernateProperties(hProps());  
  11.         return sessionFactoryBean;    
  12.     }   
  13.       
  14.     private DataSource dataSource() {  
  15.         BasicDataSource source = new BasicDataSource();  
  16.         source.setDriverClassName("org.hsqldb.jdbcDriver");  
  17.         source.setUrl("jdbc:hsqldb:mem:s3demo_db");  
  18.         source.setUsername("sa");  
  19.         source.setPassword("");  
  20.         return source;  
  21.     }  
  22.       
  23.     @Bean  public HibernateTransactionManager transactionManager() {    
  24.         HibernateTransactionManager hibernateTransactionManager = new HibernateTransactionManager();    
  25.         hibernateTransactionManager.setSessionFactory(sessionFactory().getObject());    
  26.         return hibernateTransactionManager;    
  27.     }    
  28.   
  29.     private Properties hProps() {  
  30.         Properties p = new Properties();  
  31.         p.put("hibernate.dialect""org.hibernate.dialect.HSQLDialect");  
  32.         p.put("hibernate.cache.use_second_level_cache""true");  
  33.         p.put("hibernate.cache.use_query_cache""true");  
  34.         p.put("hibernate.cache.provider_class",  
  35.                 "org.hibernate.cache.EhCacheProvider");  
  36.         p.put("hibernate.cache.provider_configuration_file_resource_path",  
  37.                 "ehcache.xml");  
  38.         p.put("hibernate.show_sql""true");  
  39.         p.put("hibernate.hbm2ddl.auto""update");  
  40.         p.put("hibernate.generate_statistics""true");  
  41.         p.put("hibernate.cache.use_structured_entries""true");  
  42.         return p;  
  43.     }  
  44.   
  45. }  

DataConfig定義了全部與數據庫和hibernate相關的bean,經過@EnableTransactionManagement實現聲明性事務。 
service是如何註冊的呢? 
Java代碼    收藏代碼
  1. @Configuration  
  2. @ComponentScan(basePackages="septem.service")  
  3. public class AppConfig {  
  4. }  

經過@ComponentScan掃描包septem.service裏定義的全部service,一個簡單service實現以下: 
Java代碼    收藏代碼
  1. @Service  @Transactional  
  2. public class GreetingService {  
  3.       
  4.     @Autowired  
  5.     private SessionFactory sessionFactory;  
  6.       
  7.     @Transactional(readOnly=true)  
  8.     public String greeting() {  
  9.         return "spring without xml works!";  
  10.     }  
  11.       
  12.     @Transactional(readOnly=true)  
  13.     public Book getBook(Long id) {  
  14.         return (Book) getSession().get(Book.class, id);  
  15.     }  
  16.       
  17.     @Transactional(readOnly=true)  
  18.     public Author getAuthor(Long id){  
  19.         return (Author) getSession().get(Author.class, id);  
  20.     }  
  21.       
  22.     public Book newBook() {  
  23.         Book book = new Book();  
  24.         book.setTitle("java");  
  25.         getSession().save(book);  
  26.         return book;  
  27.     }  
  28.       
  29.     public Author newAuthor() {  
  30.         Book book = newBook();  
  31.         Author author = new Author();  
  32.         author.setName("septem");  
  33.         author.addBook(book);  
  34.         getSession().save(author);  
  35.         return author;  
  36.     }  
  37.       
  38.     private Session getSession() {  
  39.         return sessionFactory.getCurrentSession();  
  40.     }  
  41. }  

這樣整個項目中就沒有XML文件了。在寫這些代碼的過程當中也碰到很多問題,紀錄以下: 
(一)項目沒有web.xml,maven的war插件要加上failOnMissingWebXml=false 
Xml代碼    收藏代碼
  1. <plugin>  
  2. <groupId>org.apache.maven.plugins</groupId>  
  3. <artifactId>maven-war-plugin</artifactId>  
  4. <version>2.1.1</version>  
  5. <configuration>  
  6.     <failOnMissingWebXml>false</failOnMissingWebXml>  
  7. </configuration>  
  8. </plugin>  

(二) tomcat-embeded7.0.16還有點小BUG,不能把DispatcherServlet映射爲"/",因此代碼裏把它映射爲"/s3/" 
Java代碼    收藏代碼
  1. appServlet.addMapping("/s3/");  

(三) 若是要使用spring提供的OpenSessionInViewFilter,在定義Hibernate SessionFactory的時候,不能直接new SessionFactory出來,即如下代碼是不能實現聲明性事務的: 
Java代碼    收藏代碼
  1. @Bean  public SessionFactory sessionFactory() {  
  2.     org.hibernate.cfg.Configuration config = new org.hibernate.cfg.Configuration();  
  3.     config.setProperties(hProps());  
  4.     config.addAnnotatedClass(Book.class);  
  5.     return config.buildSessionFactory();  
  6. }  

必須使用spring提供的FactoryBean: 
Java代碼    收藏代碼
  1. @Bean  public AnnotationSessionFactoryBean sessionFactory() {    
  2.     AnnotationSessionFactoryBean sessionFactoryBean = new AnnotationSessionFactoryBean();    
  3.     sessionFactoryBean.setDataSource(dataSource());    
  4.     sessionFactoryBean.setNamingStrategy(new ImprovedNamingStrategy());  
  5.     sessionFactoryBean.setPackagesToScan("septem.model");  
  6.     sessionFactoryBean.setHibernateProperties(hProps());  
  7.     return sessionFactoryBean;    
  8. }  


後記:在spring3.1以servlet3中annotation已是一等公民了,能夠實現任何原先只能在xml文件中配置的功能,並具備簡潔,靜態檢查及重構友好等優勢。整體上來說spring提供的「魔法」仍是太多了,尤爲是跟hibernate,事務,open session in view等機制結合在一塊兒的時候,簡潔代碼的背後隱藏着太多的依賴關係,若是程序出了問題,排除這些魔法,一層一層地還原程序的原本面目,將是一件很須要耐心的事情 

代碼提交到google code了,有興趣的同窗能夠checkout,運行集成測試類HomeControllerIT 
Shell代碼    收藏代碼
  1. svn checkout http://spring-no-xml.googlecode.com/svn/trunk/ spring_no_xml  
  2. cd spring_no_xml  
  3. mvn integration-test  
相關文章
相關標籤/搜索