好尴尬,发现自己写上篇技术博文,已经是一年前的事情了,好像把时间都浪费在娱乐上了?没有吧,工作还是很认真的。业余时间,是有点浪费了,不过还是有研究过一些精彩的东东来分享的。不说闲话了,这就分享出来。
这段时间里还是一直对新型的几个主流的Java框架做一些研究,现在以一个 Web Application 的模型 将笔者的心得课题化并一一分享出来。今天是第一篇,分享下笔者对一个小型Web程序的架构,以及如何将原来的web.xml和spring的applicationContext.xml对应到新型注解化的配置类中。
之前《究 Spring 3.1之无web.xml式 基于代码配置的servlet3.0应用》中已经将知晓了Spring是如何利用Servlet3中的ServletContainerInitializer进行Web初始化配置的,这里笔者就不再自己去写了,像利用Spring的FileCopyUtils一样,清楚它是怎么实现的就好,用时直接拿来用,方便。
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下后自动生成的。
WebContent下的内容是之后部署环境的所有内容,笔者这里放着个sysParams.properties就是系统的一些配置属性,部署人员做web部署时,只需要改这一个就行。开发过程中有可配置的选项都应该通过一个静态的Properties类对象去找到相应的名值对,通过一定方法转换为所需类型。至于怎么实现,这就来看。
首先来着,对应于原来web.xml的WebConfiguration代码如下:
- package com.xxxxx.webmodel.util;
- import java.io.FileReader;
- import java.io.IOException;
- import java.util.Properties;
- import javax.servlet.ServletContext;
- import javax.servlet.ServletException;
- import org.springframework.web.WebApplicationInitializer;
- import org.springframework.web.context.ContextLoaderListener;
- import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
- publicclass WebConfiguration implements WebApplicationInitializer{
- privatestatic Properties sysParams=new Properties();
- publicstatic Properties getSysParams() {
- returnsysParams;
- }
- @Override
- publicvoid onStartup(ServletContext servletContext)throws ServletException {
- try {
- String sysParamsLocation=this.getClass().getResource("/").getFile()
- .replace("/classes/", "/sysParams.properties");
- sysParams.load(new FileReader(sysParamsLocation));
- } catch (IOException e) {
- }
- AnnotationConfigWebApplicationContext annoAppCtx = new AnnotationConfigWebApplicationContext();
- annoAppCtx.register(ApplicationContext.class);
- if(servletContext!=null)servletContext.addListener(new ContextLoaderListener(annoAppCtx));
- }
- }
它实现Spring的WebApplicationInitializer后写了方法onStartup(servletContenxt), 因为支持servlet3的服务器启动后会找到WebApplicationInitializer的所有实现类并执行其onStartup方法,至于想了解它如何做到的,文章开头已经说明,请去阅读另一篇博文。 在这个方法里,笔者首先做的是load sysParams.propertyies文件到本类的一个静态Properties变量sysParams中,这样一来,任何一处代码都可以直接get到这个sysParams。 之后的两句代码是建立一个WebApplicationCotext,自然就是Spring的东西了,意思很明显,注解配置化的WebApplication环境,这个环境的配置在哪? 就是ApplicationContext.class,将它注册到上面的环境中。最后一句servletContext.addListener,就是以往配置的
- <listener>
- <listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
- listener>
用以启动Spring。当然servletContext其实就是application, 它可以add servlet, listener, filter, servlet param, 也就是说,可以做任何 web.xml的配置。不过其实我们要写这些类时,直接去实现 WebApplicationInitializer 并在里面写了onStartup方法就行,同样会在服务器启动时就被执行,关于这点,之前的无博文也有说明。
下面将关注点放在上面提到的配置类ApplicationContext.class,笔者将它命名如此,正是为了展示它就是以往的applicationContext.xml。
- package com.xxxxx.webmodel.util;
- import java.util.Properties;
- import javax.annotation.Resource;
- import javax.sql.DataSource;
- import org.apache.commons.dbcp.BasicDataSource;
- import org.hibernate.SessionFactory;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.ComponentScan;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.orm.hibernate3.HibernateInterceptor;
- import org.springframework.orm.hibernate3.HibernateTemplate;
- import org.springframework.orm.hibernate4.HibernateTransactionManager;
- import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
- import com.xxxxx.webmodel.entity.AccountEntity;
- import com.xxxxx.webmodel.entity.ActionLogEntity;
- @Configuration
- @ComponentScan("com.xxxxx.webmodel")
- publicclass ApplicationContext {
- private SessionFactory sessionFactory;
- public SessionFactory getSessionFactory() {
- return sessionFactory;
- }
- @Resource
- public void setSessionFactory(SessionFactory sessionFactory) {
- this.sessionFactory = sessionFactory;
- }
- @Bean(destroyMethod = "close")
- public DataSource dbcpDataSource() {
- Properties parameters = WebConfiguration.getSysParams();
- BasicDataSource basicDS = new BasicDataSource();
- basicDS.setDriverClassName(parameters.getProperty("jdbc.driver",
- "oracle.jdbc.OracleDriver"));
- basicDS.setUrl(parameters.getProperty("jdbc.url",
- "jdbc:oracle:thin:@localhost:1521:xe"));
- basicDS.setUsername(parameters.getProperty("jdbc.username", "system"));
- basicDS.setPassword(parameters.getProperty("jdbc.password", "oraclesystem"));
- basicDS.setMaxIdle(1000);
- basicDS.setMinIdle(4);
- basicDS.setMaxActive(8);
- basicDS.setInitialSize(3);
- basicDS.setMaxWait(10000);
- return basicDS;
- }
- @Configuration
- static class SessionFactoryConfig {
- @Resource
- private DataSource ds;
- public DataSource getDs() {
- return ds;
- }
- public void setDs(DataSource ds) {
- this.ds = ds;
- }
- @Bean
- public LocalSessionFactoryBean sessionFactory() {
- LocalSessionFactoryBean asfBean = new LocalSessionFactoryBean();
- asfBean.setDataSource(this.getDs());
- Properties parameters = WebConfiguration.getSysParams();
- Properties prop = new Properties();
- prop.setProperty("hibernate.dialect", parameters.getProperty(
- "hibernate.dialect", "org.hibernate.dialect.OracleDialect"));
- prop.setProperty("hibernate.connection.autocommit", parameters
- .getProperty("hibernate.connection.autocommit", "false"));
- prop.setProperty("hibernate.show_sql", parameters.getProperty("hibernate.show_sql", "true"));
- prop.setProperty("hibernate.hbm2ddl.auto", parameters.getProperty("hibernate.hbm2ddl.auto", "update"));
- asfBean.setHibernateProperties(prop);
- @SuppressWarnings("rawtypes")
- Class[] entities = new Class[2];
- entities[0] = AccountEntity.class;
- entities[1] = ActionLogEntity.class;
- asfBean.setAnnotatedClasses(entities);
- return asfBean;
- }
- }
- @Bean
- public HibernateTransactionManager hibernateTransactionManager() {
- HibernateTransactionManager hiberTransMana = new HibernateTransactionManager();
- hiberTransMana.setSessionFactory(this.getSessionFactory());
- return hiberTransMana;
- }
- @Bean
- public HibernateInterceptor hibernateInterceptor() {
- HibernateInterceptor hiberInter = new HibernateInterceptor();
- hiberInter.setSessionFactory(this.getSessionFactory());
- return hiberInter;
- }
- @Bean
- public HibernateTemplate hibernateTemplate() {
- HibernateTemplate hiberTempl = new HibernateTemplate();
- hiberTempl.setSessionFactory(this.getSessionFactory());
- return hiberTempl;
- }
- }
首先,applicationContex.xml是
继续,应该看到该类里还有@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类的声明,类似于