精华帖 (2) :: 良好帖 (4) :: 新手帖 (0) :: 隐藏帖 (4)
|
|
---|---|
作者 | 正文 |
发表时间:2011-07-06
最后修改:2011-07-06
spring3.0使用annotation完全代替XML spring3.0使用annotation完全代替XML(续) 用java config来代替XML,当时还遗留下一些问题:
先来说说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 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2011-07-08
web.xml这个配置文件也干掉?
太狠了…… |
|
返回顶楼 | |
发表时间:2011-07-08
KimHo 写道 web.xml这个配置文件也干掉?
太狠了…… Servlet3.0是可以把web.xml干掉了,呵呵 |
|
返回顶楼 | |
发表时间:2011-07-08
马上试一试, 但真的有必要这样吗? 难道这是趋势? 我看不出来.
|
|
返回顶楼 | |
发表时间:2011-07-09
没有必要去掉web.xml吧。
|
|
返回顶楼 | |
发表时间:2011-07-09
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; } 这段代码感觉就是为了不写XML而加,是很差的方案。 |
|
返回顶楼 | |
发表时间:2011-07-10
最后修改:2011-07-10
kiefer 写道 这段代码感觉就是为了不写XML而加,是很差的方案。 好吧,我知道有人会拿这个说事。。。 把配置写XML里面是典型的对XML的滥用啊,我觉得配置直接写代码里是简单够用的方案。当然如果你的项目足够大,确实有分离配置的需求,那么可以这样: //DataConfig.java private Properties hProps() { Properties p = new Properties(); try { p.load(this.getClass().getResourceAsStream("hibernate.properties")); } catch (IOException e) { throw new IllegalStateException("hibernate.properties not found!"); } return p; } //hibernate.properties hibernate.dialect=org.hibernate.dialect.HSQLDialect hibernate.cache.use_second_level_cache=true hibernate.cache.use_query_cache=true hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider hibernate.cache.provider_configuration_file_resource_path=ehcache.xml hibernate.show_sql=true hibernate.hbm2ddl.auto=update hibernate.generate_statistics=true hibernate.cache.use_structured_entries=true 我偏向于尽量用代码来表达,额外的配置写properties,XML能免则免 |
|
返回顶楼 | |
发表时间:2011-07-10
怎么说呢、 还是不要太绝对了。
XML、Properties、Annotation 各有所长吧 看情况而定了, 不能一棒子打死了那个。 |
|
返回顶楼 | |
发表时间:2011-07-10
xml可读性其实比代码要强。你可以找个不会写代码的人来阅读看看。
|
|
返回顶楼 | |
发表时间:2011-07-10
这还是要分情况的,不一定都用注解就好。。。
|
|
返回顶楼 | |