一、AspectJ概念
demo演示
pointcut action():call(* aop.Demo.*(..));
切点的语法:关键字为pointcut,切点名字以及匹配表达式,目标方法调用形式:call()或者execution()
5种通知:
/**
* 执行前通知
*/
before():action(){
System.out.println("beforeAction");
}
/**
* 执行后通知
*/
after():action(){
System.out.println("afterAction");
}
/**
* 目标方法return之后通知,带回返回值
*/
after() returning(String s):adviceReturn(){
System.out.println("返回值为:"+s);
}
/**
* 目标方法抛出异常后通知,带回异常类型
*/
after() throwing(Exception e):error(){
System.out.println("抛出异常:"+e.toString());
}
/**
* 环绕通知,可通过proceed()控制目标方法在何时执行
*/
Object around():aroundAction(){
System.out.println("Around 目标方法前执行");
Object result=proceed();//执行目标方法
System.out.println("Around 目标方法后执行");
return result;
}
二、Spring AOP
demo演示
容器管理统一都交给Spring,包括切面类,配置跟平常的Spring项目一样
需要额外加入的配置项:<aop:aspectj-autoproxy/>
切点定义:
@Pointcut("execution(* aop.*.*(..))")
private void allPackage(){}
@Pointcut("within(aop.child..*)")
private void allPackage(){}
execution:基于方法匹配
语法结构:execution([修饰符] 返回值类型 方法名(参数) ) []为可选项
within:基于类名匹配
语法结构:within(包、类名)
匹配表达式规则:
*:匹配任何数量字符;
..:在方法参数中匹配任何数量参数,在包路径描述时匹配所有方法,包括子路径的
+:匹配指定类型的子类型;仅能作为后缀放在类型模式后边。
bean:匹配特定名称的Bean对象的执行方法
//匹配名称中以Service结尾的Bean。
@Pointcut("bean(*Service)")
private void pointcut(){}
@within:匹配所有带指定注解的类的方法
//匹配使用了注解的类
@Pointcut("@within(com.annotation.DemoAnnotation)")
private void pointcut(){}
@annotation(com.annotation.DemoAnnotation) : 匹配所有带有该注解的方法
//匹配使用了注解的方法
@Pointcut("@annotation(com.annotation.DemoAnnotation)")
private void pointcut(){}
通知:
@Before("execution(* aop..*(..))")
public void beforeAction(JoinPoint joinPoint){
System.out.println("AOPbeforeAction");
}
@After("allPackage()")
public void afterAction_p(JoinPoint joinPoint){
System.out.println("afterAction_p");
}
@AfterReturning(value = "execution(* aop.User.returnVal())",returning = "value")
public void afterReturn(JoinPoint joinPoint,String value){
System.out.println("afterReturn--"+value);
}
@AfterThrowing(value = "execution(* aop.User.*(..))",throwing = "e")
public void afterThrowing(JoinPoint joinPoint,Throwable e){
System.out.println("AfterThrowing--"+e);
}
/**
* 环绕通知既可以在目标方法前执行也可在目标方法之后执行,并可以控制目标方法执行,
* 第一个参数必须是ProceedingJoinPoint,通过该对象的proceed()方法来执行目标方法,
* 并获取到返回值,同样的,ProceedingJoinPoint对象也是可以获取目标对象的信息,
* 如类名称,方法参数,方法名称等等。
*/
@Around("!execution(* aop.User.throwError(..))")
public Object around(ProceedingJoinPoint joinPoint){
System.out.println("Around前");
try {
Object result = joinPoint.proceed();
System.out.println("Around后");
return result;
} catch (Throwable throwable) {
throwable.printStackTrace();
return null;
}
}
切面:
@Aspect
@Component
public class Aop {
@Pointcut("within(aop.child..*)")
private void allPackage(){}
@Before("execution(* aop..*(..))")
public void beforeAction(JoinPoint joinPoint){
System.out.println("AOPbeforeAction");
}
@After("allPackage()")
public void afterAction_p(JoinPoint joinPoint){
System.out.println("afterAction_p");
}
}
可以通过先定义切点再在通知中引入,或者直接在通知中定义
JoinPoint | 方法 | 返回值 | 描述 | 例如 |
---|---|---|---|---|
getArgs() | Object[] | 获取连接点的入参 | [123,"abc"] | |
getSignature() | Signature | 获取连接点方法的签名 |
Signature | 方法 | 返回值 | 描述 | 例如 |
---|---|---|---|---|
getDeclaringType() | Class | 获取声明该连接点方法的类的类型 | com.Test | |
getDeclaringTypeName() | String | 获取声明该连接点方法的类的名字 | “com.Test” | |
getName() | Signature | 获取方法名 | createZan |
MethodSignature类api
方法 | 返回值 | 描述 | 例如 |
---|---|---|---|
getReturnType() | Class | 获取方法的返回值类型 | java.lang.Long |
getParameterTypes() | Class[] | 获取方法入参的类型 | [java.lang.Long,java.lang.String] |
getParameterNames() | String[] | 获取方法入参的名字 | [id,name] |