SpringAOP
什么是AOP
AOP(Aspect Orient Programming) ,直译过来就是 面向切面编程 . AOP 是一种编程思想 , 是面向对象编程 (OOP) 的一种补充. 面向对象编程将程序抽象成各个层次的对象, 而面向切面编程是将程序抽象成各个切面.
为什么需要AOP
想象下面的场景,开发中在多个模块间有某段重复的代码,我们通常是怎么处理的?显然,没有人会靠“复制粘贴”吧。在传统的面向过程编程中,我们也会将这段代码,抽象成一个方法,然后在需要的地方分别调用这个方法,这样当这段代码需要修改时,我们只需要改变这个方法就可以了。然而需求总是变化的,有一天,新增了一个需求,需要再多出做修改,我们需要再抽象出一个方法,然后再在需要的地方分别调用这个方法,又或者我们不需要这个方法了,我们还是得删除掉每一处调用该方法的地方。实际上涉及到多个地方具有相同的修改的问题我们都可以通过 AOP 来解决。
AOP术语
AOP 领域中的特性术语:
- 通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。
- 连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。
- 切点(PointCut): 可以插入增强处理的连接点。
- 切面(Aspect): 切面是通知和切点的结合。
- 引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。
- 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入。
通过注解声明 5 种通知类型
@Before(前置通知)
通知方法会在目标方法调用之前执行
@After(最终通知)
通知方法会在目标方法返回或异常后调用
@AfterReturning(返回通知)
通知方法会在目标方法返回后调用
@AfterThrowing(异常通知)
通知方法会在目标方法抛出异常后调用
@Around(环绕通知)
它将覆盖原有方法,但是允许你通过反射调用原有方法
简单切面Demo
/**
* 切面
* 切入点和通知的抽象
* 定义 切入点 和通知
* 切入点:定义要拦截哪些类的哪些方法
* 通知:定义了拦截之后方法要做什么
*/
@Component // 将对象交给IOC容器进行实例化
@Aspect // 声明当前的类是一个切面
public class LogCut {
/**
* 切入点
* 定义要拦截哪些类的哪些方法
* 匹配规则,拦截什么方法
* 定义切入点
*
* @Pointcut("匹配规则") AOP切入点表达式
* 1.执行所有的公共方法
* execution(pulice *(..))
* 2.执行任意的set方法
* execution(* set*(..))
* 3.设置指定包下的任意类的任意方法(指定包: com.yf.service)
* execution(* com.yf.service.*.*(..))
* 4.设置指定包及子包下的任意类的任意方法 (指定包: com.yf.service)
* execution(* com.yf.service..*.*(..))
* 表达式中的第一个*
* 代表的是方法的修饰范围(public,private,protected)
* 如果取值是 *,则表示所有范围
*/
@Pointcut("execution(* com.yf.service..*.*(..))")
public void cut() {
}
/**
* 声明前置通知,并将通知运用到指定的切入点上
* 目标类的方法执行前,执行该通知
*/
@Before(value = "cut()")
public void before() {
System.out.println("前置通知...");
}
/**
* 声明返回通知,并将通知运用到指定的切入点上
* 目标类的方法无异常执行后,执行该通知
*/
@AfterReturning(value = "cut()")
public void afterReturn() {
System.out.println("返回通知...");
}
/**
* 声明异常通知,并将通知运用到指定的切入点上
* 目标类出现异常后,执行该通知
*/
@AfterThrowing(value = "cut()")
public void afterThrowing() {
System.out.println("异常通知...");
}
/**
* 声明异常通知,并将通知运用到指定的切入点上
* 目标类执行方法后,有无异常都会执行
*/
@After(value = "cut()")
public void after() {
System.out.println("最终通知...");
}
/**
* 声明环绕通知,并将通知应用到指定的切入点上
* 目标类的方法执行前后,都可以通过环绕通知定义的响应的处理
* 需要显示调用的方法,否则无法访问指定的方法 pjp.proceed()
*
* @param pjp
* @return
*/
@Around(value = "cut()")
public Object around(ProceedingJoinPoint pjp) {
System.out.println("环绕通知-前置通知...");
Object object = null;
try {
//显示调用对应的方法
object = pjp.proceed();
System.out.println(pjp.getTarget());
System.out.println("环绕通知-返回通知...");
} catch (Throwable e) {
e.printStackTrace();
System.out.println("环绕通知-异常通知...");
}
System.out.println("环绕通知-最终通知...");
return object;
}
}
@Service
public class UserService {
public void test() {
System.out.println("UserService test...");
}
}
public class App {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) context.getBean("userService");
userService.test();
}
}
<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
https://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
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 开启自动化扫描 -->
<context:component-scan base-package="com.yf"/>
<!-- 配置AOP代理 -->
<aop:aspectj-autoproxy/>
</beans>