Spring框架学习02
一、springAOP简介
1、AOP概念
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,提高代码的灵活性和可扩展性,AOP可以说也是这种目标的一种实现。
2、AOP应用场景
Aop的功能主要是为了拦截或者实现日志安全检查等一些检查的工作,aop也常用于事务管理,防止垃圾数据进入数据库。
3、横向切割和纵向切割
Aop采用的横向切割技术,在不修改源代码的情况下完成添加功能的技术;纵向切割技术,修改源代码情况下完成功能的操作。
4、AOP的五种增强——实现横向切割技术的方式
前置增强,表示在目标方法执行前实施增强
后置增强,表示在目标方法执行后实施增强
环绕增强,表示在目标方法执行前后实施增强
异常增强,表示在目标方法抛出异常后实施增强。
最终增强,表示在目标方法执行后不管有没有异常抛出总是实施增强
二、Spring AOP的应用——基于aspectJ的xml版
1、pom.xml导入相关依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.java.Spring_AOP</groupId>
<artifactId>Spring_AOP</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
<!--支持注解-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!--spring自带的jdbc模块:jdbcTemplate对象,可调用增删改查方法-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<!--Aspectij这个jar包,不是spring框架的一部分,可以作为spring框架的一个组件,用来配合aop完成相应的操作-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
</dependencies>
</project>
2、创建一个目标类
package com.java.aop.xml;
/**
* @author Liushun
* @date Created in 2018/9/17 20:26
* @description 目标类,就是在操作过程中手动调用的类
*/
public class Target {
// 目标方法1:查询余额
public void selectAccount(){
System.out.println("模拟查询到用户余额为100");
}
}
3、 创建一个增强类
package com.java.aop.xml;
import org.aspectj.lang.ProceedingJoinPoint;
/**
* @author Liushun
* @date Created in 2018/9/17 20:23
* @description 增强类,在调用目标类的时候spring利用aop思想,执行预编译的类
*/
public class Handler {
// 前置增强:模拟用户验证
public void handlerBefore(){
System.out.println("前置增强方法--取款操作前的用户校验通过");
}
// 后置增强:模拟取款后的信息输出
public void handlerAfter(){
System.out.println("后置增强方法--取款后的信息输出");
}
// 环绕增强
public void around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕增强——检查前。。。");
// 调用切点方法
pjp.proceed();
System.out.println("环绕增强——检查后。。。");
}
// 异常增强:在业务目标方法中出现类异常情况,则会执行异常增强,如果不存在异常,则不执行异常增强
public void excep(){
System.out.println("异常增强——哎呀,出错啦!");
}
// 最终增强:不管有没有异常出现,均会执行的增强称之为最终增强
public void finallyHandler(){
System.out.println("最终增强--关闭资源");
}
}
4、applicationContext.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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--实例化目标类和增强类-->
<bean id="target" class="com.java.aop.xml.Target"/>
<bean id="handler" class="com.java.aop.xml.Handler"/>
<!--配置aop的切点和切面-->
<aop:config>
<!-- 切点:对应的是目标类的方法,在aop配置中可以有多个aop:ponitcut
expression称之为表达式,属性值等于execution(* com.java.aop.xml.Target.*(..))
execution表示执行,该方法中三个参数,参数1是*号匹配访问修饰符 ,参数2是一个空格,参数3表示目标类方法的地址
参数3方法中一定要携带两个点作为参数,第一个点表示参数类型,第二点表示参数名称,
注意:不管你的方法中是否有参数,都必须有两个点,id属性表示切点名称-->
<aop:pointcut id="myPointcut" expression="execution(* com.java.aop.xml.Target.*(..))"/>
<!--切面:对应的是增强类的方法,ref映射增强类的bean注册id-->
<aop:aspect ref="handler">
<!-- 前置增强:
method属性表示方法的意思,对应的是增强类的中具体的方法名称
pointcut-ref表示当前切面中的方法所对应的切点-->
<aop:before method="handlerBefore" pointcut-ref="myPointcut"/>
<!--后置增强-->
<aop:after-returning method="handlerAfter" pointcut-ref="myPointcut"/>
<!--环绕增强-->
<aop:around method="around" pointcut-ref="myPointcut"/>
<!--异常增强-->
<aop:after-throwing method="excep" pointcut-ref="myPointcut"/>
<!--最终增强-->
<aop:after method="finallyHandler" pointcut-ref="myPointcut"/>
</aop:aspect>
</aop:config>
</beans>
5、测试类
package com.java.aop.xml;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author Liushun
* @date Created in 2018/9/17 20:33
* @description aop测试类
*/
public class AOPTest {
// 调用目标方法,spring的aop功能会自动调用增强方法
@Test
public void aopTest(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Target target = (Target) context.getBean("target");
// 查询余额方法
target.selectAccount();
}
}
三、Spring AOP的应用——基于注解版
1、applicationContext.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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启spring注解驱动-->
<context:component-scan base-package="com.java.aop.anno"/>
<!--开启aop切面注解驱动(aop执行自动动态代理)-->
<aop:aspectj-autoproxy/>
</beans>
2、增强类
package com.java.aop.anno;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Service;
/**
* @author Liushun
* @date Created in 2018/9/17 20:23
* @description 增强类,在调用目标类的时候spring利用aop思想,执行预编译的类
*/
// 实例化增强类
@Service
// 开启切面的注解
@Aspect
public class Handler {
// 前置增强:模拟用户验证
@Before("execution(* com.java.aop.anno.Target.*(..))")
public void handlerBefore(){
System.out.println("前置增强方法--取款操作前的用户校验通过");
}
// 后置增强:模拟取款后的信息输出
@AfterReturning("execution(* com.java.aop.anno.Target.*(..))")
public void handlerAfter(){
System.out.println("后置增强方法--取款后的信息输出");
}
// 环绕增强
@Around("execution(* com.java.aop.anno.Target.*(..))")
public void around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕增强——检查前。。。");
// 调用切点方法
pjp.proceed();
System.out.println("环绕增强——检查后。。。");
}
// 异常增强:在业务目标方法中出现类异常情况,则会执行异常增强,如果不存在异常,则不执行异常增强
@AfterThrowing("execution(* com.java.aop.anno.Target.*(..))")
public void excep(){
System.out.println("异常增强——哎呀,出错啦!");
}
// 最终增强:不管有没有异常出现,均会执行的增强称之为最终增强
@After("execution(* com.java.aop.anno.Target.*(..))")
public void finallyHandler(){
System.out.println("最终增强--关闭资源");
}
}
3、目标类
package com.java.aop.anno;
import org.springframework.stereotype.Service;
/**
* @author Liushun
* @date Created in 2018/9/17 20:26
* @description 目标类,就是在操作过程中手动调用的类
*/
// 实例化目标类
@Service
public class Target {
// 目标方法1:查询余额
public void selectAccount(){
System.out.println("模拟查询到用户余额为100");
}
}
4、测试类
package com.java.aop.anno;
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.SpringJUnit4ClassRunner;
/**
* @author Liushun
* @date Created in 2018/9/17 20:33
* @description aop测试类
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/applicationContext.xml")
public class AOPTest2 {
@Autowired
private Target target;
// 调用目标方法,spring的aop功能会自动调用增强方法
@Test
public void aopTest(){
// 查询余额方法
target.selectAccount();
}
}
5、注意事项
目标类要设置@service注解,目的是为了在调用service时候完成实例化和注入操作
增强类要设置@Aspect切面注解,目的是定义当前类是切面,而且也要设置@service注解,目的也是为了在完成实例化和注入操作
注解开发,主要更改的是增强类里的注解配置
四、Spring的事务管理
1、事务的理解
事务管理对于企业应用而言至关重要。它保证了用户的每一次操作都是可靠的,即便出现了异常的访问情况,也不至于破坏后台数据的完整性。就像银行的自助取款机,通常都能正常为客户服务,但是也难免遇到操作过程中机器突然出故障的情况,此时,事务就必须确保出故障前对账户的操作不生效,就像用户刚才完全没有使用过取款机一样,以保证用户和银行的利益都不受损失
简单来说:事务是一系列的动作,一旦其中有一个动作出现错误,必须全部回滚,系统将事务中对数据库的所有已完成的操作全部撤消,滚回到事务开始的状态,避免出现由于数据不一致而导致的接下来一系列的错误。事务的出现是为了确保数据的完整性和一致性,在目前企业级应用开发中,事务管理是必不可少的
2、事务的属性
-
传播行为
定义了事务应用到方法上的边界。也就是什么时候开始一个新的事务,或者什么时候事务被暂停,或者方法是否要在事务中运行
事务传播行为类型 | 说明 |
---|---|
PROPAGATION_REQUIREO | 如果当前没有事务,就创建一个事务;如果已经存在一个事务,就加入到这个事务中。这是最常见的选择 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务就以非事务方式执行 |
PROPAGATION_MANDATORY | 使用当前的事务,如果当前没有事务就抛出异常 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务则将当前事务挂起 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务则将当前事务挂起 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务则抛出异常 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIREO类似的操作 |
-
隔离级别
用来指定一个事务受其他并发事务活动影响的程度。隔离性是数据的事务的4个特征之一,但是实际上来说,用户使用数据库产品是需要在效率和安全上找到平衡点的,所以需要设置事务的隔离级别
隔离级别 | 说明 |
---|---|
ISOLATION_DEFAULT | 使用底层数据库预设的隔离级别 |
ISOLATION_READ_COMMITTED | 允许事务读取其它并行的事务已经送出的数据字段,可以防止脏读取问题 |
ISOLATION_READ_UNCOMMITTED | 允许事务读取其它并行的事务还没送出的数据,会发生脏读取、非重复读、幻读等问题 |
ISOLATION_REPEATABLE_READ | 要求多次读取的数据必须相同,除非事务本身更新数据,可防止脏读取、非重复读问题 |
ISOLATION_SERIALIZABLE | 完整的隔离层次,可防止脏读取、非重复读、幻读等问题,会锁定相应的表格数据,导致使用此级别的应用效率降低 |
-
只读提示
Read-only=true,必须配合传播行为来设置,一般建议设置在查询的方法中
-
事务超时间隔
可以让事务在特定的秒数后自动回滚,不必等到它自己结束,它也必须配合传播行为来设置。比如,Timeout=5
-
回滚规则
默认情况下在出现运行时异常RuntimeException才会回滚,检查时异常不回滚。当然也可以改变这种规则
五、声明式事务
1、声明式事务简介
声明式事务管理:它将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。事务管理作为一种横切关注点,可以通过AOP方法模块化。Spring通过Spring AOP框架支持声明式事务管理。
声明式事务管理有三种方法实现,分别是TransactionProxyFactoryBean的代理方式、基于AspectJ的xml配置方式和基于注解的声明方式,后两种在开发应用中常常出现。
声明式事务管理是基于AOP思想完成的,类似与在给业务层加入切面,对事务操作前后进行一定的事务管理控制。
2、声明式事务应用——基于注解版–转账案例
1、applicationContext.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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启spring注解驱动-->
<context:component-scan base-package="com.java.tx"/>
<!--开启aop切面注解驱动(aop执行自动动态代理)-->
<aop:aspectj-autoproxy/>
<!--实例化jdbcTemplate模板对象,需要数据源-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--创建数据源dataSource-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/demo?useSSL=true"/>
<property name="user" value="root"/>
<property name="password" value="Root"/>
</bean>
<!--配置事务管理-->
<bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--开启事务的注解-->
<tx:annotation-driven transaction-manager="transactionManager1"/>
</beans>
2、Dao层实现类
package com.java.tx.dao.impl;
import com.java.tx.dao.IAccountDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
/**
* @author Liushun
* @date Created in 2018/9/18 20:04
* @description DAO层实现类
*/
@Repository
public class AccountDaoImpl implements IAccountDao {
@Autowired(required = false)
private JdbcTemplate jdbcTemplate;
@Override
public void modifyOutAccount(String outCard, Double money) {
String sql = "UPDATE account SET money = money - ? WHERE `name` = ?";
jdbcTemplate.update(sql,money,outCard);
}
@Override
public void modifyInAccount(String inCard, Double money) {
String sql = "UPDATE account SET money = money + ? WHERE `name` = ?";
jdbcTemplate.update(sql,money,inCard);
}
}
3、Service层实现类
package com.java.tx.service.impl;
import com.java.tx.dao.IAccountDao;
import com.java.tx.service.IAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* @author Liushun
* @date Created in 2018/9/18 20:10
* @description Service层实现类
*/
@Service
// 事务的注解,表示当前类下的所有方法均加入事务
@Transactional
public class AccountServiceImpl implements IAccountService {
@Autowired(required = false)
private IAccountDao accountDao;
@Override
public void transferAccount(String inCard, String outCard, Double money) {
// 转出
accountDao.modifyOutAccount(outCard,money);
// 异常
int i = 1/0;
// 转入
accountDao.modifyInAccount(inCard,money);
}
}
4、测试类
package com.java.tx;
import com.java.tx.service.IAccountService;
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.SpringJUnit4ClassRunner;
/**
* @author Liushun
* @date Created in 2018/9/18 20:39
* @description 声明式事务测试类
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/applicationContext.xml")
public class TxTest {
@Autowired
private IAccountService accountService;
@Test
public void test(){
accountService.transferAccount("张三","李四",50D);
}
}