spring AOP-面向切面编程
文章目录
AOP简介
- AOP:中文名称面向切面编程
- 英文名称:( Aspect Oriented Programming )
- 正常程序执行流程都是纵向执行流程
- 在原有纵向执行流程中添加横切面,叫面向切面编程
- 面向切面编程(AOP)是什么?
- 在程序原有纵向执行流程中,针对某一个或某一些方法添加通知,形成横切面过程就叫做面向切面编程.
- 见下图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Onvbun2X-1573477147038)(assets/aop.png)]
优点
- 面向切面编程的优点
- 不需要修改原有程序代码
- 高扩展性
- 原有功能相当于释放了部分逻辑.让职责更加明确.
AOP常用概念
- 原有功能: 切点, pointcut
- 前置通知: 在切点之前执行的功能. before advice
- 后置通知: 在切点之后执行的功能,after advice
- 异常通知: 出现异常的时候执行的功能
- 环绕通知: 在切点执行之前和之后都会执行的功能
- 如果切点执行过程中出现异常,会触发异常通知。 throws advice
- 所有功能总称叫做切面.
- 织入: 把切面嵌入到原有功能的过程叫做织入
两种AOP实现方式
一、Schema-based
- 每个通知都需要实现接口或类
- 配置 spring 配置文件时在 <aop:config> 配置
代码实现
- 导包
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hsM0uHqD-1573477147040)(assets/aop-jar.png)]
- 创建Demo类
public class Demo {
public void demo1() {
System.out.println("demo1");
}
public void demo2() {
System.out.println("demo2");
}
public void demo3() {
System.out.println("demo3");
}
}
- 创建自定义通知类
// 前置通知类
public class MyBeforeAdvice implements MethodBeforeAdvice {
/**
* @param method 切点方法对象 Method 对象
* @param objects 切点方法参数
* @param o 切点在哪个对象中
* @throws Throwable
*/
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("前置通知====");
}
}
// 后置通知类
public class MyAfterAdvice implements AfterReturningAdvice {
/**
* @param o 切点方法返回值
* @param method 切点方法对象
* @param objects 切点方法参数
* @param o1 切点方法所在类的对象
* @throws Throwable
*/
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("后置通知=====");
}
}
- 书写spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<!-- 1. 引入aop命名空间:xmlns:aop -->
<!-- 2. 配置自定义通知类的<bean> -->
<bean id="myAfterAdvice" class="com.fu.advice.MyAfterAdvice"></bean>
<bean id="myBeforeAdvice" class="com.fu.advice.MyBeforeAdvice"></bean>
<!-- 3. 配置切面 -->
<aop:config>
<!-- 配置切点:execution()
* 通配符,匹配任意方法名,任意类名,任意一级包名
如果希望匹配任意方法参数 (..)
-->
<aop:pointcut expression="execution(* com.fu.bean.Demo.demo2())" id="myPoint"></aop:pointcut>
<!-- 配置织入: Schema-based 方式
自定义通知类必须实现 MethodBeforeAdvice 或 AfterReturningAdvice 接口,指定前置或后置通知
-->
<aop:advisor advice-ref="myBeforeAdvice" pointcut-ref="myPoint"></aop:advisor>
<aop:advisor advice-ref="myAfterAdvice" pointcut-ref="myPoint"></aop:advisor>
</aop:config>
<!-- 4. 配置演示类IOC -->
<bean id="demo" class="com.fu.bean.Demo"></bean>
</beans>
- 书写测试类
// 测试类
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
Demo demo = ac.getBean("demo", Demo.class);
demo.demo1();
demo.demo2();
demo.demo3();
}
}
/* 代码运行结果 */
/*
demo1
前置通知====
demo2
后置通知=====
demo3
*/
二、AspectJ
- 每个通知不需要实现接口或类
- 配置 spring 配置文件是在 <aop:config> 的子标签 <aop:aspect> 中配置
spring配置文件
- <aop:after/> 后置通知,是否出现异常都执行
- <aop:after-returing/> 后置通知,只有当切点正确执行时执行
- <aop:after/> 和 <aop:after-returing/> 和 <aop:after-throwing/>执行顺序和配置顺序有关
- execution() 括号不能扩上 args
- 中间使用 and 不能使用&& 由 spring 把 and 解析成&&
- args(名称) 名称自定义的.顺序和 demo1(参数,参数)对应
- <aop:before/> arg-names=” 名 称 ” 名 称 来 源 于 expression=”” 中 args(),名称必须一样
- args() 有几个参数,arg-names 里面必须有几个参数
- arg-names=”” 里面名称必须和通知方法参数名对应
代码实现
- Demo.java
public class Demo {
public void demo1() {
int a = 5 / 0;
System.out.println("demo1");
}
}
- MyAdvice.java
// 自定义通知类
public class MyAdvice {
public void beforeAspect() {
System.out.println("前置通知:beforeAspect");
}
public void afterAspect() {
System.out.println("后置通知, 不管有没有异常都执行:afterAspect");
}
public void throwAspect() {
System.out.println("异常通知:throwAspect");
}
public Object aroundAspect(ProceedingJoinPoint p) throws Throwable {
System.out.println("前置环绕通知:aroundAspect");
Object result = p.proceed();
System.out.println("后置环绕通知:aroundAspect");
return result;
}
public void returningAspect() {
System.out.println("后置通知,只有当切点正确执行时执行:returningAspect");
}
}
- applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<bean id="myAspect" class="com.fu.advice.MyAdvice"></bean>
<!-- 切面 -->
<aop:config>
<aop:pointcut expression="execution(* com.fu.bean.Demo.demo1())" id="myPoint"></aop:pointcut>
<!-- 织入通知:aspect方式
ref : 表示方法在哪个类中
-->
<aop:aspect ref="myAspect">
<aop:after method="afterAspect" pointcut-ref="myPoint"></aop:after>
<aop:before method="beforeAspect" pointcut-ref="myPoint"></aop:before>
<!-- 环绕通知 -->
<aop:around method="aroundAspect" pointcut-ref="myPoint"></aop:around>
<!-- 异常通知 -->
<aop:after-throwing method="throwAspect" pointcut-ref="myPoint" ></aop:after-throwing>
<!-- 在目标方法正常完成后被织入 -->
<aop:after-returning method="returningAspect" pointcut-ref="myPoint"></aop:after-returning>
</aop:aspect>
</aop:config>
<bean id="demo" class="com.fu.bean.Demo"></bean>
</beans>
- 测试类
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext2.xml");
Demo demo = ac.getBean("demo", Demo.class);
demo.demo1();
/* 有异常时
前置通知:beforeAspect
前置环绕通知:aroundAspect
后置通知:afterAspect
异常通知:throwAspect
*/
/* 无异常时
前置通知:beforeAspect
前置环绕通知:aroundAspect
demo1
后置通知:afterAspect
后置环绕通知:aroundAspect
运行成功后的通知:returningAspect
*/
}
}
环绕通知
- 把前置通知和后置通知都写到一个通知中, 组成了环绕通知
schema-besed方式
-
新建一个类实现 MethodInterceptor 接口
-
重写 invoke 方法
public class MyArround implements MethodInterceptor { @Override public Object invoke(MethodInvocation arg0) throws Throwable { System.out.println("环绕-前置"); Object result = arg0.proceed();//放行,调用切点方式 System.out.println("环绕-后置"); return result; }
AspectJ方式
-
新建类,不用实现任何接口
-
类中方法名任意,添加参数 (ProceedingJoinPoint p)
public Object myarround(ProceedingJoinPoint p) throws Throwable{ System.out.println("执行环绕"); System.out.println("环绕-前置"); Object result = p.proceed(); System.out.println("环绕后置"); return result; }