- 浏览: 153185 次
- 性别:
- 来自: 石家庄
文章分类
- 全部博客 (66)
- filnet pe eform (1)
- svn (1)
- ASM (3)
- 算法 k-means (1)
- FileNet unfile query sql (1)
- FileNet fetchRows fetchObjects (1)
- linux chkconfig (1)
- websphere profile linux manageprofiles (1)
- db2 linux install (1)
- IBM Tivoli Directory Server linux (1)
- download jandan.net pic (1)
- jquery picture view (1)
- db2 db2cc (1)
- FileNet (0)
- FileNet redbook (7)
- Test upload (0)
- test 博客2doc (0)
- nginx (1)
- Google-perftools (1)
- openSSL (1)
- pcre (1)
- libunwind (1)
- nginx.conf (1)
- ASM,AOP (0)
- oracle sql (1)
- webservice cxf wsdl2java (1)
- spring3 hibernate4 mutil datasource/database (1)
- java 线程 thread run (1)
- java 64进制 (0)
- java 62进制 (1)
- activemq camel spring jms (2)
- mysql5.5 Master Master Replication (1)
- centos7 hostname httpd port (1)
- centos (1)
- hostname (1)
- change http port (1)
- test (0)
- zookeeper (2)
- 无语面试 (1)
- spring+mybatis+Mysql+proc+分页 (0)
- spring+mybatis+Mysql+proc+分页+paging (2)
最新评论
-
chenchunhuis:
我们最近也在做这个事情,一楼的评论很有道理
spring3+hibernate4+maven+junit 多库/多数据源实现 -
mjs123:
你好,我执行的时候 怎么老是报 No Session foun ...
spring3+hibernate4+maven+junit 多库/多数据源实现 -
chen_bing8:
但是不知道怎么配置默认实例,用JDBC连接后没有默认实例。
linux 下 db2 TCP 服务 配置 -
chen_bing8:
非常好,照着修改成功了
linux 下 db2 TCP 服务 配置 -
yixiandave:
不喜欢写一长串char[]。。。直接写一个String然后调c ...
短ID生成基于62进制
Spring事务配置的五种方法
2009-04-24 10:20
总结如下:
Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分。
DataSource、TransactionManager这两部分只是会根据数据访问方式有所变化,比如使用Hibernate进行数据访问时,DataSource实际为SessionFactory,TransactionManager的实现为HibernateTransactionManager。
具体如下图:
根据代理机制的不同,总结了五种Spring事务的配置方式,配置文件如下:
第一种方式:每个Bean都有一个代理
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean>
<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 配置DAO -->
<bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="userDao"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!-- 配置事务管理器 -->
<property name="transactionManager" ref="transactionManager" />
<property name="target" ref="userDaoTarget" />
<property name="proxyInterfaces" value="com.bluesky.spring.dao.GeneratorDao" />
<!-- 配置事务属性 -->
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
</beans>
第二种方式:所有Bean共享一个代理基类
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean>
<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="transactionBase"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
lazy-init="true" abstract="true">
<!-- 配置事务管理器 -->
<property name="transactionManager" ref="transactionManager" />
<!-- 配置事务属性 -->
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<!-- 配置DAO -->
<bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="userDao" parent="transactionBase" >
<property name="target" ref="userDaoTarget" />
</bean>
</beans>
第三种方式:使用拦截器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean>
<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="transactionInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager" ref="transactionManager" />
<!-- 配置事务属性 -->
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>*Dao</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>transactionInterceptor</value>
</list>
</property>
</bean>
<!-- 配置DAO -->
<bean id="userDao" class="com.bluesky.spring.dao.UserDaoImpl">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</beans>
第四种方式:使用tx标签配置的拦截器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<context:annotation-config />
<context:component-scan base-package="com.bluesky" />
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean>
<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="interceptorPointCuts"
expression="execution(* com.bluesky.spring.dao.*.*(..))" />
<aop:advisor advice-ref="txAdvice"
pointcut-ref="interceptorPointCuts" />
</aop:config>
</beans>
第五种方式:全注解
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<context:annotation-config />
<context:component-scan base-package="com.bluesky" />
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean>
<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</beans>
此时在DAO上需加上@Transactional注解,如下:
package com.bluesky.spring.dao;
import java.util.List;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.support.
HibernateDaoSupport;
import org.springframework.stereotype.Component;
import com.bluesky.spring.domain.User;
@Transactional
@Component("userDao")
public class UserDaoImpl extends HibernateDaoSupport implements UserDao {
public List<User> listUsers() {
return this.getSession().createQuery("from User").list();
}
}
注意的几点:
1 @Transactional 只能被应用到public方法上, 对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能.
2 默认情况下,一个有事务方法, 遇到RuntiomeException 时会回滚 . 遇到 受检查的异常 是不会回滚 的. 要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其它异常}) .
@Transactional 的所有可选属性如下:
属性 类型 默认值 说明
propagation Propagation枚举 REQUIRED 事务传播属性 (下有说明)
isolation isolation枚举 DEFAULT 事务隔离级别 (另有说明)
readOnly boolean false 是否只读
timeout int -1 超时(秒)
rollbackFor Class[] {} 需要回滚的异常类
rollbackForClassName String[] {} 需要回滚的异常类名
noRollbackFor Class[] {} 不需要回滚的异常类
noRollbackForClassName String[] {} 不需要回滚的异常类名
事务的隔离级别 有如下可选:
可以去看spring源码 : org.springframework.transaction.annotation.Isolation
我的"隔离级别"相关文章 有不明白的,可以去看看
DEFAULT 采用数据库默认隔离级别
READ_UNCOMMITTED 请看"隔离级别"相关文章READ_COMMITTED 请看"隔离级别"相关文章REPEATABLE_READ 请看"隔离级别"相关文章SERIALIZABLE 请看 "隔离级别"相关文章
.
事务的传播属性 ,有如下可选
可以去看spring源码 : org.springframework.transaction.annotation.Propagation
REQUIRED 业务方法需要在一个事务中运行,如果方法运行时,已处在一个事务中,那么就加入该事务,否则自己创建一个新的事务.这是spring默认的传播行为.
SUPPORTS 如果业务方法在某个事务范围内被调用,则方法成为该事务的一部分,如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行.
MANDATORY 只能在一个已存在事务中执行,业务方法不能发起自己的事务,如果业务方法在没有事务的环境下调用,就抛异常
REQUIRES_NEW 业务方法总是会为自己发起一个新的事务,如果方法已运行在一个事务中,则原有事务被挂起,新的事务被创建,直到方法结束,新事务才结束,原先的事务才会恢复执行.
NOT_SUPPORTED 声明方法需要事务,如果方法没有关联到一个事务,容器不会为它开启事务.如果方法在一个事务中被调用,该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行.
NEVER 声明方法绝对不能在事务范围内执行,如果方法在某个事务范围内执行,容器就抛异常.只有没关联到事务,才正常执行.
NESTED 如果一个活动的事务存在,则运行在一个嵌套的事务中.如果没有活动的事务,则按REQUIRED属性执行.它使用了一个单独的事务, 这个事务拥有多个可以回滚的保证点.内部事务回滚不会对外部事务造成影响, 它只对DataSour
Spring3 Annotation + Hibernate3-jpa2.0 + CGLIB + 多数据源
文章分类:Java编程
一、定义一个测试用Entity。
Java代码
@Entity
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
@Id @GeneratedValue @Column(name = "id")
private Integer id;
@Column(name = "last_name")
private String lastName;
@Column(name = "first_name")
private String firstName;
public String getFirstName(){return firstName;}
public void setFirstName(String firstName){this.firstName = firstName;}
public String getLastName(){return lastName;}
public void setLastName(String lastName){this.lastName = lastName; }
public Integer getId(){return id;}
public void setId(Integer id){this.id = id;}
}
二、applicationContext.xml与META-INF/persistent.xml
Xml代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!--指定spring容器自动扫描的包:@Repository...-->
<context:component-scan base-package="com.logcd.test,com.logcd.dao,com.logcd.service"/>
<!--使用CGLIB代理-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<bean id="ds1"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/springapp"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<bean id="ds2"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!--动态选择数据源-->
<bean id="dataSource" class="com.logcd.util.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="1" value-ref="ds1"/>
<entry key="2" value-ref="ds2"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="ds1"/>
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
destroy-method="destroy">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="MYSQL"/>
<property name="generateDdl" value="true"/>
<property name="showSql" value="true"/>
</bean>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="dataSource" ref="dataSource"/>
<property name="entityManagerFactory"
ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
Xml代码
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistencehttp://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<!-- local or global(JTA) transaction -->
<persistence-unit name="SpringJpa" transaction-type="RESOURCE_LOCAL"/>
</persistence>
三、在Spring 2.0.1中引入了AbstractRoutingDataSource, 该类充当了DataSource的路由中介, 能有在运行时, 根据某种key值来动态切换到真正的DataSource上, 同时对于不支持事务隔离级别的JTA事务来说, Spring还提供了另外一个类IsolationLevelDataSourceRouter来处理。IsolationLevelDataSourceRouter: 会根据当前Spring受管事务启用的隔离级别来选定合适的DataSource数据源。
Java代码
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DbContextHolder.getDbType();
}
}
Java代码
public class DbContextHolder {
private static final ThreadLocal<Object> contextHolder = new ThreadLocal<Object>();
public static void setDbType(Object dbType) {
contextHolder.set(dbType);
}
public static String getDbType() {
return (String) contextHolder.get();
}
public static void clearDbType() {
contextHolder.remove();
}
}
四、数据访问Dao
Java代码
import java.io.Serializable;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Repository
@Transactional
public class BaseDao<T>{
@PersistenceContext
protected EntityManager em;
public void persist(T t) {
em.persist(t);
}
@Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public T save(T t) {
return em.merge(t);
}
public void delete(T t) {
em.remove(t);
}
@Transactional(readOnly = true, isolation=Isolation.READ_COMMITTED)
public T find(Class<T> clazz, Serializable id) {
return em.getReference(clazz, id);
}
@SuppressWarnings("unchecked")
public List<T> find(String jpql) {
return em.createQuery(jpql).getResultList();
}
@SuppressWarnings("unchecked")
public List<T> find(String jpql, Object param) {
return em.createQuery(jpql).setParameter(1, param).getResultList();
}
@SuppressWarnings("unchecked")
public List<T> find(String jpql, Object[] param) {
Query query = em.createQuery(jpql);
for (int i = 1; i <= param.length; i++) {
query.setParameter(i, param[i - 1]);
}
return query.getResultList();
}
public int findTotalCount(String jpql) {
return ((Long) em.createQuery(jpql).getSingleResult()).intValue();
}
}
五、测试
Java代码
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.util.List;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.logcd.dao.BaseDao;
import com.logcd.entity.Person;
import com.logcd.util.DbContextHolder;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestBaseDao extends AbstractJUnit4SpringContextTests{
@Autowired
private BaseDao<Person> baseDao;
@Test
public void findPersonById(){
List<Person> persons =
baseDao.find("from Person a where a.id = ?", new Integer(1));
assertNotNull(persons);
assertEquals(persons.size(), 1);
for(Person person: persons){
System.out.println(person.getLastName()+" "+person.getFirstName());
}
}
@Test
public void savePerson(){
DbContextHolder.setDbType("2");
Person person = new Person();
person.setFirstName("Bella");
person.setLastName("Edward");
person = baseDao.save(person);
System.out.println(person.getId());
DbContextHolder.clearDbType();
}
@Ignore
public void deletePerson(){
baseDao.delete(baseDao.find(Person.class, new Integer(7)));
}
}
六、关于Spring-AOP: JDK代理和CGLIB代理
1、如果目标对象实现了接口,默认会采用JDK的动态代理机制实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现接口,必须使用CGLIB生成代理,spring会自动在CGLIB和JDK动态代理之间切换
4、如何强制使用CGLIB生成代理?
(1)添加CGLIB库,SPRING_HOME/lib/cglib/*.jar
(2)在spring的配置文件中加入:<aop:aspectj-autoproxy proxy-target-class="true"/>
5、JDK代理和CGLIB代理的区别?
(1)JDK代理只能对实现了接口的类生成代理,而不能针对类
(2)CGLIB是针对类实现代理的,主要对指定的类生成一个子类,并覆盖其中的方法,因为是继承,所以不能使用final来修饰类或方法
七、关于IsolationLevelDataSourceRouter
public class IsolationLevelDataSourceRouterextends AbstractRoutingDataSourceDataSource that routes to one of various target DataSources based on the current transaction isolation level. The target DataSources need to be configured with the isolation level name as key, as defined on the TransactionDefinition interface.
This is particularly useful in combination with JTA transaction management (typically through Spring's JtaTransactionManager). Standard JTA does not support transaction-specific isolation levels. Some JTA providers support isolation levels as a vendor-specific extension (e.g. WebLogic), which is the preferred way of addressing this. As alternative (e.g. on WebSphere), the target database can be represented through multiple JNDI DataSources, each configured with a different isolation level (for the entire DataSource). The present DataSource router allows to transparently switch to the appropriate DataSource based on the current transaction's isolation level.
The configuration can for example look like this, assuming that the target DataSources are defined as individual Spring beans with names "myRepeatableReadDataSource", "mySerializableDataSource" and "myDefaultDataSource":
Xml代码
<bean id="dataSourceRouter" class="org.springframework.jdbc.datasource.lookup.IsolationLevelDataSourceRouter">
<property name="targetDataSources">
<map>
<entry key="ISOLATION_REPEATABLE_READ" value-ref="myRepeatableReadDataSource"/>
<entry key="ISOLATION_SERIALIZABLE" value-ref="mySerializableDataSource"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="myDefaultDataSource"/>
</bean>
Alternatively, the keyed values can also be data source names, to be resolved through a DataSourceLookup: by default, JNDI names for a standard JNDI lookup. This allows for a single concise definition without the need for separate DataSource bean definitions.
Xml代码
<bean id="dataSourceRouter" class="org.springframework.jdbc.datasource.lookup.IsolationLevelDataSourceRouter">
<property name="targetDataSources">
<map>
<entry key="ISOLATION_REPEATABLE_READ" value="java:comp/env/jdbc/myrrds"/>
<entry key="ISOLATION_SERIALIZABLE" value="java:comp/env/jdbc/myserds"/>
</map>
</property>
<property name="defaultTargetDataSource" value="java:comp/env/jdbc/mydefds"/>
</bean>
Note: If you are using this router in combination with Spring's JtaTransactionManager, don't forget to switch the "allowCustomIsolationLevels" flag to "true". (By default, JtaTransactionManager will only accept a default isolation level because of the lack of isolation level support in standard JTA itself.)
Xml代码
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="allowCustomIsolationLevels" value="true"/>
</bean>
Oracle Errors收集
解惑 spring 嵌套事务
/**
* @author 王政
* @date 2006-11-24
* @note 转载请注明出处
*/
在所有使用 spring 的应用中, 声明式事务管理可能是使用率最高的功能了, 但是, 从我观察到的情况看,
绝大多数人并不能深刻理解事务声明中不同事务传播属性配置的的含义, 让我们来看一下 TransactionDefinition 接口中的定义
Java代码
/**
* Support a current transaction, create a new one if none exists.
* Analogous to EJB transaction attribute of the same name.
* <p>This is typically the default setting of a transaction definition.
*/
int PROPAGATION_REQUIRED = 0;
/**
* Support a current transaction, execute non-transactionally if none exists.
* Analogous to EJB transaction attribute of the same name.
* <p>Note: For transaction managers with transaction synchronization,
* PROPAGATION_SUPPORTS is slightly different from no transaction at all,
* as it defines a transaction scopp that synchronization will apply for.
* As a consequence, the same resources (JDBC Connection, Hibernate Session, etc)
* will be shared for the entire specified scope. Note that this depends on
* the actual synchronization configuration of the transaction manager.
* @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setTransactionSynchronization
*/
int PROPAGATION_SUPPORTS = 1;
/**
* Support a current transaction, throw an exception if none exists.
* Analogous to EJB transaction attribute of the same name.
*/
int PROPAGATION_MANDATORY = 2;
/**
* Create a new transaction, suspend the current transaction if one exists.
* Analogous to EJB transaction attribute of the same name.
* <p>Note: Actual transaction suspension will not work on out-of-the-box
* on all transaction managers. This in particular applies to JtaTransactionManager,
* which requires the <code>javax.transaction.TransactionManager</code> to be
* made available it to it (which is server-specific in standard J2EE).
* @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
*/
int PROPAGATION_REQUIRES_NEW = 3;
/**
* Execute non-transactionally, suspend the current transaction if one exists.
* Analogous to EJB transaction attribute of the same name.
* <p>Note: Actual transaction suspension will not work on out-of-the-box
* on all transaction managers. This in particular applies to JtaTransactionManager,
* which requires the <code>javax.transaction.TransactionManager</code> to be
* made available it to it (which is server-specific in standard J2EE).
* @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
*/
int PROPAGATION_NOT_SUPPORTED = 4;
/**
* Execute non-transactionally, throw an exception if a transaction exists.
* Analogous to EJB transaction attribute of the same name.
*/
int PROPAGATION_NEVER = 5;
/**
* Execute within a nested transaction if a current transaction exists,
* behave like PROPAGATION_REQUIRED else. There is no analogous feature in EJB.
* <p>Note: Actual creation of a nested transaction will only work on specific
* transaction managers. Out of the box, this only applies to the JDBC
* DataSourceTransactionManager when working on a JDBC 3.0 driver.
* Some JTA providers might support nested transactions as well.
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager
*/
int PROPAGATION_NESTED = 6;
我们可以看到, 在 spring 中一共定义了六种事务传播属性, 如果你觉得看起来不够直观, 那么我来转贴一个满大街都有的翻译
引用
PROPAGATION_REQUIRED -- 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS -- 支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY -- 支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW -- 新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED -- 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER -- 以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED -- 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
前六个策略类似于EJB CMT,第七个(PROPAGATION_NESTED)是Spring所提供的一个特殊变量。
它要求事务管理器或者使用JDBC 3.0 Savepoint API提供嵌套事务行为(如Spring的DataSourceTransactionManager)
在我所见过的误解中, 最常见的是下面这种:
引用
假如有两个业务接口 ServiceA 和 ServiceB, 其中 ServiceA 中有一个方法实现如下
/**
* 事务属性配置为 PROPAGATION_REQUIRED
*/
void methodA() {
// 调用 ServiceB 的方法
ServiceB.methodB();
}
那么如果 ServiceB 的 methodB 如果配置了事务, 就必须配置为 PROPAGATION_NESTED
这种想法可能害了不少人, 认为 Service 之间应该避免互相调用, 其实根本不用担心这点,PROPAGATION_REQUIRED 已经说得很明白,
如果当前线程中已经存在事务, 方法调用会加入此事务, 果当前没有事务,就新建一个事务, 所以 ServiceB#methodB() 的事务只要遵循最普通的规则配置为 PROPAGATION_REQUIRED 即可, 如果 ServiceB#methodB (我们称之为内部事务, 为下文打下基础) 抛了异常, 那么 ServiceA#methodA(我们称之为外部事务) 如果没有特殊配置此异常时事务提交 (即 +MyCheckedException的用法), 那么整个事务是一定要 rollback 的, 什么 Service 只能调 Dao 之类的言论纯属无稽之谈, spring 只负责配置了事务属性方法的拦截, 它怎么知道你这个方法是在 Service 还是 Dao 里 ?
说了这么半天, 那到底什么是真正的事务嵌套呢, 解释之前我们来看一下 Juergen Hoeller 的原话
Juergen Hoeller 写道
PROPAGATION_REQUIRES_NEW starts a new, independent "inner" transaction for the given scope. This transaction will be committed or rolled back completely independent from the outer transaction, having its own isolation scope, its own set of locks, etc. The outer transaction will get suspended at the beginning of the inner one, and resumed once the inner one has completed.
Such independent inner transactions are for example used for id generation through manual sequences, where the access to the sequence table should happen in its own transactions, to keep the lock there as short as possible. The goal there is to avoid tying the sequence locks to the (potentially much longer running) outer transaction, with the sequence lock not getting released before completion of the outer transaction.
PROPAGATION_NESTED on the other hand starts a "nested" transaction, which is a true subtransaction of the existing one. What will happen is that a savepoint will be taken at the start of the nested transaction. íf the nested transaction fails, we will roll back to that savepoint. The nested transaction is part of of the outer transaction, so it will only be committed at the end of of the outer transaction.
Nested transactions essentially allow to try some execution subpaths as subtransactions: rolling back to the state at the beginning of the failed subpath, continuing with another subpath or with the main execution path there - all within one isolated transaction, and not losing any previous work done within the outer transaction.
For example, consider parsing a very large input file consisting of account transfer blocks: The entire file should essentially be parsed within one transaction, with one single commit at the end. But if a block fails, its transfers need to be rolled back, writing a failure marker somewhere. You could either start over the entire transaction every time a block fails, remembering which blocks to skip - or you mark each block as a nested transaction, only rolling back that specific set of operations, keeping the previous work of the outer transaction. The latter is of course much more efficient, in particular when a block at the end of the file fails.
Juergen Hoeller 写道
Rolling back the entire transaction is the choice of the demarcation code/config that started the outer transaction.
So if an inner transaction throws an exception and is supposed to be rolled back (according to the rollback rules), the transaction will get rolled back to the savepoint taken at the start of the inner transaction. The immediate calling code can then decide to catch the exception and proceed down some other path within the outer transaction.
If the code that called the inner transaction lets the exception propagate up the call chain, the exception will eventually reach the demarcation code of the outer transaction. At that point, the rollback rules of the outer transaction decide whether to trigger a rollback. That would be a rollback of the entire outer transaction then.
So essentially, it depends on your exception handling. If you catch the exception thrown by the inner transaction, you can proceed down some other path within the outer transaction. If you let the exception propagate up the call chain, it's eventually gonna cause a rollback of the entire outer transaction.
也就是说, 最容易弄混淆的其实是 PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED, 那么这两种方式又有何区别呢? 我简单的翻译一下 Juergen Hoeller 的话 :
PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 "内部" 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行.
另一方面, PROPAGATION_NESTED 开始一个 "嵌套的" 事务, 它是已经存在事务的一个真正的子事务. 潜套事务开始执行时, 它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 潜套事务是外部事务的一部分, 只有外部事务结束后它才会被提交.
由此可见, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大区别在于, PROPAGATION_REQUIRES_NEW 完全是一个新的事务, 而 PROPAGATION_NESTED 则是外部事务的子事务, 如果外部事务 commit, 潜套事务也会被 commit, 这个规则同样适用于 roll back.
那么外部事务如何利用嵌套事务的 savepoint 特性呢, 我们用代码来说话
Java代码
ServiceA {
/**
* 事务属性配置为 PROPAGATION_REQUIRED
*/
void methodA() {
ServiceB.methodB();
}
}
ServiceB {
/**
* 事务属性配置为 PROPAGATION_REQUIRES_NEW
*/
void methodB() {
}
}
这种情况下, 因为 ServiceB#methodB 的事务属性为 PROPAGATION_REQUIRES_NEW, 所以两者不会发生任何关系, ServiceA#methodA 和 ServiceB#methodB 不会因为对方的执行情况而影响事务的结果, 因为它们根本就是两个事务, 在 ServiceB#methodB 执行时 ServiceA#methodA 的事务已经挂起了 (关于事务挂起的内容已经超出了本文的讨论范围, 有时间我会再写一些挂起的文章) .
那么 PROPAGATION_NESTED 又是怎么回事呢? 继续看代码
Java代码
ServiceA {
/**
* 事务属性配置为 PROPAGATION_REQUIRED
*/
void methodA() {
ServiceB.methodB();
}
}
ServiceB {
/**
* 事务属性配置为 PROPAGATION_NESTED
*/
void methodB() {
}
}
现在的情况就变得比较复杂了, ServiceB#methodB 的事务属性被配置为 PROPAGATION_NESTED, 此时两者之间又将如何协作呢? 从 Juergen Hoeller 的原话中我们可以找到答案, ServiceB#methodB 如果 rollback, 那么内部事务(即 ServiceB#methodB) 将回滚到它执行前的 SavePoint(注意, 这是本文中第一次提到它, 潜套事务中最核心的概念), 而外部事务(即 ServiceA#methodA) 可以有以下两种处理方式:
1. 改写 ServiceA 如下
Java代码
ServiceA {
/**
* 事务属性配置为 PROPAGATION_REQUIRED
*/
void methodA() {
try {
ServiceB.methodB();
} catch (SomeException) {
// 执行其他业务, 如 ServiceC.methodC();
}
}
}
这种方式也是潜套事务最有价值的地方, 它起到了分支执行的效果, 如果 ServiceB.methodB 失败, 那么执行 ServiceC.methodC(), 而 ServiceB.methodB 已经回滚到它执行之前的 SavePoint, 所以不会产生脏数据(相当于此方法从未执行过), 这种特性可以用在某些特殊的业务中, 而 PROPAGATION_REQUIRED 和 PROPAGATION_REQUIRES_NEW 都没有办法做到这一点. (题外话 : 看到这种代码, 似乎似曾相识, 想起了 prototype.js 中的 Try 函数 )
2. 代码不做任何修改, 那么如果内部事务(即 ServiceB#methodB) rollback, 那么首先 ServiceB.methodB 回滚到它执行之前的 SavePoint(在任何情况下都会如此),
外部事务(即 ServiceA#methodA) 将根据具体的配置决定自己是 commit 还是 rollback (+MyCheckedException).
上面大致讲述了潜套事务的使用场景, 下面我们来看如何在 spring 中使用 PROPAGATION_NESTED, 首先来看 AbstractPlatformTransactionManager
Java代码
/**
* Create a TransactionStatus for an existing transaction.
*/
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
... 省略
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
if (!isNestedTransactionAllowed()) {
throw new NestedTransactionNotSupportedException(
"Transaction manager does not allow nested transactions by default - " +
"specify 'nestedTransactionAllowed' property with value 'true'");
}
if (debugEnabled) {
logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
}
if (useSavepointForNestedTransaction()) {
// Create savepoint within existing Spring-managed transaction,
// through the SavepointManager API implemented by TransactionStatus.
// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
DefaultTransactionStatus status =
newTransactionStatus(definition, transaction, false, false, debugEnabled, null);
status.createAndHoldSavepoint();
return status;
}
else {
// Nested transaction through nested begin and commit/rollback calls.
// Usually only for JTA: Spring synchronization might get activated here
// in case of a pre-existing JTA transaction.
doBegin(transaction, definition);
boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER);
return newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);
}
}
}
一目了然
1. 我们要设置 transactionManager 的 nestedTransactionAllowed 属性为 true, 注意, 此属性默认为 false!!!
再看 AbstractTransactionStatus#createAndHoldSavepoint() 方法
Java代码
/**
* Create a savepoint and hold it for the transaction.
* @throws org.springframework.transaction.NestedTransactionNotSupportedException
* if the underlying transaction does not support savepoints
*/
public void createAndHoldSavepoint() throws TransactionException {
setSavepoint(getSavepointManager().createSavepoint());
}
可以看到 Savepoint 是 SavepointManager.createSavepoint 实现的, 再看 SavepointManager 的层次结构, 发现
其 Template 实现是 JdbcTransactionObjectSupport, 常用的 DatasourceTransactionManager, HibernateTransactionManager
中的 TransactonObject 都是它的子类 :
JdbcTransactionObjectSupport 告诉我们必须要满足两个条件才能 createSavepoint :
2. java.sql.Savepoint 必须存在, 即 jdk 版本要 1.4+
3. Connection.getMetaData().supportsSavepoints() 必须为 true, 即 jdbc drive 必须支持 JDBC 3.0
确保以上条件都满足后, 你就可以尝试使用 PROPAGATION_NESTED 了. (全文完)
第一章
Spring框架简介
2
Spring简介
Spring是一个开源框架,是为了解决企业应用开发的复杂性而创建的。
目的:解决企业应用开发的复杂性。
功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能 。
范围:任何Java应用 。
简单来说,Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。
3
Spring简介
轻量——从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。
非侵入 ——在应用中,一般不需要引用springjar包里的类。
控制反转——Spring通过一种称作控制反转(IoC)的技术促进了松耦合。当应用了IoC,某一接口的具体实现类的选择控制权从调用类中移除,转交给第三方裁决。
面向切面——Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务()管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责其它的系统级关注点,例如日志或事务支持。
4
Spring简介
Spring即是一个容器又是一个框架。
容器——Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建——基于一个可配置原型(prototype),你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例——以及它们是如何相互关联的。
框架——Spring提供了很多基础的与业务逻辑无关的功能,比如:事务管理、持久化框架集成等等,使用Spring框架,开发人员可以专注于业务逻辑开发,这个意义上讲它是一个框架。
5
第二章
控制反转(IOC)
6
控制反转
IOC(控制反转:Inverse of Control)是Spring的内核,AOP,声明式事务等功能都建立在该功能之上,它涉及到代码解耦,设计模式,代码优化等问题的考虑。
它包括两个方面的内容:其一是控制;其二是反转。到底是什么东西的控制被反转了呢,来看个例子:
导演,剧本,场景,剧中人,演员,
7
控制反转
对于软件来说:控制反转即是某一接口的具体实现类的选择控制权从调用类中移除,转交给第三方裁决。
依赖注入DI(Dependency Injection): 将调用类对接口的实现类的依赖关系由第三方注入,以移除调用类对接口实现类的依赖。
8
控制反转
public class SaveEmployeeEditAction extends SsoaAction {
//定义接口
public EmployeeManager employeeManager;
//属性注入
public void setEmployeeManager(EmployeeManager employeeManager) {
this.employeeManager = employeeManager;
}
public String execute() throws Exception {
//使用接口
employeeManager.updateEmployee(ee);
}
}
第三方控制实现类
<bean id="employeeManager"
class="com.soft.ssoa.services.department.EmployeeManagerImpl">
<property name="employeeManagerDao">
<ref bean="employeeManagerDao" />
</property>
</bean>
9
IOC类型
构造函数注入
在构造函数注入中,通过调用类的构造函数将接口实现类通过构造函数变量传入,如下代码所示
public class MenuBufferImpl {
public MenuBufferImpl(){}
public MenuBufferImpl(PrivilegeDAO privilegeDAO) {
this.privilegeDAO = privilegeDAO;
}
}
//注入
<bean id="menuBuffer" class="com.topsoft.prv.domain.MenuBufferImpl">
<constructor-arg>
<ref local="privilegeDAO"/>
</constructor-arg>
</bean>
<bean id="privilegeDAO" class="com.topsoft.prv.domain.dao.PrivilegeDAOImpl"/>
控制反转
10
控制反转
属性注入
属性注入可以有选择地通过Setter方法完成调用类所需依赖的注入,更加灵活方便。
一般使用属性注入。
见前面例子
11
控制反转
课后扩展:
刚才看到,spring框架通过一个配置文件实现控制反转,在容器启动时根据配置文件的描述信息,自动实例化Bean并完成依赖关系的建立,从容器中即可返回准备就绪的Bean实例,以待以后使用。Spring之所以有这样的能力,主要归功于java语言本身的类反射功能,反射机制简单的说就是我们利用编程的方式实现类的实例化,大家课下可以查询这方面的知识,加深对控制反转的了解。
12
BeanFactory
Spring实现控制反转是通过配置方式来实现的,主要是xml配置文件,该文件一般命名为 *-beans.xml,格式一般如下:
13
控制反转-BeanFactory
Spring通过该配置文件描述Bean及Bean之间的依赖关系,在容器启动时实例化Bean并建立Bean之间的依赖关系。Spring的Ioc容器在完成这些底层工作的基础上,还提供了Bean实例缓存、生命周期管理、Bean实例代理、事件发布、资源装载等高级服务。
BeanFactory(Bean工厂)是Spring框架最核心的接口它提供了高级Ioc的配置机制,使管理不同类型的java对象成为可能。
一般我们称BeanFactory为Ioc容器。
使用如下:
14
ApplicationContext(应用上下文)建立在BeanFactory之上,提供了更多面向应用的功能,更易于创建实际应用,有时在web开发中为了方便也称ApplicationContext为Spring容器。
控制反转-ApplicationContext
15
WebApplicationContext是专门为Web应用准备的,它允许从相对于Web根目录的路径中装载配置文件完成初始化工作。从WebApplicationContext中可以获得ServletContext的引用整个web应用上下文对象将作为属性放置到ServletContext中,以便Web应用环境可以访问Spring引用上下文。Spring专门提供了一个工具类WebApplicationContextUtils,通过该类的getWebApplicationContext(ServletContext sc) 方法可以从ServletCOntext中获取WebApplicationContext实例。
使用例子:
控制反转-WebApplicationContext
16
控制反转
17
控制反转-装配bean
装配一个bean
<?xml version="1.0" encoding="gb2312"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<description>权限管理prv</description>
<bean id="car" class="Ioc.car"></bean>
<bean id="boss" class="Ioc.Boss"></bean>
</beans>
上段信息提供了实例化Car和Boss这两个Bean必须的信息,Ioc容器完全可以根据该信息创建者连个Bean实例。
Bean命名:
一般情况,在配置一个bean是,需要为期指定一个id属性作为Bean的名称,id在Ioc容器中必须是唯一的,此外id的命名需要满足XML对id的命名规范:必须以字母开始,逗号和空格这些非完全结束符号是非法的。如果实际使用中需要使用一些id不允许的特殊字符,可以使用bean的name属性来命名,name没有命名上的限制,几乎可以使用任何字符,如?ab,123等。
Id和name都可以指定多个名字,名字中间使用逗号、分号、或者空格进行分割,
<bean name=“?car1,car2,#car3” calss=“Ioc.car”/>
这里为bean定义了三个名字?car1,car2,#car3,他们是一样的,可以通过任何一个取得bean实例。
配置文件中不能出现相同的id,但可以出现相同的name,根据name取得bean时,以最后出现的name为准。
推荐使用id .
18
属性注入配置
使用属性注入时,bean配置方式如下:
<bean id="car" class="Ioc.car"></bean>
<bean id="boss" class="Ioc.Boss">
<property name="name">
<idref bean="car"/>
</property>
<property name="car">
<value>hongqi,200,2000</value>
</property>
</bean>
两种方式设置属性的值,1,通过字面值value;2,通过idref引用别的bean.
上例中name的值是car.
控制反转-装配bean
19
控制反转-装配bean
构造函数注入装配
构造函数注入通过<constructor-arg>标记完成。
1,按类型匹配入参
<bean id="car" class="Ioc.Car">
<constructor-arg type=“java.lang.String”>
<value><![CDATA[hongi&asldf]]></value>
</constructor-arg>
<constructor-arg type=“java.lang.String” value="hehe"/>
<constructor-arg type=“java.lang.int” value="3"/>
<constructor-arg>
<set>
<value>${barand}</value>
<value>${color}</value>
<value>${maxSpeed}</value>
</set>
</constructor-arg>
</bean>
2,按索引匹配入参
<bean id="car" class="Ioc.Car">
<constructor-arg index="0">
<value><![CDATA[hongi&asldf]]></value>
</constructor-arg>
<constructor-arg index="1" value="hehe"/>
<constructor-arg index="2" value="3"/>
<constructor-arg>
<set>
<value>${barand}</value>
<value>${color}</value>
<value>${maxSpeed}</value>
</set>
</constructor-arg>
</bean>
20
注入参数详解
1,字面值
即可用字符串表示的值,这些值<value>元素标签进行注入 ,比如
<property name="car">
<value>hongqi</value>
</property>
对于一些特殊符号可以使用<![CDATA[]]>的特殊处理标签,比如:
<property name="car">
<value> <![CDATA[轿车&@@car]]></value>
</property>
当然也可以使用转义序列处理特殊符号,xml中共有五个特殊符号,它们的转义序列如下:
< <
> >
& &
“ "
‘ '
控制反转-装配bean
21
2, 引用其它bean
idref
idref元素用来将容器内其它bean的id传给<constructor-arg/>或<property/>元素,同时提供错误难功能。
<property name="name">
<idref bean="car"/>
</property>
ref
形式一:<ref bean=“someBean”>
这是最常见的形式是通过使用ref标记指定bean属性的目标bean,通过该标签可以引用同一容器或父容器内的任何bean(无论是否在同一XML文件中)。
XML‘bean’元素的值即可以是指定的bean的id值也可以是其name值。
形式二:<ref local=“someBean”>
使用ref的local属性指定目标bean,它可以利用XML解析器来难所引用的bean是否存在同一文件中。local属性值必须是目标bean的id属性值。
形式三:<bean parent="someBean">
通过使用ref的parent属性来引用当前窗口的父容器中的bean。parent属性值即可以是目标bean的id值,也可以是name属性值。
控制反转-装配bean
22
3、内部 bean:
所谓内部bean(inner bean)是指在一个bean的<property/>或<constructor-arg/>中使用<bean/>元素定义的bean.
<bean id="outer" class="...">
?<property name="target">
? <bean class="com.mycoompany.Person">
?? <property name="name" value="Fiona Apple"/>
?? <property name="age" value="25"/>
? </bean>
?</property>
</bean>
内部bean不能被其它bean引用,只能在声明处为外部bean提供实例注入。
内部bean即使提供了id,name,scope属性也会被忽略.
控制反转-装配bean
23
4、集合合并:
从Spring2.0开始,Spring IoC容器将支持集合的合并。父子集合元素合并后的值就是子集合中的最终结果,而且子集合中的元素值将覆盖父集合中的对应的值。
<beans>
<bean id="parent" abstract="true" class="example.ComplexObject">
?<property name="adminEmails">
? <props>
?? <prop key="administrator">administrator@somecompany.com</prop>
?? <prop key="support">support@somecompany.com</prop>
? </props>
?</property>
</bean>
<bean id="child" parent="parent">
?<property name="adminEmails">
? <props merge="trur">
?? <prop key="sales">sales@somecompany.com</prop>
?? <prop key="support">support@somecompany.co.uk</prop>
? </props>
?</property>
</bean>
</beans>
合并后内容:
administrator=administrator@somecompany.co m
sales=sales@somecompany.com
support=support@somecompany.co.uk
list集合有排序功能,父bean的列表内容将排在子bean列表内容的前面;
merge属性必须在继承的子bean中定义。
控制反转-装配bean
24
5、Nulls
<null/>用于处理null值。Spring会把属性的空参数当作空字符串处理。
<bean class="ExampleBean">
?<property name="email">
? <value></value>
?</property>
</bean>
等同于
excapleBean.setEamil("");
而null值则可以使用<null/>元素来表示:
<bean class="ExampleBean">
?<property name="email"><null/></property>
</bean>
控制反转-装配bean
25
6、简写:
针对常见的value值或bean的引用,Spring提供了简化格式用于替代<value/>和<ref/>元素。
<property/>、<constructor-arg/>、<entry/>元素都支持value属性,它可以用来替代内嵌的<value/>元素。
<property name="myProperty">???????????????????????????
?<value>hello</value>?????????? =====??????????? <property name="myProperty" value="helo" />
</property>
<constructor-arg>
?<value>hello</value>?????????? =====??????????? <constructor-arg value="hello" />
</construtctor-arg>
<entry key="myKey">
?<value>hello</value>?????????? =====??????????? <entry key="myKey" value="hello" />
</entry>
<property/>和<constructor-arg/>支持类似的简写属性ref,它可以替找整个内嵌的</ref>元素。
<property name="myProperty">
?<ref bean="myBean">??????????? =====??????????? <property name="myProperty" ref="myBean" />
</property>
<constructor-arg>
?<ref bean="myBean">??????????? =====??????????? <constructor-arg ref="myBean" />
</constructor-arg>
切记:尽管存在等同于<ref bean="xxx" >元素的简写形式,但并没有<ref local="xxx">的简写形式。
map中的entry元素的简写形式为key/key-ref和value/value-ref属性。
<entry>
?<key>
? <ref bean="myKeyBean" />??????????? =====????????? <entry key-ref="myKeyBean" value-ref="myValueBean" />
?</key>
?<ref bean="myValueBean" />
</entry>
Spring 建议使用简写形式
控制反转-装配bean
26
7、延迟初始化bean--lazy-init 属性:
<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true">
?<!-- various properties here... -->
</bean>
<bean name="noo.lazy" class="com.foo.AnotherBean">
?<!-- various properties here... -->
</bean>
如果一个bean被设置为延迟初始化,而另一个非延迟初始化的singleton bean依赖于它,那么当ApplicationContext提前实例化singleton bean时,它必须也确保所有上述singleton依赖bean也被预先初始化,当然也包括设置为延迟实例化的bean.
在容器层次中通过在<beans />元素上使用‘default-lazy-init’属性来控制延迟初始化也是可能的。
<beans default-lazy-init="true">
?<!-- no beans will be eagerly pre-instantiated... -->
</beans>
控制反转-装配bean
27
8、自动装配< autowire > 属性:
模式???? 说明
no????? 不使用自动装配,必须通过ref元素指定依赖,这是默认设置。
byName???? 根据属性名自动装配。Spring将检查容器并根据名字查找与属性完全一致的bean,并将其与属性自动装配。
byType???? 如果容器中存在一个与指定属性类型相同的bean,那么将与该属性自动装配。如果存在多个,则抛出异常。
constructor??? 与byType的方式类似,不同之处在于它应用于构造器参数。如果在容器中未找到与构造器参数类型一致的bean,那么将抛出异常。
autodetect??? 通过bean类的自省机制(introspection)来决定是使用constructor还是byType方式进行自动装配。如果发现默认的构造器,那么将使用byType方式。
控制反转-装配bean
28
Bean之间的关系
1,继承
如果多个bean存在相同的配置信息,spring允许我们定义一个父<bean>,子<bean>将自动继承父<bean>的配置信息。
<bean id="abstractCar" class="Ioc.Car" abstract="true">
<property name="barand" value="barand"/>
<property name="color" value="cc"/>
</bean>
<bean id="car2" parent="abstractCar">
<property name="maxSpeed">
<value>200</value>
</property>
</bean>
控制反转-装配bean
29
2,依赖
depends-on属性可以用于当前bean初始化之前显式的强制一个或多个bean被初始化。
<bean id="beanOne" class="ExampleBean" depends-on="manager">
?<property name="manager" ref="manager" />
</bean>
<bean id="manager" class="ManagerBean" />
若需要表达对多个bean的依赖,可以在<depends-on />中将指定的多个bean名字用分隔符进行分隔,分隔符可以是逗号、空格及分号等。
<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
?<property name="manager" ref="manager" />
</bean>
<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
3,引用
即前面讲的idref
控制反转-装配bean
30
循环依赖问题
Spring容器能够顺利实例化以构造函数注入进行装配的bean有一个前提:Bean构造函数入参引用的对象必须已经准备就绪。由于这个机制的限制,如果两个bean都采用构造函数注入,而且都通过构造函数入参引用对方,就会发生类似于线程死锁的循环依赖问题。如下:
Public class car{
…
Public car(Boss boss){
this.boss=boss;
}
}
Public class boss{
…
Public boss(Car car){
this.car=carl;
}
}
当启动Ioc容器时,由于存在循环依赖问题,Spring容器将无法成功启动,这时我们可以混合使用函数注入和属性输入来解决该问题。即,把上面的一个函数注入改为属性注入方式。
控制反转-装配bean
31
第三章
面向切面编程(AOP)基础
32
面向切面编程(AOP)
AOP相关概念
连接点(Joinpoint):
程序执行的某个特定位置:比如类开始初始化前、类初始化后、某个方法调用前、调用后等。一个类或一段程序代码拥有的一些具有边界性质的特定点,这些代码中的特定点就称为“连接点”,Spring仅支持方法的连接点。
切点(Pointcut):
匹配连接点(Joinpoint)的断言。连接点相当于数据记录,切点相当于查询条件,一个切点可以匹配多个连接点。
增强(Advice):
织入到目标类连接点上的一段程序代码。
目标对象(Target):
增强逻辑的织入目标类
引介(Introduction)
引介是一种特殊的增强,为类添加一些属性和方法。通过它可以动态的为业务类增加接口的实现逻辑,让业务类成为这个接口的实现类。
33
织入(Weaving):
将增强添加到对目标类具体连接点上的过程。
代理(Proxy)
一个类被AOP织入增强后,就产生了一个结果类,它是融合了原类和增强逻辑的代理类。根据不同的代理方式,代理类既可能是和原类具有相同接口的类,也可能是原类的子类,所以我们可以采用调用原类相同的方式调用代理类
切面(Aspect)
切面由切点和增强(引介)组成,它既包括了横切逻辑的定义,也包括了连接点的定义,spring AOP就是负责实施 切面的框架,它将切面定义的横切逻辑织入到切面所指定的连接点中。
AOP的工作重点在于如何将增强应用于目标对象的连接点上,这包括两个工作:
1,如何通过切点和增强定位到连接点上;
2,如何在增强中编写切面的代码;
面向切面编程(AOP)
34
代理机制:
AOP通过动态代理技术在运行期织入增强代码,首先了解下AOP使用的两种代理机制:
1,基于JDK的动态代理
JDK1.3以后,java提供了动态代理技术,允许开发者在运行期创建接口的代理实例。
JDK动态代理主要涉及两个类,
Java.lang.reflect.Proxy
Java.lang.reflect. InvocationHandler
InvocationHandler是一
2009-04-24 10:20
总结如下:
Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分。
DataSource、TransactionManager这两部分只是会根据数据访问方式有所变化,比如使用Hibernate进行数据访问时,DataSource实际为SessionFactory,TransactionManager的实现为HibernateTransactionManager。
具体如下图:
根据代理机制的不同,总结了五种Spring事务的配置方式,配置文件如下:
第一种方式:每个Bean都有一个代理
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean>
<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 配置DAO -->
<bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="userDao"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!-- 配置事务管理器 -->
<property name="transactionManager" ref="transactionManager" />
<property name="target" ref="userDaoTarget" />
<property name="proxyInterfaces" value="com.bluesky.spring.dao.GeneratorDao" />
<!-- 配置事务属性 -->
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
</beans>
第二种方式:所有Bean共享一个代理基类
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean>
<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="transactionBase"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
lazy-init="true" abstract="true">
<!-- 配置事务管理器 -->
<property name="transactionManager" ref="transactionManager" />
<!-- 配置事务属性 -->
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<!-- 配置DAO -->
<bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="userDao" parent="transactionBase" >
<property name="target" ref="userDaoTarget" />
</bean>
</beans>
第三种方式:使用拦截器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean>
<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="transactionInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager" ref="transactionManager" />
<!-- 配置事务属性 -->
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>*Dao</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>transactionInterceptor</value>
</list>
</property>
</bean>
<!-- 配置DAO -->
<bean id="userDao" class="com.bluesky.spring.dao.UserDaoImpl">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</beans>
第四种方式:使用tx标签配置的拦截器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<context:annotation-config />
<context:component-scan base-package="com.bluesky" />
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean>
<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="interceptorPointCuts"
expression="execution(* com.bluesky.spring.dao.*.*(..))" />
<aop:advisor advice-ref="txAdvice"
pointcut-ref="interceptorPointCuts" />
</aop:config>
</beans>
第五种方式:全注解
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<context:annotation-config />
<context:component-scan base-package="com.bluesky" />
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean>
<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</beans>
此时在DAO上需加上@Transactional注解,如下:
package com.bluesky.spring.dao;
import java.util.List;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.support.
HibernateDaoSupport;
import org.springframework.stereotype.Component;
import com.bluesky.spring.domain.User;
@Transactional
@Component("userDao")
public class UserDaoImpl extends HibernateDaoSupport implements UserDao {
public List<User> listUsers() {
return this.getSession().createQuery("from User").list();
}
}
注意的几点:
1 @Transactional 只能被应用到public方法上, 对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能.
2 默认情况下,一个有事务方法, 遇到RuntiomeException 时会回滚 . 遇到 受检查的异常 是不会回滚 的. 要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其它异常}) .
@Transactional 的所有可选属性如下:
属性 类型 默认值 说明
propagation Propagation枚举 REQUIRED 事务传播属性 (下有说明)
isolation isolation枚举 DEFAULT 事务隔离级别 (另有说明)
readOnly boolean false 是否只读
timeout int -1 超时(秒)
rollbackFor Class[] {} 需要回滚的异常类
rollbackForClassName String[] {} 需要回滚的异常类名
noRollbackFor Class[] {} 不需要回滚的异常类
noRollbackForClassName String[] {} 不需要回滚的异常类名
事务的隔离级别 有如下可选:
可以去看spring源码 : org.springframework.transaction.annotation.Isolation
我的"隔离级别"相关文章 有不明白的,可以去看看
DEFAULT 采用数据库默认隔离级别
READ_UNCOMMITTED 请看"隔离级别"相关文章READ_COMMITTED 请看"隔离级别"相关文章REPEATABLE_READ 请看"隔离级别"相关文章SERIALIZABLE 请看 "隔离级别"相关文章
.
事务的传播属性 ,有如下可选
可以去看spring源码 : org.springframework.transaction.annotation.Propagation
REQUIRED 业务方法需要在一个事务中运行,如果方法运行时,已处在一个事务中,那么就加入该事务,否则自己创建一个新的事务.这是spring默认的传播行为.
SUPPORTS 如果业务方法在某个事务范围内被调用,则方法成为该事务的一部分,如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行.
MANDATORY 只能在一个已存在事务中执行,业务方法不能发起自己的事务,如果业务方法在没有事务的环境下调用,就抛异常
REQUIRES_NEW 业务方法总是会为自己发起一个新的事务,如果方法已运行在一个事务中,则原有事务被挂起,新的事务被创建,直到方法结束,新事务才结束,原先的事务才会恢复执行.
NOT_SUPPORTED 声明方法需要事务,如果方法没有关联到一个事务,容器不会为它开启事务.如果方法在一个事务中被调用,该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行.
NEVER 声明方法绝对不能在事务范围内执行,如果方法在某个事务范围内执行,容器就抛异常.只有没关联到事务,才正常执行.
NESTED 如果一个活动的事务存在,则运行在一个嵌套的事务中.如果没有活动的事务,则按REQUIRED属性执行.它使用了一个单独的事务, 这个事务拥有多个可以回滚的保证点.内部事务回滚不会对外部事务造成影响, 它只对DataSour
Spring3 Annotation + Hibernate3-jpa2.0 + CGLIB + 多数据源
文章分类:Java编程
一、定义一个测试用Entity。
Java代码
@Entity
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
@Id @GeneratedValue @Column(name = "id")
private Integer id;
@Column(name = "last_name")
private String lastName;
@Column(name = "first_name")
private String firstName;
public String getFirstName(){return firstName;}
public void setFirstName(String firstName){this.firstName = firstName;}
public String getLastName(){return lastName;}
public void setLastName(String lastName){this.lastName = lastName; }
public Integer getId(){return id;}
public void setId(Integer id){this.id = id;}
}
二、applicationContext.xml与META-INF/persistent.xml
Xml代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!--指定spring容器自动扫描的包:@Repository...-->
<context:component-scan base-package="com.logcd.test,com.logcd.dao,com.logcd.service"/>
<!--使用CGLIB代理-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<bean id="ds1"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/springapp"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<bean id="ds2"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!--动态选择数据源-->
<bean id="dataSource" class="com.logcd.util.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="1" value-ref="ds1"/>
<entry key="2" value-ref="ds2"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="ds1"/>
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
destroy-method="destroy">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="MYSQL"/>
<property name="generateDdl" value="true"/>
<property name="showSql" value="true"/>
</bean>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="dataSource" ref="dataSource"/>
<property name="entityManagerFactory"
ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
Xml代码
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistencehttp://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<!-- local or global(JTA) transaction -->
<persistence-unit name="SpringJpa" transaction-type="RESOURCE_LOCAL"/>
</persistence>
三、在Spring 2.0.1中引入了AbstractRoutingDataSource, 该类充当了DataSource的路由中介, 能有在运行时, 根据某种key值来动态切换到真正的DataSource上, 同时对于不支持事务隔离级别的JTA事务来说, Spring还提供了另外一个类IsolationLevelDataSourceRouter来处理。IsolationLevelDataSourceRouter: 会根据当前Spring受管事务启用的隔离级别来选定合适的DataSource数据源。
Java代码
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DbContextHolder.getDbType();
}
}
Java代码
public class DbContextHolder {
private static final ThreadLocal<Object> contextHolder = new ThreadLocal<Object>();
public static void setDbType(Object dbType) {
contextHolder.set(dbType);
}
public static String getDbType() {
return (String) contextHolder.get();
}
public static void clearDbType() {
contextHolder.remove();
}
}
四、数据访问Dao
Java代码
import java.io.Serializable;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Repository
@Transactional
public class BaseDao<T>{
@PersistenceContext
protected EntityManager em;
public void persist(T t) {
em.persist(t);
}
@Transactional(readOnly = false, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public T save(T t) {
return em.merge(t);
}
public void delete(T t) {
em.remove(t);
}
@Transactional(readOnly = true, isolation=Isolation.READ_COMMITTED)
public T find(Class<T> clazz, Serializable id) {
return em.getReference(clazz, id);
}
@SuppressWarnings("unchecked")
public List<T> find(String jpql) {
return em.createQuery(jpql).getResultList();
}
@SuppressWarnings("unchecked")
public List<T> find(String jpql, Object param) {
return em.createQuery(jpql).setParameter(1, param).getResultList();
}
@SuppressWarnings("unchecked")
public List<T> find(String jpql, Object[] param) {
Query query = em.createQuery(jpql);
for (int i = 1; i <= param.length; i++) {
query.setParameter(i, param[i - 1]);
}
return query.getResultList();
}
public int findTotalCount(String jpql) {
return ((Long) em.createQuery(jpql).getSingleResult()).intValue();
}
}
五、测试
Java代码
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.util.List;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.logcd.dao.BaseDao;
import com.logcd.entity.Person;
import com.logcd.util.DbContextHolder;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestBaseDao extends AbstractJUnit4SpringContextTests{
@Autowired
private BaseDao<Person> baseDao;
@Test
public void findPersonById(){
List<Person> persons =
baseDao.find("from Person a where a.id = ?", new Integer(1));
assertNotNull(persons);
assertEquals(persons.size(), 1);
for(Person person: persons){
System.out.println(person.getLastName()+" "+person.getFirstName());
}
}
@Test
public void savePerson(){
DbContextHolder.setDbType("2");
Person person = new Person();
person.setFirstName("Bella");
person.setLastName("Edward");
person = baseDao.save(person);
System.out.println(person.getId());
DbContextHolder.clearDbType();
}
@Ignore
public void deletePerson(){
baseDao.delete(baseDao.find(Person.class, new Integer(7)));
}
}
六、关于Spring-AOP: JDK代理和CGLIB代理
1、如果目标对象实现了接口,默认会采用JDK的动态代理机制实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现接口,必须使用CGLIB生成代理,spring会自动在CGLIB和JDK动态代理之间切换
4、如何强制使用CGLIB生成代理?
(1)添加CGLIB库,SPRING_HOME/lib/cglib/*.jar
(2)在spring的配置文件中加入:<aop:aspectj-autoproxy proxy-target-class="true"/>
5、JDK代理和CGLIB代理的区别?
(1)JDK代理只能对实现了接口的类生成代理,而不能针对类
(2)CGLIB是针对类实现代理的,主要对指定的类生成一个子类,并覆盖其中的方法,因为是继承,所以不能使用final来修饰类或方法
七、关于IsolationLevelDataSourceRouter
public class IsolationLevelDataSourceRouterextends AbstractRoutingDataSourceDataSource that routes to one of various target DataSources based on the current transaction isolation level. The target DataSources need to be configured with the isolation level name as key, as defined on the TransactionDefinition interface.
This is particularly useful in combination with JTA transaction management (typically through Spring's JtaTransactionManager). Standard JTA does not support transaction-specific isolation levels. Some JTA providers support isolation levels as a vendor-specific extension (e.g. WebLogic), which is the preferred way of addressing this. As alternative (e.g. on WebSphere), the target database can be represented through multiple JNDI DataSources, each configured with a different isolation level (for the entire DataSource). The present DataSource router allows to transparently switch to the appropriate DataSource based on the current transaction's isolation level.
The configuration can for example look like this, assuming that the target DataSources are defined as individual Spring beans with names "myRepeatableReadDataSource", "mySerializableDataSource" and "myDefaultDataSource":
Xml代码
<bean id="dataSourceRouter" class="org.springframework.jdbc.datasource.lookup.IsolationLevelDataSourceRouter">
<property name="targetDataSources">
<map>
<entry key="ISOLATION_REPEATABLE_READ" value-ref="myRepeatableReadDataSource"/>
<entry key="ISOLATION_SERIALIZABLE" value-ref="mySerializableDataSource"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="myDefaultDataSource"/>
</bean>
Alternatively, the keyed values can also be data source names, to be resolved through a DataSourceLookup: by default, JNDI names for a standard JNDI lookup. This allows for a single concise definition without the need for separate DataSource bean definitions.
Xml代码
<bean id="dataSourceRouter" class="org.springframework.jdbc.datasource.lookup.IsolationLevelDataSourceRouter">
<property name="targetDataSources">
<map>
<entry key="ISOLATION_REPEATABLE_READ" value="java:comp/env/jdbc/myrrds"/>
<entry key="ISOLATION_SERIALIZABLE" value="java:comp/env/jdbc/myserds"/>
</map>
</property>
<property name="defaultTargetDataSource" value="java:comp/env/jdbc/mydefds"/>
</bean>
Note: If you are using this router in combination with Spring's JtaTransactionManager, don't forget to switch the "allowCustomIsolationLevels" flag to "true". (By default, JtaTransactionManager will only accept a default isolation level because of the lack of isolation level support in standard JTA itself.)
Xml代码
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="allowCustomIsolationLevels" value="true"/>
</bean>
Oracle Errors收集
解惑 spring 嵌套事务
/**
* @author 王政
* @date 2006-11-24
* @note 转载请注明出处
*/
在所有使用 spring 的应用中, 声明式事务管理可能是使用率最高的功能了, 但是, 从我观察到的情况看,
绝大多数人并不能深刻理解事务声明中不同事务传播属性配置的的含义, 让我们来看一下 TransactionDefinition 接口中的定义
Java代码
/**
* Support a current transaction, create a new one if none exists.
* Analogous to EJB transaction attribute of the same name.
* <p>This is typically the default setting of a transaction definition.
*/
int PROPAGATION_REQUIRED = 0;
/**
* Support a current transaction, execute non-transactionally if none exists.
* Analogous to EJB transaction attribute of the same name.
* <p>Note: For transaction managers with transaction synchronization,
* PROPAGATION_SUPPORTS is slightly different from no transaction at all,
* as it defines a transaction scopp that synchronization will apply for.
* As a consequence, the same resources (JDBC Connection, Hibernate Session, etc)
* will be shared for the entire specified scope. Note that this depends on
* the actual synchronization configuration of the transaction manager.
* @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setTransactionSynchronization
*/
int PROPAGATION_SUPPORTS = 1;
/**
* Support a current transaction, throw an exception if none exists.
* Analogous to EJB transaction attribute of the same name.
*/
int PROPAGATION_MANDATORY = 2;
/**
* Create a new transaction, suspend the current transaction if one exists.
* Analogous to EJB transaction attribute of the same name.
* <p>Note: Actual transaction suspension will not work on out-of-the-box
* on all transaction managers. This in particular applies to JtaTransactionManager,
* which requires the <code>javax.transaction.TransactionManager</code> to be
* made available it to it (which is server-specific in standard J2EE).
* @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
*/
int PROPAGATION_REQUIRES_NEW = 3;
/**
* Execute non-transactionally, suspend the current transaction if one exists.
* Analogous to EJB transaction attribute of the same name.
* <p>Note: Actual transaction suspension will not work on out-of-the-box
* on all transaction managers. This in particular applies to JtaTransactionManager,
* which requires the <code>javax.transaction.TransactionManager</code> to be
* made available it to it (which is server-specific in standard J2EE).
* @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
*/
int PROPAGATION_NOT_SUPPORTED = 4;
/**
* Execute non-transactionally, throw an exception if a transaction exists.
* Analogous to EJB transaction attribute of the same name.
*/
int PROPAGATION_NEVER = 5;
/**
* Execute within a nested transaction if a current transaction exists,
* behave like PROPAGATION_REQUIRED else. There is no analogous feature in EJB.
* <p>Note: Actual creation of a nested transaction will only work on specific
* transaction managers. Out of the box, this only applies to the JDBC
* DataSourceTransactionManager when working on a JDBC 3.0 driver.
* Some JTA providers might support nested transactions as well.
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager
*/
int PROPAGATION_NESTED = 6;
我们可以看到, 在 spring 中一共定义了六种事务传播属性, 如果你觉得看起来不够直观, 那么我来转贴一个满大街都有的翻译
引用
PROPAGATION_REQUIRED -- 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS -- 支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY -- 支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW -- 新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED -- 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER -- 以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED -- 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。
前六个策略类似于EJB CMT,第七个(PROPAGATION_NESTED)是Spring所提供的一个特殊变量。
它要求事务管理器或者使用JDBC 3.0 Savepoint API提供嵌套事务行为(如Spring的DataSourceTransactionManager)
在我所见过的误解中, 最常见的是下面这种:
引用
假如有两个业务接口 ServiceA 和 ServiceB, 其中 ServiceA 中有一个方法实现如下
/**
* 事务属性配置为 PROPAGATION_REQUIRED
*/
void methodA() {
// 调用 ServiceB 的方法
ServiceB.methodB();
}
那么如果 ServiceB 的 methodB 如果配置了事务, 就必须配置为 PROPAGATION_NESTED
这种想法可能害了不少人, 认为 Service 之间应该避免互相调用, 其实根本不用担心这点,PROPAGATION_REQUIRED 已经说得很明白,
如果当前线程中已经存在事务, 方法调用会加入此事务, 果当前没有事务,就新建一个事务, 所以 ServiceB#methodB() 的事务只要遵循最普通的规则配置为 PROPAGATION_REQUIRED 即可, 如果 ServiceB#methodB (我们称之为内部事务, 为下文打下基础) 抛了异常, 那么 ServiceA#methodA(我们称之为外部事务) 如果没有特殊配置此异常时事务提交 (即 +MyCheckedException的用法), 那么整个事务是一定要 rollback 的, 什么 Service 只能调 Dao 之类的言论纯属无稽之谈, spring 只负责配置了事务属性方法的拦截, 它怎么知道你这个方法是在 Service 还是 Dao 里 ?
说了这么半天, 那到底什么是真正的事务嵌套呢, 解释之前我们来看一下 Juergen Hoeller 的原话
Juergen Hoeller 写道
PROPAGATION_REQUIRES_NEW starts a new, independent "inner" transaction for the given scope. This transaction will be committed or rolled back completely independent from the outer transaction, having its own isolation scope, its own set of locks, etc. The outer transaction will get suspended at the beginning of the inner one, and resumed once the inner one has completed.
Such independent inner transactions are for example used for id generation through manual sequences, where the access to the sequence table should happen in its own transactions, to keep the lock there as short as possible. The goal there is to avoid tying the sequence locks to the (potentially much longer running) outer transaction, with the sequence lock not getting released before completion of the outer transaction.
PROPAGATION_NESTED on the other hand starts a "nested" transaction, which is a true subtransaction of the existing one. What will happen is that a savepoint will be taken at the start of the nested transaction. íf the nested transaction fails, we will roll back to that savepoint. The nested transaction is part of of the outer transaction, so it will only be committed at the end of of the outer transaction.
Nested transactions essentially allow to try some execution subpaths as subtransactions: rolling back to the state at the beginning of the failed subpath, continuing with another subpath or with the main execution path there - all within one isolated transaction, and not losing any previous work done within the outer transaction.
For example, consider parsing a very large input file consisting of account transfer blocks: The entire file should essentially be parsed within one transaction, with one single commit at the end. But if a block fails, its transfers need to be rolled back, writing a failure marker somewhere. You could either start over the entire transaction every time a block fails, remembering which blocks to skip - or you mark each block as a nested transaction, only rolling back that specific set of operations, keeping the previous work of the outer transaction. The latter is of course much more efficient, in particular when a block at the end of the file fails.
Juergen Hoeller 写道
Rolling back the entire transaction is the choice of the demarcation code/config that started the outer transaction.
So if an inner transaction throws an exception and is supposed to be rolled back (according to the rollback rules), the transaction will get rolled back to the savepoint taken at the start of the inner transaction. The immediate calling code can then decide to catch the exception and proceed down some other path within the outer transaction.
If the code that called the inner transaction lets the exception propagate up the call chain, the exception will eventually reach the demarcation code of the outer transaction. At that point, the rollback rules of the outer transaction decide whether to trigger a rollback. That would be a rollback of the entire outer transaction then.
So essentially, it depends on your exception handling. If you catch the exception thrown by the inner transaction, you can proceed down some other path within the outer transaction. If you let the exception propagate up the call chain, it's eventually gonna cause a rollback of the entire outer transaction.
也就是说, 最容易弄混淆的其实是 PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED, 那么这两种方式又有何区别呢? 我简单的翻译一下 Juergen Hoeller 的话 :
PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 "内部" 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行.
另一方面, PROPAGATION_NESTED 开始一个 "嵌套的" 事务, 它是已经存在事务的一个真正的子事务. 潜套事务开始执行时, 它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 潜套事务是外部事务的一部分, 只有外部事务结束后它才会被提交.
由此可见, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大区别在于, PROPAGATION_REQUIRES_NEW 完全是一个新的事务, 而 PROPAGATION_NESTED 则是外部事务的子事务, 如果外部事务 commit, 潜套事务也会被 commit, 这个规则同样适用于 roll back.
那么外部事务如何利用嵌套事务的 savepoint 特性呢, 我们用代码来说话
Java代码
ServiceA {
/**
* 事务属性配置为 PROPAGATION_REQUIRED
*/
void methodA() {
ServiceB.methodB();
}
}
ServiceB {
/**
* 事务属性配置为 PROPAGATION_REQUIRES_NEW
*/
void methodB() {
}
}
这种情况下, 因为 ServiceB#methodB 的事务属性为 PROPAGATION_REQUIRES_NEW, 所以两者不会发生任何关系, ServiceA#methodA 和 ServiceB#methodB 不会因为对方的执行情况而影响事务的结果, 因为它们根本就是两个事务, 在 ServiceB#methodB 执行时 ServiceA#methodA 的事务已经挂起了 (关于事务挂起的内容已经超出了本文的讨论范围, 有时间我会再写一些挂起的文章) .
那么 PROPAGATION_NESTED 又是怎么回事呢? 继续看代码
Java代码
ServiceA {
/**
* 事务属性配置为 PROPAGATION_REQUIRED
*/
void methodA() {
ServiceB.methodB();
}
}
ServiceB {
/**
* 事务属性配置为 PROPAGATION_NESTED
*/
void methodB() {
}
}
现在的情况就变得比较复杂了, ServiceB#methodB 的事务属性被配置为 PROPAGATION_NESTED, 此时两者之间又将如何协作呢? 从 Juergen Hoeller 的原话中我们可以找到答案, ServiceB#methodB 如果 rollback, 那么内部事务(即 ServiceB#methodB) 将回滚到它执行前的 SavePoint(注意, 这是本文中第一次提到它, 潜套事务中最核心的概念), 而外部事务(即 ServiceA#methodA) 可以有以下两种处理方式:
1. 改写 ServiceA 如下
Java代码
ServiceA {
/**
* 事务属性配置为 PROPAGATION_REQUIRED
*/
void methodA() {
try {
ServiceB.methodB();
} catch (SomeException) {
// 执行其他业务, 如 ServiceC.methodC();
}
}
}
这种方式也是潜套事务最有价值的地方, 它起到了分支执行的效果, 如果 ServiceB.methodB 失败, 那么执行 ServiceC.methodC(), 而 ServiceB.methodB 已经回滚到它执行之前的 SavePoint, 所以不会产生脏数据(相当于此方法从未执行过), 这种特性可以用在某些特殊的业务中, 而 PROPAGATION_REQUIRED 和 PROPAGATION_REQUIRES_NEW 都没有办法做到这一点. (题外话 : 看到这种代码, 似乎似曾相识, 想起了 prototype.js 中的 Try 函数 )
2. 代码不做任何修改, 那么如果内部事务(即 ServiceB#methodB) rollback, 那么首先 ServiceB.methodB 回滚到它执行之前的 SavePoint(在任何情况下都会如此),
外部事务(即 ServiceA#methodA) 将根据具体的配置决定自己是 commit 还是 rollback (+MyCheckedException).
上面大致讲述了潜套事务的使用场景, 下面我们来看如何在 spring 中使用 PROPAGATION_NESTED, 首先来看 AbstractPlatformTransactionManager
Java代码
/**
* Create a TransactionStatus for an existing transaction.
*/
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
... 省略
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
if (!isNestedTransactionAllowed()) {
throw new NestedTransactionNotSupportedException(
"Transaction manager does not allow nested transactions by default - " +
"specify 'nestedTransactionAllowed' property with value 'true'");
}
if (debugEnabled) {
logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
}
if (useSavepointForNestedTransaction()) {
// Create savepoint within existing Spring-managed transaction,
// through the SavepointManager API implemented by TransactionStatus.
// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
DefaultTransactionStatus status =
newTransactionStatus(definition, transaction, false, false, debugEnabled, null);
status.createAndHoldSavepoint();
return status;
}
else {
// Nested transaction through nested begin and commit/rollback calls.
// Usually only for JTA: Spring synchronization might get activated here
// in case of a pre-existing JTA transaction.
doBegin(transaction, definition);
boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER);
return newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);
}
}
}
一目了然
1. 我们要设置 transactionManager 的 nestedTransactionAllowed 属性为 true, 注意, 此属性默认为 false!!!
再看 AbstractTransactionStatus#createAndHoldSavepoint() 方法
Java代码
/**
* Create a savepoint and hold it for the transaction.
* @throws org.springframework.transaction.NestedTransactionNotSupportedException
* if the underlying transaction does not support savepoints
*/
public void createAndHoldSavepoint() throws TransactionException {
setSavepoint(getSavepointManager().createSavepoint());
}
可以看到 Savepoint 是 SavepointManager.createSavepoint 实现的, 再看 SavepointManager 的层次结构, 发现
其 Template 实现是 JdbcTransactionObjectSupport, 常用的 DatasourceTransactionManager, HibernateTransactionManager
中的 TransactonObject 都是它的子类 :
JdbcTransactionObjectSupport 告诉我们必须要满足两个条件才能 createSavepoint :
2. java.sql.Savepoint 必须存在, 即 jdk 版本要 1.4+
3. Connection.getMetaData().supportsSavepoints() 必须为 true, 即 jdbc drive 必须支持 JDBC 3.0
确保以上条件都满足后, 你就可以尝试使用 PROPAGATION_NESTED 了. (全文完)
第一章
Spring框架简介
2
Spring简介
Spring是一个开源框架,是为了解决企业应用开发的复杂性而创建的。
目的:解决企业应用开发的复杂性。
功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能 。
范围:任何Java应用 。
简单来说,Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。
3
Spring简介
轻量——从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。
非侵入 ——在应用中,一般不需要引用springjar包里的类。
控制反转——Spring通过一种称作控制反转(IoC)的技术促进了松耦合。当应用了IoC,某一接口的具体实现类的选择控制权从调用类中移除,转交给第三方裁决。
面向切面——Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务()管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责其它的系统级关注点,例如日志或事务支持。
4
Spring简介
Spring即是一个容器又是一个框架。
容器——Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建——基于一个可配置原型(prototype),你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例——以及它们是如何相互关联的。
框架——Spring提供了很多基础的与业务逻辑无关的功能,比如:事务管理、持久化框架集成等等,使用Spring框架,开发人员可以专注于业务逻辑开发,这个意义上讲它是一个框架。
5
第二章
控制反转(IOC)
6
控制反转
IOC(控制反转:Inverse of Control)是Spring的内核,AOP,声明式事务等功能都建立在该功能之上,它涉及到代码解耦,设计模式,代码优化等问题的考虑。
它包括两个方面的内容:其一是控制;其二是反转。到底是什么东西的控制被反转了呢,来看个例子:
导演,剧本,场景,剧中人,演员,
7
控制反转
对于软件来说:控制反转即是某一接口的具体实现类的选择控制权从调用类中移除,转交给第三方裁决。
依赖注入DI(Dependency Injection): 将调用类对接口的实现类的依赖关系由第三方注入,以移除调用类对接口实现类的依赖。
8
控制反转
public class SaveEmployeeEditAction extends SsoaAction {
//定义接口
public EmployeeManager employeeManager;
//属性注入
public void setEmployeeManager(EmployeeManager employeeManager) {
this.employeeManager = employeeManager;
}
public String execute() throws Exception {
//使用接口
employeeManager.updateEmployee(ee);
}
}
第三方控制实现类
<bean id="employeeManager"
class="com.soft.ssoa.services.department.EmployeeManagerImpl">
<property name="employeeManagerDao">
<ref bean="employeeManagerDao" />
</property>
</bean>
9
IOC类型
构造函数注入
在构造函数注入中,通过调用类的构造函数将接口实现类通过构造函数变量传入,如下代码所示
public class MenuBufferImpl {
public MenuBufferImpl(){}
public MenuBufferImpl(PrivilegeDAO privilegeDAO) {
this.privilegeDAO = privilegeDAO;
}
}
//注入
<bean id="menuBuffer" class="com.topsoft.prv.domain.MenuBufferImpl">
<constructor-arg>
<ref local="privilegeDAO"/>
</constructor-arg>
</bean>
<bean id="privilegeDAO" class="com.topsoft.prv.domain.dao.PrivilegeDAOImpl"/>
控制反转
10
控制反转
属性注入
属性注入可以有选择地通过Setter方法完成调用类所需依赖的注入,更加灵活方便。
一般使用属性注入。
见前面例子
11
控制反转
课后扩展:
刚才看到,spring框架通过一个配置文件实现控制反转,在容器启动时根据配置文件的描述信息,自动实例化Bean并完成依赖关系的建立,从容器中即可返回准备就绪的Bean实例,以待以后使用。Spring之所以有这样的能力,主要归功于java语言本身的类反射功能,反射机制简单的说就是我们利用编程的方式实现类的实例化,大家课下可以查询这方面的知识,加深对控制反转的了解。
12
BeanFactory
Spring实现控制反转是通过配置方式来实现的,主要是xml配置文件,该文件一般命名为 *-beans.xml,格式一般如下:
13
控制反转-BeanFactory
Spring通过该配置文件描述Bean及Bean之间的依赖关系,在容器启动时实例化Bean并建立Bean之间的依赖关系。Spring的Ioc容器在完成这些底层工作的基础上,还提供了Bean实例缓存、生命周期管理、Bean实例代理、事件发布、资源装载等高级服务。
BeanFactory(Bean工厂)是Spring框架最核心的接口它提供了高级Ioc的配置机制,使管理不同类型的java对象成为可能。
一般我们称BeanFactory为Ioc容器。
使用如下:
14
ApplicationContext(应用上下文)建立在BeanFactory之上,提供了更多面向应用的功能,更易于创建实际应用,有时在web开发中为了方便也称ApplicationContext为Spring容器。
控制反转-ApplicationContext
15
WebApplicationContext是专门为Web应用准备的,它允许从相对于Web根目录的路径中装载配置文件完成初始化工作。从WebApplicationContext中可以获得ServletContext的引用整个web应用上下文对象将作为属性放置到ServletContext中,以便Web应用环境可以访问Spring引用上下文。Spring专门提供了一个工具类WebApplicationContextUtils,通过该类的getWebApplicationContext(ServletContext sc) 方法可以从ServletCOntext中获取WebApplicationContext实例。
使用例子:
控制反转-WebApplicationContext
16
控制反转
17
控制反转-装配bean
装配一个bean
<?xml version="1.0" encoding="gb2312"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<description>权限管理prv</description>
<bean id="car" class="Ioc.car"></bean>
<bean id="boss" class="Ioc.Boss"></bean>
</beans>
上段信息提供了实例化Car和Boss这两个Bean必须的信息,Ioc容器完全可以根据该信息创建者连个Bean实例。
Bean命名:
一般情况,在配置一个bean是,需要为期指定一个id属性作为Bean的名称,id在Ioc容器中必须是唯一的,此外id的命名需要满足XML对id的命名规范:必须以字母开始,逗号和空格这些非完全结束符号是非法的。如果实际使用中需要使用一些id不允许的特殊字符,可以使用bean的name属性来命名,name没有命名上的限制,几乎可以使用任何字符,如?ab,123等。
Id和name都可以指定多个名字,名字中间使用逗号、分号、或者空格进行分割,
<bean name=“?car1,car2,#car3” calss=“Ioc.car”/>
这里为bean定义了三个名字?car1,car2,#car3,他们是一样的,可以通过任何一个取得bean实例。
配置文件中不能出现相同的id,但可以出现相同的name,根据name取得bean时,以最后出现的name为准。
推荐使用id .
18
属性注入配置
使用属性注入时,bean配置方式如下:
<bean id="car" class="Ioc.car"></bean>
<bean id="boss" class="Ioc.Boss">
<property name="name">
<idref bean="car"/>
</property>
<property name="car">
<value>hongqi,200,2000</value>
</property>
</bean>
两种方式设置属性的值,1,通过字面值value;2,通过idref引用别的bean.
上例中name的值是car.
控制反转-装配bean
19
控制反转-装配bean
构造函数注入装配
构造函数注入通过<constructor-arg>标记完成。
1,按类型匹配入参
<bean id="car" class="Ioc.Car">
<constructor-arg type=“java.lang.String”>
<value><![CDATA[hongi&asldf]]></value>
</constructor-arg>
<constructor-arg type=“java.lang.String” value="hehe"/>
<constructor-arg type=“java.lang.int” value="3"/>
<constructor-arg>
<set>
<value>${barand}</value>
<value>${color}</value>
<value>${maxSpeed}</value>
</set>
</constructor-arg>
</bean>
2,按索引匹配入参
<bean id="car" class="Ioc.Car">
<constructor-arg index="0">
<value><![CDATA[hongi&asldf]]></value>
</constructor-arg>
<constructor-arg index="1" value="hehe"/>
<constructor-arg index="2" value="3"/>
<constructor-arg>
<set>
<value>${barand}</value>
<value>${color}</value>
<value>${maxSpeed}</value>
</set>
</constructor-arg>
</bean>
20
注入参数详解
1,字面值
即可用字符串表示的值,这些值<value>元素标签进行注入 ,比如
<property name="car">
<value>hongqi</value>
</property>
对于一些特殊符号可以使用<![CDATA[]]>的特殊处理标签,比如:
<property name="car">
<value> <![CDATA[轿车&@@car]]></value>
</property>
当然也可以使用转义序列处理特殊符号,xml中共有五个特殊符号,它们的转义序列如下:
< <
> >
& &
“ "
‘ '
控制反转-装配bean
21
2, 引用其它bean
idref
idref元素用来将容器内其它bean的id传给<constructor-arg/>或<property/>元素,同时提供错误难功能。
<property name="name">
<idref bean="car"/>
</property>
ref
形式一:<ref bean=“someBean”>
这是最常见的形式是通过使用ref标记指定bean属性的目标bean,通过该标签可以引用同一容器或父容器内的任何bean(无论是否在同一XML文件中)。
XML‘bean’元素的值即可以是指定的bean的id值也可以是其name值。
形式二:<ref local=“someBean”>
使用ref的local属性指定目标bean,它可以利用XML解析器来难所引用的bean是否存在同一文件中。local属性值必须是目标bean的id属性值。
形式三:<bean parent="someBean">
通过使用ref的parent属性来引用当前窗口的父容器中的bean。parent属性值即可以是目标bean的id值,也可以是name属性值。
控制反转-装配bean
22
3、内部 bean:
所谓内部bean(inner bean)是指在一个bean的<property/>或<constructor-arg/>中使用<bean/>元素定义的bean.
<bean id="outer" class="...">
?<property name="target">
? <bean class="com.mycoompany.Person">
?? <property name="name" value="Fiona Apple"/>
?? <property name="age" value="25"/>
? </bean>
?</property>
</bean>
内部bean不能被其它bean引用,只能在声明处为外部bean提供实例注入。
内部bean即使提供了id,name,scope属性也会被忽略.
控制反转-装配bean
23
4、集合合并:
从Spring2.0开始,Spring IoC容器将支持集合的合并。父子集合元素合并后的值就是子集合中的最终结果,而且子集合中的元素值将覆盖父集合中的对应的值。
<beans>
<bean id="parent" abstract="true" class="example.ComplexObject">
?<property name="adminEmails">
? <props>
?? <prop key="administrator">administrator@somecompany.com</prop>
?? <prop key="support">support@somecompany.com</prop>
? </props>
?</property>
</bean>
<bean id="child" parent="parent">
?<property name="adminEmails">
? <props merge="trur">
?? <prop key="sales">sales@somecompany.com</prop>
?? <prop key="support">support@somecompany.co.uk</prop>
? </props>
?</property>
</bean>
</beans>
合并后内容:
administrator=administrator@somecompany.co m
sales=sales@somecompany.com
support=support@somecompany.co.uk
list集合有排序功能,父bean的列表内容将排在子bean列表内容的前面;
merge属性必须在继承的子bean中定义。
控制反转-装配bean
24
5、Nulls
<null/>用于处理null值。Spring会把属性的空参数当作空字符串处理。
<bean class="ExampleBean">
?<property name="email">
? <value></value>
?</property>
</bean>
等同于
excapleBean.setEamil("");
而null值则可以使用<null/>元素来表示:
<bean class="ExampleBean">
?<property name="email"><null/></property>
</bean>
控制反转-装配bean
25
6、简写:
针对常见的value值或bean的引用,Spring提供了简化格式用于替代<value/>和<ref/>元素。
<property/>、<constructor-arg/>、<entry/>元素都支持value属性,它可以用来替代内嵌的<value/>元素。
<property name="myProperty">???????????????????????????
?<value>hello</value>?????????? =====??????????? <property name="myProperty" value="helo" />
</property>
<constructor-arg>
?<value>hello</value>?????????? =====??????????? <constructor-arg value="hello" />
</construtctor-arg>
<entry key="myKey">
?<value>hello</value>?????????? =====??????????? <entry key="myKey" value="hello" />
</entry>
<property/>和<constructor-arg/>支持类似的简写属性ref,它可以替找整个内嵌的</ref>元素。
<property name="myProperty">
?<ref bean="myBean">??????????? =====??????????? <property name="myProperty" ref="myBean" />
</property>
<constructor-arg>
?<ref bean="myBean">??????????? =====??????????? <constructor-arg ref="myBean" />
</constructor-arg>
切记:尽管存在等同于<ref bean="xxx" >元素的简写形式,但并没有<ref local="xxx">的简写形式。
map中的entry元素的简写形式为key/key-ref和value/value-ref属性。
<entry>
?<key>
? <ref bean="myKeyBean" />??????????? =====????????? <entry key-ref="myKeyBean" value-ref="myValueBean" />
?</key>
?<ref bean="myValueBean" />
</entry>
Spring 建议使用简写形式
控制反转-装配bean
26
7、延迟初始化bean--lazy-init 属性:
<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true">
?<!-- various properties here... -->
</bean>
<bean name="noo.lazy" class="com.foo.AnotherBean">
?<!-- various properties here... -->
</bean>
如果一个bean被设置为延迟初始化,而另一个非延迟初始化的singleton bean依赖于它,那么当ApplicationContext提前实例化singleton bean时,它必须也确保所有上述singleton依赖bean也被预先初始化,当然也包括设置为延迟实例化的bean.
在容器层次中通过在<beans />元素上使用‘default-lazy-init’属性来控制延迟初始化也是可能的。
<beans default-lazy-init="true">
?<!-- no beans will be eagerly pre-instantiated... -->
</beans>
控制反转-装配bean
27
8、自动装配< autowire > 属性:
模式???? 说明
no????? 不使用自动装配,必须通过ref元素指定依赖,这是默认设置。
byName???? 根据属性名自动装配。Spring将检查容器并根据名字查找与属性完全一致的bean,并将其与属性自动装配。
byType???? 如果容器中存在一个与指定属性类型相同的bean,那么将与该属性自动装配。如果存在多个,则抛出异常。
constructor??? 与byType的方式类似,不同之处在于它应用于构造器参数。如果在容器中未找到与构造器参数类型一致的bean,那么将抛出异常。
autodetect??? 通过bean类的自省机制(introspection)来决定是使用constructor还是byType方式进行自动装配。如果发现默认的构造器,那么将使用byType方式。
控制反转-装配bean
28
Bean之间的关系
1,继承
如果多个bean存在相同的配置信息,spring允许我们定义一个父<bean>,子<bean>将自动继承父<bean>的配置信息。
<bean id="abstractCar" class="Ioc.Car" abstract="true">
<property name="barand" value="barand"/>
<property name="color" value="cc"/>
</bean>
<bean id="car2" parent="abstractCar">
<property name="maxSpeed">
<value>200</value>
</property>
</bean>
控制反转-装配bean
29
2,依赖
depends-on属性可以用于当前bean初始化之前显式的强制一个或多个bean被初始化。
<bean id="beanOne" class="ExampleBean" depends-on="manager">
?<property name="manager" ref="manager" />
</bean>
<bean id="manager" class="ManagerBean" />
若需要表达对多个bean的依赖,可以在<depends-on />中将指定的多个bean名字用分隔符进行分隔,分隔符可以是逗号、空格及分号等。
<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
?<property name="manager" ref="manager" />
</bean>
<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
3,引用
即前面讲的idref
控制反转-装配bean
30
循环依赖问题
Spring容器能够顺利实例化以构造函数注入进行装配的bean有一个前提:Bean构造函数入参引用的对象必须已经准备就绪。由于这个机制的限制,如果两个bean都采用构造函数注入,而且都通过构造函数入参引用对方,就会发生类似于线程死锁的循环依赖问题。如下:
Public class car{
…
Public car(Boss boss){
this.boss=boss;
}
}
Public class boss{
…
Public boss(Car car){
this.car=carl;
}
}
当启动Ioc容器时,由于存在循环依赖问题,Spring容器将无法成功启动,这时我们可以混合使用函数注入和属性输入来解决该问题。即,把上面的一个函数注入改为属性注入方式。
控制反转-装配bean
31
第三章
面向切面编程(AOP)基础
32
面向切面编程(AOP)
AOP相关概念
连接点(Joinpoint):
程序执行的某个特定位置:比如类开始初始化前、类初始化后、某个方法调用前、调用后等。一个类或一段程序代码拥有的一些具有边界性质的特定点,这些代码中的特定点就称为“连接点”,Spring仅支持方法的连接点。
切点(Pointcut):
匹配连接点(Joinpoint)的断言。连接点相当于数据记录,切点相当于查询条件,一个切点可以匹配多个连接点。
增强(Advice):
织入到目标类连接点上的一段程序代码。
目标对象(Target):
增强逻辑的织入目标类
引介(Introduction)
引介是一种特殊的增强,为类添加一些属性和方法。通过它可以动态的为业务类增加接口的实现逻辑,让业务类成为这个接口的实现类。
33
织入(Weaving):
将增强添加到对目标类具体连接点上的过程。
代理(Proxy)
一个类被AOP织入增强后,就产生了一个结果类,它是融合了原类和增强逻辑的代理类。根据不同的代理方式,代理类既可能是和原类具有相同接口的类,也可能是原类的子类,所以我们可以采用调用原类相同的方式调用代理类
切面(Aspect)
切面由切点和增强(引介)组成,它既包括了横切逻辑的定义,也包括了连接点的定义,spring AOP就是负责实施 切面的框架,它将切面定义的横切逻辑织入到切面所指定的连接点中。
AOP的工作重点在于如何将增强应用于目标对象的连接点上,这包括两个工作:
1,如何通过切点和增强定位到连接点上;
2,如何在增强中编写切面的代码;
面向切面编程(AOP)
34
代理机制:
AOP通过动态代理技术在运行期织入增强代码,首先了解下AOP使用的两种代理机制:
1,基于JDK的动态代理
JDK1.3以后,java提供了动态代理技术,允许开发者在运行期创建接口的代理实例。
JDK动态代理主要涉及两个类,
Java.lang.reflect.Proxy
Java.lang.reflect. InvocationHandler
InvocationHandler是一
相关推荐
赠送jar包:springfox-spring-web-2.9.2.jar; 赠送原API文档:springfox-spring-web-2.9.2-javadoc.jar; 赠送源代码:springfox-spring-web-2.9.2-sources.jar; 赠送Maven依赖信息文件:springfox-spring-web-...
赠送jar包:dynamic-datasource-spring-boot-starter-3.4.1.jar; 赠送原API文档:dynamic-datasource-spring-boot-starter-3.4.1-javadoc.jar; 赠送源代码:dynamic-datasource-spring-boot-starter-3.4.1-sources...
开发工具 mybatis-spring-1.3.1开发工具 mybatis-spring-1.3.1开发工具 mybatis-spring-1.3.1开发工具 mybatis-spring-1.3.1开发工具 mybatis-spring-1.3.1开发工具 mybatis-spring-1.3.1开发工具 mybatis-spring-...
赠送jar包:dynamic-datasource-spring-boot-starter-3.4.1.jar; 赠送原API文档:dynamic-datasource-spring-boot-starter-3.4.1-javadoc.jar; 赠送源代码:dynamic-datasource-spring-boot-starter-3.4.1-sources...
赠送jar包:springfox-spring-webflux-3.0.0.jar; 赠送原API文档:springfox-spring-webflux-3.0.0-javadoc.jar; 赠送源代码:springfox-spring-webflux-3.0.0-sources.jar; 赠送Maven依赖信息文件:springfox-...
《深入解析jasypt-spring-boot-starter 3.0.5依赖的POM与JAR》 在Java开发领域,构建和管理依赖是至关重要的环节。jasypt-spring-boot-starter是一个流行的安全库,它允许开发者在Spring Boot应用中轻松地实现加密...
赠送jar包:mybatis-spring-boot-autoconfigure-1.3.2.jar; 赠送原API文档:mybatis-spring-boot-autoconfigure-1.3.2-javadoc.jar; 赠送源代码:mybatis-spring-boot-autoconfigure-1.3.2-sources.jar; 赠送...
mybatis-spring-boot-starter-2.1.3.jarmybatis-spring-boot-starter-2.1.3.jarmybatis-spring-boot-starter-2.1.3.jar
开发工具 框架JAR mybatis-spring-1.3.1开发工具 框架JAR mybatis-spring-1.3.1开发工具 框架JAR mybatis-spring-1.3.1开发工具 框架JAR mybatis-spring-1.3.1开发工具 框架JAR mybatis-spring-1.3.1开发工具 框架...
mybatis-spring-boot-starter-2.1.4.jarmybatis-spring-boot-starter-2.1.4.jar
druid-spring-boot-starter-1.2.8.jar
针对jms开发的架包 xbean-spring-3.9.jar
mybatis-spring-boot-autoconfigure-2.1.3mybatis-spring-boot-autoconfigure-2.1.3
druid-spring-boot-starter-1.1.22.jar
Spring5 框架 ---- Spring5的新功能 ---- 代码 Spring5 框架 ---- Spring5的新功能 ---- 代码 Spring5 框架 ---- Spring5的新功能 ---- 代码 Spring5 框架 ---- Spring5的新功能 ---- 代码 Spring5 框架 ---- Spring...
activiti-spring-boot-starter-basic-6.0.0适配springboot2.1.2
easypoi-spring-boot-starter-4.0.0.jar 包
pagehelper-spring-boot-starter-1.2.5.jar
springfox-spring-web-2.9.2
mybatis-spring-2.0.6.jar