AOP:【动态代理】
指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式;
1、定义一个业务逻辑类(MathCalculator);在业务逻辑运行的时候将日志进行打印(方法之前、方法运行结束、方法出现异常,xxx)
2、定义一个日志切面类(LogAspects):切面类里面的方法需要动态感知MathCalculator.div运行到哪里然后执行;
通知方法:
前置通知(@Before):logStart:在目标方法(div)运行之前运行
后置通知(@After):logEnd:在目标方法(div)运行结束之后运行(无论方法正常结束还是异常结束)
返回通知(@AfterReturning):logReturn:在目标方法(div)正常返回之后运行
异常通知(@AfterThrowing):logException:在目标方法(div)出现异常以后运行
环绕通知(@Around):动态代理,手动推进目标方法运行(joinPoint.procced())
---------------------
1.业务逻辑类MathCalculator代码如下
public class MathCalculator {
public int div(int i,int j){
System.out.println("MathCalculator...div...");
return i/j;
}
}
2.日志切面类LogAspects代码逻辑如下,必须告诉Spring哪个类是切面类(给切面类上加一个注解:@Aspect);
@Aspect
public class LogAspects {
//抽取公共的切入点表达式
//1、本类引用
//2、其他的切面引用
@Pointcut("execution(public int com.ustc.reed.spring.aop.MathCalculator.*(..))")
public void pointCut(){};
//@Before在目标方法之前切入;切入点表达式(指定在哪个方法切入)
@Before("pointCut()")
public void logStart(JoinPoint joinPoint){
Object[] args = joinPoint.getArgs();
System.out.println(""+joinPoint.getSignature().getName()+"运行。。。@Before:参数列表是:{"+ Arrays.asList(args)+"}");
}
@After("com.ustc.reed.spring.aop.LogAspects.pointCut()")
public void logEnd(JoinPoint joinPoint){
System.out.println(""+joinPoint.getSignature().getName()+"结束。。。@After");
}
//JoinPoint一定要出现在参数表的第一位
@AfterReturning(value="pointCut()",returning="result")
public void logReturn(JoinPoint joinPoint,Object result){
System.out.println(""+joinPoint.getSignature().getName()+"正常返回。。。@AfterReturning:运行结果:{"+result+"}");
}
@AfterThrowing(value="pointCut()",throwing="exception")
public void logException(JoinPoint joinPoint,Exception exception){
System.out.println(""+joinPoint.getSignature().getName()+"异常。。。异常信息:{"+exception+"}");
}
}
3、将切面类和业务逻辑类(目标方法所在类)都加入到容器中;给配置类中加 @EnableAspectJAutoProxy 【开启基于注解的aop模式】
@EnableAspectJAutoProxy
@Configuration
public class MainConfigOfAOP {
//业务逻辑类加入容器中
@Bean
public MathCalculator calculator(){
return new MathCalculator();
}
//切面类加入到容器中
@Bean
public LogAspects logAspects(){
return new LogAspects();
}
}
启动AOP并调用div方法
@Test
public void test06(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAOP.class);
MathCalculator mathCalculator = applicationContext.getBean(MathCalculator.class);
mathCalculator.div(1, 1);
applicationContext.close();
}
运行结果如下图
修改成执行1/0的操作,执行结果如下图
环绕通知能控制整个过程,使用方法如下。
//环绕通知:需要携带ProceedingJoinPoint类型的参数
//环绕通知类似于动态代理的全过程:ProceedingJoinPoint类型的参数可以决定是否执行目标方法
//且环绕通知必须有返回值,返回值即目标方法的返回值。
@Around("pointCut()")
public Object aroundMethod(ProceedingJoinPoint pjd){
Object result = null;
String methodName = pjd.getSignature().getName();
Object args = Arrays.asList(pjd.getArgs());
//执行目标方法
try {
//前置通知
System.out.println("Arround:The method "+methodName +" begins with "+ args);
result = pjd.proceed();
//后置通知
System.out.println("Arround:The method "+ methodName+" ends");
} catch (Throwable e) {
e.printStackTrace();
//异常通知
System.out.println("Arround:The method "+ methodName+"occurs exception:"+e);
//throw new RuntimeException(e);
//不抛出异常的话,异常就被上面抓住,执行下去,返回result,result值为null,转换为int
}
//返回通知
System.out.println("Arround:The method "+ methodName+" ends with the Result "+ result);
return result;
}
注掉前面几种通知
运行结果如下