Spring AOP
AOP:面向切面编程(aspect oriented programming)
将系统中共同的业务处理从传统的业务处理中抽离出来,
单独封装,然后以配置的形式进行关联
可以在不修改原有逻辑代码的情况下,给系统追加功能
AOP的典型应用
追加事务控制
异常日志记录
案例:
要求:在每个controller方法执行前输出一个打桩信息
封装一个组件
通过配置将封装的组件追加到controller方法上
切面:实现追加功能的一段代码
配置文件:
<bean id="loggerBean" class="cn.tedu.cloudnote.aspect.LoggerBean"></bean>
<aop:config>
<!-- 通过ref关联组件类 -->
<aop:aspect ref="loggerBean">
<!-- 通过method指定处理方法 -->
<!-- pointcut:切入点,作用到controller -->
<aop:before method="logController" pointcut="within(cn.tedu.cloudnote.controller..*)"/>
</aop:aspect>
</aop:config>
导入aspectjweaver.jar
AOP相关概念
OOP:类,对象,封装,继承,多态
AOP:
切面:aspect
封装了共同处理的组件,并且能够切入到其他组件的方法上
切入点:pointcut
用于指定目标组件的方法
切入点的有关表达式
方法限定表达式
可以给某个组件中部分方法追加共同功能
execution(修饰符 返回类型 方法名称(参数)异常抛出)
execution(* add*(..))//匹配到add开头的所有方法
execution (* cn.tedu.Userservice.*(..))//匹配Userservice包的所有方法
execution (* cn.tedu.service..*(..))//匹配service包及其子包的所有方法
<aop:before method="logController"
pointcut="execution(*cn.tedu.cloudnote.controller..*(..))">
类型限定表达式(常用)
可以给某个组件的所有方法追加功能
Within(类型)
//匹配UserService组件下所有的方法,不指定具体的方法
Within(cn.tedu.cloudnote.service.UserService)
//匹配某个包下所有类的所有方法
Within(cu.tedu.cloudnote.service.*)
//匹配某个包及其子包下所有类的所有方法
Within(cu.tedu.cloudnote.service..*)
Bean名称限定表达式
可以给某个组件中所有的方法追加功能
bean(id名)
//匹配id为UserService的组件中的所有方法
bean(userService)
//匹配以Service结尾的所有组件的所有方法
bean(*Service)
实例:
<bean id="loggerBean" class="cn.tedu.cloudnote.aspect.LoggerBean"></bean>
<aop:config>
<!-- 通过ref关联组件类 -->
<aop:aspect ref="loggerBean">
<!-- 通过method指定处理方法 -->
<!-- pointcut:切入点,作用到controller -->
<!-- 类型限定表达式(常用) -->
<!-- <aop:before method="logController" pointcut="within(cn.tedu.cloudnote.controller..*)"/> -->
<!-- 方法限定表达式 -->
<!-- <aop:before method="logController" pointcut="execution(* cn.tedu.cloudnote.controller..*(..))"/> -->
<!-- Bean名称限定表达式 -->
<aop:before method="logController" pointcut="bean(*Service)"/>
</aop:aspect>
</aop:config>
通知:
用于指定切入的时机
Spring提供了5种通知类型
try{
前置通知:<aop:before>
//执行目标方法
后置通知:<aop:after-returning>
}catch{
异常通知:<aop:after-throwing>
}finall{
最终通知:<aop:after>
}
环绕通知:@around=前置通知+后置通知
切面:住家啥功能?单独封装的代码
切入点:切谁?所有Controller
通知:什么时候切入?前置/后置/环绕
<!-- spring 的事务管理 -->
<!-- 定义事务管理的bean -->
<bean id="txManger" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dbcp"></property>
</bean>
<!--开启transaction标记,将标记作用在具体方法上 -->
<tx:annotation-driven transaction-manager="txManger"/>
动态代理
AOP原理:
使用动态代理技术
可以创建一个新的类型,重写目标接口或目标类的方法
在重写方法中,追加了要切入的功能代码和方法代码
Spring有两种动态技术
基于目标接口
基于目标类
Public class $Proxy22 implements UserService{
Public void checkLogin(){
//追加事务处理
//重写了UserServiceImpl.checkLogin
}
}
Public class $Proxy22 extends 目标类{}
AOP注解配置
注解的标记
@Component<---- ---> 起到应以<bean>的作用
@Aspect <---- ---> <aop:aspect ref=”loggerBean” >
@Before <---- ---> <aop:before Pointcut=within()>
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.cloudnote.controller..*)")
public void logController(){
System.out.println("AOP功能注入");
}
@Before("within(cn.tedu.cloudnote.service..*)")
public void service(){
System.out.println("service");
}
}
实际应用:cloudnote性能审计
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.cloudnote.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(str+"耗时:"+(timeEnd-timeStart));
} catch (Throwable e) {
e.printStackTrace();
}
return obj;
}
}
Obj:打印如下
User:[id=e4f340d64b574aac82aab6547d822baa,name=zj,
password=4QrcOUm6Wau+VuBX8g+IPg==, token=null, nick=ZJ]
案例:
要求:当系统发生Service异常,将异常信息写入日志
切面:将异常信息写入文件
切入点:after-throwing(“within(service..*)”)
import java.io.FileWriter;
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;
//扫描到spring容器
@Component
//将该类作为切面的组件
@Aspect
public class ExceptionBean {
//e:目标组件抛出的异常对象
@AfterThrowing(throwing="e",pointcut="within(cn.tedu.cloudnote.service..*)")
public void execute(Exception e){
//将异常信息输入文件
try {
FileWriter fw=new FileWriter("D:\\note_error.log",true);//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);
fw.close();
pw.close();
} catch (Exception ex) {
System.out.println("记录异常失败");
}
}
}