八、切面与环绕拦截(AOP)
想要环绕拦截一个 Solon Bean 的函数(AOP)。需要三个前置条件:
- 通过注解做为“切点”,进行拦截(不能无缘无故给拦了吧?费性能)
- Bean 的 method 是被代理的(比如 @Controller、@Remoting、@Component 注解的类)
- 在 Bean 被扫描之前,完成拦截器的注册
被代理的 method,最后会包装成 MethodWrap。其中 invokeByAspect 接口,提供了环绕拦截支持:
1、定义切点和注册拦截器
Solon 的切点,通过注解实现,得先定义一个。例如:@Tran
//
// @Target 是决定可以注在什么上面的!!!
//
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Tran { ... }
定义拦截器
public class TranInterceptor implements Interceptor {
@Override
public Object doIntercept(Invocation inv) throws Throwable {
AtomicReference val0 = new AtomicReference();
Tran anno = inv.method().getAnnotation(Tran.class);
TranUtils.execute(anno, () -> {
val0.set(inv.invoke());
});
return val0.get();
}
}
手动注册拦截器:(必须要在扫描之前,完成注册)
//比如在应用启动时
public class App{
public static void main(String[] args){
Solon.start(App.class, args, app->{
app.context().beanInterceptorAdd(Tran.class, new TranInterceptor());
});
}
}
//比如在插件启动时
public class PluginImpl impl Plugin{
public void start(AppContext context){
context.beanInterceptorAdd(Tran.class, new TranInterceptor());
}
}
//或者别的时机点(可以看一下应用生命周期)
也可以"免"手动注册拦截器:通过 @Around 注解继承
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Around(TranInterceptor.class)
public @interface Tran { ... }
现在切点定义好了,可以到处“埋”点了...
2、应用:把切点“埋”到需要的地方
@Component
public class DemoService{
@Tran //注到函数上(仅对当前函数有效)
public void addUser(UserModel user){
//...
}
}
@Tran //注到类上(对所有函数有效)
@Component
public class DemoService{
public void addUser(UserModel user){
//...
}
}
就这样完成一个AOP的开发案例。
3、通过插件及插件配置,变成一个复用的东西
这是刚才定义注解和拦截器:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Tran { ... }
public class TranInterceptor implements Interceptor { ... }
开发插件:
public class XPluginImpl impl Plugin{
public void start(AppContext context){
context.beanInterceptorAdd(Tran.class, new TranInterceptor());
}
}
配置插件:
solon.plugin=xxx.xxx.log.XPluginImpl
一个可复用的插件开发完成了。关于Solon插件开发,可参考别的章节内容。