Spring AOP
##AOP概念
aspect oriented programming
面向切面编程
是什么?
将共同的业务处理从传统业务处理中抽离出来,单独封装,
然后以配置的形式进行关联
为什么要使用AOP?
可以在不修改原有逻辑代码的情况下,给系统追加功能.
##AOP的典型应用
-追加事务控制
-异常日志记录
案例:
-要求:在每个Controller方法执行前输出打桩信息
-封装一个组件
-通过配置将封装的组件追加到Controller方法上
#AOP相关概念
-OOP:类,对象,封装,继承,多态
-AOP:切面,切入点,通知,动态代理
##切面(aspect)
指的是封装了共同处理的组件,并且能够切入到其它组件的方法上.
##切入点(pointcut)
用于指定目标组件的方法.
-方法限定表达式
可以给某个组件中部分方法追加共同功能
execution(修饰符? 返回类型 方法名(参数) 异常抛出?)
//匹配到add开头的所有方法
execution(* add*(..))
//匹配UserService下的所有方法
execution(* cn.tedu.UserService.*(..))
//匹配到service包下所有类的所有方法
execution(* cn.tedu.service.*.*(..))
//匹配到service包及子包下的所有方法
execution(* cn.tedu.service..*(..))
-类型限定表达式
within(类型)
//匹配UserService组件下所有的方法
within(cn.tedu.service.UserService)
//匹配service包下所有类的所有方法
within(cn.tedu.service.*)
//匹配service包及子包下的所有方法
within(cn.tedu.service..*)
-bean名称限定表达式
可以给某个组件中所有的方法追加功能
bean(id名)
//匹配id为userService的组件的所有方法
bean(userService)
//匹配以Service结尾的所有组件的所有方法
bean(*Service)
##通知
用于指定切入的时机
spring提供了五种通知类型
try{
前置通知<aop:before>
//执行的目标方法
后置通知<aop:after-returning>
}catch{
异常通知<aop:after-throwing>
}finally{
最终通知<aop:after>
}
@around=前置+后置通知
可以用在给Service设置性能审计audit
切面:追加啥功能?单独封装的代码
切入点:切谁?所有的Controller
通知:啥时候切?前置/后置/环绕
##动态代理
AOP原理:使用动态代理技术
可以创建 一个新的类型,重写目标接口或目标类的方法
在重写方法中,追加了要切入的功能代码和方法代码
spring有两种动态技术:
-基于目标接口的
public class $Proxy25 implements 目标接口{
public void checkLogin(){
//追加了事务处理
//重写了目标接口的方法
}
}
-基于目标类的
public class $Proxy25 extends 目标类{
}
##maven引入 aspectjweaver.jar
#使用配置xml文件方式
LoggerBean类
package cn.tedu.cloud_note.aspect; public class LoggerBean { public void logController() { System.out.println("AOP功能注入!"); } }
spring-aop.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:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:util="http://www.springframework.org/schema/util" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd 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"> <bean id="loggerBean" class="cn.tedu.cloud_note.aspect.LoggerBean"></bean> <aop:config> <!-- 通过REF关联组件类 --> <aop:aspect ref="loggerBean"> <!-- 通过method指定处理方法 <aop:before method="logController" pointcut="within(cn.tedu.cloud_note.controller..*)"/> --> <!-- 方法限定类型 <aop:before method="logController" pointcut="execution(* cn.tedu.cloud_note.service.*.*(..))"/> --> <!-- bean名称限定类型 --> <aop:before method="logController" pointcut="bean(userLoginController)"/> </aop:aspect> </aop:config> </beans>
#AOP注解配置
##注解标记
@Component 起到定义<bean>的作用
@Aspect <aop:aspect ref="loggerBean">
@Before <aop:before pointcut= within()>
spring-aop.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:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:util="http://www.springframework.org/schema/util" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd 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"> <!-- 配置AOP的注解扫描 --> <context:component-scan base-package="cn.tedu.cloud_note.aspect"/> <!-- 开启注解标记 --> <aop:aspectj-autoproxy/> </beans>
LoggerBean.java
package cn.tedu.cloud_note.aspect; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Component @Aspect public class LoggerBean { @Before("within(cn.tedu.cloud_note.controller..*)") public void logController() { System.out.println("AOP功能注入!"); } }
#性能审计的小案例
AuditBean.java
package cn.tedu.cloud_note.aspect; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; @Component @Aspect public class AuditBean { @Around("within(cn.tedu.cloud_note.service..*)") public Object audit(ProceedingJoinPoint point) { Object obj = null; try { long timeStart = System.currentTimeMillis(); //模拟调用service obj = point.proceed(); long timeEnd = System.currentTimeMillis(); //调用的Service名称 String str = point.getSignature().toString(); System.out.println("耗时:"+(timeEnd-timeStart)); } catch (Throwable e) { e.printStackTrace(); } return point; } }
#切入记录异常的功能
案例:生成异常日志
-要求:当系统发生service异常,将异常信息写入日志文件
-切面:将异常信息写入文件
-切入点:after-throwing("within(service..*)")
ExceptionBean.java
package cn.tedu.cloud_note.aspect; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; @Component @Aspect public class ExceptionBean { //e是目标组件抛出的异常对象 @AfterThrowing(throwing="e",pointcut="within(cn.tedu.cloud_note.service..*)") public void execute(Exception e) { //将异常信息输入文件 try { //true:以追加的方式写出信息 FileWriter fw = new FileWriter("F:\\note_error.log",true); PrintWriter pw = new PrintWriter(fw); //利用pw对象写入异常信息 Date time = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String timeStr = sdf.format(time); pw.println("**************************************"); pw.println("*异常类型:"+e); pw.println("*异常时间:"+timeStr); pw.println("***********异常详细信息**************"); e.printStackTrace(pw); pw.close(); fw.close(); } catch (Exception ex) { System.out.println("记录异常失败"); } } }