- 前置通知
- 环绕通知
- 后置通知
- 最终通知
- 异常通知
API类似,只是生效的时机不一样,并且,不能保证,各个通知的顺序
try {
//前置通知
before();
//环绕通知,内部是执行的方法
around(proxy,method,args,methodProxy);
//后置通知
afterReturn();
} catch (Throwable e) {
exception();//异常通知
}finally {
after();//最终通知
}
前置通知
被访问的方法,执行之前执行的通知
先执行通知的方法,然后再执行被访问的方法本身
public void f2(JoinPoint joinPoint){
System.out.println("----------前置通知");
//被访问方法的参数
Object[] args = joinPoint.getArgs();
//被访问方法所在类的,bean对象
Object target = joinPoint.getTarget();
//被访问的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
System.out.println(method.getName()+"被调用了,传入了参数:"+ Arrays.toString(args)+"---所在类:"+target);
}
环绕通知
public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("==========环绕通知开始=========");
//调用方法,必须写的,如果不写,目标方法不会被调用
//Object o 是被执行方法的返回值
Object o = joinPoint.proceed();
//
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Object[] args = joinPoint.getArgs();
//method.invoke(12);
System.out.println("==========环绕通知结束========="+method.getName());
return o;
}
-
配置通知
<aop:aspect ref="test1">
<aop:before method="f2" pointcut-ref="q3"/>
<aop:around method="aroundMethod" pointcut-ref="q3"/>
<aop:after-throwing method="f3" pointcut-ref="q3"/>
<aop:after method="f4" pointcut-ref="q3"/>
<aop:after-returning method="f5" pointcut-ref="q3"/>
</aop:aspect>
SpringAOP原理
动态代理
注解AOP
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="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
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd"
>
<context:component-scan base-package="com.javasm"/>
<!--开启AOP注解-->
<aop:aspectj-autoproxy/>
</beans>
//<aop:aspect ref='aspect2'>
@Component("aspect2")
@Aspect
public class TestAspect2 {
//切点,标记
@Pointcut("execution(* com.javasm.*.service.*.*.query*(..))")
void f1(){}
@Before("f1()")
void before(JoinPoint joinPoint){
System.out.println("注解前置通知");
}
@Around("f1()")
Object aroundTest(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("-----环绕通知开始");
Object proceed = joinPoint.proceed();
System.out.println("------环绕通知结束");
return proceed;
}
@After("f1()")
void after(JoinPoint joinPoint){
System.out.println("========最终通知");
}
//@AfterReturning("f1()")
@AfterReturning(value = "f1()",returning = "msg")
void afterReturn(JoinPoint joinPoint,Object msg){
System.out.println("##########后置通知########返回值:"+msg);
}
//@AfterThrowing("f1()")
@AfterThrowing(value = "f1()",throwing = "e")
void afterThrowing(JoinPoint joinPoint,Throwable e){
System.out.println("=========异常通知:"+e);
}
}
自定义注解
自定义注解搭配AOP注解,灵活的控制方法
案例:
想在项目中记录日志
但是不是每个模块每个方法都记录
哪个方法需要记录,现在还不确定,但是要提前把业务逻辑写好
利用自定义注解,哪个方法需要增加日志,就在哪个方法上面加上自定义注解
-
自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SaveLog {
//String value();
} -
切面类
@Component
@Aspect
public class SaveLogAspect {@Resource LogService logService; //方法执行结束之后,保存日志 @AfterReturning("@annotation(com.javasm.aspect.SaveLog)") public void saveLog(JoinPoint joinPoint){ String msg = "%s方法,在%s时间,被调用了,参数是%s"; //方法 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); String methodName = method.getName(); //时间 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String time = simpleDateFormat.format(new Date()); //参数 String parameters = Arrays.toString(joinPoint.getArgs()); msg = String.format(msg,methodName,time,parameters); logService.save(msg); }
}
@SaveLog
- 控制到类
案例:
统计方法运行消耗的时间
统计某一个类所有的方法
@within必须是在类级别生效
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Time {
}
@Component
@Aspect
public class TimeAspect {
@Around("@within(com.javasm.aspect.Time)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object proceed = joinPoint.proceed();
long time = System.currentTimeMillis() - start;
String msg = "%s方法,运行消耗的时间是%s毫秒";
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
String methodName = signature.getMethod().getName();
System.out.printf(msg,methodName,time);
return proceed;
}
}
@Time
配置类启动
@Configuration
@ComponentScan("com.javasm")
@EnableAspectJAutoProxy//开启注解AOP
public class JavasmConfig {
}
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(JavasmConfig.class);