今天有同学问到如何对 ActiveRecord 的组件添加拦截器:https://jfinal.com/feedback/7218
这个问题挺有意义,因为 jfinal 的拦截器平常看似都是针对 Controller、Service,让人误以为不支持这两个者组件以外的拦截。要是这么来看的话,那是对 jfinal 的灵活、强大缺乏深入认知。
其实 jfinal 的拦截器是可以施加到几乎任何地方的,下面就以 Model 为例,将 Interceptor 用在 Model 上。
1、创建一个拦截器
public class Test implements Interceptor { @Override public void intercept(Invocation inv) { // 通过 inv.getMethodName() 得知当前的拦截的是哪个方法 System.out.println(inv.getMethodName()); // 通过 inv.getArgs() 可以获取当前被拦截方法的实参值 Object[] args = inv.getArgs(); for (Object arg : args) { System.out.println(arg); } // 通过 inv.setArg(...) 可以改变当前被拦截方法的实参值 inv.setArg(0, null); // 调用被拦截的方法 inv.invoke(); // 通过 inv.getReturnValue() 获取被拦截方法的返回值 Object ret = inv.getReturnValue(); } }
以上拦截器 Test 可以实现很多强大的功能:识别目标方法、获取实参、改变实参、调用目标方法、获取目标方法调用后的返回值等等。上述代码的注释中给出了部分方法的详细说明。
此外通过 Invocation 对象还可以做很多事情,例如通过 inv.getMethod().getAnnotations() 可以获取被拦截方法上的 java 注解,从而可以实现很多通过注解扩展出来的功能。
提问的 @netwild 同学,希望对 update、query 方法的 sql 进行预处理,只需要通过 if ( inv.getMethodName().equals("update") 这种方法判断被拦截方法,然后通过 inv.getArgs() 获取形参,再通过 inv.setArg(...) 进行干预就可以了。
2、配置拦截器
假定需要拦截的 Model 是 Article
@Before(Test.class) public class Article extends BaseArticle { }
注意上面的 Article 继承了 BaseArticle,是假定了你的 model 生成了 base model,如果没有生成的话,直接继承 Model 也可以。
3、在 serivce 中使用
jfinal 最佳实践是在 service 中使用 dao
public class ArticleService { @Inject Article dao; public List<Article> findAll() { return dao.find("select * from article"); } }
通过上面的方法为业务层注入 Article dao 对象,如果没有开启注入的话,可以使用 Aop 工具类。
public class ArticleService { Article dao = Aop.get(Article.class).dao(); public List<Article> findAll() { return dao.find("select * from article"); } }
3、配置使用 cglib 代理实现
对于 Model 的代理需要使用 cglib,配置如下:
public void configConstant(Constants me) { me.setToCglibProxyFactory(); }
需要引入 cglib 依赖
<dependency> <groupId>cglib</groupId> <artifactId>cglib-nodep</artifactId> <version>3.2.5</version> </dependency>
同理对于 Db.java 内的方法也可以像上面这样处理,大致流程一样,只是扩展方式稍有不同:
1、扩展 DbPro
@Before(Test.class) public class MyDbPro extends DbPro { public MyDbPro(String configName) { super(configName); } }
以上的重点为:扩展类 MyDbPro 上添加了 @Before(Test.class) 拦截器。
2、切换为自己的 MyDbPro 实现
public void configPlugin(Plugins me) { ActiveRecordPlugin arp = new ActiveRecordPlugin(...); // 将自己扩展的 MyDbPro 切换掉默认实现 arp.setDbProFactory((configName) -> new MyDbPro(configName) ); me.add(arp); }
以上代码是在初始化 ActiveRecordPlugin 的地方通过 arp.setDbProFactory(...) 方法来接管 Db.java 内的所有方法。
jfinal 有无数隐藏功能在文档中是不可能一一例举的,否则文档内容会膨胀到不可想象的地步。
要掌握并运用这些隐藏功能,只需要熟悉 jfinal 源代码,在碰到一个新功能需求的时候,解决方案自然就显现出来了。