zoukankan      html  css  js  c++  java
  • 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(null, false, "/*");  
    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等机制结合在一起的时候,简洁代码的背后隐藏着太多的依赖关系,如果程序出了问题,排除这些魔法,一层一层地还原程序的本来面目,将是一件很需要耐心的事情

  • 相关阅读:
    linux
    linux
    linux
    linux
    linux
    linux
    linux
    idea插件篇之java内存分析工具(JProfiler)
    Jmeter(线程组+http请求+汇总报告)
    ZK客户端zkClient.bat
  • 原文地址:https://www.cnblogs.com/hyl8218/p/6128159.html
Copyright © 2011-2022 走看看