AOP: 面向切面编程[底层就是动态代理]
指程序在运行期间动态的将某段代码切入到指定方法位置进行运行的编程方式
先建立Cap10MainConfigAop配置类
在POM.XML中导入spring-aspects依赖包
新建立一个业务逻辑类Calculator.java
package com.caojiulu.cap10.aop;
//业务逻辑类
public class Calculator {
public int div(int i,int j){
return i/j;
}
}
在div()方法运行之前, 记录一下日志, 运行后也记录一下,运行出异常,也打印一下
但这样会有问题, 这种方式耦合了
新建一个日志切面类
package com.caojiulu.cap10.aop;
import java.util.Arrays;
import org.aopalliance.intercept.Joinpoint;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
//日志切面类
@Aspect
public class LogAspects {
@Pointcut("execution (public int com.caojiulu.cap10.aop.Calculator.*(..))")
public void pointCut(){
}
// 不想区分是哪个方法加*
//@Before("execution public int com.caojiulu.cap10.aop.Calculator.div(int , int))")
@Before("execution ( public int com.caojiulu.cap10.aop.Calculator.*(..))")
public void logStart(JoinPoint joinPoint){
System.out.println(joinPoint.getSignature().getName()+"除法运行。。。参数列表是:{"+Arrays.asList(joinPoint.getArgs())+"}");
}
//after 方法无论是正常结束,还是异常结束,都会调用
@After("execution ( public int com.caojiulu.cap10.aop.Calculator.div(int ,int ))")
public void logEnd(JoinPoint joinPoint){
System.out.println(joinPoint.getSignature().getName()+"除法结束。。。。");
}
@AfterReturning(value ="pointCut()",returning="result")
public void logReturn(Object result){
System.out.println("除法正常返回。。运行结果是:{"+result+"}");
}
@AfterThrowing(value="com.caojiulu.cap10.aop.LogAspects.pointCut()",throwing="exceptio")
public void logException(Exception exceptio){
System.out.println("除法异常。。异常信息是{"+exceptio+"}");
}
/*@Around("pointCut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
System.out.println("around:执行目标方法之前。。。");
Object obj = proceedingJoinPoint.proceed();
System.out.println("around:执行目标方法之后。。。");
return obj;
}*/
}
日志切面类的方法需要动态感知到div()方法运行到哪里了, 然后再执行, 如果除法开始, 就日志开始方法,
也叫通知方法, 分以下几种:
前置通知: logStart(),在目标方法(div)运行之前运行 (@Before)
后置通知:logEnd(), 在目标方法(div)运行结束之后运行,无论正常或异常结束 (@After)
返回通知:logReturn, 在目标方法(div)正常返回之后运行 (@AfterReturning)
异常通知:logException, 在目标方法(div)出现异常后运行(@AfterThrowing)
环绕通知:以上没写,动态代理, 手动执行目标方法运行joinPoint.procced(),最底层通知,手动指定执行目标方法(@Around), 执行之前相当于前置通知, 执行之后相当于返回通知
其实就是通过反射执行目标对象的连接点处的方法
给配置类中加@EnableAspectJAutoProxy[一定得加上,关键]
注意: 在spring以后会有很多@EnableXXXX, 表示开启某项功能, 取代XML配置
package com.caojiulu.cap10.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import com.caojiulu.cap10.aop.Calculator;
import com.caojiulu.cap10.aop.LogAspects;
@Configuration
@EnableAspectJAutoProxy
public class Cap10MainConfigAop {
@Bean
public Calculator calculator(){
return new Calculator();
}
@Bean
public LogAspects logAspects(){
return new LogAspects();
}
}
测试类:
package com.caojiulu.cap10.config;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.caojiulu.cap10.aop.Calculator;
public class Cap10Test {
@Test
public void test01(){
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap10MainConfigAop.class);
Calculator calculator = app.getBean(Calculator.class);
int div = calculator.div(4,2);
System.out.println(div);
app.close();
}
}
运行结果:
div除法运行。。。参数列表是:{[4, 2]}
div除法结束。。。。
除法正常返回。。运行结果是:{2}
2