springorm源码分析(读懂spring源码)
本文目录一览:
- 1、谈谈spring是如何实现的?
- 2、spring JdbcTemplate批量插入 怎么获得数据库自动增长的id
- 3、spring—AOP与事务
- 4、Spring框架入门
- 5、spring集成了springmvc为什么不直接用,spring+mybatis,而还要用ssm?
- 6、如何在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 容器实现的那样)如何用松散耦合的方式将分散的系统集成在一起。在这个示例中可以看到,将依赖关系或服务注入工作中的信用卡帐户应用程序,要比从头开始构建它们容易得多。
spring JdbcTemplate批量插入 怎么获得数据库自动增长的id
如下,插入一条记录的时候可以这样返回id(一下代码只是说明事例): //保存一个test对象,并返回该对象 public Test save(Test test){ KeyHolder keyHolder = new GeneratedKeyHolder(); getJdbcTemplate().update(new PreparedStatementCreator() { @Override public PreparedStatement createPreparedStatement( Connection con) throws SQLException { PreparedStatement ps = con.prepareStatement("insert into tb_test (name,sex) values (?,?)", Statement.RETURN_GENERATED_KEYS); ps.setString(1, test.getName()); ps.setString(2, test.getSex()); return ps; } }, keyHolder); //这里可以获得数据库id test.setId(Integer.valueOf(keyHolder.getKeyList().get(0) .toString())); return test; } // 批量插入, public ListTest saveOrUpdateAll(final ListTest list) { getJdbcTemplate().batchUpdate( "insert into tb_test (name,sex) values (?,?)", new BatchPreparedStatementSetter() { @Override public void setValues(PreparedStatement ps, int i) throws SQLException { ps.setString(1, list.get(i).getName()); ps.setString(2, list.get(i).getSex()); } @Override public int getBatchSize() { return list.size(); } }); //哪个方法可以获得list中每个test对象的id?我不想重新进行查询 return list; } 问题补充:飞雪无情 写道你已经写好了插入一个对象的方法,就是public Test save(Test test),批量插入的时候可以直接使用该方法的。# public ListTest saveOrUpdateAll(final ListTest list) { ListTest resultList=new ArrayListTest(); for(Test test:list){ resultList.add(save(test)); } return resultList; } 这样就好了。我也考虑过这样的方式,但是把: getJdbcTemplate().update(new PreparedStatementCreator() { @Override public PreparedStatement createPreparedStatement( Connection con) throws SQLException { PreparedStatement ps = con.prepareStatement("insert into tb_test (name,sex) values (?,?)", Statement.RETURN_GENERATED_KEYS); ps.setString(1, test.getName()); ps.setString(2, test.getSex()); return ps; } }, keyHolder); 这么一段代码放在循环里面,会不会性能有些影响吧?Spring既然提供了batchUpdate,该会不会在批处理上有些性能优势呢? 问题补充:飞雪无情 写道spring这个批量插入有点限制,比如你这个特殊需要,我感觉它实现不了,所以你用我上面说的那个方法迂回实现。我感觉性能方面应该不会有太大的影响。你看spring的批量插入的时候这个BatchPreparedStatementSetter借口的方法setValues(PreparedStatement ps, int i) ,提供了一个索引i,它肯定也遍历了,要么怎么知道i的值。所以有特殊需求的时候就用上面那种方案,没有的时候推荐用spring提供了批量操作,我们项目中就这么做的。看了Spring这两个方法的源码,觉得这个东西在封装的时候考虑的不是很周全,确实局限性太大了,比如我批量保存的时候有可能一部分是插入,一部分是更新,这个时候也只能按照你说的这种方式了,比较而言还是orm框架持久层用起来的方便。 spring的这个两个方法看了源码感觉在设计上确实存在缺陷,灵活性不够,其实返回数据库执行的条数意义根本就不大 //其实我想的是这样的一个方法 /** * * @author BAOSJ * @date 2010-7-2 * @param sql * @param values * @param batchSize * @return * @throws Exception */ public ListObject saveOrUpdateAll(String sql, ListObject[] values, Integer batchSize) throws Exception { ResultSet rs = null; PreparedStatement ps = null; Connection conn = null; // 返回执行对象的id ListObject ids = new ArrayListObject(); try { conn = getConnection(); conn.setAutoCommit(false); ps = conn.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS); for (int i = 0; i values.size(); i++) { Object[] objects = values.get(i); for (int j = 0; j objects.length; j++) { ps.setObject(i + 1, objects[j]); } if (i % batchSize == 0 || values.size() = batchSize) { ps.executeBatch(); rs = ps.getGeneratedKeys(); int c = 0; while (rs.next()) { ids.add(rs.getObject(c)); c++; } } } conn.commit(); } catch (Exception e) { conn.rollback(); throw e; } finally { destroy(rs, ps, conn); } return ids; } //update源码 public int update(final PreparedStatementCreator psc, final KeyHolder generatedKeyHolder) throws DataAccessException { Assert.notNull(generatedKeyHolder, "KeyHolder must not be null"); logger.debug("Executing SQL update and returning generated keys"); Integer result = (Integer) execute(psc, new PreparedStatementCallback() { public Object doInPreparedStatement(PreparedStatement ps) throws SQLException { int rows = ps.executeUpdate(); List generatedKeys = generatedKeyHolder.getKeyList(); generatedKeys.clear(); ResultSet keys = ps.getGeneratedKeys(); if (keys != null) { try { RowMapper rowMapper = getColumnMapRowMapper(); RowMapperResultSetExtractor rse = new RowMapperResultSetExtractor(rowMapper, 1); generatedKeys.addAll((List) rse.extractData(keys)); } finally { JdbcUtils.closeResultSet(keys); } } if (logger.isDebugEnabled()) { logger.debug("SQL update affected " + rows + " rows and returned " + generatedKeys.size() + " keys"); } return new Integer(rows); } }); return result.intValue(); } //batchUpdate源码 public int[] batchUpdate(String sql, final BatchPreparedStatementSetter pss) throws DataAccessException { if (logger.isDebugEnabled()) { logger.debug("Executing SQL batch update [" + sql + "]"); } return (int[]) execute(sql, new PreparedStatementCallback() { public Object doInPreparedStatement(PreparedStatement ps) throws SQLException { try { int batchSize = pss.getBatchSize(); InterruptibleBatchPreparedStatementSetter ipss = (pss instanceof InterruptibleBatchPreparedStatementSetter ? (InterruptibleBatchPreparedStatementSetter) pss : null); if (JdbcUtils.supportsBatchUpdates(ps.getConnection())) { for (int i = 0; i batchSize; i++) { pss.setValues(ps, i); if (ipss != null ipss.isBatchExhausted(i)) { break; } ps.addBatch(); } return ps.executeBatch(); } else { List rowsAffected = new ArrayList(); for (int i = 0; i batchSize; i++) { pss.setValues(ps, i); if (ipss != null ipss.isBatchExhausted(i)) { break; } rowsAffected.add(new Integer(ps.executeUpdate())); } int[] rowsAffectedArray = new int[rowsAffected.size()]; for (int i = 0; i rowsAffectedArray.length; i++) { rowsAffectedArray[i] = ((Integer) rowsAffected.get(i)).intValue(); } return rowsAffectedArray; } } finally { if (pss instanceof ParameterDisposer) { ((ParameterDisposer) pss).cleanupParameters(); } } } }); }
spring—AOP与事务
title: spring——AOP与事务.md
date: 2020-07-14 13:10:16
categories: [Spring]
tags: [AOP,事务]
toc: true
先列出源码中比较重点的几个类:
1、aop:before method="before" pointcut-ref="myMethods"/包装成一个advisor
2、AspectJAwareAdvisorAutoProxyCreator,当实例化所有bean都会执行到AspectJAwareAdvisorAutoProxyCreator类
它会检测bean是否advisor以及advice存在,如果有就说明这个bean有切面,有切面那么就会生成代理
3、jdk的代理,bean里面的所有advisor加入到proxyFactory。
4、jdkDynamicProxy invoke,拿到bean里面的所有Interceptor,会循环proxyFactory里面的所有advisor
里面有advice,里面的advice有两种类型,要么是advice,要么是MethodInterceptor类型的
5、当代理对象调用方式,是一个MethodInterceptor类型的类的链式调用过程,直到容器的大小和索引一致的时候调用JoinPoint目标方法
before:this.advice.before(),invocation.processd();
装配参数,切面里面before方法的method对象,method.getParamterTypes()[0]
最终会把advice封装成MethodInterceptor类型的对象
程序执行的某个特定位置:如类开始初始化前、类初始化后、类某个方法调用前、调用后、方法抛出异常后。一个类或一段程序代码拥有一些具有边界性质的特定点,这些点中的特定点就称为“连接点”。Spring仅支持方法的连接点,即仅能在方法调用前、方法调用后、方法抛出异常时以及方法调用前后这些程序执行点织入增强。连接点由两个信息确定:第一是用方法表示的程序执行点;第二是用相对点表示的方位。
每个程序类都拥有多个连接点,如一个拥有两个方法的类,这两个方法都是连接点,即连接点是程序类中客观存在的事物。AOP通过“切点”定位特定的连接点。连接点相当于数据库中的记录,而切点相当于查询条件。切点和连接点不是一对一的关系,一个切点可以匹配多个连接点。在Spring中,切点通过org.springframework.aop.Pointcut接口进行描述,它使用类和方法作为连接点的查询条件,Spring AOP的规则解析引擎负责切点所设定的查询条件,找到对应的连接点。其实确切地说,不能称之为查询连接点,因为连接点是方法执行前、执行后等包括方位信息的具体程序执行点,而切点只定位到某个方法上,所以如果希望定位到具体连接点上,还需要提供方位信息。
增强是织入到目标类连接点上的一段程序代码,在Spring中,增强除用于描述一段程序代码外,还拥有另一个和连接点相关的信息,这便是执行点的方位。结合执行点方位信息和切点信息,我们就可以找到特定的连接点。
增强逻辑的织入目标类。如果没有AOP,目标业务类需要自己实现所有逻辑,而在AOP的帮助下,目标业务类只实现那些非横切逻辑的程序逻辑,而性能监视和事务管理等这些横切逻辑则可以使用AOP动态织入到特定的连接点上。
引介是一种特殊的增强,它为类添加一些属性和方法。这样,即使一个业务类原本没有实现某个接口,通过AOP的引介功能,我们可以动态地为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。
织入是将增强添加对目标类具体连接点上的过程。AOP像一台织布机,将目标类、增强或引介通过AOP这台织布机天衣无缝地编织到一起。根据不同的实现技术,AOP有三种织入的方式:
a、编译期织入,这要求使用特殊的Java编译器。
b、类装载期织入,这要求使用特殊的类装载器。
c、动态代理织入,在运行期为目标类添加增强生成子类的方式。
Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
一个类被AOP织入增强后,就产出了一个结果类,它是融合了原类和增强逻辑的代理类。根据不同的代理方式,代理类既可能是和原类具有相同接口的类,也可能就是原类的子类,所以我们可以采用调用原类相同的方式调用代理类。
切面由切点和增强(引介)组成,它既包括了横切逻辑的定义,也包括了连接点的定义,Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。
advisor: pointCut advice
一类功能的增强
around方法里面代码切面
事务切面
缓存切面
日志切面
事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。是数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作;这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行;事务是一组不可再分割的操作集合(工作逻辑单元)。
大致流程形如
数据库事务拥有几大特性:
事务的四大特性:
事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做
事 务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统 运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是 不一致的状态。
一个事务的执行不能其它事务干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。
也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。
个人理解,事务在Spring中是借助AOP技术来实现的,可以作为AOP中的一个事务切面。spring源码对事务的处理逻辑,自己研究吧!
ORM框架中以Mybatis为例,事务处理就是用到了一个类Transaction,部分源码如下
可以看出Transaction管理的就是一个connection,而connection我们很清楚是与用户会话挂钩的。
那么关系就是Transaction 管理Connection ,而connection与 用户session一对一存在。
在springBoot中,只需要加入POM就可以了,配合注解使用即可。
接下来就是事务的控制了。
首先事务有几大传播属性:
其中最常见的,用得最多就 PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、 PROPAGATION_NESTED 这三种。事务的传播属性是 spring 特有的,是 spring 用来控制方法事务的一种手段,说直白点就是用来控制方法是否使用同一事务的一种属性,以及按照什么规则回滚的一种手段。
下面用代码演示这三种属性的机制:
事务的默认属性就是required,通过Transactional.java中的Propagation propagation() default Propagation.REQUIRED; 可以看出。
这种情况就是事务1,事务2 都加入到了事务0中。不管是1,2哪个事务抛出异常,事务0都会回滚。数据添加会失败。
这种情况就是:
事务0(required) {
事务1 (REQUIRES_NEW)
事务2
}
此时。
情况a:
1、如果只是事务2出现了异常,那么事务1会提交,事务2加入到事务0中会回滚。
2、如果只是事务1出现了异常,那么事务1会回滚,向上层事务0抛异常,事务2会加入到事务0中,这时都会回滚。
情况b:
如果事务1,事务2都是REQUIRES_NEW传播属性。那么结果就是:
1、如果事务1,抛出了异常,那么事务2是不会执行的,那么事务0必然回滚。
2、如果事务2,抛出异常,那么事务1会提交,表中会有数据。事务2有异常回滚并抛出,事务0回滚。
NESTED属性其实就是创建了回滚点,有异常时,会回滚到指定的回滚点。
在这通过代码测试,出现一种情况是,无论事务1,事务2哪个有异常,数据都不会插入成功,原因是,不论是事务1还是事务2都会向事务0抛出异常,事务0捕获到异常后,执行rollback()方法,这就操作成了,事务的全部回滚。
如果想要事务1和事务2 想要根据自己的回滚点回滚,那么事务0必须自己处理异常,不让spring捕获到这个异常,那么就满足了。把代码改成这种:
Jack大佬提供了,伪代码分析法。
按照Spring源码的事务处理逻辑,伪代码大致为:
Spring框架入门
Spring是 分层 的JavaSE/EE ( 一站式 ) 轻量级 开源的 容器框架 springorm源码分析,以 IOC (Inverse of Control 控制反转springorm源码分析:对象之间的关系由容器创建,降低springorm源码分析了程序之间的依赖性)和 AOP (Aspect Oriented Programming 面向切面编程)为内核的容器框架
一站式、分层:
JavaEE的有三层结构:web层、业务层、数据访问层(持久层,集成层),而Spring框架有对三层的每层解决方案:
web层:Spring MVC
持久层:JDBC Template (applicationContext.xml)
业务层:Spring的Bean管理(Bean:是spring根据用户提供相应的需求创建的对象,由IOC管理)(service)
spring官网:
Spring的英文翻译为春天,可以说是给Java程序员带来了春天,因为它 极大地简化了开发,有效的解决了当时软件开发比较复杂的问题 (当时采用EJB开发JAVA EE应用,但EJB存在着学习成本太高,开发效率比较低,资源消耗很高的问题)。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。可以说Spring是一个提供了更完善开发环境的一个框架,可以为POJO(Plain Ordinary Java Object)对象提供企业级的服务。
Spring的优秀之处:
1、最完善的轻量级核心框架。
2、简化了开发,方便解耦
4、集成了Toplink, Hibernate, JDO, and iBATIS SQL Maps等优秀框架。
5、强大的AOP功能:Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能。
6、 方便程序的测试:Spring对Junit4支持,可以通过注解方便的测试Spring程序
这是Spring框架最基础的部分,所有的Spring模块都是在核心容器之上构建的。它提供了依赖注入(DependencyInjection)特征来实现容器对Bean的管理。这里最基本的概念是BeanFactory,它是任何Spring应用的核心。BeanFactory是工厂模式的一个实现,它使用IOC(控制反转)将应用配置和依赖说明从实际的应用代码中分离出来。
核心模块的BeanFactory使Spring成为一个容器,而上下文模块使它成为一个框架。这个模块扩展了BeanFactory的概念,增加了对国际化(I18N)消息、事件传播以及验证的支持(Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息)。
另外,这个模块提供了许多企业服务,例如电子邮件、JNDI访问、EJB集成、远程以及时序调度(scheduling)服务。也包括了对模版框架例如Velocity和FreeMarker集成的支持。
Spring在它的AOP模块中提供了对面向切面编程的丰富支持。这个模块是在Spring应用中实现切面编程的基础。为了确保Spring与其它AOP框架的互用性,Spring的AOP支持基于AOP联盟定义的API。AOP联盟是一个开源项目,它的目标是通过定义一组共同的接口和组件来促进AOP的使用以及不同的AOP实现之间的互用性。通过访问他们的站点,你可以找到关于AOP联盟的更多内容。
Spring的AOP模块也将元数据编程引入了Spring。使用Spring的元数据支持,你可以为你的源代码增加注释,指示Spring在何处以及如何应用切面函数。
使用JDBC经常导致大量的重复代码,取得连接、创建语句、处理结果集,然后关闭连接。Spring的JDBC和DAO模块抽取了这些重复代码,因此你可以保持你的数据库访问代码干净简洁,并且可以防止因关闭数据库资源失败而引起的问题。
这个模块还在几种数据库服务器给出的错误消息之上建立了一个有意义的异常层。使你不用再试图破译神秘的私有的SQL错误消息!
另外,这个模块还使用了Spring的AOP模块为Spring应用中的对象提供了事务管理服务。
对那些更喜欢使用对象/关系映射工具而不是直接使用JDBC的人,Spring提供了ORM模块。Spring并不试图实现它自己的ORM解决方案,而是为几种流行的ORM框架提供了集成方案,包括Hibernate、JDO和iBATIS SQL映射。Spring的事务管理支持这些ORM框架中的每一个也包括JDBC。
Web上下文模块建立于应用上下文模块之上,提供了一个适合于Web应用的上下文。另外,这个模块还提供了一些面向服务支持。例如:实现文件上传的multipart请求,它也提供了Spring和其它Web框架的集成,比如Struts、WebWork。
Spring为构建Web应用提供了一个功能全面的MVC框架。虽然Spring可以很容易地与其它MVC框架集成,例如Struts,但Spring的MVC框架使用IoC对控制逻辑和业务对象提供了完全的分离。
UserService的接口
UserService的实现类
spring集成了springmvc为什么不直接用,spring+mybatis,而还要用ssm?
Spring是一个轻型容器(light-weight container)springorm源码分析,其核心是Bean工厂(Bean Factory),用以构造我们所需要springorm源码分析的M(Model)。在此基础之上,Spring提供springorm源码分析了AOP(Aspect-Oriented Programming, 面向层面的编程)的实现,用它来提供非管理环境下申明方式的事务、安全等服务;对Bean工厂的扩展ApplicationContext更加方便我们实现J2EE的应用;DAO/ORM的实现方便我们进行数据库的开发;Web MVC和Spring Web提供了Java Web应用的框架或与其springorm源码分析他流行的Web框架进行集成。
1、开源框架。
2、IoC(控制反转),将类的创建和依赖关系写在配置文件里,由配置文件注入,实现了松耦合。
3、AOP 将安全,事务等于程序逻辑相对独立的功能抽取出来,利用spring的配置文件将这些功能插进去,实现了按照方面编程,提高了复用性。
如何在spring框架中解决多数据源的问题
转载我首先想到在spring但是springorm源码分析,我很快发现一个问题:当多用户同时并发访问数据库springorm源码分析的时候会出现资源争用的问题。这都是“单例模式”惹的祸。众所周知springorm源码分析,我们在使用spring通过以上的分析springorm源码分析,解决多数据源访问问题的关键,就集中在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="b4ac-d49a-703f-8665 org.apache.commons.dbcp.BasicDataSource"
property name="driverClassName"
valueoracle.jdbc.driver.OracleDrivervalue
property
......
bean
bean id="dataSource2"
class="d49a-703f-8665-44d7 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="703f-8665-44d7-815e com.htxx.service.dao.DynamicLoadBean"bean
bean id="dataSource" class="8665-44d7-815e-701f com.htxx.service.dao.MultiDataSource"
property name="dataSource"
ref bean="dataSource1" /
property
bean
bean id="sessionFactory" class="44d7-815e-701f-5fb7 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="815e-701f-5fb7-59b1 com.htxx.service.dao.MultiSessionFactory"
property name="sessionFactory"ref bean="hostSessionFactory"/property
SpServer的写法与《如何在另外,在spring也许有些朋友对以上方案还不满意,因为在执行数据访问前毕竟还要多做一步指定sessionFactory另外,在这个方案中的核心是运用Decorator前面我已经给出了第一种和第二种情况的解决方案:各个数据源的数据结构不同的情况用MultiSessionFactory
example.rar
描述: 示例文件
下载
文件名: example.rar
文件大小: 16 KB
下载过的: 文件被下载或查看 180 次
建议到原作者博客查看原文,那里有很好的回复