关于spring:Spring学习笔记菜鸟必看带你吃透Spring

43次阅读

共计 19484 个字符,预计需要花费 49 分钟才能阅读完成。

Spring 介绍


Spring 是一个开源框架,是一个分层的 JavaEE 一站式框架。

所谓一站式框架是指 Spring 有 JavaEE 开发的每一层解决方案。

  • WEB 层:SpringMVC
  • Service 层:Spring 的 Bean 治理,申明式事务
  • DAO 层:Spring 的 JDBC 模板,ORM 模板

长处:

  • IOC:不便解耦合
  • AOP:对程序进行扩大
  • 轻量级框架
  • 不便与其余框架整合

Spring 应用


Spring 开发包解压后的目录介绍:

  • docs: Spring 开发标准和 API
  • libs: Spring jar 包和源代码
  • schema: Spring 配置文件的约

DataAccess 用于数据拜访,WEB 用于页面显示,外围容器也就是 IOC 局部。

管制反转(IOC)


管制反转(Inversion of Control)是指将对象的创立权反转(交给)Spring。

应用 IOC 就须要导入 IOC 相干的包,也就是上图中外围容器中的几个包:beans,context,core,expression 四个包。

实现原理

传统形式创建对象:

UserDAO userDAO=new UserDAO();

进一步面向接口编程,能够多态:

UserDAO userDAO=new UserDAOImpl();

这种形式的毛病是接口和实现类高耦合,切换底层实现类时,须要批改源代码。程序设计应该满足 OCP 元祖,在尽量不批改程序源代码的根底上对程序进行扩大。此时,能够应用工厂模式:

class BeanFactory{

public static UserDAO getUserDAO(){

return new UserDAOImpl();

}

}

此种形式尽管在接口和实现类之间没有耦合,然而接口和工厂之间存在耦合。

应用工厂 + 反射 + 配置文件的形式,实现解耦,这也是 Spring 框架 IOC 的底层实现。

//xml 配置文件

//<bean id=”userDAO” class=”xxx.UserDAOImpl”></bean>

class BeanFactory{

public static Object getBean(String id){

// 解析 XML

// 反射

Class clazz=Class.forName();

return clazz.newInstance();

}

}

IOC XML 开发

在 docs 文件中蕴含了 xsd-configuration.hmtl 文件。其中定义了 beans schema。

<beans xmlns=”http://www.springframework.org/schema/beans”

xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”

xsi:schemaLocation=”

http://www.springframework.or… http://www.springframework.org/schema/beans/spring-beans.xsd”>

// 在此配置 bean

<bean id=”userService” class=”x.y.UserServiceImpl”>

</bean>

</beans>

调用类:

ApplicationContext applicationContext=new ClassPathXmlApplicationContext(“applicationContext.xml”);

UserService userService=(UserService)applicationContext.getBean(“userService”);

userService.save();

IOC 和 DI

DI 指依赖注入,其前提是必须有 IOC 的环境,Spring 治理这个类的时候将类的依赖的属性注入进来。

例如,在 UserServiceImpl.java 中:

public class UserServiceImpl implements UserService{

private String name;

public void setName(String name){

this.name=name;

}

public void save(){

System.out.println(“save “+name);

}

}

在配置文件中:

<beans xmlns=”http://www.springframework.org/schema/beans”

xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”

xsi:schemaLocation=”

http://www.springframework.or… http://www.springframework.org/schema/beans/spring-beans.xsd”>

<bean id=”userService” class=”spring.demo1.UserServiceImpl”>

<!– 配置依赖的属性 –>

<property name=”name” value=”tony”/>

</bean>

</beans>

测试代码:

@Test

public void demo2(){

// 创立 Spring 工厂

ApplicationContext applicationContext=new ClassPathXmlApplicationContext(“applicationContext.xml”);

UserService userService=(UserService)applicationContext.getBean(“userService”);

userService.save();

}

运行后果:

save tony

能够看到,在配置文件中配置的属性,在 Spring 治理该类的时候将其依赖的属性胜利进行了设置。如果不应用依赖注入,则无奈应用接口,只能应用实现类来进行设置,因为接口中没有该属性。

Spring 的工厂类

  • BeanFactory: 老版本的工厂类,在调用 getBean() 办法时,才会生成类的实例。
  • ApplicationContext: 在加载配置文件的时候,就会将 Spring 治理的类都实例化。有两个实现类:
  1. ClassPathXmlApplicationContext: 加载类门路下的配置文件
  2. FileSystemXmlApplicationContext: 加载磁盘下的配置文件

bean 标签配置

  • id: 惟一束缚,不能呈现特殊字符
  • name: 实践上能够反复,然而开发中最好不要。能够呈现特殊字符

生命周期:

  • init-method: bean 被初始化的时候执行的办法
  • destroy-method: bean 被销毁的时候执行的办法

作用范畴:

  • scope:bean 的作用范畴,有如下几种,罕用的是前两种
  • singleton: 默认应用单例模式创立
  • prototype: 多例
  • request: 在 web 我的项目中,spring 创立类后,将其存入到 request 范畴中
  • session: 在 web 我的项目中,spring 创立类后,将其存入到 session 范畴中
  • globalsession: 在 web 我的项目中,必须用在 porlet 环境

属性注入设置

  1. 构造方法形式的属性注入:Car 类在构造方法中有两个属性,别离为 name 和 price。

<bean id=”car” class=”demo.Car”>

<constructor-arg name=”name” value=”bmw”>

<constructor-arg name=”price” value=”123″>

</bean>

  1. set 办法属性注入:Employee 类在有两个 set 办法,别离设置一般类型的 name 和援用类型的 Car(应用 ref 指向援用类型的 id 或 name)。

<bean id=”employee” class=”demo.Employee”>

<property name=”name” value=”xiaoming”>

<property name=”car” ref=”car”>

</bean>

  1. P 名称空间的属性注入:首先须要引入 p 名称空间:

<beans xmlns=”http://www.springframework.org/schema/beans”

// 引入 p 名称空间

xmlns:p=”http://www.springframework.org/schema/p”

xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”

xsi:schemaLocation=”

http://www.springframework.or… http://www.springframework.org/schema/beans/spring-beans.xsd”>

</beans>

如果是一般属性:

<bean id=”car” class=”demo.Car” p:name=”bmv” p:price=”123″>

</bean>

如果是援用类型:

<bean id=”employee” class=”demo.Employee” p:name=”xiaoming” p:car-ref:”car”>

</bean>

  1. SpEL(Spring Expression Language) 属性注入(Spring 3.x 以上版本)

<bean id=”car” class=”demo.Car”>

<property name=”name” value=”#{‘xiaoming’}”>

<property name=”car” ref=”#{car}”>

</bean>

  1. 汇合类型属性注入:

<bean id=”car” class=”demo.Car”>

<property name=”namelist”>

<list>

<value>qirui</value>

<value>baoma</value>

<value>benchi</value>

</list>

</property>

</bean>

多模块开发配置

  1. 在加载配置文件的时候,加载多个配置文件
  2. 在一个配置文件中引入多个配置文件,通过实现

IOC 注解开发

示例

  1. 引入 jar 包:除了要引入上述的四个包之外,还须要引入 aop 包。
  2. 创立 applicationContext.xml,应用注解开发引入 context 束缚(xsd-configuration.html)

<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” xsi:schemaLocation=”

http://www.springframework.or… http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.or… http://www.springframework.org/schema/context/spring-context.xsd”>

<!– bean definitions here –>

</beans>

  1. 组件扫描:应用 IOC 注解开发,须要配置组件扫描,也就是哪些包下的类应用 IOC 的注解。

<context:component-scan base-package=”demo1″>

  1. 在类上增加注解
  2. 应用注解设置属性的值

属性如果有 set 办法,将属性注入的注解增加到 set 办法

属性没有 set 办法,将注解增加到属性上。

@Component(“UserDao”)// 相当于配置了一个 <bean> 其 id 为 UserDao, 对应的类为该类

public class UserDAOImpl implements UserDAO {

@Override

public void save() {

// TODO Auto-generated method stub

System.out.println(“save”);

}

}

注解详解

  1. @Component

组件注解,用于润饰一个类,将这个类交给 Spring 治理。

有三个衍生的注解,性能相似,也用来润饰类。

  • @Controller:润饰 web 层类
  • @Service:润饰 service 层类
  • @Repository:润饰 dao 层类

2. 属性注入

  • 一般属性应用 @Value 来设置属性的值
  • 对象属性应用 @Autowired,这个注解是依照类型来进行属性注入的。如果心愿依照 bean 的名称或 id 进行属性注入,须要用 @Autowired 和 @Qualifier 一起应用
  • 理论开发中,应用 @Resource(name=” “) 来进行依照对象的名称实现属性注入

3. 其余注解

  • @PostConstruct 相当于 init-method,用于初始化函数的注解
  • @PreDestroy 相当于 destroy-method,用于销毁函数的注解
  • @Scope 作用范畴的注解,罕用的是默认单例,还有多例 @Scope(“prototype”)

IOC 的 XML 和注解开发比拟

  • 实用场景:XML 实用于任何场景;注解只适宜本人写的类,不是本人提供的类无奈增加注解。
  • 能够应用 XML 治理 bean,应用注解来进行属性注入

AOP 开发


AOP 是 Aspect Oriented Programming 的缩写,意为面向切面编程,通过预编译形式和运行期动静代理实现程序性能的对立保护的一种技术,是 OOP 的连续。

AOP 可能对程序进行加强,在不批改源码的状况下,能够进行权限校验,日志记录,性能监控,事务管制等。

也就是说性能分为两大类,一类是外围业务性能,一类是辅助加强性能。两类性能彼此独立进行开发。比方登录性能是外围业务性能,日志性能是辅助加强性能,如果有须要,将日志和登录编制在一起。辅助性能就称为切面,这种能选择性的、低耦合的把切面和外围业务性能联合的编程思维称为切面编程。

底层实现

JDK 动静代理只能对实现了接口的类产生代理。Cglib 动静代理能够对没有实现接口的类产生代理对象,生成的是子类对象。

应用 JDK 动静代理:

public interface UserDao {

public void insert();

public void delete();

public void update();

public void query();

}

实现类:

public class UserDaoImpl implements UserDao {@Override public void insert() {System.out.println(“insert”); } @Override public void delete() { System.out.println(“delete”); } @Override public void update() { System.out.println(“update”); } @Override public void query() { System.out.println(“query”); } }

JDK 代理:

public class JDKProxy implements InvocationHandler{

private UserDao userDao;

public JDKProxy(UserDao userDao){

this.userDao=userDao;

}

public UserDao createProxy(){

UserDao userDaoProxy=(UserDao)Proxy.newProxyInstance(userDao.getClass().getClassLoader(),

userDao.getClass().getInterfaces(), this);

return userDaoProxy;

}

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

if(“update”.equals(method.getName())){

System.out.println(“ 权限校验 ”);

return method.invoke(userDao, args);

}

return method.invoke(userDao, args);

}

}

通过动静代理加强了 update 函数。测试类:

public class Demo1 {

@Test

public void demo1(){

UserDao userDao=new UserDaoImpl();

UserDao proxy=new JDKProxy(userDao).createProxy();

proxy.insert();

proxy.delete();

proxy.update();

proxy.query();

}

}

运行后果为:

insert

delete

权限校验

update

query

CglibCglib 是第三方开源代码生成类库,能够动静增加类的属性和办法。

与上边 JDK 代理不同,Cglib 的应用形式如下:

public class CglibProxy implements MethodInterceptor{

// 传入加强的对象

private UserDao customerDao;

public CglibProxy(UserDao userDao){

this.userDao=userDao;

}

public UserDao createProxy(){

Enhancer enhancer=new Enhancer();

enhancer.setSuperclass(userDao.getClass());

enhancer.setCallback(this);

UserDao proxy=(UserDao)enhancer.create();

return proxy;

}

@Override

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

if(“save”.equals(method.getName())){

System.out.println(“enhance function”);

return methodProxy.invokeSuper(proxy, args);

}

return methodProxy.invokeSuper(proxy, args);

}

}

如果实现了接口的类,底层采纳 JDK 代理。如果不是实现了接口的类,底层采纳 Cglib 代理。

IOC 与传统形式的比拟

  1. 获取对象形式:传统通过 new 关键字被动创立一个对象。IOC 形式中,将对象的生命周期交给 Spring 治理,间接从 Spring 获取对象。也就是管制反转————将控制权从本人手中交到了 Spring 手中。

Spring 的 AOP 开发(AspectJ 的 XML 形式)

AspectJ 是一个 AOP 的框架,Spring 引入 AspectJ,基于 AspectJ 进行 AOP 的开发。

相干术语

  • Joinpoint: 连接点,能够被拦挡到的点。也就是能够被加强的办法都是连接点。
  • Pointcut: 切入点,真正被拦挡到的点,也就是真正被加强的办法
  • Advice: 告诉,办法层面的加强。对某个办法进行加强的办法,比方对 save 办法进行权限校验,权限校验的办法称为告诉。
  • Introduction: 引介,类层面的加强。
  • Target: 指标,被加强的对象(类)。
  • Weaving: 织入,将 advice 利用到 target 的过程。
  • Proxy: 代理对象,被加强的对象。
  • Aspect: 切面,多个告诉和多个切入点的组合。

应用办法

  1. 引入相干包
  2. 引入配置文件

<?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:aop=”http://www.springframework.org/schema/aop” xsi:schemaLocation=”

http://www.springframework.or… http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.or… http://www.springframework.org/schema/aop/spring-aop.xsd”> <!– bean definitions here –>

</beans>

  1. 编写指标类并配置:

public class ProductDaoImpl implements ProductDao {

@Override

public void save() {

System.out.println(“save”);

}

@Override

public void update() {

System.out.println(“update”);

}

@Override

public void find() {

System.out.println(“find”);

}

@Override

public void delete() {

System.out.println(“delete”);

}

}

<bean id=”productDao” class=”demo1.ProductDaoImpl”></bean>

  1. 编写切面类,假如用于权限验证并配置

public class MyAspectXML {

public void checkPri(){

System.out.println(“check auth”);

}

}

<bean id=”myAspect” class=”demo1.MyAspectXML”></bean>

  1. 通过 AOP 配置实现对指标类的加强

<aop:config>

<aop:pointcut expression=”execution(* demo1.ProductDaoImpl.save(..))” id=”pointcut1″/>

<aop:aspect ref=”myAspect”>

<aop:before method=”chechPri” pointcut-ref=”pointcut1″/>

</aop:aspect>

</aop:config>

告诉类型

  1. 前置告诉:在指标办法执行前操作,能够取得切入点信息

<aop:before method=”chechPri” pointcut-ref=”pointcut1″/>

public void checkPri(JoinPoint joinPoint){

System.out.println(“check auth “+joinPoint);

}

  1. 后置告诉:在指标办法执行后操作,能够取得办法返回值

<aop:after-returning method=”writeLog” pointcut-ref=”pointcut2″ returning=”result”/>

public void writeLog(Object result){

System.out.println(“writeLog “+result);

}

  1. 盘绕告诉:在指标办法执行前和后操作,能够阻止指标办法执

<aop:around method=”around” pointcut-ref=”pointcut3″/>

public Object around(ProceedingJoinPoint joinPoint) throws Throwable{

System.out.println(“before”);

Object result=joinPoint.proceed();

System.out.println(“after”);

return result;

}

  1. 异样抛出告诉:程序出现异常时操作

<aop:after-throwing method=”afterThrowing” pointcut-ref=”pointcut4″ throwing=”ex”/>

public void afterThrowing(Throwable ex){

System.out.println(“exception “+ex.getMessage());

}

  1. 最终告诉:相当于 finally 块,无论代码是否有异样,都会执行

<aop:after method=”finallyFunc” pointcut-ref=”pointcut4″/>

public void finallyFunc(){

System.out.println(“finally”);

}

  1. 引介告诉:不罕用

Spring 切入点表达式

基于 execution 函数实现

语法:[拜访修饰符] 办法返回值 包名. 类名. 办法名 (参数)

其中任意字段能够应用 * 代替示意任意值

Spring 的 AOP 基于 AspectJ 注解开发

开发步骤

  1. 引入 jar 包
  2. 设置配置文件:

<?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.or…

http://www.springframework.or…

http://www.springframework.or…

http://www.springframework.or…

http://www.springframework.or…

http://www.springframework.or…

http://www.springframework.or…d”>

</beans>

  1. 编写配置指标类

<bean id=”orderDao” class=”demo1.OrderDao”></bean>

public class OrderDao {

public void save(){

System.out.println(“save order”);

}

public void update(){

System.out.println(“update order”);

}

public void delete(){

System.out.println(“delete order”);

}

public void find(){

System.out.println(“find order”);

}

}

  1. 开启 aop 注解主动代理

<aop:aspectj-autoproxy/>

  1. 编写切面类并配置

@Aspect

public class MyAspectAnno {

@Before(value=”execution(* demo1.OrderDao.save(..))”)

public void before(){

System.out.println(“before”);

}

}

<bean id=”myAspect” class=”demo1.MyAspectAnno”>

注解告诉类型

  • @Before:前置告诉
  • @AfterReturning: 后置告诉

@AfterReturning(value=”execution(* demo1.OrderDao.save(..))”,returning=”result”)

public void after(Object result){

System.out.println(“after “+result);

}

  • @Around:盘绕告诉

@Around(value=”execution(* demo1.OrderDao.save(..))”)

public Object around(ProceedingJoinPoint joinPoint) throws Throwable{

System.out.println(“before”);

Object obj=joinPoint.proceed();

System.out.println(“after”);

return obj;

}

  • @AfterThrowing: 抛出异样

@AfterThrowing(value=”execution(* demo1.OrderDao.save(..))”,throwing=”e”)

public void afterThrowing(Throwable e){

System.out.println(“exception:”+e.getMessage();

}

  • @After: 最终告诉

@After(value=”execution(* demo1.OrderDao.save(..))”)

public void after(){

System.out.println(“finally”);

}

  • @PointCut: 切入点注解

@PointCut(value=”execution(* demo1.OrderDao.save(..))”)

private void pointcut1(){}

此时,在上述告诉的注解中,value 能够替换为该函数名,例如:

@After(value=”MyAspect.pointcut1()”)

public void after(){

System.out.println(“finally”);

}

这个注解的益处是,只须要保护切入点即可,不必在批改时批改每个注解。

Spring 的 JDBC 模板


Spring 对长久层也提供了解决方案,也就是 ORM 模块和 JDBC 的模板。针对 JDBC,提供了 org.springframework.jdbc.core.JdbcTemplate 作为模板类。

应用 JDBC 模板

  1. 引入 jar 包,数据库驱动,Spring 的 jdbc 相干包。
  2. 根本应用:

public void demo1(){

// 创立连接池

DriverManagerDataSource dataSource=new DriverManagerDataSource();

dataSource.setDriverClassName(“com.mysql.jdbc.Driver”);

dataSource.setUrl(“jdbc:mysql:///spring4”);

dataSource.setUsername(“root”);

dataSource.setPassword(“123456”);

// 创立 JDBC 模板

JdbcTemplate jdbcTemplate=new JdbcTemplate(dataSource);

jdbcTemplate.update(“insert into account values (null,?,?)”, “xiaoming”,1000d);

}

  1. 将连接池和模板交给 Spring 治理
  • 配置文件:

<bean id=”dataSource” class=”org.springframework.jdbc.datasource.DriverManagerDataSource;”>

<property name=”driverClassName” value=”com.mysql.jdbc.Driver”></property>

<property name=”url” value=”jdbc:mysql:///spring4″></property>

<property name=”username” value=”root”></property>

<property name=”password” value=”123456″></property>

</bean>

<bean id=”jdbcTemplate” class=”org.springframework.jdbc.core.JdbcTemplate;”>

<property name=”dataSource” ref=”dataSource”></property>

</bean>

  • 测试文件:

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(“classpath:applicationContext.xml”)

public class JdbcDemo2 {

@Resource(name=”jdbcTemplate”)

private JdbcTemplate jdbcTemplate;

@Test

public void demo2(){

jdbcTemplate.update(“insert into account values (null,?,?)”, “xiaolan”,1000d);

}

}

应用开源数据库连接池

  1. 应用 DBCP 的配置:

<bean id=”dataSource” class=”org.apache.commons.dbcp.BasicDataSource”>

<property name=”driverClassName” value=”com.mysql.jdbc.Driver”></property>

<property name=”url” value=”jdbc:mysql://192.168.66.128/spring4″></property>

<property name=”username” value=”root”></property>

<property name=”password” value=”123456″></property>

  1. 应用 C3P0 的配置:

<bean id=”dataSource” class=”com.mchange.v2.c3p0.ComboPooledDataSource”>

<property name=”driverClass” value=”com.mysql.jdbc.Driver”></property>

<property name=”jdbcUrl” value=”jdbc:mysql://192.168.66.128/spring4″></property>

<property name=”user” value=”root”></property>

<property name=”password” value=”123456″></property>

</bean>

  1. 引入内部属性文件

首先建设内部属性文件:

jdbc.driverClass=com.mysql.jdbc.Driver

jdbc.url=jdbc:mysql://192.168.66.128/spring4

jdbc.username=root

jdbc.password=123456

而后对属性文件进行配置:

<context:property-placeholder location=”classpath:jdbc.properties”/>

<bean id=”dataSource” class=”com.mchange.v2.c3p0.ComboPooledDataSource”>

<property name=”driverClass” value=”${jdbc.driverClass}”></property>

<property name=”jdbcUrl” value=”${jdbc.url}”></property>

<property name=”user” value=”${jdbc.username}”></property>

<property name=”password” value=”${jdbc.password}”></property>

</bean>

CRUD 操作

insert, update, delete 语句都借助模板的 update 办法进行操作。

public void demo(){

jdbcTemplate.update(“insert into account values (null,?,?)”, “xiaoda”,1000d);

jdbcTemplate.update(“update account set name=?,money=? where id=?”, “xiaoda”,1000d,2);

jdbcTemplate.update(“delete from account where id=?”, 6);

}

查问操作:

public void demo3(){

String name=jdbcTemplate.queryForObject(“select name from account where id=?”,String.class,5);

long count=jdbcTemplate.queryForObject(“select count(*) from account”,Long.class);

}

将返回的后果封装成为类:

public void demo4(){

Account account=jdbcTemplate.queryForObject(“select * from account where id=?”, new MyRowMapper(),5);

}

其中:

class MyRowMapper implements RowMapper<Account>{

@Override

public Account mapRow(ResultSet rs, int rowNum) throws SQLException {

Account account=new Account();

account.setId(rs.getInt(“id”));

account.setName(rs.getString(“name”));

account.setMoney(rs.getDouble(“money”));

return account;

}

}

Spring 的事务管理


事务

事务是指逻辑上的一组操作,组成这组操作的各个单元,要么全副胜利,要么全副失败。

具备四个个性:

  • 原子性:事务不可分
  • 一致性:事务执行前后数据完整性保持一致
  • 隔离性:一个事务的执行不应该受到其余事务烦扰
  • 持久性:一旦事务完结,数据就长久化到数据库

如果不思考隔离性会引发安全性问题:

  • 读问题:
  • 脏读:一个事务读到另一个事务未提交的数据
  • 不可反复读:一个事务读到另一个事务曾经提交的 update 数据,导致一个事务中屡次查问后果不统一
  • 幻读:一个事务读到另一个事务曾经提交的 insert 数据,导致一个事务中屡次查问后果不统一
  • 写问题:
  • 失落更新

解决读问题:设置事务隔离级别

  • Read uncommitted: 未提交读,无奈解决任何读问题
  • Read committed: 已提交读,解决脏读问题
  • Repeatable read: 反复读,解决脏读和不可反复读问题
  • Serializable:序列化,解决所有读问题

事务管理 API

  1. PlatformTransactionManager: 平台事务管理器

这是一个接口,领有多个不同的实现类,如 DataSourceTransactionManager 底层应用了 JDBC 治理事务;HibernateTransactionManager 底层应用了 Hibernate 治理事务。

  1. TransactionDefinition: 事务定义信息

用于定义事务的相干信息,如隔离级别、超时信息、流传行为、是否只读等

  1. TransactionStatus: 事务的状态

用于记录在事务管理过程中,事务的状态的对象。

上述 API 的关系:Spring 在进行事务管理的时候,首先平台事务管理器依据事务定义信息进行事务管理,在事务管理过程当中,产生各种此状态,将这些状态信息记录到事务状态的对象当中。

事务的流传行为

事务的流传行为次要解决业务层(Service)办法互相调用的问题,也就是不同的业务中存在不同的事务时,如何操作。

Spring 中提供了 7 种事务的流传行为,分为三类:

  • 保障多个操作在同一个事务中
  • PROPAGATION_REQUIRED: B 办法调用 A 办法,如果 A 中有事务,应用 A 中的事务并将 B 中的操作蕴含到该事务中;否则新建一个事务,将 A 和 B 中的操作蕴含进来。(默认)
  • PROPAGATION_SUPPORTS:如果 A 中有事务,应用 A 的事务;否则不应用事务
  • PROPAGATION_MANDATORY:如果 A 中有事务,应用 A 的事务;否则抛出异样
  • 保障多个操作不在同一个事务中
  • PROPAGATION_REQUIRES_NEW:如果 A 中有事务,将其挂起,创立新事务,只蕴含本身操作。否则,新建一个事务,只蕴含本身操作。
  • PROPAGATION_NOT_SUPPORTED:如果 A 中有事务,挂起,不应用事务。
  • PROPAGATION_NEVER:如果 A 中有事务,抛出异样,也即不能用事务运行。
  • 嵌套事务
  • PROPAGATION_NESTED:如果 A 有事务,依照 A 的事务执行,执行实现后,设置一个保留点,而后执行 B 的操作。如果出现异常,能够回滚到最后状态或保留点状态。

实例

以转账为例,业务层的 DAO 层类如下:

public interface AccountDao {

public void outMoney(String from,Double money);

public void inMoney(String to,Double money);

}

public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao{

@Override

public void outMoney(String from, Double money) {

this.getJdbcTemplate().update(“update account set money = money – ? where name = ?”,money,from);

}

@Override

public void inMoney(String to, Double money) {

this.getJdbcTemplate().update(“update account set money = money + ? where name = ?”,money,to);

}

}

public interface AccountService {

public void transfer(String from,String to,Double money);

}

public class AccountServiceImpl implements AccountService {

private AccountDao accountDao;

public void setAccountDao(AccountDao accountDao) {

this.accountDao = accountDao;

}

@Override

public void transfer(String from, String to, Double money) {

accountDao.outMoney(from, money);

accountDao.inMoney(to, money);

}

}

在 xml 中进行类的配置:

<bean id=”accountService” class=”tx.demo.AccountServiceImpl”>

<property name=”accountDao” ref=”accountDao”/>

</bean>

<bean id=”accountDao” class=”tx.demo.AccountDaoImpl”>

<property name=”dataSource” ref=”dataSource”/>

</bean>

事务管理 1: 编程式事务管理

  1. 配置平台事务管理器

<bean id=”transactionManager” class=”org.springframework.jdbc.datasource.DataSourceTransactionManager”>

<property name=”dataSource” ref=”dataSource”></property>

</bean>

  1. 配置事务管理模板类

<bean id=”transactionTemplate” class=”org.springframework.transaction.support.TransactionTemplate”>

<property name=”transactionManager” ref=”transactionManager”></property>

</bean>

  1. 在业务层注入事务管理模板

<bean id=”accountService” class=”tx.demo1.AccountServiceImpl”>

<property name=”accountDao” ref=”accountDao”/>

<property name=”transactionTemplate” ref=”transactionTemplate”/>

</bean>

  1. 编码实现事务管理

//ServiceImpl 类中:

private TransactionTemplate transactionTemplate;

@Override

public void transfer(String from, String to, Double money) {

transactionTemplate.execute(new TransactionCallbackWithoutResult() {

@Override

protected void doInTransactionWithoutResult(TransactionStatus arg0) {

accountDao.outMoney(from, money);

accountDao.inMoney(to, money);

}

});

}

申明式事务管理(配置实现,基于 AOP 思维)

  1. XML 形式的申明式事务管理
  • 配置事务管理器

<bean id=”transactionManager” class=”org.springframework.jdbc.datasource.DataSourceTransactionManager”>

<property name=”dataSource” ref=”dataSource”></property>

</bean>

  • 配置事务告诉

<tx:advice id=”txAdvice” transaction-manager=”transactionManager”>

<tx:attributes>

<tx:method name=”transfer” propagation=”REQUIRED”/>

</tx:attributes>

</tx:advice>

  • 配置 aop 事务

<aop:config>

<aop:pointcut expression=”execution( tx.demo2.AccountServiceImpl.(..))” id=”pointcut1″/>

<aop:advisor advice-ref=”txAdvice” pointcut-ref=”pointcut1″/>

</aop:config>

  1. 注解形式
  • 配置事务管理器,和上方统一
  • 开启事务管理的注解:

<tx:annotation-driven transaction-manager=”transactionManager”/>

  • 在应用事务的类上增加一个注解 @Transactional
正文完
 0