AOP
AOP(Aspect-Oriented Programming), 即 面向切面编程, 它与 OOP( Object-Oriented Programming, 面向对象编程) 相辅相成, 提供了与 OOP 不同的抽象软件结构的视角.
在 AOP 中, 我们以类(class)作为我们的基本单元, 而 AOP 中的基本单元是 Aspect(切面)
彻底征服 Spring AOP 之 理论篇 - SegmentFault 思否 (opens new window)
# AOP实现原理
是使用动态代理来实现的,当目标类为接口时用JDKProxy,否则用Cglib,下面来介绍一下这两种方法
# 使用JDK动态代理
我们的目标类端口和代理类实现如下
public interface WorkInter {
void workInDay(double money);
void workInNight(double money);
}
public class Worker implements WorkInter{
@Override
public void workInDay(double money) {
System.out.println("workInDay");
}
@Override
public void workInNight(double money) {
System.out.println("workInNight");
}
}
public class JdkProxy {
@Test
public void run() {
WorkInter proxy = (WorkInter) Proxy.newProxyInstance(Worker.class.getClassLoader(), Worker.class.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("中介费"+((double)args[0])/2);
Object invoke = method.invoke(Worker.class.newInstance(), args);
System.out.println("下班");
return invoke;
}
});
proxy.workInDay(500);
proxy.workInNight(1000);
}
}
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
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
我们这里使用invoke来调用代理类,执行结果如下图所示
# 使用CGLIB来实现
实现步骤如下:
- 获得代理类的核心类Enhancer对象
- 设置父类(目标类),setSuperclass()方法,底层是创建目标类的子类
- 设置回调函数enhancer.setCallback(new MethodInterceptor())
- 创建代理对象 create()方法
public class Worker{
public void workInDay(double money) {
System.out.println("workInDay");
}
public void workInNight(double money) {
System.out.println("workInNight");
}
}
public class WorkerProxy {
@Test
public void run() {
Enhancer enhancer=new Enhancer();
//2.设置父类(目标类),setSuperclass()方法,底层是创建目标类的子类
enhancer.setSuperclass(Worker.class);
//3.设置回调函数enhancer.setCallback(new MethodInterceptor())
enhancer.setCallback(new MethodInterceptor() {
*//**
* Object object:代理对象
* Method method:目标类的方法
* Object[] args:方法的形参
* MethodProxy methodProxy:方法的代理对象
*
*//*
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// TODO Auto-generated method stub
//开启事务
//前置通知
System.out.println("代理收取费用"+ (double)args[0]/2);
//执行目标方法,需要目标类实例
Object invoke = method.invoke(Worker.class.newInstance(), (double)args[0]/2);
//后置通知
System.out.println("go home~~~~~");
return invoke;
}
});
//4.创建代理对象 create()方法
Worker workerPeoxy = (Worker) enhancer.create();
workerPeoxy.workInDay(500);
workerPeoxy.workInNight(1000);
}
}
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
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
# 基本知识点
切面:拦截器类,其中会定义切点以及通知
织入:织入是把切面应用到目标对象并创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。
切点:具体拦截的某个业务点。
通知:切面当中的方法,声明通知方法在目标业务层的执行位置,通知类型如下:
- 前置通知:@Before 在目标业务方法执行之前执行
- 后置通知:@After 在目标业务方法执行之后执行
- 返回通知:@AfterReturning 在目标业务方法返回结果之后执行
- 异常通知:@AfterThrowing 在目标业务方法抛出异常之后
- 环绕通知:@Around 功能强大,可代替以上四种通知,还可以控制目标业务方法是否执行以及何时执行
# 两种实现方式
- jdk实现,缺点是只能对目标接口实现动态代理
- cglib实现,可以对类进行动态代理,但是类不可以是final。而且JDK动态代理不依赖其他包,Cglib需要导入ASM包,对于简单的有接口的代理使用JDK动态代理可以少导入一个包。
# 使用
先使用Aspect来标识为切面类
package com.jpeony.spring;
import org.aspectj.lang.annotation.After;
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
@Aspect
public class PersonAspect {
// /**
// * 开会之前--找个位置坐下
// */
// @Before("execution(* com.jpeony.spring.ConferenceServiceImpl.conference(..))")
// public void takeSeats() {
// System.out.println("找位置坐");
// }
//
// /**
// * 开会之前--手机调成静音
// */
// @Before("execution(* com.jpeony.spring.ConferenceServiceImpl.conference(..))")
// public void silenceCellPhones() {
// System.out.println("手机调成静音");
// }
//
// /**
// * 开会之后--写会议总结报告
// */
// @After("execution(* com.jpeony.spring.ConferenceServiceImpl.conference(..))")
// public void summary() {
// System.out.println("写会议总结报告");
// }
/**
* =========================================================================
* 从上面的执行代码可以看出切点execution表达式内容都是一样,
* 我们可以通过@Pointcut进行优化。
* =========================================================================
*/
/**
* 通过注解@Pointcut定义切点,conference()只是一个标识,无所谓是什么,
* 方法中内容本身也是空的,使用该切点的地方直接通过标识conference()引用切点表达式。
*/
@Pointcut("execution(* com.jpeony.spring.ConferenceServiceImpl.conference(..))")
public void conference() {}
/**
* 开会之前--找个位置坐下
*/
@Before("conference()")
public void takeSeats() {
System.out.println("找位置坐");
}
/**
* 开会之前--手机调成静音
*/
@Before("conference()")
public void silenceCellPhones() {
System.out.println("手机调成静音");
}
/**
* 开会之后--写会议总结报告
*/
@After("conference()")
public void summary() {
System.out.println("写会议总结报告");
}
}
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
70
71
72
73
74
75
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
70
71
72
73
74
75
然后我们创建一个目标类
package com.jpeony.spring;
import org.springframework.stereotype.Component;
@Component
public class ConferenceServiceImpl implements ConferenceService {
@Override
public void conference() {
System.out.println("开会......");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
最后我们开启自动代理功能
package com.jpeony.spring;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* Jdk代理:基于接口的代理,一定是基于接口,会生成目标对象的接口的子对象。
* Cglib代理:基于类的代理,不需要基于接口,会生成目标对象的子对象。
*
* 1. 注解@EnableAspectJAutoProxy开启代理;
*
* 2. 如果属性proxyTargetClass默认为false, 表示使用jdk动态代理织入增强;
*
* 3. 如果属性proxyTargetClass设置为true,表示使用Cglib动态代理技术织入增强;
*
* 4. 如果属性proxyTargetClass设置为false,但是目标类没有声明接口,
* Spring aop还是会使用Cglib动态代理,也就是说非接口的类要生成代理都用Cglib。
*/
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ComponentScan("com.jpeony")
public class AppConfig {
}
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
参考:
【Spring基础】AOP使用注解实战_yhl_jxy的博客-CSDN博客_aop实战 (opens new window)
编辑 (opens new window)
上次更新: 2021/04/24, 10:38:20