AOP 简介
AOP(Aspect-Oriented Programming, 面向切面编程): 是一种新的方法论, 是对传统 OOP(Object-Oriented Programming, 面向对象编程) 的补充.
AOP 的主要编程对象是切面(aspect), 而切面模块化横切关注点.
在应用 AOP 编程时, 仍然需要定义公共功能, 但可以明确的定义这个功能在哪里, 以什么方式应用, 并且不必修改受影响的类. 这样一来横切关注点就被模块化到特殊的对象(切面)里.
AOP 的好处:
每个事物逻辑位于一个位置, 代码不分散, 便于维护和升级
业务模块更简洁, 只包含核心业务代码.
AOP 术语
切面(Aspect): 横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象
通知(Advice): 切面必须要完成的工作
目标(Target): 被通知的对象
代理(Proxy): 向目标对象应用通知之后创建的对象
连接点(Joinpoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。例如 ArithmethicCalculator#add() 方法执行前的连接点,执行点为 ArithmethicCalculator#add(); 方位为该方法执行前的位置
切点(pointcut):每个类都拥有多个连接点:例如 ArithmethicCalculator 的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。
AspectJ
AspectJ:Java 社区里最完整最流行的 AOP 框架.
在 Spring2.0 以上版本中, 可以使用基于 AspectJ 注解或基于 XML 配置的 AOP
使用AOP(@AspectJ注解)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId> <!-- 检查是否有spring-aop,有了就不用引了 -->
<version>4.3.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.10.RELEASE</version>
</dependency>
<!-- spring-aspects已经包含了 aspectjweaver
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.14</version>
<scope>runtime</scope>
</dependency>
-->
<?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-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<context:component-scan base-package="cn.fg.aop" />
<!-- 开启@Aspect注解,自动代理目标类 -->
<aop:aspectj-autoproxy />
</beans>
package cn.fg.aop;
import java.util.Arrays;
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;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Order(1) //切面优先级,存在多个切面时,值越小优先级越高,默认优先级是Integer.MAX_VALUE
//切面注解,开启<aop:aspectj-autoproxy>时有效,若只在父容器中开启切面,则切面只会在<context:component-scan>包下的bean被调用时进行切入,
//若在子容器中开启切面,由于子容器可以调用父容器的bean,所以在可在所有bean切入
@Aspect
@Component //必须向ioc容器中注入bean
public class LoggingAspect { //日志切面类
/**
* execution表达式语法
* execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)throws-pattern?)
* modifier-pattern:修饰符匹配,如public
* ret-type-pattern:返回值匹配,如int或全路径类名,可以为*表示任何返回值
* declaring-type-pattern:类路径匹配(需要和name-pattern配合使用)
* 1. cn.fg.aop.ArithmeticCalculator.<name-pattern> 精准的类路径匹配
* 2. cn.fg.aop.*.<name-pattern> cn.fg.aop包名下的任意类匹配
* 3. cn.fg..ArithmeticCalculator.<name-pattern> cn.fg及子包下的ArithmeticCalculator匹配
* 4. cn.fg..*.<name-pattern> cn.fg及子包下的任意类匹配
* name-pattern:方法名匹配,如add;可以为 *表示所有方法或者set*代表以set开头的所有方法
* param-pattern:参数类型匹配,如int,int 多个参数逗号分隔;可以为*表示任意参数类型,如*,int; ..表示零个或多个任意参数
* throws-pattern:异常类型匹配
* 表达式中的?号表示是可选的,所以最短的表达式为execution(* *(..))
*/
//前置通知,在目标方法之前执行
@Before("execution(* cn.fg..*.*(..))")
public void beforeMethod(JoinPoint joinPoint){
//在这里写切入的逻辑
String methodName = joinPoint.getSignature().getName(); //目标的方法名
Object [] args = joinPoint.getArgs(); //目标方法的参数
Object targetObject = joinPoint.getTarget(); //目标对象
Object proxyObject = joinPoint.getThis(); //AOP框架为目标对象生成的代理对象
System.out.println("methodName=" + methodName);
System.out.println("args=" + Arrays.asList(args));
System.out.println("targetObject=" + targetObject);
System.out.println("proxyObject=" + proxyObject);
}
//定义公共execution表达式
@Pointcut("execution(* cn.fg..*.*(..))")
public void declareJointPointExpression(){}
//后置通知,在目标方法执行后执行(即使目标方法抛出异常也会执行)
//在后置通知中不能拿到目标方法返回的结果
@After("declareJointPointExpression()") //使用公共execution
//@After("cn.fg.aop.VlidationAspect.declareJointPointExpression()") 引用其他切面的表达式
public void afterMethod(JoinPoint joinPoint){
System.out.println("@After.........................");
}
/**
* 在方法法正常结束受执行的代码
* 返回通知是可以访问到方法的返回值的!
* returning:设置接收返回值的参数名,名字随便取要与接收的参数名对应=Object result
*/
@AfterReturning(value="execution(* cn.fg..*.*(..))",returning="result")
public void afterReturning(JoinPoint joinPoint, Object result){
System.out.println("返回值="+result);
System.out.println("@AfterReturning.......................");
}
/**
* 在目标方法出现异常时会执行的代码.
* 可以访问到异常对象; 且可以指定在出现特定异常时在执行通知代码
* throwing:设置接收异常的参数名称,名字随便取要与接收的参数名对应=Exception e
*/
@AfterThrowing(value = "execution(* cn.fg..*.*(..))", throwing = "e")
public void afterThrowing(JoinPoint joinPoint, Exception e) {
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " occurs excetion:" + e);
}
/*
* aop通知结构
* try {
* //前置通知
* int result = 0;
* //返回通知
* } catch (Exception e) {
* //异常通知
* }
* //后置通知
*/
/**
* 环绕通知需要携带 ProceedingJoinPoint 类型的参数.
* 环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是否执行目标方法.
* 且环绕通知必须有返回值, 返回值即为目标方法的返回值
*/
@Around("execution(* cn.fg..*.*(..))")
public Object aroundMethod(ProceedingJoinPoint pjd){
Object result = null;
String methodName = pjd.getSignature().getName();
try {
//前置通知
System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
//执行目标方法
result = pjd.proceed();
//返回通知
System.out.println("The method " + methodName + " ends with " + result);
} catch (Throwable e) {
//异常通知
System.out.println("The method " + methodName + " occurs exception:" + e);
throw new RuntimeException(e);
}
//后置通知
System.out.println("The method " + methodName + " ends");
return result; //可以修改返回值
}
//切入点表达式可以通过操作符 && || !结合起来
//@Pointcut("execution(* Spring4_AOP.aopAnnotation.*.add(..)) || execution(* Spring4_AOP.aopAnnotation.*.sub(..))")
}
使用AOP(xml)
仅了解,可以这样配,省略。。。。。