springorm源码分析(springmvc源码分析)
本文目录一览:
- 1、Java架构的路上必学知识点,你又知道多少
- 2、恳请spring配置文件逐条详解,详见问题补充。
- 3、关于spring配置的理解
- 4、如何在spring框架中解决多数据源的问题
- 5、spring的原理
- 6、谈谈spring是如何实现的?
Java架构的路上必学知识点,你又知道多少
Jenkins多套环境(test/pre/production)系统自动化发布
Jenkins自动发布到远程主机
MavenMaven私服搭建setting.xml文件剖析pom.xml详解Maven实用插件教学(静态代码检查、生成可执行jar包)profile使用
源码分析
源码分析 Spring源码分析
Spring IOC的实现原理Spring BeanFactory源码分析Spring AOP的实现原理及配置文件详解Spring AOP的各种应用场景分析Spring MVC与Struts对比Spring HandlerMapping详解手写实现SpringMVC框架Spring与各种框架集成原理Spring JDBC操作原理基于Spring JDBC手写ORM框架
MyBatis源码分析
MyBatis3简介MyBatis3 SqlMap那些事儿数据库连接池到底是什么MyBatis3 SessionFactory实现原理MyBatis3 配置文件详解MyBatis3 事务管理与集成浅谈HibernateMyBatis3与Hibernate框架对比Netty源码分析
NIO通信原理剖析深入了解NIO缓冲区Buffer
NIO Selector原理AIO编程Netty产生的背景以及基础入门
Netty高性能之道Netty的HTTP与Socket通信原理利用Netty搭建高性能的
WebSocket聊天室
Netty聊天室客户端架构实现Netty的编码解码
Netty的拆包粘包操作MsgPack原理讲解及各种序列化框架对比MsgPack与Netty整合
Netty HTTP通信与Spring整合Netty RPC架构Netty与各种架构整合以及Netty源码分析
性能调优
性能调优 JVMJVM内存模型JVM运行时数据区垃圾回收机制GC日志详解
根据GC日志调优系统,调优不靠碰运气!Mysql数据库优化
数据库底层数据结构索引数据存储结构 innodb详解SQL调优及原理分库、分表实现Nginx调优动静资源分离
nginx参数详解nginx + lua使用应用:ip过滤,扛DDOSTomcat调优
Tomcat源码、架构分析Tomcat具体调优参数设置Tomcat压力基准测试Tomcat NIO配置
恳请spring配置文件逐条详解,详见问题补充。
逐条解释肯定解释不完,就跟你讲一下事务管理的相关理解吧。
下面是Spring较早时期的一个配置,能体现出一点底层的东西。
bean id="dataSource" class="c66f-adb9-8276-cd14 com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"
property name="driverClass" value="com.mysql.jdbc.Driver" /
property name="jdbcUrl" value="jdbc:mysql://localhost:3306/" /
property name="user" value="root" /
property name="password" value="fuhaiwei" /
/bean
bean id="sessionFactory" class="adb9-8276-cd14-1a93 org.springframework.orm.hibernate4.LocalSessionFactoryBean"
property name="dataSource" ref="dataSource" /
property name="mappingResources" value="com/fuhaiwei/domain/domain.xml" /
property name="hibernateProperties"
props
prop key="hibernate.hbm2ddl.auto"update/prop
prop key="hibernate.dialect"org.hibernate.dialect.MySQL5Dialect/prop
/props
/property
/bean
bean id="transactionManager" class="8276-cd14-1a93-36f6 org.springframework.orm.hibernate4.HibernateTransactionManager"
property name="dataSource" ref="dataSource" /
/bean
//看这个,这个是关键,事务管理是靠AOP实现的,下面是一个拦截器。
bean id="transactionInterceptor" class="cd14-1a93-36f6-8e26 org.springframework.transaction.interceptor.TransactionInterceptor"
property name="transactionManager" ref="transactionManager" /
property name="transactionAttributes"
//指定事务传播属性,例如getUser给予只读事务
props
prop key="get*"PROPAGATION_REQUIRED,readOnly/prop
prop key="add*"PROPAGATION_REQUIRED/prop
prop key="update*"PROPAGATION_REQUIRED/prop
prop key="update*"PROPAGATION_REQUIRED/prop
/props
/property
/bean
//这个也是关键,指定那些Bean会启动上面的拦截器。
bean class="1a93-36f6-8e26-38a8 org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"
property name="interceptorNames" value="transactionInterceptor" /
//指定所有Service结尾的Bean会启用事务管理。
property name="beanNames" value="*Service" /
/bean
//现在使用的tx:annotation-driven transaction-manager="transactionManager"/
//直接替代了上述两条配置。
从上面的配置可以看出,Spring的事务管理要发挥作用有一些条件。
一:这个类要由Spring来管理,也就是说配置成一个Bean,并且程序中实际使用的必须是由容器注入的。(因为事务管理的实现原理是AOP代理,我们实际使用的UserService对象将是一个代理对象)
二:我们应该告诉Spring,我们想在那些类或者说Bean上启用事务。
三:我们应该告诉Spring,我们想在那些方法上启用事务。(因为AOP代理一般是使用JDK动态代理机制,而JDK想要代理的方法,必须实现于一个接口,所以我们经常发现由Spring管理的项目,其Service和Dao常常都有接口类)
四:我们在Dao中,获取Session时,应该用getCurrentSession()方法,而不是openSession()方法,因为openSession()方法会打开一个全新的Session,且不与线程关联。事务管理器到时候无法获取这个Session也就无法管理事务。而如果使用getCurrentSession()方法,这个方法获取的事务是与线程关联的,到时候事务管理器就可以打开事务,提交事务,或者回滚事务。
五:这是的transactionInterceptor一个片段
public Object invoke(final MethodInvocation invocation) throws Throwable {
//如有必要打开事务。
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
//运行被代理类的实际方法,也就是我们的Service类的某个方法
retVal = invocation.proceed();
}
catch (Throwable ex) {
//如果抛出异常就回滚事务(默认配置情况下)
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
//如果没有异常就提交事务
commitTransactionAfterReturning(txInfo);
return retVal;
}
最后总结,这里面设计到很多个知识点,如什么是AOP,SpringAOP实现的方法是什么,什么是JDK动态代理,什么是Spring自动代理工厂,很多很多,不知道你掌握的情况,所以有问题请追问吧。
关于spring配置的理解
bean id="UserDAO" class="36f6-8e26-38a8-0149 cn.com.sh.dao.impl.UserDAOImpl"
property name="sessionFactory"
ref bean="sessionFactory"/
/property
/bean
这里的property name="sessionFactory"
ref bean="sessionFactory"/
这样理解
这个property 就是属性的意思,意思是说id="UserDAO"的类class="8e26-38a8-0149-0a02 cn.com.sh.dao.impl.UserDAOImpl"中有一个属性sessionFactory.
你看下你的那个UserDAOImpl有没有sessionFactory这个属性.
如果这个类中没有,再找这个类的父类.
象UserDAOImpl这些类一般都是通过extends HibernateDaoSupport来进行操作的.也就是说UserDAOImpl的父类HibernateDaoSupport中肯定有sessionFactory这个属性了.
在配置文件中
property name="sessionFactory"
ref bean="sessionFactory"/
意思是说这个sessionFactory参照了某个id="sessionFactory".
你再仔细看下你的配置文件
里面肯定有一个配置sessionFactory
象配置sessionFactory也许是下面这样的:
bean id="sessionFactory" class="38a8-0149-0a02-b0a0 org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
............
/bean
也就是说上面你那个
bean id="UserDAO" class="0149-0a02-b0a0-d872 cn.com.sh.dao.impl.UserDAOImpl"
property name="sessionFactory"这个sessionFactory是固定的
ref bean="sessionFactory"/这个sessionFactory是你先定义好的.
/property
/bean
有些属性的名字是固定,有些自定的id你可以自定名字的.你刚学这个,最好参照规范.
至于sessionFactory是什么,简单来讲就相当于JDBC里面的一个Connection
如何在spring框架中解决多数据源的问题
转载我首先想到在spring但是,我很快发现一个问题:当多用户同时并发访问数据库的时候会出现资源争用的问题。这都是“单例模式”惹的祸。众所周知,我们在使用spring通过以上的分析,解决多数据源访问问题的关键,就集中在sessionFactory(一) 采用Decorator设计模式
要解决这个问题,我的思路锁定在了这个dataSource什么是“
(二) 设计MultiDataSource类
现在回到我们的问题,我们需要对dataSource
对比原Decoratorspan times="" span=""
private DataSource dataSource = null;
public MultiDataSource(DataSource dataSource){
this.dataSource = dataSource;
}
/* (non-Javadoc)
* @see javax.sql.DataSource#getConnection()
*/
public Connection getConnection() throws SQLException {
return getDataSource().getConnection();
}
//其它DataSource接口应当实现的方法
public DataSource getDataSource(){
return this.dataSource;
}
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
}
客户在发出请求的时候,将dataSourceName放到request中,然后把request中的数据源名通过调用new MultiDataSource(dataSource)时可以告诉客户需要的数据源,就可以实现动态切换数据源了。但细心的朋友会发现这在单例的情况下就是问题的,因为在系统中只有一个对象,它的实例变量也只有一个,就如同一个静态变量一般。正因为如此,(三) 单例模式下的MultiDataSource
在单例模式下,由于我们在每次调用MultiDataSource
log.debug("dataSourceName:"+dataSourceName);
try{
if(dataSourceName==null||dataSourceName.equals("")){
return this.dataSource;
}
return (DataSource)this.applicationContext.getBean(dataSourceName);
}catch(NoSuchBeanDefinitionException ex){
throw new DaoException("There is not the dataSource
}
}
值得一提的是,我需要的数据源已经都在spring就是其对应的span courier="" span="" new?;bean id="dataSource1"
class="0a02-b0a0-d872-f548 org.apache.commons.dbcp.BasicDataSource"
property name="driverClassName"
valueoracle.jdbc.driver.OracleDrivervalue
property
......
bean
bean id="dataSource2"
class="b0a0-d872-f548-f063 org.apache.commons.dbcp.BasicDataSource"
property name="driverClassName"
valueoracle.jdbc.driver.OracleDrivervalue
property
......
bean
为了得到spring,并且实现方法:
java 代码
private ApplicationContext applicationContext = null;
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
如此这样,我就可以通过得到了。
(四) 通过线程传递dataSourceName
查看以上设计,MultiDataSource
span times="" span="" roman?;class SpObserver {
private static ThreadLocal local = new ThreadLocal();
public static void putSp(String sp) {
local.set(sp);
}
public static String getSp() {
return (String)local.get();
}
}
做一个filter,将request中的dataSourceName对象。
String sp = SpObserver.getSp();
return getDataSource(sp);
}
完整的MultiDataSource(五) 动态添加数据源
通过以上方案,我们解决了动态分配数据源的问题,但你可能提出疑问:方案中的数据源都是配置在spring中(见附件)。不通过配置文件直接加载对象,在的源码中也有,感兴趣的朋友可以自己研究。
(六) 在spring中配置
在完成了所有这些设计以后,我最后再唠叨一句。我们应当在springspan times="" span="" roman?;bean id="dynamicLoadBean" class="d946-f17a-1061-2cd1 com.htxx.service.dao.DynamicLoadBean"bean
bean id="dataSource" class="f17a-1061-2cd1-c66f com.htxx.service.dao.MultiDataSource"
property name="dataSource"
ref bean="dataSource1" /
property
bean
bean id="sessionFactory" class="1061-2cd1-c66f-adb9 org.springframework.orm.hibernate3.LocalSessionFactoryBean"
property name="dataSource"
ref bean="dataSource" /
property
......
bean
其中dataSource以上方案与其它方案相比,它有哪些优势呢?
首先,这个方案完全是在spring其次,实现简单,易于维护。这个方案虽然我说了这么多东西,其实都是分析,真正需要我们写的代码就只有MultiDataSource最后,这个方案可以使单数据源与多数据源兼容。这个方案完全不影响BUS相关博客:再析在spring框架中解决多数据源的问题
example.rar
描述: 源码及示例
下载
文件名: example.rar
文件大小: 32 KB
下载过的: 文件被下载或查看 521 次
再析在spring框架中解决多数据源的问题
关键字: Spring Hibernate Decorator 设计模式
如何在spring总结多数据源的问题,其实它需要分为以下三种情况:各个数据源的数据结构不同、各个数据源的数据结构相同、各个数据源的数据结构部分相同又有部分不同。对于第二种情况,各个数据源的数据结构相同,我们使用一个sessionFactory对于各个数据源的数据结构不同的情况,使用一个sessionFactory与MultiDataSource
在该方案中,SessionFactory接口,Decorator就是MultiSessionFactory,SessionFactory1和SessionFactory2往往是spring的。细心的朋友可能会注意,实际上并不是SessionFactory的时候其实并不是真正地得到了它,而是得到了一个SessionFactory重写了getObject()。在整个这个方案中,我们需要实现的只有MultiSessionFactory。MultiSessionFactoryspan times="" span="" roman?;class MultiSessionFactory implements SessionFactory, ApplicationContextAware {
private static final long serialVersionUID = 2064557324203496378L;
private static final Log log = LogFactory.getLog(MultiSessionFactory.class);
private ApplicationContext applicationContext = null;
private SessionFactory sessionFactory = null;
public ApplicationContext getApplicationContext() {
return applicationContext;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public SessionFactory getSessionFactory(String sessionFactoryName) {
log.debug("sessionFactoryName:"+sessionFactoryName);
try{
if(sessionFactoryName==null||sessionFactoryName.equals("")){
return sessionFactory;
}
return (SessionFactory)this.getApplicationContext().getBean(sessionFactoryName);
}catch(NoSuchBeanDefinitionException ex){
throw new DaoException("There is not the sessionFactory
}
}
public SessionFactory getSessionFactory() {
String sessionFactoryName = SpObserver.getSp();
return getSessionFactory(sessionFactoryName);
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
// SessionFactory接口需要实现的方法
......
}
MultiSessionFactory的完整代码见我提供的附件。span times="" roman?;="" new="" span="" newbean id="sessionFactory" class="2cd1-c66f-adb9-8276 com.htxx.service.dao.MultiSessionFactory"
property name="sessionFactory"ref bean="hostSessionFactory"/property
SpServer的写法与《如何在另外,在spring也许有些朋友对以上方案还不满意,因为在执行数据访问前毕竟还要多做一步指定sessionFactory另外,在这个方案中的核心是运用Decorator前面我已经给出了第一种和第二种情况的解决方案:各个数据源的数据结构不同的情况用MultiSessionFactory
example.rar
描述: 示例文件
下载
文件名: example.rar
文件大小: 16 KB
下载过的: 文件被下载或查看 180 次
建议到原作者博客查看原文,那里有很好的回复
spring的原理
Spring的哲学是在不影响Java对象的设计的情况下将Java对象加入到框架中。 我们下面来看看Spring的工作原理,看看Spring是如何做到不影响Java对象的。
EJB的框架采用了一种侵略性(Invasive)的方法来设计对象,它要求你在设计中加入符合EJB规范的代码。一些轻量级的COP框架,例如Avalon,也要求对象设计时必须符合某种规范,例如Serviceable接口,这种做法是典型的Type 1做法。
这种设计思路要求Spring采用一种动态的、灵活的方式来设计框架。在Spring的工作原理中大量采用了反射。首先Spring要解决的一个问题就是如何管理bean。因为IOC的思想要求bean之间不能够直接调用,而应该采用一种被动的方式进行协作。所以bean的管理是Spring工作原理中的核心部分。
反射和内省在代码的层次上思考问题,有时候能够带来出人意料的灵活性。但它的使用有时候也是一个哲学问题,不论是在ORM设计还是在AOP设计上都出现了类似的问题-究竟是使用反射,还是使用代码生成。
在Spring中,处理这个问题的核心是在org.springframework.beans包中。而其中最为核心的部分,则是BeanWrapper。BeanWrapper,顾名思义,就是bean的包装器。所以,它的主要工作,就是对任何一个bean,进行属性(包括内嵌属性)的设置和方法的调用。在
BeanWrapper的默认实现类BeanWrapperImpl中,虽然代码较长,但完成的工作却是非常的集中的。
BeanWrapper的深入研究
我们看看这个BeanWrapper是如何发挥运作的,假设我们有两个bean:
public class Company { private String name; private Employee managingDirector; public String getName() { return this.name; } public void setName(String name) { this.name = name; } public Employee getManagingDirector() { return this.managingDirector; } public void setManagingDirector(Employee managingDirector) { this.managingDirector = managingDirector; } } public class Employee { private float salary; public float getSalary() { return salary; } public void setSalary(float salary) { this.salary = salary; } }
然后我们使用BeanWrapper来调用这两个bean:
Company c = new Company(); BeanWrapper bwComp = BeanWrapperImpl(c); // setting the company name... bwComp.setPropertyValue("name", "Some Company Inc."); // ... can also be done like this: PropertyValue v = new PropertyValue("name", "Some Company Inc."); bwComp.setPropertyValue(v); // ok, lets create the director and tie it to the company: Employee jim = new Employee(); BeanWrapper bwJim = BeanWrapperImpl(jim); bwJim.setPropertyValue("name", "Jim Stravinsky"); bwComp.setPropertyValue("managingDirector", jim); // retrieving the salary of the managingDirector through the company Float salary = (Float)bwComp.getPropertyValue("managingDirector.salary");
看起来麻烦了许多,但是这样Spring就可以使用统一的方式来管理bean的属性了。
Bean的制造工厂
有了对单个Bean的包装,还需要对多个的bean进行管理。在spring中,把bean纳入到一个核心库中进行管理。bean的生产有两种方法:一种是一个bean产生多个实例,一种是一个bean只产生一个实例。如果对设计模式熟悉的话,我们就会想到,前者可以采用Prototype,后者可以采用Singleton。
注意到,反射技术的使用使得我们不再像原始的工厂方法模式那样创建对象。反射可以非常灵活的根据类的名称创建一个对象。所以spring只使用了Prototype和Singleton这两个基本的模式。
Spring正是这样处理的,但是我们希望用户能够维护统一的接口,而不需要关心当前的bean到底是Prototype产生的独立的bean,还是Singleton产生的共享的bean。所以,在org.springframework.beans.factory包中的BeanFactory定义了统一的getBean方法。
JDBC再封装JDBC优雅的封装了底层的数据库,但是JDBC仍然存在诸多的不变。你需要编写大量的代码来完成CRUD操作,而且,JDBC无论是遇到什么样的问题,都抛出一个SQLException,这种做法在异常使用上被称为不完备的信息。因为问题可能是很复杂的,也许是数据库连接的问题,也许是并发控制的问题,也许只是SQL语句出错。没有理由用一个简单的SQLException就搞定全部的问题了,这种做法有些不负责任。针对这两个问题,Spring Framework提出了两种解决方法:首先,提供一个框架,把JDBC应用中的获取连接、异常处理、释放等比较通用的操作全部都集中起来,用户只需要提供特定的实现就OK了。实现的具体细节采用的是模板方法。举个例子,在org.springframework.jdbc.object包中,MappingSqlQuery类实现了将SQL查询映射为具体的业务对象。JavaDoc中这样写到:Reusable query in which concrete subclasses must implement the abstract mapRow(ResultSet, int) method to convert each row of the JDBC ResultSet into an object. 用户必须实现mapRow方法,这是典型模板方法的应用。我们拿一个具体的例子来看看:
class UserQuery extends MappingSqlQuery { public UserQuery(DataSource datasource) { super(datasource, "SELECT * FROM PUB_USER_ADDRESS WHERE USER_ID = ?"); declareParameter(new SqlParameter(Types.NUMERIC)); compile(); } // Map a result set row to a Java object protected Object mapRow(ResultSet rs, int rownum) throws SQLException { User user = new User(); user.setId(rs.getLong("USER_ID")); user.setForename(rs.getString("FORENAME")); return user; } public User findUser(long id) { // Use superclass convenience method to provide strong typing return (User) findObject(id); } }
其次是第二个问题,最麻烦的地方应该说是需要截住JDBC的异常,然后判断异常的类型,并重新抛出异常。错误的问题可以通过连接来获取,所以麻烦的是如何截获异常。Spring 框架采用的方法是回调,处理回调的类在Spring Framework中被称为template 。
JdbcTemplate template = new JdbcTemplate(dataSource); final List names = new LinkedList(); template.query("SELECT USER.NAME FROM USER", new RowCallbackHandler() { public void processRow(ResultSet rs) throws SQLException { names.add(rs.getString(1)); } });
回调函数是一个匿名类,其中也使用了模板方法,异常的处理都在父类中完成了。
层间松耦合
在开放源码界已经出现了大量的基于MVC的Web容器,但是这些容器都仅限于Web的范围 ,不涉及Web层次后端的连接,Spring作为一个整体性的框架,定义了一种Web层和后端业务层的连接方式, 这个思路仍然疏运图MVC的范畴,但耦合更松散,不依赖于具体的集成层次。
public class GoogleSearchController implements Controller { private IGoogleSearchPort google; private String googleKey; public void setGoogle(IGoogleSearchPort google) { this.google = google; } public void setGoogleKey(String googleKey) { this.googleKey = googleKey; } public ModelAndView handleRequest( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String query = request.getParameter("query"); GoogleSearchResult result = // Google property definitions omitted... // Use google business object google.doGoogleSearch(this.googleKey, query,start, maxResults, filter, r estrict, safeSearch, lr, ie, oe); return new ModelAndView("googleResults", "result", result); } }
回调函数是一个匿名类,其中也使用了模板方法,异常的处理都在父类中完成了。
2.Spring是一个开源框架,它由Rod Johnson创建。它是为了解决企业应用开发的复杂性而创建的。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。 • 目的:解决企业应用开发的复杂性 • 功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能 • 范围:任何Java应用 简单来说,Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。 轻量——从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。此外,Spring是非侵入式的:典型地,Spring应用中的对象不依赖于Spring的特定类。 控制反转——Spring通过一种称作控制反转(IoC)的技术促进了松耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。 面向切面——Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务()管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。 容器——Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建——基于一个可配置原型(prototype),你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例——以及它们是如何相互关联的。然而,Spring不应该被混同于传统的重量级的EJB容器,它们经常是庞大与笨重的,难以使用。 框架——Spring可以将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。Spring也提供了很多基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你。 所有Spring的这些特征使你能够编写更干净、更可管理、并且更易于测试的代码。它们也为Spring中的各种模块提供了基础支持。
谈谈spring是如何实现的?
Spring 是一个开源框架,是为了解决企业应用程序开发复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许您选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。
在这篇由三部分组成的 Spring 系列 的第 1 部分中,我将介绍 Spring 框架。我先从框架底层模型的角度描述该框架的功能,然后将讨论两个最有趣的模块:Spring 面向方面编程(AOP)和控制反转 (IOC) 容器。接着将使用几个示例演示 IOC 容器在典型应用程序用例场景中的应用情况。这些示例还将成为本系列后面部分进行的展开式讨论的基础,在本文的后面部分,将介绍 Spring 框架通过 Spring AOP 实现 AOP 构造的方式。
请参阅 下载,下载 Spring 框架和 Apache Ant,运行本系列的示例应用程序需要它们。
Spring 框架
Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式,如图 1 所示。
图 1. Spring 框架的 7 个模块
Spring 框架图示
组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:
* 核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转 (IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
* Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
* Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。
* Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
* Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
* Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
* Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。
Spring 框架的功能可以用在任何 J2EE 服务器中,大多数功能也适用于不受管理的环境。Spring 的核心要点是:支持不绑定到特定 J2EE 服务的可重用业务和数据访问对象。毫无疑问,这样的对象可以在不同 J2EE 环境 (Web 或 EJB)、独立应用程序、测试环境之间重用。
回页首
IOC 和 AOP
控制反转模式(也称作依赖性介入)的基本概念是:不创建对象,但是描述创建它们的方式。在代码中不直接与对象和服务连接,但在配置文件中描述哪一个组件需要哪一项服务。容器 (在 Spring 框架中是 IOC 容器) 负责将这些联系在一起。
在典型的 IOC 场景中,容器创建了所有对象,并设置必要的属性将它们连接在一起,决定什么时间调用方法。下表列出了 IOC 的一个实现模式。
类型 1 服务需要实现专门的接口,通过接口,由对象提供这些服务,可以从对象查询依赖性(例如,需要的附加服务)
类型 2 通过 JavaBean 的属性(例如 setter 方法)分配依赖性
类型 3 依赖性以构造函数的形式提供,不以 JavaBean 属性的形式公开
Spring 框架的 IOC 容器采用类型 2 和类型3 实现。
面向方面的编程
面向方面的编程,即 AOP,是一种编程技术,它允许程序员对横切关注点或横切典型的职责分界线的行为(例如日志和事务管理)进行模块化。AOP 的核心构造是方面,它将那些影响多个类的行为封装到可重用的模块中。
AOP 和 IOC 是补充性的技术,它们都运用模块化方式解决企业应用程序开发中的复杂问题。在典型的面向对象开发方式中,可能要将日志记录语句放在所有方法和 Java 类中才能实现日志功能。在 AOP 方式中,可以反过来将日志服务模块化,并以声明的方式将它们应用到需要日志的组件上。当然,优势就是 Java 类不需要知道日志服务的存在,也不需要考虑相关的代码。所以,用 Spring AOP 编写的应用程序代码是松散耦合的。
AOP 的功能完全集成到了 Spring 事务管理、日志和其他各种特性的上下文中。
回页首
IOC 容器
Spring 设计的核心是 org.springframework.beans 包,它的设计目标是与 JavaBean 组件一起使用。这个包通常不是由用户直接使用,而是由服务器将其用作其他多数功能的底层中介。下一个最高级抽象是 BeanFactory 接口,它是工厂设计模式的实现,允许通过名称创建和检索对象。BeanFactory 也可以管理对象之间的关系。
BeanFactory 支持两个对象模型。
* 单态 模型提供了具有特定名称的对象的共享实例,可以在查询时对其进行检索。Singleton 是默认的也是最常用的对象模型。对于无状态服务对象很理想。
* 原型 模型确保每次检索都会创建单独的对象。在每个用户都需要自己的对象时,原型模型最适合。
bean 工厂的概念是 Spring 作为 IOC 容器的基础。IOC 将处理事情的责任从应用程序代码转移到框架。正如我将在下一个示例中演示的那样,Spring 框架使用 JavaBean 属性和配置数据来指出必须设置的依赖关系。
BeanFactory 接口
因为 org.springframework.beans.factory.BeanFactory 是一个简单接口,所以可以针对各种底层存储方法实现。最常用的 BeanFactory 定义是 XmlBeanFactory,它根据 XML 文件中的定义装入 bean,如清单 1 所示。
清单 1. XmlBeanFactory
BeanFactory factory = new XMLBeanFactory(new FileInputSteam("mybean.xml"));
在 XML 文件中定义的 Bean 是被消极加载的,这意味在需要 bean 之前,bean 本身不会被初始化。要从 BeanFactory 检索 bean,只需调用 getBean() 方法,传入将要检索的 bean 的名称即可,如清单 2 所示。
清单 2. getBean()
MyBean mybean = (MyBean) factory.getBean("mybean");
每个 bean 的定义都可以是 POJO (用类名和 JavaBean 初始化属性定义) 或 FactoryBean。FactoryBean 接口为使用 Spring 框架构建的应用程序添加了一个间接的级别。
回页首
IOC 示例
理解控制反转最简单的方式就是看它的实际应用。在对由三部分组成的 Spring 系列 的第 1 部分进行总结时,我使用了一个示例,演示了如何通过 Spring IOC 容器注入应用程序的依赖关系(而不是将它们构建进来)。
我用开启在线信用帐户的用例作为起点。对于该实现,开启信用帐户要求用户与以下服务进行交互:
* 信用级别评定服务,查询用户的信用历史信息。
* 远程信息链接服务,插入客户信息,将客户信息与信用卡和银行信息连接起来,以进行自动借记(如果需要的话)。
* 电子邮件服务,向用户发送有关信用卡状态的电子邮件。
回页首
三个接口
对于这个示例,我假设服务已经存在,理想的情况是用松散耦合的方式把它们集成在一起。以下清单显示了三个服务的应用程序接口。
清单 3. CreditRatingInterface
public interface CreditRatingInterface {
public boolean getUserCreditHistoryInformation(ICustomer iCustomer);
}
清单 3 所示的信用级别评定接口提供了信用历史信息。它需要一个包含客户信息的 Customer 对象。该接口的实现是由 CreditRating 类提供的。
清单 4. CreditLinkingInterface
public interface CreditLinkingInterface {
public String getUrl();
public void setUrl(String url);
public void linkCreditBankAccount() throws Exception ;
}
信用链接接口将信用历史信息与银行信息(如果需要的话)连接在一起,并插入用户的信用卡信息。信用链接接口是一个远程服务,它的查询是通过 getUrl() 方法进行的。URL 由 Spring 框架的 bean 配置机制设置,我稍后会讨论它。该接口的实现是由 CreditLinking 类提供的。
清单 5. EmailInterface
public interface EmailInterface {
public void sendEmail(ICustomer iCustomer);
public String getFromEmail();
public void setFromEmail(String fromEmail) ;
public String getPassword();
public void setPassword(String password) ;
public String getSmtpHost() ;
public void setSmtpHost(String smtpHost);
public String getUserId() ;
public void setUserId(String userId);
}
EmailInterface 负责向客户发送关于客户信用卡状态的电子邮件。邮件配置参数(例如 SMPT 主机、用户名、口令)由前面提到的 bean 配置机制设置。Email 类提供了该接口的实现。
回页首
Spring 使其保持松散
这些接口就位之后,接下来要考虑的就是如何用松散耦合方式将它们集成在一起。在 清单 6 中可以看到信用卡帐户用例的实现。
注意,所有的 setter 方法都是由 Spring 的配置 bean 实现的。所有的依赖关系 (也就是三个接口)都可以由 Spring 框架用这些 bean 注入。createCreditCardAccount() 方法会用服务去执行其余实现。在 清单 7 中可以看到 Spring 的配置文件。我用箭头突出了这些定义。
回页首
运行应用程序
要运行示例应用程序,首先必须 下载 Spring 框架 及其所有依赖文件。接下来,将框架释放到(比如说)磁盘 c:\,这会创建 C:\spring-framework-1.2-rc2 (适用于当前发行版本) 这样的文件夹。在继续后面的操作之前,还必须下载和释放 Apache Ant。
接下来,将源代码释放到文件夹,例如 c:\ 盘,然后创建 SpringProject。将 Spring 库(即 C:\spring-framework-1.2-rc2\dist 下的 spring.jar 和 C:\spring-framework-1.2-rc2\lib\jakarta-commons 下的 commons-logging.jar)复制到 SpringProject\lib 文件夹中。完成这些工作之后,就有了必需的构建依赖关系集。
打开命令提示符,将当前目录切换到 SpringProject,在命令提示符中输入以下命令:build。
这会构建并运行 CreateCreditAccountClient 类,类的运行将创建 Customer 类对象并填充它,还会调用 CreateCreditCardAccount 类创建并链接信用卡帐户。CreateCreditAccountClient 还会通过 ClassPathXmlApplicationContext 装入 Spring 配置文件。装入 bean 之后,就可以通过 getBean() 方法访问它们了,如清单 8 所示。
清单 8. 装入 Spring 配置文件
ClassPathXmlApplicationContext appContext =
new ClassPathXmlApplicationContext(new String[] {
"springexample-creditaccount.xml"
});
CreateCreditCardAccountInterface creditCardAccount =
(CreateCreditCardAccountInterface)
appContext.getBean("createCreditCard");
回页首
结束语
在这篇由三部分组成的 Spring 系列 的第一篇文章中,我介绍了 Spring 框架的基础。我从讨论组成 Spring 分层架构的 7 个模块开始,然后深入介绍了其中两个模块:Spring AOP 和 IOC 容器。
由于学习的最佳方法是实践,所以我用一个工作示例介绍了 IOC 模式 (像 Spring 的 IOC 容器实现的那样)如何用松散耦合的方式将分散的系统集成在一起。在这个示例中可以看到,将依赖关系或服务注入工作中的信用卡帐户应用程序,要比从头开始构建它们容易得多。