AOP基本概念、AOP底层实现原理、AOP经典应用【事务管理、异常日志处理、方法审计】
1 什么是AOP
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
2 通过配置实现AOP编程
2.1 导包
2.2 编写切面类(其实就是一个类)
1 package cn.xiangxu.cloudNote.aspect; 2 3 public class AspectDemo01 { 4 public void logController() { 5 System.out.println("AOP功能注入Controller"); 6 } 7 }
2.3 配置切面组件
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:jdbc="http://www.springframework.org/schema/jdbc" 6 xmlns:jee="http://www.springframework.org/schema/jee" 7 xmlns:tx="http://www.springframework.org/schema/tx" 8 xmlns:aop="http://www.springframework.org/schema/aop" 9 xmlns:mvc="http://www.springframework.org/schema/mvc" 10 xmlns:util="http://www.springframework.org/schema/util" 11 xmlns:jpa="http://www.springframework.org/schema/data/jpa" 12 xsi:schemaLocation=" 13 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 14 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd 15 http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd 16 http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd 17 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd 18 http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd 19 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 20 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd 21 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd"> 22 23 <!-- 案例:将AspectDemo01组件的logController方法作用与controller包及其自爆中所有方法 --> 24 <!-- 配置切入功能类的bean组件 --> 25 <bean id="aspectDemo01" class="cn.xiangxu.cloudNote.aspect.AspectDemo01"></bean> 26 <!-- AOP配置 --> 27 <aop:config> 28 <!-- 指定切面组件,通过ref属性进行关联 --> 29 <aop:aspect ref="aspectDemo01"> 30 <!-- 通过method指定处理方法 --> 31 <!-- 通过poincut指定切入点 --> 32 <!-- within为类限定表达式 --> 33 <aop:before method="logController" pointcut="within(cn.xiangxu.cloudNote.controller..*)"></aop:before> 34 </aop:aspect> 35 </aop:config> 36 37 </beans>
3 通过注解实现AOP编程
3.1 导包
3.2 创建切面类(在类中添加相关注解)
1 package cn.xiangxu.cloudNote.aspect; 2 3 import org.aspectj.lang.annotation.AfterReturning; 4 import org.aspectj.lang.annotation.Aspect; 5 import org.aspectj.lang.annotation.Before; 6 import org.springframework.stereotype.Component; 7 8 @Component 9 @Aspect 10 public class AspectDemo02 { 11 // @Before(value = "within(cn.xiangxu.cloudNote.service..*)") // service包及其子包中的所有方法 12 // @Before(value = "within(cn.xiangxu.cloudNote.service.*)") // service中的所有方法(子包中的除外) 13 @Before(value = "within(cn.xiangxu.cloudNote.service.NoteServiceImpl)") // NoteServiceImpl类中的所有方法 14 15 public void serviceAspect() { 16 System.out.println("给笔记相关的业务实现类添加切面"); 17 } 18 19 // @Before("execution(* cn.xiangxu.cloudNote.service..*.*(..))") // service包及其子包下的所有方法(方法的返回值和参数没有限制) 20 @Before("execution(* cn.xiangxu.cloudNote.service.ShareSerive.shareNote(String))") // 指定方法名以及参数 21 public void shareAspect() { 22 System.out.println("给分享笔记方法添加切面"); 23 } 24 25 @Before("bean(*Dao)") 26 public void daoAspect() { 27 System.out.println("给所有持久层方法添加切面"); 28 } 29 30 }
@Component 代替在配置文件中配置bean
@Aspect 说明该类中的代码是一个切面的组件;等价于在配置文件中配置指定切面组件 <aop:aspect ref="loggerBean">
@Before(value = "within(cn.xiangxu.cloudnote.service..*)") 代替在配置文件中配置通知和切入点配置
3.3 在配置文件中配置注解扫描和启动AOP注解
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:jdbc="http://www.springframework.org/schema/jdbc" 6 xmlns:jee="http://www.springframework.org/schema/jee" 7 xmlns:tx="http://www.springframework.org/schema/tx" 8 xmlns:aop="http://www.springframework.org/schema/aop" 9 xmlns:mvc="http://www.springframework.org/schema/mvc" 10 xmlns:util="http://www.springframework.org/schema/util" 11 xmlns:jpa="http://www.springframework.org/schema/data/jpa" 12 xsi:schemaLocation=" 13 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 14 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd 15 http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd 16 http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd 17 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd 18 http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd 19 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 20 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd 21 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd"> 22 23 <!-- 扫描组件 --> 24 <context:component-scan base-package="cn.xiangxu.cloudNote.aspect"></context:component-scan> 25 <!-- 启动aop注解 --> 26 <aop:aspectj-autoproxy></aop:aspectj-autoproxy> 27 28 </beans>
4 AOP编程三要素
切面:追加啥? 就是追加我们单独封装的切面代码
通知:啥时候切入? (前置/后置/异常/最终/环绕)
切入点:切谁? (三种表达式:方法/类型/Bean限定表达式)
4.1 切面(Aspect)
指的是封装了共同处理的组件,即切面代码
4.2 通知(Before/AfterReturning/AfterThrowing/After/Around)
就是指定且面儿在哪里执行
1 try{ 2 前置通知(@Before) 3 //执行组件方法 4 后置通知(@AfterReturning) 5 }catch{ 6 异常通知(@AfterThrowing) 7 }finally{ 8 最终通知(@After) 9 } 10 环绕通知(@Around)
4.3 切入点
4.3.1 切入点 Pointcut
用于指定目标方法 ,利用配置文件配置的写法为:pointcut="within(cn.xiangxu.cloudnote.controller..*)
如果利用注解的方式进行AOP编程,那么就会将切入点用表达式来代替并且写入通知中
4.3.2 表达式
4.3.2.1 方法限定表达式 execution
为某个组件的部分方法追加功能
execution(【修饰符】 返回类型 方法名(参数列表)) 【抛出异常】
execution(* add*(..))
匹配的是所有以add开头的方法,方法的返回值和参数可以是任意
execution(* cn.xiangxu.cloudnote.service.UserService.*(..))
匹配的是在UserService组件下的所有方法,方法的返回值和参数可以是任意
execution(* cn.xiangxu.cloudnote.service.*.*(..))
匹配的是service下所有组件的所有方法
execution(* cn.xiangxu.cloudnote.service..*.*(..))
匹配的是service包以及子包下所有组件的所有方法s
4.3.2.2 类限定表达式 within
within(cn.xiangxu.cloudnote.service.UserService)
匹配UserService组件下的所有方法
within(cn.xiangxu.cloudnote.service.*)
匹配service包下所有类的所有方法
within(cn.xiangxu.cloudnote.service..*)
匹配到service包及子包下的所有类的所有方法
4.3.2.3 bean限定表达式 bean(id名)
bean(userService)
匹配userService组件的所有方法
bean(*Service)
匹配以Service结尾的组件的所有方法
例子
@Before("bean(userController)")
5 AOP实现原理
动态代理
AOP底层实现是通过动态代理技术实现的
动态代理技术:动态创建类的技术
两种动态代理技术:
基于接口的(java.reflect.Proxy):实现接口,重写了接口中的方法
public class $Proxy23 implements UserService {
事务处理 + login()处理
将事务处理和login()处理整合到login()方法中
}
通过动态代理创建了一个新的类型
基于类的(CGLIB包):继承类,重写类中的方法
6 AOP经典应用
事务管理
方法审计
异常日志处理
6.1 案例:实现性能审计
切面:输出消耗时间
切入点:service下的所有方法
通知:环绕(@Around)
1 package cn.xiangxu.cloudNote.aspect; 2 3 import org.aspectj.lang.ProceedingJoinPoint; 4 import org.aspectj.lang.annotation.Around; 5 import org.aspectj.lang.annotation.Aspect; 6 import org.springframework.stereotype.Component; 7 8 @Component 9 @Aspect 10 public class AuditBean { 11 @Around("within(cn.xiangxu.cloudNote.service..*)") // 所有业务层添加切面 12 public Object log(ProceedingJoinPoint point) throws Throwable { 13 Object obj = new Object(); 14 try { 15 Long startTime = System.currentTimeMillis(); 16 obj = point.proceed(); // point.proceed()代表某个方法的执行 17 Long endTime = System.currentTimeMillis(); 18 String str = point.getSignature().toString(); // 获取执行方法签名 19 System.out.println(str + "耗时:" + (endTime - startTime)); 20 } catch (Throwable e) { 21 // TODO Auto-generated catch block 22 e.printStackTrace(); 23 throw e; 24 } 25 26 return obj; 27 } 28 }
6.2 案例:异常信息写入日志
要求:当service处理发生异常的时候,将异常信息写入文件中(利用AOP实现)
切面:将异常信息写入文件(FileWriter--PrintWriter)
切入点:service下的所有方法
通知:异常(@AfterThrowing)
1 package cn.xiangxu.cloudNote.aspect; 2 3 import java.io.FileWriter; 4 import java.io.PrintWriter; 5 import java.text.SimpleDateFormat; 6 import java.util.Date; 7 8 import org.aspectj.lang.annotation.AfterThrowing; 9 import org.aspectj.lang.annotation.Aspect; 10 import org.springframework.stereotype.Component; 11 12 @Component 13 @Aspect 14 public class ExceptionBean { 15 // e : 表示目标组件方法抛出的异常对象 16 @AfterThrowing(throwing="e", pointcut="within(cn.xiangxu.cloudNote..*)") 17 public void execute(Exception e) { 18 try { 19 System.out.println("hello world"); 20 // 创建日志文件 ("D:\\note_error.log") /home/soft01/note_error.log 21 FileWriter fw = new FileWriter("D:/note_error.log", true); 22 // 利用PrintWriter对象写入信息 23 PrintWriter pw = new PrintWriter(fw); 24 // 定义时间字符串 25 Date date = new Date(); 26 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 27 String time_str = sdf.format(date); 28 // 写入异常信息 29 pw.println("**********************"); 30 pw.println("==异常类型:" + e); 31 pw.println("==发生时间:" + time_str); 32 pw.println("==异常详细信息=="); 33 e.printStackTrace(pw); 34 pw.println("**********************"); 35 pw.close(); 36 fw.close(); 37 38 } catch (Exception e2) { 39 // TODO: handle exception 40 System.out.println("记录异常失败"); 41 } 42 } 43 }
6.3 事务管理
6.3.1 事务
程序为了保证业务处理的完整性,执行的一条或者多条SQL语句
6.3.2 事务管理
对事务中的SQL语句进行提交或者回滚
6.3.3 为什么要使用事务管理
确保数据库的完整性,不出现脏数据
6.3.4 事务回顾
oracle:commit/rollback (DML操作)
jdbc:默认是自动commit
6.3.5 怎么使用事务管理
》编程式事务管理
利用jdbc连接数据库就是使用的编程式事务管理
1 try{ 2 业务SQL01 3 业务SQL02 4 业务SQL03 5 conn.commit(); 6 }catch(Exception e) { 7 conn.rollback(); 8 }
》声明式事务管理
》》在配置文件中配置spring事务管理
》》》定义事务管理的bean
org.springframework.jdbc.datasource.DataSourceTransactionManager
》》》开启注解
@Transactional
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:jdbc="http://www.springframework.org/schema/jdbc" 6 xmlns:jee="http://www.springframework.org/schema/jee" 7 xmlns:tx="http://www.springframework.org/schema/tx" 8 xmlns:aop="http://www.springframework.org/schema/aop" 9 xmlns:mvc="http://www.springframework.org/schema/mvc" 10 xmlns:util="http://www.springframework.org/schema/util" 11 xmlns:jpa="http://www.springframework.org/schema/data/jpa" 12 xsi:schemaLocation=" 13 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 14 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd 15 http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd 16 http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd 17 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd 18 http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd 19 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 20 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd 21 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd"> 22 23 <!-- Spring 事务处理 --> 24 <!-- 定义事务管理的Bean --> 25 <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 26 <property name="dataSource" ref="dbcp"></property> 27 </bean> 28 <!-- 开启@Transactional --> 29 <tx:annotation-driven transaction-manager="txManager"></tx:annotation-driven> 30 31 </beans>
》》使用注解标记@Transactional (加到类上就表示对所有的方法都进行事务管理,加到方法上就表示只对该方法进行事务管理)
6.3.6 @Transactional新特性
1 @Transactional标记的特性 2 可读可写:readOnly 3 作用于select语句的事务上 4 语法 5 @Transactional(readOnly=true) 6 回滚特性:rollBackFor 7 用于指定回滚的异常类型,因为默认只对运行异常进行处理 8 语法 9 @Transactional(rollBackFor=异常类型) 10 传播特性 11 @Transactional 12 public void fn1() { 13 业务1处理 14 fn2() // 如果fn2()出现错误,就会对业务1进行回滚 15 业务2处理 16 } 17 18 @Transactional 19 public void fn2() { 20 业务3处理 21 } 22 23 隔离特性 24 为解决并发访问数据库问题而设计的 25 脏读:事务一进行了增删改操作,但并未提交;此时事务二读取了事务操作的数据;此时, 26 事务一进行了回滚,那么我们就说事务二进行了一次脏读操作 27 幻读:事务一在一定范围内查询数据,同时事务二在该范围内又增加了数据,这种现象我们 28 就说事务一做了一次幻读