Solon v3.0.3

八、切面与环绕拦截(AOP)

</> markdown

想要环绕拦截一个 Solon Bean 的函数(AOP)。需要三个前置条件:

  1. 通过注解做为“切点”,进行拦截(不能无缘无故给拦了吧?费性能)
  2. Bean 的 method 是被代理的(比如 @Controller、@Remoting、@Component 注解的类)
  3. 在 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插件开发,可参考别的章节内容。