Skip to content

Commit 0c5fac0

Browse files
author
litongjava
committed
add aop
1 parent 5d011cd commit 0c5fac0

File tree

5 files changed

+640
-72
lines changed

5 files changed

+640
-72
lines changed

docs/.vuepress/config/sidebar-zh.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
{
7272
"title": "07_aop",
7373
"collapsable": false,
74-
"children": ["07_aop/01.md"]
74+
"children": ["07_aop/01.md", "07_aop/02.md", "07_aop/03.md", "07_aop/03.md", "07_aop/04.md"]
7575
},
7676
{
7777
"title": "08_token",

docs/zh/07_aop/01.md

+202-71
Original file line numberDiff line numberDiff line change
@@ -2,65 +2,127 @@
22

33
tio-boot 已经内置了 jfinal-aop 依赖
44
jfinal-aop 源码:https://github.com/litongjava/jfinal-aop
5-
jfinal-aop 文档:https://litongjava.github.io/jfinal-doc/zh/4%20AOP/4.1%20%E6%A6%82%E8%BF%B0.html
65

7-
### 7.1.Aop.get
6+
## 概述
87

9-
```java
10-
import java.util.HashMap;
11-
import java.util.Map;
8+
传统 AOP 实现需要引入大量繁杂而多余的概念,例如:Aspect、Advice、Joinpoint、Poincut、Introduction、Weaving、Around 等等,并且需要引入 IOC 容器并配合大量的 XML 或者 annotation 来进行组件装配。
129

13-
public class IndexService {
10+
传统 AOP 不但学习成本极高,开发效率极低,开发体验极差,而且还影响系统性能,尤其是在开发阶段造成项目启动缓慢,极大影响开发效率。
1411

15-
public Map<String, String> index() {
16-
Map<String, String> ret = new HashMap<>();
17-
ret.put("data", "Hello 4");
18-
return ret;
19-
}
20-
}
12+
JFinal 采用极速化的 AOP 设计,专注 AOP 最核心的目标,将概念减少到极致,仅有三个概念:Interceptor、Before、Clear,并且无需引入 IOC 也无需使用啰嗦的 XML。
2113

22-
```
14+
## Aop 相关注解
2315

24-
```
16+
1. **@AComponentScan**: 用于指定 在初始化时要扫描的包。这个注解会查找标记有 `@AComponent``@AService``@ARepository``@AController` 等注解的类,并注册为 Aop 容器中的 Bean。
2517

26-
import java.util.Map;
18+
2. **@AConfiguration**: 表示该类是一个配置类,该类可以包含有 `@ABean` 注解的方法。jfinal 容器会服务器启动后,动时自动调用这些方法.
2719

28-
import com.litongjava.jfinal.aop.Aop;
29-
import com.litongjava.tio.http.server.annotation.RequestPath;
30-
import com.litongjava.tio.web.hello.service.IndexService;
31-
@AController
32-
@RequestPath("/")
33-
public class IndexController {
34-
@RequestPath()
35-
public Map<String,String> index() {
36-
return Aop.get(IndexService.class).index();
37-
}
38-
}
20+
3. **@BeforeStartConfiguration**:表示该类是一个配置类,该类可以包含有 `@ABean` 注解的方法。jfinal 容器会服务器启动前调用这些方法
21+
22+
4. **@ABean**: 标记在方法上,该方法返回一个 Bean 对象,然后这个对象被 Aop 容器管理。通常在 `@AConfiguration` 注解的类中使用。
23+
24+
5. **@Initialization**: 标记在方法上,该方法返回没有返回值,也不会添加到 bean 容器中,但是会在 Aop 容器初始化时执行该方法
25+
26+
6. **@AComponent**: 基本的注解,标记一个类为组件。当使用基于注解的配置和类路径扫描时,这个注解的类会自动注册为 Spring Bean。
27+
28+
7. **@AController**: 用于标记控制器组件,通常用在 MVC 模式的 Web 应用程序中。这个注解表明类的实例是一个控制器。
29+
30+
8. **@AService**: 用于标记服务层组件,通常用于业务逻辑层。这个注解表明类的实例是一个“服务”,它可以包含业务逻辑,调用数据访问层等。
31+
32+
9. **@ARepository**: 用于标记数据访问组件,即 DAO(Data Access Object)组件。这个注解表明类的实例是一个“仓库”,用于封装数据库访问和异常处理。
33+
34+
10. **@AHttpApi**: 用于标记 Http 组件,例如用于 HttpClient 请求。
35+
36+
11. **@Inject**: `@AAutowired` 类似,但它是来自 Java CDI(Contexts and Dependency Injection)的标准注解。用于依赖注入。
37+
38+
12. **@AAutowired**: 用于自动注入依赖。它可以应用于字段、构造器、方法等,Spring 容器会在创建 Bean 时自动注入相应的依赖。
39+
40+
13. **@Clear**: 用于清除 Aop 拦截器
41+
42+
14. **@Before**: 这个注解与 AOP(面向切面编程)有关,用于标记一个方法在某操作之前执行。
43+
44+
15. **@AImport**: 用于导入其他配置类。在一个配置类上使用 `@AImport`,可以将其他配置类中的 Bean 导入当前的配置类中。
45+
46+
## Interceptor
47+
48+
### 1、基本用法
49+
50+
Interceptor 可以对方法进行拦截,并提供机会在方法的前后添加切面代码,实现 AOP 的核心目标。Interceptor 接口仅仅定义了一个方法 public void intercept(Invocation inv)。以下是简单示例:
3951

4052
```
53+
public class DemoInterceptor implements Interceptor {
54+
public void intercept(Invocation inv) {
55+
System.out.println("Before method invoking");
56+
inv.invoke();
57+
System.out.println("After method invoking");
58+
}
59+
}
60+
```
61+
62+
以上代码中的 DemoInterceptor 将拦截目标方法,并且在目标方法调用前后向控制台输出文本。inv.invoke() 这一行代码是对目标方法的调用,在这一行代码的前后插入切面代码可以很方便地实现 AOP。
4163

42-
执行后返回的数据是
64+
注意:必须调用 inv.invoke() 方法,才能将当前调用传递到后续的 Interceptor 与 Action。
65+
66+
常见错误:目前为止仍有很多同学忘了调用 inv.invoke() 方法,造成 不会被执行。在此再次强调一次,一定要调用一次 inv.invoke().
67+
68+
Invocation 作为 Interceptor 接口 intercept 方法中的唯一参数,提供了很多便利的方法在拦截器中使用。以下为 Invocation 中的方法:
69+
70+
| 方法 | 描述 |
71+
| --------------------------- | ---------------------------------------- |
72+
| void invoke() | 传递本次调用,调用剩下的拦截器与目标方法 |
73+
| &lt;T&gt; getTarget() | 获取被拦截方法所属的对象 |
74+
| Method getMethod() | 获取被拦截方法的 Method 对象 |
75+
| String getMethodName() | 获取被拦截方法的方法名 |
76+
| Object[] agetArgs() | 获取被拦截方法的所有参数值 |
77+
| setArg(int, Object) | 获取被拦截方法指定序号的参数值 |
78+
| &lt;T&gt; getReturnValue() | 获取被拦截方法的返回值 |
79+
| void setArg(int) | 设置被拦截方法指定序号的参数值 |
80+
| void setReturnValue(Object) | 设置被拦截方法的返回值 |
81+
82+
### 2、 全局共享,注意线程安全问题
83+
84+
Interceptor 是全局共享的,所以如果要在其中使用属性需要保证其属性是线程安全的,如下代码将是错误的:
4385

4486
```
87+
public class MyInterceptor implements Interceptor {
88+
89+
private int value = 123;
4590
46-
{"data":"Hello 4"}
91+
public void intercept(Invocation inv) {
92+
// 多线程将会并发访问 value 值,造成错乱
93+
value++;
4794
95+
inv.invoke();
96+
}
97+
}
4898
```
4999

50-
这两个类构成了一个简单的 MVC (Model-View-Controller) 结构。
100+
如上代码所示,其中的 value 属性将会被多线程访问到,从而引发线程安全问题。
101+
102+
## Before
51103

52-
1. `IndexService`
104+
Before 注解用来对拦截器进行配置,该注解可配置 Class、Method 级别的拦截器,以下是代码示例
53105

54-
- 这是一个服务类,包含一个 `index` 方法,用于创建并返回一个 `Map<String, String>` 类型的数据。在这个方法中,它向 `Map` 中添加了一个键值对 `"data": "Hello 4"`
106+
```
107+
// 配置一个Class级别的拦截器,她将拦截本类中的所有方法
108+
@Before(AaaInter.class)
109+
@RequestPath("/before")
110+
public class BlogController{
111+
112+
// 配置多个Method级别的拦截器,仅拦截本方法
113+
@Before({BbbInter.class, CccInter.class})
114+
public void index() {
115+
}
55116
56-
2. `IndexController` 类:
57-
- 这是一个控制器类,标记了 `@RequestPath("/")`,表明它处理根路径(`/`)的 HTTP 请求。
58-
- 其中的 `index` 方法通过 `Aop.get(IndexService.class).index()` 调用 `IndexService``index` 方法。
117+
// 未配置Method级别拦截器,但会被Class级别拦截器AaaInter所拦截
118+
public void show() {
119+
}
120+
}
121+
```
59122

60-
`Aop.get` 方法的作用是从 AOP(面向切面编程)容器中获取 `IndexService` 类的实例。这意味着 `IndexService` 类可能被作为一个单例来管理,它的实例化与生命周期可能由 AOP 框架来控制,而非手动创建。这种做法允许 `IndexService` 拥有如依赖注入、拦截器等 AOP 功能
123+
如上代码所示,Before 可以将拦截器配置为 Class 级别与 Method 级别,前者将拦截本类中所有方法,后者仅拦截本方法。此外 Before 可以同时配置多个拦截器,只需用在大括号内用逗号将多个拦截器进行分隔即可
61124

62-
当 Aop.get 方法时,如果荣器中不存在对于的对象,Aop 容器会创建后在返回
63-
`IndexController``index` 方法被调用时,它会返回 `IndexService.index` 方法生成的 `Map`,即 `{"data": "Hello 4"}`
125+
除了 Class 与 Method 级别的拦截器以外,JFinal 还支持全局拦截器以及 Routes 拦截器,全局拦截器分为控制层全局拦截器与业务层全局拦截器,前者拦截控制 层所有 Action 方法,后者拦截业务层所有方法。
64126

65127
### 7.2.Aop 拦击器@Before
66128

@@ -109,78 +171,147 @@ public class IndexController {
109171

110172
`IndexController` 类中,`@Before(IndexInteceptor.class)` 注解被应用于 `index` 方法。这表示当调用 `index` 方法时,`IndexInteceptor` 将被触发,执行其 `intercept` 方法。这允许在 `index` 方法执行之前和之后执行额外的逻辑,例如日志记录、验证等。
111173

112-
如果使用了 Hotswap-classloader 需要在启动类中添加 SwapClassPrefix,添加之后才可以支持 切面代理类的 热加载,否则会出现异常
174+
## Clear
113175

114-
```
115-
116-
HotSwapResolver.addHotSwapClassPrefix("com.litongjava.jfinal");
176+
拦截器从上到下依次分为 Global、Routes、Class、Method 四个层次,Clear 用于清除自身所处层次以上层的拦截器。
117177

118-
```
178+
Clear 声明在 Method 层时将针对 Global、Routes、Class 进行清除。Clear 声明在 Class 层时将针对 Global、Routes 进行清除。Clear 注解携带参数时清除目标层中指定的拦截器。
119179

120-
### 7.3.Aop 相关注解
180+
Clear 用法记忆技巧:
121181

122-
1. **@AComponentScan**: 用于指定 在初始化时要扫描的包。这个注解会查找标记有 `@AComponent``@AService``@ARepository``@AController` 等注解的类,并注册为 Aop 容器中的 Bean。
182+
- 一共有 Global、Routes、Class、Method 四层拦截器
123183

124-
2. **@AConfiguration**: 表示该类是一个配置类,该类可以包含有 `@ABean` 注解的方法。jfinal 容器会服务器启动后,动时自动调用这些方法.
184+
- 清除只针对 Clear 本身所处层的向上所有层,本层与下层不清除
125185

126-
3. **@BeforeStartConfiguration**:表示该类是一个配置类,该类可以包含有 `@ABean` 注解的方法。jfinal 容器会服务器启动前调用这些方法
186+
- 不带参数时清除所有拦截器,带参时清除参数指定的拦截器
127187

128-
4. **@ABean**: 标记在方法上,该方法返回一个 Bean 对象,然后这个对象被 Aop 容器管理。通常在 `@AConfiguration` 注解的类中使用。
188+
在某些应用场景之下,需要移除 Global 或 Class 拦截器。例如某个后台管理系统,配置了一个全局的权限拦截器,但是其登录 action 就必须清除掉她,否则无法完成登录操作,以下是代码示例:
129189

130-
5. **@Initialization**: 标记在方法上,该方法返回没有返回值,也不会添加到 bean 容器中,但是会在 Aop 容器初始化时执行该方法
190+
```
191+
// login方法需要移除该权限拦截器才能正常登录
192+
@Before(AuthInterceptor.class)
193+
@RequestPath("/user")
194+
public class UserController {
195+
// AuthInterceptor 已被Clear清除掉,不会被其拦截
196+
@Clear
197+
public void login() {
198+
}
199+
200+
// 此方法将被AuthInterceptor拦截
201+
public void show() {
202+
}
203+
}
204+
```
131205

132-
6. **@AComponent**: 基本的注解,标记一个类为组件。当使用基于注解的配置和类路径扫描时,这个注解的类会自动注册为 Spring Bean。
206+
Clear 注解带有参数时,能清除指定的拦截器,以下是一个更加全面的示例:
133207

134-
7. **@AController**: 用于标记控制器组件,通常用在 MVC 模式的 Web 应用程序中。这个注解表明类的实例是一个控制器。
208+
```
209+
@Before(AAA.class)
210+
public class UserController {
211+
@Clear
212+
@Before(BBB.class)
213+
public void login() {
214+
// Global、Class级别的拦截器将被清除,但本方法上声明的BBB不受影响
215+
}
135216
136-
8. **@AService**: 用于标记服务层组件,通常用于业务逻辑层。这个注解表明类的实例是一个“服务”,它可以包含业务逻辑,调用数据访问层等。
217+
@Clear({AAA.class, CCC.class})// 清除指定的拦截器AAA与CCC
218+
@Before(CCC.class)
219+
public void show() {
220+
// 虽然Clear注解中指定清除CCC,但她无法被清除,因为清除操作只针对于本层以上的各层
221+
}
222+
}
223+
```
137224

138-
9. **@ARepository**: 用于标记数据访问组件,即 DAO(Data Access Object)组件。这个注解表明类的实例是一个“仓库”,用于封装数据库访问和异常处理。
225+
上面的清除都用在了 method 上,还可以将其用于 class 之上,例如:
139226

140-
10. **@AHttpApi**: 用于标记 Http 组件,例如用于 HttpClient 请求。
227+
```
228+
@Clear(AAA.class)
229+
public class UserController {
230+
public void index() {
231+
...
232+
}
233+
}
234+
```
141235

142-
11. **@Inject**: `@AAutowired` 类似,但它是来自 Java CDI(Contexts and Dependency Injection)的标准注解。用于依赖注入
236+
如上所示,@Clear(AAA.class) 将清除上层也就是 Global、Route 这两层中配置的 AAA.java 这个拦截器
143237

144-
12. **@AAutowired**: 用于自动注入依赖。它可以应用于字段、构造器、方法等,Spring 容器会在创建 Bean 时自动注入相应的依赖。
238+
## Inject 依赖注入
145239

146-
13. **@Clear**: 用于清除 Aop 拦截器
240+
使用 @Inject 注解可以向 属性中 中注入依赖对象
147241

148-
14. **@Before**: 这个注解与 AOP(面向切面编程)有关,用于标记一个方法在某操作之前执行。
242+
```
243+
public class AccountController {
149244
150-
15. **@AImport**: 用于导入其他配置类。在一个配置类上使用 `@AImport`,可以将其他配置类中的 Bean 导入当前的配置类中。
245+
@Inject
246+
AccountService service; // 此处会注入依赖对象
151247
152-
### 7.4.Aop 其他方法
248+
public void index() {
249+
service.justDoIt(); // 调用被注入对象的方法
250+
}
251+
}
252+
```
153253

154-
获取 Aop 容器中的所有 bean
254+
@Inject 还可以用于拦截器的属性注入,例如:
155255

156256
```
257+
public class MyInterceptor implements Interceptor {
157258
158-
String[] beans = Aop.beans();
259+
@Inject
260+
Service service; // 此处会注入依赖对象
159261
262+
public void intercept(Invocation inv) {
263+
service.justDoIt(); // 调用被注入对象的方法
264+
inv.invoke();
265+
}
266+
}
160267
```
161268

162-
添加一个类到 Bean 容器中
269+
特别注意:使用 Inject 注入的前提是使用 @Inject 注解的类的对象的创建是由 jfinal aop 接管的,这样 jfinal aop 才有机会对其进行注入。例如 Controller、Interceptor、的创建是 jfinal aop 接管的,所以这三种组件中可以使用 @Inject 注入。
270+
271+
此外:注入动作可以向下传递。例如在 Controller 中使用 @Inject 注入一个 AaaService,那么在 AaaService 中可以使用 @Inject 注入一个 BbbService,如此可以一直向下传递进行注入.
272+
273+
如果需要创建的对象并不是 jfinal aop 接管的,那么可以使用 Aop.get(...) 方法进行依赖对象的创建以及注入,例如:
163274

164275
```
276+
public class MyKit {
165277
166-
AopManager.me().addSingletonObject(bean);
278+
static Service service = Aop.get(Service.class);
167279
280+
public void doIt() {
281+
service.justDoIt();
282+
}
283+
}
168284
```
169285

170-
添加一个实现带有接口的实现类到 Bean 容器中
286+
由于 MyKit 的创建并不是 jfinal aop 接管的,所以不能使用 @Inject 进行依赖注入。 而 Controller、Interceptor 的创建和组装是由 jfinal aop 接管的,所以可以使用 @Inject 注入依赖。
171287

288+
有了 Aop.get(...) 就可以在任何地方创建对象并且对创建的对象进行注入。此外还可以使用 Aop.inject(...) 仅仅向对象注入依赖但不创建对象。
289+
290+
@Inject 注解还支持指定注入的实现类,例如下面的代码,将为 Service 注入 MyService 对象:
291+
292+
```
293+
@Inject(MyService.class)
294+
Service service;
172295
```
173296

174-
AopManager.me().addMapping(SharedPreferences.class, sharedPreferences.getClass());
175-
AopManager.me().addSingletonObject(sharedPreferences);
297+
### 添加映射来指定被注入的类型
176298

299+
@Inject(...) 注解不指定被注入的类型时,还可以通过 AopManager.me().addMapping(...) 事先添加映射来指定被注入的类型,例如:
300+
301+
```
302+
AopManager.me().addMapping(Service.class, MyService.class);
177303
```
178304

179-
1. **AopManager.me().addMapping(SharedPreferences.class, sharedPreferences.getClass());**
305+
通过上面的映射,下面的代码将会为 Service 注入 MyService
180306

181-
- `AopManager.me()`:这通常获取 `AopManager` 的单例实例。`AopManager` 可能是一个管理 AOP 行为和配置的类。
182-
- `addMapping(SharedPreferences.class, sharedPreferences.getClass())`:这个方法调用可能是在告诉 AOP 框架,当遇到 `SharedPreferences` 类型的依赖注入请求时,应该实例化 `sharedPreferences.getClass()` 返回的类。这里 `SharedPreferences` 是一个接口或类,而 `sharedPreferences.getClass()` 是具体的实现类。
307+
```
308+
public class IndexController {
183309
184-
2. **AopManager.me().addSingletonObject(sharedPreferences);**
185-
- `addSingletonObject(sharedPreferences)`:这个方法调用可能是在告诉 AOP 框架,`sharedPreferences` 对象应该被视为单例,并且在 AOP 框架的上下文中管理。这意味着当有依赖注入请求 `SharedPreferences` 类型的实例时,框架会提供这个已经创建的 `sharedPreferences` 实例。
310+
@Inject
311+
Service service;
186312
313+
public void index() {
314+
service.justDoIt();
315+
}
316+
}
317+
```

0 commit comments

Comments
 (0)