SpringAOP
文章目录
介绍
AOP:面向切面编程( Aspect Oriented Programming ),在不修改原来逻辑代码的前提下,使用切面的方式添加功能(一般是通用功能,例如:日志,事务,校验等)
AOP实现原理:动态代理
好处:少写代码,降低通用功能与业务代码的耦合
注:AOP在几乎每个框架中都有,介绍的是spring的AOP
常见概念
1.通知advice
也就是你想加的功能
2.连接点 joinpoint
就是允许你加入功能的地方,一般是在一个方法的周期中,包括方法调用前,方法调用后,方法抛出异常等情况下,spring允许加功能的所有地方
3.切入点(Pointcut)
不是每个连接点都需要加强,程序员可以根据自己的情况,指定需要加强的方法以及方法运行的某个时间,这就是切入点.由程序员指定哪些方法的什么时候被增强
4.切面(Aspect)
切面=通知+切入点。通知让你知道需要增强的功能,切入点让你知道切入到哪儿,一整合一张的切面就形成了。其实就是你自定义的需要增强的类.
指定哪些方法的什么时候要被增强+增强的内容
就是invoke方法中去掉method.invoke这行代码以外所有的叫切面
5.引介(introduction)
引介是一个过程的概念,就是把上面的切面真正的加在被代理的方法上
6.目标(target)
就是要增强的类,就是被代理类
7.代理(proxy)
动态代理(cglib,jdk)
8.织入(weaving)
就是通过Proxy.newInstance()方法生成代理类的过程,比如jdk代理就是调用类加载器生成的类过程
注解方式完成spring AOP
1.加入jar包
jar包 | 概述 |
---|---|
aspectjweaver | 是spring的切入点表达式需要用的包,指定切入点 |
aopalliance | 是AOP联盟的API包,里面包含了针对面向切面的接口。 通常Spring等其它具备动态织入功能的框架依赖此包 |
aspectjrt | 处理事务和AOP所需的包 |
cglib | cglib动态代理需要的包 |
spring-aop | spring对AOP支持的包 |
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.26</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.5.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<source>1.8</source> <!-- 源代码使用jdk1.8支持的特性 -->
<target>1.8</target> <!-- 使用jvm1.8编译目标代码 -->
<compilerArgs> <!-- 传递参数 -->
<arg>-parameters</arg>
<arg>-Xlint:unchecked</arg>
<arg>-Xlint:deprecation </arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
2.编写配置文件与测试类
<?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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启AOP注解支持
spring 的AOP默认支持JDK代理与cglib代理
当被代理类实现了接口的情况下使用JDK代理,否则使用CGLIB代理
可以在<aop:aspectj-autoproxy></aop:aspectj-autoproxy>这个配置中加入
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
告诉spring强制使用cglib代理
-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<context:component-scan base-package="cn.cdqf.aop"></context:component-scan>
</beans>
3.编写被代理类
package cn.cdqf.aop;
import org.springframework.stereotype.Service;
@Service
public class StudentService {
public void show(){
System.out.println("show方法被调用");
}
}
4.编写切面(增强类)
package cn.cdqf.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect //该类为增强类:切面
@Component
public class StudentAspect {
@Pointcut("execution(* cn.cdqf.aop..*.*(..))")//切入点
public void aa(){}
//Before:定义前置通知,在方法调用前被执行
//execution表达式:
// 第一个*表示任意返回值
//第一个.. 表示当前包及其子包
//第二个*表示任意类
//第三个*表示任意方法
//第二个..表示任意参数
@Before("aa()")
public void before(){
System.out.println("前置通知执行了:在方法调用前被执行");
}
//AfterReturning:后置通知,在方法正常执行结束后执行
@AfterReturning("execution(* cn.cdqf.aop..*.*(..))")
public void afterReturning(){
System.out.println("正常返回通知执行了:在方法正常执行结束后执行");
}
@AfterThrowing("execution(* cn.cdqf.aop..*.*(..))")
public void afterThrowing(){
System.out.println("异常通知执行了:在方法抛出异常后执行");
}
@After("execution(* cn.cdqf.aop..*.*(..))")
public void after(){
System.out.println("后置通知执行了:不管抛没抛出异常都会在方法运行后执行,相当于finally");
}
}
5.测试
package cn.cdqf.aop;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath*:applicationAop.xml"})
public class StudentServiceTest {
@Autowired
private StudentService studentService;
@Test
public void show() {
studentService.show();
}
}
6.环绕通知
切面代码:invoke的所有代码都可以在这里写
package cn.cdqf.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
@Aspect //该类为增强类:切面
@Component
public class StudentAspect {
//Around:就相当于我们实现动态代理的invoke方法
//比其它通知厉害的地方:
//1.可以选择是否执行目标方法
//2.可以改变返回值与参数等等
@Around("execution(* cn.cdqf.aop..*.*(..))")
public Object around(ProceedingJoinPoint joinPoint){
//获得方法参数 既然能获得就能改变
Object[] args = joinPoint.getArgs();
//获得方法签名
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
//获取所有参数的名称
String[] parameterNames = signature.getParameterNames();
//获取方法参数类型数组,配合前面的参数 可以秀几波
Class[] paramTypeArray = signature.getParameterTypes();
try {
//1.如果在方法前面加内容 就相当于前置通知
//调用方法
Object proceed = joinPoint.proceed(args);
//2.如果在方法后面加内容就相当于正常返回通知
//正常返回方法的返回值
return proceed;
} catch (Throwable throwable) {
//3.如果在异常出现后加内容 就相当于异常通知
throwable.printStackTrace();
}finally {
//4.如果在这里加内容就相当于后置通知
}
return null;
}
}
配置方式实现实现AOP
<!-- 定义目标对象 -->
<bean name="productDao" class="com.zejian.spring.springAop.dao.daoimp.ProductDaoImpl" />
<!-- 定义切面 -->
<bean name="myAspectXML" class="com.zejian.spring.springAop.AspectJ.MyAspectXML" />
<!-- 配置AOP 切面 -->
<aop:config>
<!-- 定义切点函数 -->
<aop:pointcut id="pointcut" expression="execution(* com.zejian.spring.springAop.dao.ProductDao.add(..))" />
<!-- 定义其他切点函数 -->
<aop:pointcut id="delPointcut" expression="execution(* com.zejian.spring.springAop.dao.ProductDao.delete(..))" />
<!-- 定义通知 order 定义优先级,值越小优先级越大-->
<aop:aspect ref="myAspectXML" order="0">
<!-- 定义通知
method 指定通知方法名,必须与MyAspectXML中的相同
pointcut 指定切点函数
-->
<aop:before method="before" pointcut-ref="pointcut" />
<!-- 后置通知 returning="returnVal" 定义返回值 必须与类中声明的名称一样-->
<aop:after-returning method="afterReturn" pointcut-ref="pointcut" returning="returnVal" />
<!-- 环绕通知 -->
<aop:around method="around" pointcut-ref="pointcut" />
<!--异常通知 throwing="throwable" 指定异常通知错误信息变量,必须与类中声明的名称一样-->
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="throwable"/>
<!--
method : 通知的方法(最终通知)
pointcut-ref : 通知应用到的切点方法
-->
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
AOP总结
AOP是面向切面编程思想,强调不影响正常代码的情况下切入通用逻辑,核心实现原理是动态代理,最大好处是减少代码量与解耦合
可以想象AOP的功能巨大
在各大框架中也常常使用AOP,例如mybatis的插件,spring的事务控制,springmvc的拦截器等
在实际开发中常常使用到AOP