梦入琼楼寒有月,行过石树冻无烟

Java Spring AOP

Spring AOP(面向切面,Asoect-Oriented Programming)

Spring AOP(面向切面,Asoect-Oriented Programming)即面向切面变成,依赖并与OO(面向对象,Object Oriented)即面向对象编程相辅相成,这主要体现在当面向切面过程时,先对业务逻辑进行抽象,而这个业务逻辑抽象是由面向对象(OO,Object Oriented)。在面向切面编程之中,最基本的单元是切面(Aspect):

Asoect-Oriented Programming 面向切面术语

ID DA FA
切面 (Aspect) 至封装横切到系统功能(事物处理)的类; 1
连接点 (Joinpoint) 指程序运行中的一些时间点,如方法的异常或抛出 2
切入点 (Pointcut) 指需要处理的连接点 3
通知 (增强处理) 由切面添加到特定的连接点(满足切入点规则)的一段代码 4
引入 (Introduction) 允许在现有的实现类中添加自定义的方法以及属性 5
目标对象 (Target Object) 指所有被通知的对象 6
代理 (Proxy) 通知应用到目标对象之后,被动态创建的对象 7
织入 (Weaving) 将切面代码插入到目标对象上,从而生成代理对象的不同 8

通俗点的来讲就是:

ID DA FA
切面 就是将所有共有或集成的功能实现,如我们可以将一块一块的组建合并为一个切面 1
通知 也就是且面的具体实现,以指定的切入点作为规则来寻找或添加的一段代码 2
连接点 也就是说程序运行中能够插入切面的连接地点 3
切入点 即用于定义通知应该切入到那些的链接点上(不同的通知用于需要切入不同的连接点上) 4
目标对象 也就是书所有被通知的对象,都是目标对象 5
代理对象 当通知到目标对象之后,就会被变成一个动态的对象 代理对象的功能等于目标对象的核心业务逻辑功能+共有对象功能是程序运行中的产物 6
织入 将切面代码插入到目标的对象上,生成代理对象的不同 7

切面(Aspect)

通过上图可以看出,通过切面Aspect分别在,在业务类1和业务2中可以看出,分别加入了日志记录、性能统计、安全控制、事物处理等操作。也就是说在一个类中,加入了日志记录、性能统计、安全控制、事物处理等功能或构造方法。

切入点(Pointcut)

切入点(Pointcut)是指需要处理的连接点,在Spring AOP面向切面编程中,所有的方法执行都是连接点,而切入点是一个描述信息,修饰的是链接点,可通过切入点来链接需要被处理,而切面、连接点、切入点的关系如下;
而连接点即程序运行中的调用或异常抛出,但切入点主要处理需要处理的连接点(在Spring 面向切面中,所有方法都是连接点),而切面指的就是事物处理的类。

AspectJ 基于注解开发

AspectJ是一个面向切面AOP的一个框架,扩展了Java语言和定义了AOP语法,基于注解的开发AsoectJ会比和XML配置开发Aspect便捷许多没而Spring中通知的目标方法的连接点位置主要可以分为以下六种类型:

ID DA FA
环绕通知 在目标方法执行前和执行后的实施增强 1
前置通知 在目标方法执行前实施的增强 2
后置返回通知 在目标方法成够执行后实施增强 3
最终通知即后置通知 目标方法执行后进行增强,与后置返回通知不同的不管发生异常,都要执行该通知 4
异常通知 在方法抛出异常后实施增加 5
引入通知 在目标类中添加一些新的方法和属性,用于修改目标类 6

AspectJ 注解

ID DA FA
@Aspect 声明一个切面 1
@Pointcut 用于定义切入点,使用需要定义一个切入点的Viod方法,且方法体为空的普通方法 2
@Before 定义前置通知,通常为其指定value属性值 3
@AfterReturning 定义后置返回通知,使用时为其指定value属性值 4
@Around 用于定义一个环绕通知,在使用是也通常为其制定为value属性值 5
@AfterThrowing 定义一个异常通知,使用是通常为其指定value属性值 6
@After 用于定义后置即最终通知,使用时通常使用value属性值 7

前提条件

在开发项目之前,需要使用六个Spring核心的依赖包,分别为aop、aspects、beans、context、core、expression,之后还需要commons-logging,下载地址为:“https://downloads.apache.org//commons/logging/binaries/commons-logging-1.2-bin.zip”,然后还需要一个外部第三方依赖包为aspectjweaver.jar,下载地址为:“https://repo1.maven.org/maven2/org/aspectj/aspectjweaver/1.9.2/aspectjweaver-1.9.2.jar”

创建实现类的接口 aspectj.dao

TestDao.java
1
2
3
4
5
6
7
package aspectj.dao;

public interface TestDao {
public void save();
public void modify();
public void delete();
}

TestDaoImpl.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
25
26
27
package aspectj.dao;

import org.springframework.stereotype.Repository;

@Repository("testDao")
public class TestDaoImpl implements TestDao {
// @Override 用于方法重写
@Override
public void save() {
// TODO Auto-generated method stub
System.out.println("save - TestDaoImpl");
}

@Override
public void modify() {
// TODO Auto-generated method stub
System.out.println("modify - TestDaoImpl");

}

@Override
public void delete() {
// TODO Auto-generated method stub
System.out.println("delete - TestDaoImpl");
}

}

创建切面类 aspectj.annotaition

MyAspect.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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package aspectj.annotaition;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect // 声明切面
@Component // 让此且面成为Spring容器管理的Bean
public class MyAspect {
/*
* Pointcut 定义了切入点
* 而"execution(* aspectj.dao.*.*(..))"定义了切入点表达式;
* 而切入点表达式的意思大概是匹配 “aspectj.dao“包中任意类的方法执行;
* 而execution()是表达式的主体;
* ”*“表示所有返回类型,而使用”*“可代表所有类型;
* 其中"aspectj.dao"表示的是需要匹配的包名,第二个"*"是类型,代表所有匹配的类
* 第三个”*“表示方法名,使用他表示所有方法;
* "(...)"是方法参数,其中”...“表示任意参数
*/
@Pointcut("execution(* aspectj.dao.*.*(..))")
private void myPointCut() {

}

@Before("myPointCut()")
public void before(JoinPoint joinpoint) {
System.out.println("前置通知");
System.out.println("目标类对象:" + joinpoint.getTarget()
+ "被增强处理的方法:" + joinpoint.getSignature().getName());
}

// 后置返回通知
@AfterReturning("myPointCut()")
public void afterReturing(JoinPoint joinpoint) {
System.out.println("后置返回通知:" + "模拟删除临时文件");
System.out.println("被增强处理的方法" + joinpoint.getSignature().getName());
}

// 环绕通知
@Around("myPointCut()")
public Object around(ProceedingJoinPoint procedingjoinpoint) throws Throwable {
// run
System.out.println("环绕开始,执行目标方法前模拟开启事物");
Object object = procedingjoinpoint.proceed();

// end
System.out.println("环绕结束,执行目标方法后模拟关闭事物");
return object;
}

// 异常通知
@AfterThrowing(value="myPointCut()",throwing="e")
public void except(Throwable e) {
System.out.println("异常通知" + "程序执行异常" + e.getMessage());
}

// 最终通知
@After("myPointCut()")
public void after() {
System.out.println("最终通知,模拟释放");
}
}

配置类 aspectj.config

AOPTest.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package aspecti.config;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import aspectj.dao.TestDao;

public class AOPTest {
public static void main(String args[]) {
// 初始化AnnotationConfigApplicationContext
AnnotationConfigApplicationContext application = new AnnotationConfigApplicationContext(AspectjAOPConfig.class);

// 从容器中获取增强后的目标对象
TestDao testDaoAdvice = application.getBean(TestDao.class);
// 执行方法
testDaoAdvice.save();
System.out.println("-----------");
testDaoAdvice.modify();
System.out.println("-----------");
testDaoAdvice.delete();
// 关闭
application.close();
}
}
AspectjAOPconfig.java
1
2
3
4
5
6
7
8
9
10
11
12
package aspecti.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration // 声明一个配置类
@ComponentScan("aspectj") // 自动扫描指定包下的注解
@EnableAspectJAutoProxy // 开启Spring对Aspectj的支持
public class AspectjAOPConfig {

}
⬅️ Go back