好尴尬,发现自己写上篇技术博文,已经是一年前的事情了,好像把时间都浪费在娱乐上了?没有吧,工作还是很认真的。业余时间,是有点浪费了,不过还是有研究过一些精彩的东东来分享的。不说闲话了,这就分享出来。

         这段时间里还是一直对新型的几个主流的Java框架做一些研究,现在以一个 Web Application 的模型 将笔者的心得课题化并一一分享出来。今天是第一篇,分享下笔者对一个小型Web程序的架构,以及如何将原来的web.xml和spring的applicationContext.xml对应到新型注解化的配置类中。

        之前《究 Spring 3.1之无web.xml式 基于代码配置的servlet3.0应用》中已经将知晓了Spring是如何利用Servlet3中的ServletContainerInitializer进行Web初始化配置的,这里笔者就不再自己去写了,像利用Spring的FileCopyUtils一样,清楚它是怎么实现的就好,用时直接拿来用,方便。

架构如图所示。src包中,分层为三

1,控制层ctrl,MVC的C大家都知道,表层,与UI的数据调度,接收Http请求并做出相应响应,这里还未确定具体用什么框架,先放着。

2,业务层hub,叫biz, business, service都行,即业务逻辑层,整个web核心调度层。

3 外部层pin,通常是dao数据访问层,然后自我认为这里不单单会借助数据库做为外部辅助系统,与外部其他系统都应该放在这层里,做为与外部系统沟通的底层,所以用了个很形象的 针脚pin 一词。

        所谓的外部系统还有可能是邮件服务器,webService调用与被调用,等等,甚至是文件资源。比方说,文件资源有时不一定会存储在部署的服务器上,也有可能外包于其它专门的服务器,例如amazon的S3, 这时,业务层hub无需要知道文件资源的存储是何策略,直接调用底层的方法,由pin层的专门管理文件资源存储的类去具体操作。再往下面的persistence 做传统意义的dao服务。

        当然别忘了entity, 就是业务实体模型。

        Util 里放一些配置,工具等的类 。例如图中选中的高亮文件,就是对应于以往web.xml的文件,这里命名其为WebConfiguration, 当然,名字因人而异。

        测试包testsrc在后面的 单元测试一节会具体说明,这里就过了。

        然后用到的库,JRE不多说,Web App Lib是将所需的jar文件放到WebContent/WEB-INF/lib下后自动生成的。

        测试库SpringJunit4也是测试章节的。后面的运行时库是为了做servlet包的,随便加一个有servlet3支持的就好。

        WebContent下的内容是之后部署环境的所有内容,笔者这里放着个sysParams.properties就是系统的一些配置属性,部署人员做web部署时,只需要改这一个就行。开发过程中有可配置的选项都应该通过一个静态的Properties类对象去找到相应的名值对,通过一定方法转换为所需类型。至于怎么实现,这就来看。

       首先来着,对应于原来web.xml的WebConfiguration代码如下:


  1. package com.xxxxx.webmodel.util; 
  2.  
  3. import java.io.FileReader; 
  4. import java.io.IOException; 
  5. import java.util.Properties; 
  6. import javax.servlet.ServletContext; 
  7. import javax.servlet.ServletException; 
  8. import org.springframework.web.WebApplicationInitializer; 
  9. import org.springframework.web.context.ContextLoaderListener; 
  10. import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; 
  11.  
  12. publicclass WebConfiguration implements WebApplicationInitializer{ 
  13.     privatestatic Properties sysParams=new Properties(); 
  14.     publicstatic Properties getSysParams() { 
  15.         returnsysParams; 
  16.     } 
  17.  
  18.     @Override 
  19.     publicvoid onStartup(ServletContext servletContext)throws ServletException { 
  20.         try { 
  21.             String sysParamsLocation=this.getClass().getResource("/").getFile()
  22. .replace("/classes/""/sysParams.properties"); 
  23.             sysParams.load(new FileReader(sysParamsLocation)); 
  24.         } catch (IOException e) { 
  25.         } 
  26.  
  27.         AnnotationConfigWebApplicationContext annoAppCtx new AnnotationConfigWebApplicationContext(); 
  28.         annoAppCtx.register(ApplicationContext.class); 
  29.         if(servletContext!=null)servletContext.addListener(new ContextLoaderListener(annoAppCtx)); 
  30.    } 

        它实现Spring的WebApplicationInitializer后写了方法onStartup(servletContenxt), 因为支持servlet3的服务器启动后会找到WebApplicationInitializer的所有实现类并执行其onStartup方法,至于想了解它如何做到的,文章开头已经说明,请去阅读另一篇博文。 在这个方法里,笔者首先做的是load sysParams.propertyies文件到本类的一个静态Properties变量sysParams中,这样一来,任何一处代码都可以直接get到这个sysParams。 之后的两句代码是建立一个WebApplicationCotext,自然就是Spring的东西了,意思很明显,注解配置化的WebApplication环境,这个环境的配置在哪? 就是ApplicationContext.class,将它注册到上面的环境中。最后一句servletContext.addListener,就是以往配置的


  1. <listener> 
  2. <listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class> 
  3. listener> 

用以启动Spring。当然servletContext其实就是application, 它可以add servlet, listener, filter, servlet param, 也就是说,可以做任何 web.xml的配置。不过其实我们要写这些类时,直接去实现 WebApplicationInitializer 并在里面写了onStartup方法就行,同样会在服务器启动时就被执行,关于这点,之前的无博文也有说明。

        下面将关注点放在上面提到的配置类ApplicationContext.class,笔者将它命名如此,正是为了展示它就是以往的applicationContext.xml。

 


  1. package com.xxxxx.webmodel.util; 
  2.  
  3. import java.util.Properties; 
  4.  
  5. import javax.annotation.Resource; 
  6. import javax.sql.DataSource; 
  7.  
  8. import org.apache.commons.dbcp.BasicDataSource; 
  9. import org.hibernate.SessionFactory; 
  10. import org.springframework.context.annotation.Bean; 
  11. import org.springframework.context.annotation.ComponentScan; 
  12. import org.springframework.context.annotation.Configuration; 
  13. import org.springframework.orm.hibernate3.HibernateInterceptor; 
  14. import org.springframework.orm.hibernate3.HibernateTemplate; 
  15. import org.springframework.orm.hibernate4.HibernateTransactionManager; 
  16. import org.springframework.orm.hibernate4.LocalSessionFactoryBean; 
  17.  
  18. import com.xxxxx.webmodel.entity.AccountEntity; 
  19. import com.xxxxx.webmodel.entity.ActionLogEntity; 
  20.  
  21. @Configuration 
  22. @ComponentScan("com.xxxxx.webmodel"
  23. publicclass ApplicationContext { 
  24.     private SessionFactory sessionFactory; 
  25.  
  26.     public SessionFactory getSessionFactory() { 
  27.         return sessionFactory; 
  28.     } 
  29.     @Resource 
  30.     public void setSessionFactory(SessionFactory sessionFactory) { 
  31.         this.sessionFactory = sessionFactory; 
  32.     } 
  33.  
  34. @Bean(destroyMethod = "close"
  35.  
  36. public DataSource dbcpDataSource() { 
  37.   Properties parameters = WebConfiguration.getSysParams(); 
  38. BasicDataSource basicDS = new BasicDataSource(); 
  39.   basicDS.setDriverClassName(parameters.getProperty("jdbc.driver"
  40. "oracle.jdbc.OracleDriver")); 
  41. basicDS.setUrl(parameters.getProperty("jdbc.url"
  42. "jdbc:oracle:thin:@localhost:1521:xe"));
  43. basicDS.setUsername(parameters.getProperty("jdbc.username""system")); 
  44.   basicDS.setPassword(parameters.getProperty("jdbc.password", "oraclesystem"));
  45. basicDS.setMaxIdle(1000); 
  46.   basicDS.setMinIdle(4); 
  47. basicDS.setMaxActive(8); 
  48. basicDS.setInitialSize(3); 
  49. basicDS.setMaxWait(10000); 
  50. return basicDS; 
  51.  
  52.  
  53. @Configuration 
  54. static class SessionFactoryConfig { 
  55.  
  56. @Resource 
  57. private DataSource ds; 
  58.  
  59.  
  60. public DataSource getDs() { 
  61. return ds; 
  62.  
  63.  
  64. public void setDs(DataSource ds) { 
  65.   this.ds = ds; 
  66.  
  67. @Bean 
  68. public LocalSessionFactoryBean sessionFactory() { 
  69.   LocalSessionFactoryBean asfBean = new LocalSessionFactoryBean(); 
  70.   asfBean.setDataSource(this.getDs()); 
  71. Properties parameters = WebConfiguration.getSysParams(); 
  72.   Properties prop = new Properties(); 
  73.  
  74. prop.setProperty("hibernate.dialect", parameters.getProperty( 
  75.   "hibernate.dialect""org.hibernate.dialect.OracleDialect")); 
  76.   prop.setProperty("hibernate.connection.autocommit", parameters 
  77.   .getProperty("hibernate.connection.autocommit""false")); 
  78.   prop.setProperty("hibernate.show_sql"parameters.getProperty("hibernate.show_sql""true")); 
  79.   prop.setProperty("hibernate.hbm2ddl.auto"parameters.getProperty("hibernate.hbm2ddl.auto""update")); 
  80.   asfBean.setHibernateProperties(prop); 
  81.  
  82. @SuppressWarnings("rawtypes"
  83. Class[] entities = new Class[2]; 
  84.  
  85. entities[0] = AccountEntity.class
  86.   entities[1] = ActionLogEntity.class
  87.   asfBean.setAnnotatedClasses(entities); 
  88. return asfBean; 
  89.  
  90.  
  91. @Bean 
  92. public HibernateTransactionManager hibernateTransactionManager() { 
  93. HibernateTransactionManager hiberTransMana = new HibernateTransactionManager(); 
  94.   hiberTransMana.setSessionFactory(this.getSessionFactory()); 
  95. return hiberTransMana; 
  96.  
  97. @Bean 
  98. public HibernateInterceptor hibernateInterceptor() { 
  99. HibernateInterceptor hiberInter = new HibernateInterceptor(); 
  100.   hiberInter.setSessionFactory(this.getSessionFactory()); 
  101. return hiberInter; 
  102.  
  103. @Bean 
  104. public HibernateTemplate hibernateTemplate() { 
  105. HibernateTemplate hiberTempl = new HibernateTemplate(); 
  106. hiberTempl.setSessionFactory(this.getSessionFactory()); 
  107. return hiberTempl; 
 
好,一点一点理解这个ApplicationContext的代码.

        首先,applicationContex.xml是包裹着的各种, 这里被@Configuration注解的一个class ApplicationContext总体就可以看作是, 其实@Configuration本身就是个bean, 去看它的代码就知道,它被@Component注解,我们可以理解它为 配置bean。然后这个ApplicationContext中有很多被@Bean注解的方法,就是在这个配置bean中配置的Bean以供程序注入使用。这些方法的返回值是bean的class类型,方法名是bean的name, 即我们xml中配置的, 在方法体中,对该bean进行配置。为下文的方便理解,总结一点,配置bean的格式为: @Configuration注解的类称为 配置bean类型,其中有很多@Bean注解的方法,用这些方法中的代码配置中一个类实例以供注入准备。

        继续,应该看到该类里还有@Configuration注解的静态类,格式如同类ApplicationContext, 再啰嗦一遍:@Configuration注解的配置类中有一或多个@Bean注解的方法。 这是干什么用的?想象一下我们在xml中配置这个SesstionFactory, 需要用到一个ref将配置好的dataSource 注入进来,那么到了代码的方式怎么办?其实再熟悉不过了,定义一个类注册为Spring的bean类型,并用声明一个类成员变量,用@Resource将它注入进来。 OK, 到这儿有两个问题,这个内置的配置bean为什么是静态的?还有没有别的方法去配这种需要ref其它bean的bean?留给读者思考一下。

        总结一句话, 其实体会一下就会理解,@Configuration注解的class是一种配置bean的声明, 将会被Spring代理实例化作为特殊的bean去用,@bean注解的方法中,我们会去实例化一个类,并返回出来,很大的可能Spring会用BeanType bean ={调用@Bean注解的会返回BeanType实例的方法} 的形式来生成一个bean实例, 然后用一个诸如Map等方式储存这个bean到它的Context中,以便注入到其它bean中。

        别忘记看@ComponentScan("com.xxxxx.webmodel"), 它的意思是寻找类路径为com.xxx.webmodel的bean类的声明,类似于(这个需要xml命名空间) 。即是说寻找我们自定义的bean类型,那么我们需要用@Component及其子注解类型如@Controller,@Service, @Repository注解一些com.xxxx.webmodel为类路径开头的类来让spring找到并代理生成bean实例。也就是用Spring的主旨,让它来托管我们的业务类实例。

       第一篇就分享到这里。