一、Spring AOP
1、什么是AOP技术
面向切面编程,解决代码复用问题。
AOP的核心:在方法之前或者方法之后处理事情。底层使用 代理设计模式。
2、AOP技术应用场景
日志、事务、权限、参数验证、性能监控
3、AOP底层实现原理
通过代理设计模式实现
4、AOP技术具体使用
pom.xml
<!-- 相关jar -->
<dependencies>
<!-- 引入Spring-AOP等相关Jar -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>3.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>3.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.1_2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version>
</dependency>
</dependencies>
在spring.xml 增加aop标签
<aop:aspectj-autoproxy></aop:aspectj-autoproxy> <!-- 开启事物注解 -->
AOP注解:
@Aspect 指定一个类为切面类
@Pointcut("execution(* 包名.方法名(..))") 指定切入点表达式
@Before("pointCut_()")/@Before("execution(* 包名.方法名(..))") 前置通知:在目标方法执行前执行。
@After("pointCut_()")/@After("execution(* 包名.方法名(..))") 后置通知:在目标方法执行后执行。
@AfterReturning("pointCut_()")/@AfterReturning("execution(* 包名.方法名(..))") 返回后通知: 执行方法结束前执行(异常不执行)
@AfterThrowing("pointCut_()")/@AfterThrowing("execution(* 包名.方法名(..))") 异常通知: 出现异常时候执行
@Around("pointCut_()")/@Around("execution(* 包名.方法名(..))") 环绕通知: 环绕目标方法执行
二、Spring事务
1、事务基本特性
原子性:事务包含的操作,要么全部成功,要么全部失败回滚。
一致性:一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。
拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。
隔离性:隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启一个的事务,不能被其他事务的操作干扰,多个并发事务之间要相互隔离。
持久性:提一个事务一旦被提交了,那么对数据库中的数据改变是永久性的。即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
2、事务传播行为
PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。(默认)
PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起,新建一个事务。
PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,就抛出异常。
3、事务隔离级别
Isolation.DEFAULT--默认隔离级别,每种数据库支持的事务隔离级别不一样,使用数据库的默认隔离级别。
Isolation.READ_COMMITTED--读取已提交,能够读取到已经提交的数据,可以防止脏读,但无法限制不可重复读和幻读。
Isolation.READ_UNCOMMITTED--读取未提交,能够读取到没有被提交的数据,无法解决脏读、不可重复读、幻读。
Isolation.REPEATABLE_READ--重复读取,在数据读取出来后加锁,防止别人修改它。类似 select * from table for update。这个事务不结束,别的事务就修改不了,可以解决脏读、不可重复读的问题,但无法解决幻读。
Isolation.SERIALIZABLE--串行化,最高级别的事务隔离,如果事务操作一张表时没有提交,会将表锁住,其他事务只能做读操作。所以效率极低。
4、事务控制分类
(1) 编程式事务控制
自定义事务工具类,通过AOP技术在调用方法前开启事务,在方法执行结束后提交事务,在方法异常时回滚事务。
事务工具类:
@Component//注入到spring
@Scope("prototype") // 每个事务都是新的实例 目的解决线程安全问题 多例子
public class TransactionUtils {
@Autowired
private DataSourceTransactionManager dataSourceTransactionManager;
private TransactionStatus status;//因为是多例模式,所的每个都是新实例,即事务状态也可以定义成全局变量
//开启事务
public TransactionStatus begin(){
status=dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());
return status;
}
//提交事务
public void commit(){
if(null!=status) dataSourceTransactionManager.commit(status);
}
//回滚事务
public void rollback(){
if(null!=status) dataSourceTransactionManager.rollback(status);
}
}
事务控制AOP类:
@Component
@Aspect//指定当前类开切面类
public class AopTransaction {
@Autowired
private TransactionUtils transactionUtils;
@AfterThrowing("execution(* com.mg.service.UserService.addUser(..))")
public void afterThrowing(){
System.out.println("-----------------回滚事务------------------");
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
@Around("execution(* com.mg.service.UserService.addUser(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("-----------------开启事务------------------");
TransactionStatus status=transactionUtils.begin();
proceedingJoinPoint.proceed();
transactionUtils.commit();
System.out.println("-----------------提交事务------------------");
}
}
(2) 声明式事务
注解:
@Target 说明注解所修饰的对象范围,可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数):
a. ElementType.CONSTRUCTOR 用于描述构造器。
b. ElementType.FIELD 用于描述域。
c. ElementType.LOCAL_VARIABLE 用于描述局部变量。
d. ElementType.METHOD 用于描述方法。
e. ElementType.PACKAGE 用于描述包。
f. ElementType.PARAMETER 用于描述参数。
g. ElementType.TYPE 用于描述类、接口(包括注解类型) 或 enm声明。
@Retention 用于描述注解的生命周期,即注解在什么范围内有效:
a. RetentionPolicy.SOURCE 注解只保留在源文件中,如果.java转换成.class后,注解就被忽视掉。
b. RetentionPolicy.CLASS 注解只保留在class文件,但jvm加载class时会被遗弃。(默认生命周期)
c. RetentionPolicy.RUNTIME 注解会一直保留
@Scope 是springioc里的一个作用域:
a. singleton单例模式 -- 全局有且仅有一个实例
b. prototype原型模式 -- 每次获取Bean的时候会有一个新的实例
c. request -- request表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效
d. session -- session作用域表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效
e. globalsession -- global session作用域类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义
//定义一个注解
@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AddAnnotation {
int age() default 0;
String name() default "默认名称";
String[] arrs();
}
//定义实体类使用注解
public class User {
@AddAnnotation(age=10,name="张三",arrs={"1"})
public void add(){
}
public void del(){}
public static void main(String[] args) throws ClassNotFoundException {
//1 先获取实体类
Class<?> obj=Class.forName("com.mg.annotation.User");
//2 获取该类的所有方法
Method[] methods=obj.getDeclaredMethods();
//3 循环判断方法上是否有 AddAnnotation 注解
for(Method m:methods){
System.out.println("-----methodName="+m.getName());
//4 判断方法上是否有注解
AddAnnotation addAnnotation = m.getDeclaredAnnotation(AddAnnotation.class);
if(null==addAnnotation){
continue;
}
System.out.println("-----user.age"+addAnnotation.age());
System.out.println("-----user.name"+addAnnotation.name());
}
}
}
自定义一个事务注解:@ExtTransanction
//定义AOP
@Component
@Aspect//指定当前类开切面类
public class AopExtTransanction {
@Autowired
private TransactionUtils transactionUtils;
@AfterThrowing("execution(* com.mg.service.UserService.addUser(..))")
public void afterThrowing() throws NoSuchMethodException {
transactionUtils.rollback();
System.out.println("-----------------回滚事务------------------");
}
@Around("execution(* com.mg.service.*.*.*(..))")
public void around(ProceedingJoinPoint pjp) throws Throwable{
//1 先获取代理对象
ExtTransaction extTransaction=getMethodExtTransaction(pjp);
//2 获取方法是否有注解
TransactionStatus status=begin(extTransaction);
pjp.proceed();
//3 提交事务
commit(status);
System.out.println("-----------------提交事务------------------");
}
private TransactionStatus begin(ExtTransaction extTransaction) {
if (extTransaction == null) {
return null;
}
System.out.println("-----------------开启事务------------------");
// 如果存在事务注解,开启事务
return transactionUtils.begin();
}
//提交事务
private void commit(TransactionStatus transactionStatus) {
if (transactionStatus != null) {
// 如果存在注解,提交事务
transactionUtils.commit();
}
}
// 获取方法上是否存在事务注解
private ExtTransaction getMethodExtTransaction(ProceedingJoinPoint pjp)
throws NoSuchMethodException, SecurityException {
String methodName = pjp.getSignature().getName();
// 获取目标对象
Class<?> classTarget = pjp.getTarget().getClass();
// 获取目标对象类型
Class<?>[] par = ((MethodSignature) pjp.getSignature()).getParameterTypes();
// 获取目标对象方法
Method objMethod = classTarget.getMethod(methodName, par);
ExtTransaction extTransaction = objMethod.getDeclaredAnnotation(ExtTransaction.class);
return extTransaction;
}
}
//具体使用
@Service("uService")
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@ExtTransaction
public void addUser() {
userDao.add("aaa","1");
int m=1/0;
userDao.add("bbb","2");
}
}
//定义测试类
public class Test01 {
public static void main(String[] args){
ClassPathXmlApplicationContext app=new ClassPathXmlApplicationContext("spring.xml");
UserService userService=(UserService)app.getBean("uService");
userService.addUser();
}
}
spring.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:p="http://www.springframework.org/schema/p"
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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.mg"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy> <!-- 开启事物注解 -->
<!-- 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://localhost:3306/test"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- 2. JdbcTemplate工具类实例 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 3.配置事务 -->
<bean id="dataSourceTransactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>