提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
Spring AOP-Spring面向切面编程概述
一、AOP?
1.什么是 AOP
AOP (Aspect Orient Programming),直译过来就是 面向切面编程。AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。
所谓切面,相当于应用对象间的横切点,我们可以将其单独抽象为单独的模块。
2.为什么需要 AOP?
想象下面的场景,开发中在多个模块间有某段重复的代码,我们通常是怎么处理的?显然,没有人会靠“复制粘贴”吧。在传统的面向过程编程中,我们也会将这段代码,抽象成一个方法,然后在需要的地方分别调用这个方法,这样当这段代码需要修改时,我们只需要改变这个方法就可以了。然而需求总是变化的,有一天,新增了一个需求,需要再多出做修改,我们需要再抽象出一个方法,然后再在需要的地方分别调用这个方法,又或者我们不需要这个方法了,我们还是得删除掉每一处调用该方法的地方。实际上涉及到多个地方具有相同的修改的问题我们都可以通过 AOP 来解决。
3.AOP 实现分类
AOP 要达到的效果是,保证开发者不修改源代码的前提下,去为系统中的业务组件添加某种通用功能。AOP 的本质是由 AOP 框架修改业务组件的多个方法的源代码,看到这其实应该明白了,AOP 其实就是前面一篇文章讲的代理模式的典型应用。
按照 AOP 框架修改源代码的时机,可以将其分为两类:
- 静态 AOP 实现, AOP 框架在编译阶段对程序源代码进行修改,生成了静态的 AOP 代理类(生成的 *.class文件已经被改掉了,需要使用特定的编译器),比如 AspectJ。
- 动态 AOP 实现, AOP 框架在运行阶段对动态生成代理对象(在内存中以 JDK 动态代理,或 CGlib 动态地生成 AOP代理类),如 SpringAOP。
4.AOP概念
- 通知(Advice): AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。
- 连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。
- 切点(PointCut): 可以插入增强处理的连接点。
- 切面(Aspect): 切面是通知和切点的结合。
- 引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。
- 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入。概念看起来总是有点懵,不同的参考书籍上翻译还不一样,所以需要慢慢在应用中理解。
通知(Advice)的类型:
- 前置通知(Before advice):在某个连接点(Join point)之前执行的通知,但这个通知不能阻止连接点的执行(除非它抛出一个异常)。
- 返回后通知(After returning advice):在某个连接点(Join point)正常完成后执行的通知。例如,一个方法没有抛出任何异常正常返回。
- 抛出异常后通知(After throwing advice):在方法抛出异常后执行的通知。
- 后置通知(After(finally)advice):当某个连接点(Join point)退出的时候执行的通知(不论是正常返回还是发生异常退出)。
- 环绕通知(Around advice):包围一个连接点(Join point)的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。
二、Spring AOP的使用
1.方法一:使用原生态Spring API接口
代码如下(示例):
配置pom.xml
<dependencies>
<!-- AOP-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<!-- Spring框架依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.14.RELEASE</version>
</dependency>
</dependencies>
创建接口(MasterService )
随机写几个方法
package com.java.ym.service;
/**
* @Author: YuMi
* @Date: 2020/10/9 11:00
* ----------------------
* @Comments:
*/
public interface MasterService {
void add();
void delete();
void update();
void select();
}
接口实现类(MasterServiceImpl )
package com.java.ym.service.impl;
import com.java.ym.service.MasterService;
/**
* @Author: YuMi
* @Date: 2020/10/9 11:02
* ----------------------
* @Comments:
*/
public class MasterServiceImpl implements MasterService {
@Override
public void add() {
System.out.println("增加");
}
@Override
public void delete() {
System.out.println("删除");
}
@Override
public void update() {
System.out.println("修改");
}
@Override
public void select() {
System.out.println("查询");
}
}
创建假例日志类
package com.java.ym.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
/**
* @Author: YuMi
* @Date: 2020/10/9 11:03
* ----------------------
* @Comments: 日志
*/
public class Log implements MethodBeforeAdvice {
/**
* method: 需要执行的目标对象的方法
* objects: 参数
* target: 目标对象
* @param method
* @param objects
* @param target
* @throws Throwable
*/
@Override
public void before(Method method, Object[] objects, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
}
}
package com.java.ym.log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
/**
* @Author: YuMi
* @Date: 2020/10/9 11:07
* ----------------------
* @Comments:
*/
public class AfterLog implements AfterReturningAdvice {
/**
* returnValue: 返回值
* @param returnValue
* @param method
* @param objects
* @param target
* @throws Throwable
*/
@Override
public void afterReturning(Object returnValue, Method method, Object[] objects, Object target) throws Throwable {
System.out.println("执行了"+method.getName()+"返回结果为:"+returnValue);
}
}
配置applicationContext.xml
<?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: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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="masterService" class="com.java.ym.service.impl.MasterServiceImpl"></bean>
<bean id="log" class="com.java.ym.log.Log"></bean>
<bean id="afterLog" class="com.java.ym.log.AfterLog"></bean>
<!--方法一:使用原生态Spring API接口
配置AOP:需要导入aop的约束-->
<aop:config>
<!-- 切入点: expression: 表达式,expression(需要执行的位置 *代表所有)-->
<aop:pointcut id="pointcut" expression="execution(* com.java.ym.service.impl.MasterServiceImpl.*(..))"/>
<!-- 执行环绕增加-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
测试
import com.java.ym.service.MasterService;
import com.java.ym.service.impl.MasterServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @Author: YuMi
* @Date: 2020/10/9 11:32
* ----------------------
* @Comments:
*/
public class MyTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
// 动态代理,代理的是接口
MasterService masterService = (MasterService) applicationContext.getBean("masterService");
masterService.add();
}
}
// 运行结果
com.java.ym.service.impl.MasterServiceImpl的add被执行了
增加
执行了add返回结果为:null
2.方法二:自定义类
代码如下(示例):
创建接口(MasterService )
随机写几个方法
package com.java.ym.service;
/**
* @Author: YuMi
* @Date: 2020/10/9 11:00
* ----------------------
* @Comments:
*/
public interface MasterService {
void add();
void delete();
void update();
void select();
}
接口实现类(MasterServiceImpl )
package com.java.ym.service.impl;
import com.java.ym.service.MasterService;
/**
* @Author: YuMi
* @Date: 2020/10/9 11:02
* ----------------------
* @Comments:
*/
public class MasterServiceImpl implements MasterService {
@Override
public void add() {
System.out.println("增加");
}
@Override
public void delete() {
System.out.println("删除");
}
@Override
public void update() {
System.out.println("修改");
}
@Override
public void select() {
System.out.println("查询");
}
}
创建自定义切入点类
package com.java.ym.diy;
/**
* @Author: YuMi
* @Date: 2020/10/9 11:39
* ----------------------
* @Comments: 自定义切入点
*/
public class DiyPointCut {
public void diyFront(){
System.out.println("方法执行前***");
}
public void diyLater(){
System.out.println("方法执行后***");
}
}
配置applicationContext.xml
<?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: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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="masterService" class="com.java.ym.service.impl.MasterServiceImpl"></bean>
<bean id="log" class="com.java.ym.log.Log"></bean>
<bean id="afterLog" class="com.java.ym.log.AfterLog"></bean>
<!-- 方法二:自定义类-->
<bean id="diy" class="com.java.ym.diy.DiyPointCut"></bean>
<aop:config>
<!-- 自定义切面 ref:需要引用的类-->
<aop:aspect ref="diy">
<!-- 切入点--> <!-- 匹配所有MasterServiceImpl包下面的所有类的所有方法-->
<aop:pointcut id="point" expression="execution(* com.java.ym.service.impl.MasterServiceImpl.*(..)) "/>
<!-- 前置通知-->
<aop:before method="diyFront" pointcut-ref="point"></aop:before>
<!-- 后置通知-->
<aop:after method="diyLater" pointcut-ref="point"></aop:after>
</aop:aspect>
</aop:config>
</beans>
测试
import com.java.ym.service.MasterService;
import com.java.ym.service.impl.MasterServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @Author: YuMi
* @Date: 2020/10/9 11:32
* ----------------------
* @Comments:
*/
public class MyTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
// 动态代理,代理的是接口
MasterService masterService = (MasterService) applicationContext.getBean("masterService");
masterService.add();
}
}
// 运行结果
方法执行前***
增加
方法执行后***
3.方法三:以注解的形式实现
这里我就不再写接口和接口实现类了,还是引用上面两个方法的接口和实现类。
创建注解类
package com.java.ym.diy;
/**
* @Author: YuMi
* @Date: 2020/10/10 9:58
* ----------------------
* @Comments:
*/
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/**
* 方法三:使用注解实现AOP
* Aspect: 表示这个类是一个切面
*/
// 声明事务
@Aspect
public class Annotate {
/**
* 切入点 匹配所有MasterServiceImpl包下面的所有类的所有方法–>
* <aop:pointcut id="point" expression="execution(* com.java.ym.service.impl.MasterServiceImpl.*(..)) "/>
*/
@Before("execution(* com.java.ym.service.impl.MasterServiceImpl.*(..))")
public void before(){
System.out.println("方法执行前***");
}
@After("execution(* com.java.ym.service.impl.MasterServiceImpl.*(..))")
public void after(){
System.out.println("方法执行后***");
}
/**
* 在环绕增强中,可以定一个参数,代表需要处理切入的点
*/
@Around("execution(* com.java.ym.service.impl.MasterServiceImpl.*(..))")
public void around(ProceedingJoinPoint point) throws Throwable {
System.out.println("环绕前");
// 执行方法
Object proceed = point.proceed();
System.out.println("环绕后");
}
}
配置applicationContext.xml
<?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: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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="masterService" class="com.java.ym.service.impl.MasterServiceImpl"></bean>
<!-- 方法三-->
<bean id="annotate" class="com.java.ym.diy.Annotate"></bean>
<!-- 开启注解 jdk(默认)proxy-target-class="false" 如果改为true就会使用cglib实现 (结果一样)-->
<aop:aspectj-autoproxy proxy-target-class="false"/>
</beans>
测试
import com.java.ym.service.MasterService;
import com.java.ym.service.impl.MasterServiceImpl;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @Author: YuMi
* @Date: 2020/10/9 11:32
* ----------------------
* @Comments:
*/
public class MyTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
// 动态代理,代理的是接口
MasterService masterService = (MasterService) applicationContext.getBean("masterService");
masterService.add();
}
}
// 执行结果
环绕前
方法执行前***
增加
环绕后
方法执行后***