一、AOP的几个概念
1、Aspect,表示切面功能
配置如下:
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<!-- 启用AOP配置 -->
<aop:aspectj-autoproxy />
</beans>
定义Aspect:在XML中定一个切面功能的bean,同时在对应的bean上添加@Aspect注解
<bean id="loggingAspect" class="com.xiaol.study.LoggingAspect">
</bean>
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class LoggingAspect{
}
2、Pointcut,用于定义哪些函数要执行切面功能
@Pointcut("execution(* com.xiaol.study.Caculator.*(..))")
private void arithmetic()
execution(* com.xiaol.study.Caculator.*(..))表达式用于匹配执行切面功能的方法。
arithmetic相当于是这个表达式的一个名称,可以在其他地方被引用。
Pointcut表达式解析:
designator(modifiers? return-type declaring-type? name(param) throws?)
designator:
execution表示函数执行过程,
within表示某个包或者类下执行的函数
modifiers:访问修饰符,public、private等
return-type:返回类型,可以使用通配符*
declaring-type:包名,类名
name:函数名
param:参数列表,()表示无参,(..)表示任意参数
throws:异常类型
其中末尾带有?的都是可选项,不写也可以。
3、Advice,表示执行函数的某个点,也就是被Pointcut匹配到的方法执行到哪个点时去执行切面功能
定义Advice:可以直接写Pointcut的名称,也可以写Pointcut表达式。
// Pointcut名称
@Before("com.xiaol.study.LoggingAspect.arithmetic()")
public void doLog(){
}
// Pointcut表达式
@Before("execution(* com.xiaol.study.Caculator.*(..))")
public void doLog(){
}
Advice类型:
@Before,在函数执行前
@AfterReturning,函数正常返回之后
@AfterThrowing,函数抛出异常之后
@After,函数返回之后(无论是否有异常)
@Around,函数执行前后
Advice参数:除了Around使用ProceedingJoinPoint,其他都使用JoinPoint从中获得上下文信息。
@Before("com.xiaol.study.LoggingAspect.arithmetic()")
public void doLog(JoinPoint jp){
System.out.println(jp.getSignature() + "," + jp.getArgs());
}
@Around("com.xiaol.study.LoggingAspect.arithmetic()")
public Object doLog(ProceedingJoinPoint pjp){
System.out.println("start : " + pjp.toString());
Object retVal = pjp.proceed();
System.out.println("end : " + pjp.toString());
return retVal;
}
Advice获取返回值:
@AfterReturning(
pointcut="com.xiaol.study.LoggingAspect.arithmetic()",
returning="retVal")
public void doLog(Object retVal){
// do something
}
Advice获取异常类型:可以根据需要获取对应异常类型
@AfterReturning(
pointcut="com.xiaol.study.LoggingAspect.arithmetic()",
throwing="ex")
public void doLog(Execption ex){
// do something
}
Advice获取函数参数:(a, ..)表示第一个参数变量名叫a,之后..表示任意个参数
@Before("com.xiaol.study.LoggingAspect.arithmetic() && args(a, ..)")
public void doLog(JoinPoint jp, int a){
// do something
}
二、AOP例子 - 基于注解
1、编写需要添加(切面功能)日志功能的类TeacherService
package com.xiaol.aop;
public class TeacherService {
public boolean saveTeacher(Teacher teacher) throws Exception {
if (teacher.getTeacherID() < 0) {
throw new Exception("教师编号异常");
}
return true;
}
public boolean deleteTeacher(int teacherID) {
return true;
}
public boolean findTeacher(Teacher teacher) {
return true;
}
public boolean modifyTeacher(Teacher teacher) {
return true;
}
}
2、实现(切面功能)日志功能类MyLoggingAspect
package com.xiaol.aop;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class MyLoggingAspect {
/**
* 函数正常执行后输出日志
*/
@AfterReturning("execution(public boolean com.xiaol.aop.*.*(..))")
private void normalLog(JoinPoint jp) {
System.out.println("正常返回,函数签名[" + jp.getSignature() + "],参数值" + Arrays.toString(jp.getArgs()));
}
/**
* 函数执行异常时输出日志
*/
@AfterThrowing("execution(public boolean com.xiaol.aop.*.*(..))")
private void exceptionLog(JoinPoint jp) {
System.err.println("抛出异常,函数签名[" + jp.getSignature() + "],参数值" + Arrays.toString(jp.getArgs()));
}
/**
* 执行前后输出日志
*
* @throws Throwable
*/
@Around("execution(public boolean com.xiaol.aop.*.*(..))")
private Object beforeExecuteLog(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("函数执行前,函数签名[" + pjp.toString() + "],参数值" + Arrays.toString(pjp.getArgs()));
Object retVal = pjp.proceed(pjp.getArgs());
System.out.println("函数正常调用返回后,函数签名[" + pjp.toString() + "],返回值[" + retVal + "]");
return retVal;
}
}
3、定义bean
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<!-- 启用AOP配置 -->
<aop:aspectj-autoproxy />
<bean id="myLoggingAspect" class="com.xiaol.aop.MyLoggingAspect"></bean>
<bean id="teacherService" class="com.xiaol.aop.TeacherService"></bean>
</beans>
4、编写测试类
package com.xiaol.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestAop {
public static void main(String[] args) throws Exception {
ApplicationContext context = new ClassPathXmlApplicationContext("application-context-unittest.xml");
TeacherService teacherService = context.getBean("teacherService", TeacherService.class);
Teacher teacher = new Teacher();
teacher.setCourse("化学");
teacher.setName("刘强");
teacher.setTeacherID(-1);
teacherService.deleteTeacher(3);
// teacherService.modifyTeacher(teacher);
// teacherService.findTeacher(teacher);
// teacherService.saveTeacher(teacher);
((ConfigurableApplicationContext) context).close();
}
}
测试deleteTeacher方法,结果:
函数执行前,函数签名[execution(boolean com.xiaol.aop.TeacherService.deleteTeacher(int))],参数值[3]
函数正常调用返回后,函数签名[execution(boolean com.xiaol.aop.TeacherService.deleteTeacher(int))],返回值[true]
正常返回,函数签名[boolean com.xiaol.aop.TeacherService.deleteTeacher(int)],参数值[3]
测试saveTeacher方法,结果:
函数执行前,函数签名[execution(boolean com.xiaol.aop.TeacherService.saveTeacher(Teacher))],参数值[Teacher [teacherID=-1, name=刘强, course=化学]]
抛出异常,函数签名[boolean com.xiaol.aop.TeacherService.saveTeacher(Teacher)],参数值[Teacher [teacherID=-1, name=刘强, course=化学]]
Exception in thread "main" java.lang.Exception: 教师编号异常
三、AOP例子 - 基于XML配置
1、编写需要添加(切面功能)日志功能的类TeacherService,参见基于注解的编写
2、实现(切面功能)日志功能类MyLoggingAspect
package com.xiaol.aop;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyLoggingAspect {
/**
* 函数正常执行后输出日志
*/
private void normalLog(JoinPoint jp) {
System.out.println("正常返回,函数签名[" + jp.getSignature() + "],参数值" + Arrays.toString(jp.getArgs()));
}
/**
* 函数执行异常时输出日志
*/
private void exceptionLog(JoinPoint jp) {
System.err.println("抛出异常,函数签名[" + jp.getSignature() + "],参数值" + Arrays.toString(jp.getArgs()));
}
/**
* 执行前后输出日志
*
* @throws Throwable
*/
private Object beforeExecuteLog(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("函数执行前,函数签名[" + pjp.toString() + "],参数值" + Arrays.toString(pjp.getArgs()));
Object retVal = pjp.proceed(pjp.getArgs());
System.out.println("函数正常调用返回后,函数签名[" + pjp.toString() + "],返回值[" + retVal + "]");
return retVal;
}
}
3、配置文件
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<!-- 当使用AOP的XML配置时aspectj-autoproxy不需要 -->
<!-- <aop:aspectj-autoproxy /> -->
<bean id="myLoggingAspect" class="com.xiaol.aop.MyLoggingAspect"></bean>
<bean id="teacherService" class="com.xiaol.aop.TeacherService"></bean>
<!-- 这是一个简单的配置,其他形式的配置可以参考Spring的文档 -->
<aop:config>
<aop:aspect id="loggingAspect" ref="myLoggingAspect">
<aop:pointcut id="normal"
expression="execution(public boolean com.xiaol.aop.*.*(..))" />
<aop:before pointcut-ref="normal" method="normalLog" />
</aop:aspect>
</aop:config>
</beans>
4、测试类同注解