Spring-aop

AOP

AOP为Aspect Oriented Programming的缩写,意思是面向切面编程,是通过预编译和运行期动态代理实现程序功能的统一维护的一种技术。

Spring框架两大重点,AOP和IOC(Inversion Of Control)控制反转

AOP是OOP的延续,是软件开发中的一个热点, 也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。

利用AOP可以对业务逻辑的各个部分进行解耦合,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

作用及优势

  • 作用:在程序运行期间,在不修改源码的情况下进行功能增强
  • 优势:减少代码重复性,提高开发效率,并且便于维护

底层原理

实际上,AOP的底层是通过Spring提供的的动态代理技术实现的。

在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。

常用代理技术

  • JDK代理:基于接口的动态代理技术

Adivce.java

1
2
3
4
5
6
7
8
public class Advice {
public void before(){
System.out.println("前置增强");
}
public void after(){
System.out.println("后置增强");
}
}

TargetInterface.java

1
2
3
public interface TargetInterface {
void save();
}

Target.java

1
2
3
4
5
6
public class Target implements TargetInterface {
@Override
public void save(){
System.out.println("saving running......");
}
}

ProxyTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ProxyTest {
public static void main(String[] args) {
final Target = new Target();
final Adivice = new Advice();

TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandle(){
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable {
advice.before();
Object invoke = method.invoke(target,args);
advice.after();
return invoke;
}
}
);
proxy.save();
}
}
  • cglib代理:基于父类的动态代理技术

ProxyTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ProxyTest {
public static void main(String[] args) {
Target target = new Target();
Enhance enhance = new Enhance();

//创建增强其
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(Target.class);
//设置回调
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
enhance.before();
Object invoke = method.invoke(target, args);
enhance.after();
return invoke;
}
});
//创建代理对象
Target proxy = (Target) enhancer.create();
proxy.save();
}
}

AOP相关术语

Spring的AOP实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强。

  • Target (目标对象) :代理的目标对象

  • Proxy (代理) :一个类被AOP织入增强后,就产生一 个结果代理类

  • Joinpoint (连接点) :所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点

  • Pointcut (切入点) :所谓切入点是指我们要对哪些Joinpoint进行拦截的定义

  • Advice (通知/增強) :所谓通知是指拦截到Joinpoint之后所要做的事情就是通知

  • Aspect (切面) :是切入点和通知(引介)的结合

  • Weaving (织入) :是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而Aspect采用编译期织入和类装载期织入

开发明确事项

需要编写的内容

  • 编写核心业务代码(目标类的目标方法)

  • 编写切面类,切面类中有通知(增强功能方法)

  • 在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合

AOP技术实现的内容

Spring框架监控切入点方法的执行。

一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。

基于XML的AOP开发

步骤

  1. 导入AOP坐标
1
2
3
4
5
6
7
8
9
10
11
12
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.8.M1</version>
</dependency>
</dependencies>
  1. 创建目标类和目标接口

  2. 创建切面类(内部有增强方法)

  3. 将目标类和切面类的对象的创建权交给Spring

  4. 在applicationContext.xml中配置中织入关系

  5. 测试

切点表达式

1
execution([修饰符]返回值类型 包名.类名.方法名(参数列表))
  • 访问修饰符可以省略

  • 返回值类型、 包名、类名、方法名可以使用星号*代表任意

  • 包名与类名之间一 个点.代表当前包下的类,两个点.. 表示当前包及其子包下的类

  • 参数列表可以使用两个点 .. 表示任意个数,任意类型的参数列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//代表切口是com.lizhi.aop.Target类的返回void的method的无参数方法
execution (public void com.itheima.aop.Target.method() )

//代表切口是com.lizhi.aop.Target类的返回void的所有方法,参数个数为任意
execution (void com.itheima.aop.Target.*(.. ))

//代表切口是com.lizhi.aop包下的所有类的所有方法,参数个数为任意
execution(* com.lizhi.aop.*.*(..))

//代表切口是com.lizhi.aop包及其子包下的所有类所有方法,参数个数为任意
execution(* com.lizhi.aop..*.* (..) )

//代表切口是项目下的所有包的所有方法,参数为任意参数
execution(* *..*.*(..) )

通知类型

名称 通知类型 说明
前置通知 aop:before 指定增强的方法在切入点之前执行
后置通知 aop:after-returning 指定增强的方法在切入点之后执行
环绕通知 aop:around 指定增强的方法在切入点之前和之后都会执行
异常抛出通知 aop:throwing 指定增强的方法在出现切点异常时执行
最终通知 aop:after 无论增强方式执行是否有异常都会执行

around

1
2
3
4
5
6
7
//ProceedingJoinPoint指的是正在执行的连接点==切点
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前增强。。。。。");
Object obj = pjp.proceed();//切点方法
System.out.println("环绕后增强。。。。。");
return obj;
}

切点表达式的抽取

1
2
3
4
5
6
<apo:config>
<aop:aspect>
<apo:pointcut id="myPointcut" expression="excution( * com.lizhi.apo.*.*(..))"></apo:pointcut>
<apo:before method="before" pointcut-ref="myPointcut"></apo:before>
</aop:aspect>
</apo:config>

基于注解的AOP开发

  1. 创建目标接口和目标类(内部有切点)
  2. 创建切面类(内部有增强方法)
  3. 将目标类和切面类的对象创建权交给spring
  4. 在切面类中使用注解配置织入关系
  5. 在配置文件中开启组件扫描和AOP的自动代理
  6. 测试
给作者买杯咖啡吧~~~