两种编程思想。是对面向对象编程的一种补充。AOP编程操作的主要对象是切面(aspect),而切面模块化横切关注点。
面向切面编程:是指在程序运行期间将某段代码,动态的切入到某个类的指定方法的指定位置的这种编程思想叫做面向切面编程。
每个事物逻辑位于一个位置,代码不分散,便于维护和升级
业务模块更简洁,只包含核心业务代码
定位连接点的方式。每个类的方法中都包含多个连接点,所以连接点是类中客观存在的事物。如果把连接点看作数据库中的记录,那么切入点就是查询条件——AOP可以通过切入点定位到特定的连接点。切点通过org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。
为了方便理解以上专业术语,给出下图:
commons-logging-1.1.3.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
AOP依赖的包
spring-aop-4.0.0.RELEASE.jar
spring-aspects-4.0.0.RELEASE.jar
//增强版
com.springsource.net.sf.cglib-2.2.0.jar
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
②引入aop名称空间
③配置
当Spring IOC容器侦测到bean配置文件中的
[1]@Before:前置通知,在方法执行之前执行
[2]@After:后置通知,在方法执行之后执行
[3]@AfterRunning:返回通知,在方法返回结果之后执行
[4]@AfterThrowing:异常通知,在方法抛出异常之后执行
[5]@Around:环绕通知,围绕着方法执行
/**
* 1、告诉ioc这个组件的存在
* 2、告诉ioc这是一个切面使用@Aspect
* @author syl
*
*/
@Aspect
@Component
public class LogAspect {
/**
* try{
* @Before前置通知
* method.invoke();
* @AfterRunning返回通知
* }catch(e){
* @AfterThrowing:异常通知,
* }
* @After
*
* 告诉Spring这些放在都在那个方法的哪个位置执行
* 1)、告诉位置
[1]@Before:前置通知,在方法执行之前执行
[2]@After:后置通知,在方法执行最终结束之后执行。
如果没异常
[3]@AfterRunning:返回通知,在方法返回结果之后执行
[4]@AfterThrowing:异常通知,在方法抛出异常之后执行
1、编写切入点表达式,来告诉spring是切入哪个方法的这个位置
*/
@Before(value="execution(public * *.add(int, int))")
public void logStart(){
System.out.println("AOP日志,方法开始");
}
@After(value="execution(public * *.add(int, int))")
public void logEnd(){
System.out.println("AOP日志,方法最终结束");
}
@AfterThrowing(value="execution(public * *.add(int, int))")
public void logException(){
System.out.println("AOP日志,方法出现异常");
}
@AfterReturning(value="execution(public * *.add(int, int))")
public void logReturn(){
System.out.println("AOP日志,方法正常执行");
}
表达式 execution(* com.atguigu.spring.ArithmeticCalculator.*(..))
含义 ArithmeticCalculator接口中声明的所有方法;第一个“*”代表任意修饰符及任意返回值;第二个“*”代表任意方法;
“..”匹配任意数量、任意类型的参数;若目标类、接口与该切面类在同一个包中可以省略包名。
表达式 execution(public * ArithmeticCalculator.*(..))
含义 ArithmeticCalculator接口的所有公有方法
表达式 execution(public double ArithmeticCalculator.*(..))
含义 ArithmeticCalculator接口中返回double类型数值的方法
表达式 execution(public double ArithmeticCalculator.*(double, ..))
含义 第一个参数为double类型的方法;“..” 匹配任意数量、任意类型的参数。
表达式 execution(public double ArithmeticCalculator.*(double, double))
含义 参数类型为double,double类型的方法
③在AspectJ中,切入点表达式可以通过 “&&”、“||”、“!”等操作符结合起来。
表达式 execution (* *.add(int,..)) || execution(* *.sub(int,..))
含义 任意类中第一个参数为int类型的add方法或sub方法
//1、不能用本身的类型去找,只能用接口类型
//2、IOC容器中保存的不是这个对象的本身,而是代理对象
//3、为什么接口类型又是可以的.因为jdk在创建动态代理的时候,
//需要被代理对象的接口
/*Calculator bean = ioc.getBean(Calculator.class);
bean.add(1, 2);
System.out.println(bean.getClass());*/
MathCalculator bean = ioc.getBean(MathCalculator.class);
bean.add(1, 2);
System.out.println(bean.getClass());
@AfterReturning(value = "com.atguigu.aspect.LogAspect.mypoint()", returning = "res")
public void validReturn(JoinPoint joinPoint, Object res) {
Signature signature = joinPoint.getSignature();
String name = signature.getName();
System.out.println("AOP参数验证,【" + name + "】方法正常返回,返回值为:" + res);
}
@AfterThrowing(value = "execution(public * *.*(int, int))", throwing = "e")
public void logException(JoinPoint joinPoint, Throwable e) {
// 获取方法名
String name = joinPoint.getSignature().getName();
System.out.println("AOP日志,【" + name + "】方法出现异常:异常对象:" + abc);
}
/**
* 最强大通知。 一般不常用
*
* @param proceedingJoinPoint
* @return
*/
@Around(value = "com.atguigu.aspect.LogAspect.mypoint()")
public Object vaildAround(ProceedingJoinPoint proceedingJoinPoint) {
// proceedingJoinPoint封装了连接点的详细信息
// proceed,执行目标方法 method.invoke
Object proceed = null;
Object[] args = proceedingJoinPoint.getArgs();
try {
// //传入目标执行时需要的参数列表
// 前置通知
System.out.println("proceed...之前");
// method.invoke
// 目标方法执行完成后会有返回值,这个返回值一定return出去
proceed = proceedingJoinPoint.proceed(args);
// 返回通知
System.out.println("proceed...之后");
} catch (Throwable e) {
// e.printStackTrace();
// 异常通知
System.out.println("proceed...异常");
// 1、注意:
// 一定将这个异常继续抛出去,以方便外界都能收到这个异常
throw new RuntimeException(e);
} finally {
// 后置通知
System.out.println("proceed...结束");
}
return proceed;
}
@Pointcut(value = "execution(public * *.*(int, int))")
public void mypoint() {
}
@Before(value = "mypoint()")
public void logStart(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
String name = joinPoint.getSignature().getName();
System.out.println("AOP日志:【" + name + "】方法开始运行,参数是:"
+ Arrays.asList(args));
}
@After("mypoint()")
public void logEnd(JoinPoint joinPoint) {
String name = joinPoint.getSignature().getName();
System.out.println("AOP日志:【" + name + "】方法运行结束!");
}
@AfterThrowing(value = "mypoint()", throwing = "e")
public void logException(JoinPoint joinPoint, Exception e) {
String name = joinPoint.getSignature().getName();
System.out.println("AOP日志:【" + name + "】方法运行出现异常:" + e);
}
@AfterReturning(value = "mypoint()", returning = "res")
public void logReturn(JoinPoint joinPoint, Object res) {
String name = joinPoint.getSignature().getName();
System.out.println("AOP日志:【" + name + "】方法正常结束,返回值为:" + res);
}
@Aspect
@Component
@Order(2)
public class LogAspect {
}
@Component
@Aspect
@Order(1)
public class ValidatorAspect {
}
正常情况下,基于注解的声明要优先于基于XML的声明。通过AspectJ注解,切面可以与AspectJ兼容,而基于XML的配置则是Spring专有的。由于AspectJ得到
越来越多的 AOP框架支持,所以以注解风格编写的切面将会有更多重用的机会。
在bean配置文件中,所有的Spring AOP配置都必须定义在
引用后端bean实例。切面bean必须有一个标识符,供