Java Web Application 自架构 一 注解化配置

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

        这段时间里还是一直对新型的几个主流的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代码如下:

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.ContextLoaderListener</listener-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=\'#\'"  class="string">"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; 
    } 
} 

好,一点一点理解这个ApplicationContext的代码.

        首先,applicationContex.xml是<beans>包裹着的各种<bean>, 这里被@Configuration注解的一个class ApplicationContext总体就可以看作是<beans>, 其实@Configuration本身就是个bean, 去看它的代码就知道,它被@Component注解,我们可以理解它为 配置bean。然后这个ApplicationContext中有很多被@Bean注解的方法,就是在这个配置bean中配置的Bean以供程序注入使用。这些方法的返回值是bean的class类型,方法名是bean的name, 即我们xml中配置的<bean class=”” name=””>, 在方法体中,对该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类的声明,类似于<context:component-scan base-package="com.foo.bar"/>(这个需要xml命名空间) 。即是说寻找我们自定义的bean类型,那么我们需要用@Component及其子注解类型如@Controller,@Service, @Repository注解一些com.xxxx.webmodel为类路径开头的类来让spring找到并代理生成bean实例。也就是用Spring的主旨,让它来托管我们的业务类实例。

       第一篇就分享到这里。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值