`
掌心童话
  • 浏览: 14754 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

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.gxino.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;

public class WebConfiguration implements WebApplicationInitializer{

	private static Properties sysParams=new Properties();
	
	public static Properties getSysParams() {
		return sysParams;
	}

	@Override
	public void 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.gxino.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.gxino.webmodel.entity.AccountEntity;
import com.gxino.webmodel.entity.ActionLogEntity;

@Configuration
@ComponentScan("com.xxxx.webmodel")
public class 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;
	}

}
 

 

好,一点一点理解这个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的主旨,让它来托管我们的业务类实例。

第一篇就分享到这里。

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics