java 添加拦截器_如何动态添加拦截器

现在有这样的需求:根据数据库中设置的一个拦截器类路径,对本类动态添加拦截器。也可以这样理解:对特定的方法(如增删改查系列方法)执行可后期配置的拦截器。此特性主要用于“开发平台”等项目。

我原先的思路/代码,如下:public class MyController extends Controller {

public void index(){

MyController mc = this;

this.setJfastObject();

try {

mc = diyObjectInterceptor();

} catch (ClassNotFoundException e) {

e.printStackTrace();

renderText("未定义元数据拦截器");

return;

} //绑定自定义元对象拦截器

mc.afterBindIntercepter();

}

private MyController diyObjectInterceptor() throws ClassNotFoundException{

//获取自定义拦截器

//String intercept=this.getIntercept();

//这里为了说明简便,直接写死拦截器路径

String intercept = "com.core.MyIntercept";

Class extends Interceptor> class1=null;

MyController mc=this;

if(intercept!=null && !intercept.equals("")){

class1=(Class extends Interceptor>) Class.forName(intercept);

mc=Enhancer.enhance(this, class1);

}

return mc;

}

private void afterBindIntercepter(){

……

}

……

}这个思路基本解决了动态绑定拦截器的目的;

但是有限制,比如,实际只有index方法添加了拦截器,index实现此功能时还需要其他方法配合。

有没有更好的方法?

花了两天的时间对詹总的方案进行了思考和实践,不知对与不对,下面写出核心代码和思路,请大家批评指正: (2017-18-19修改)

1、新建一个业务类,对需要进行全局业务拦截的方法进行定义:package com.jfast.core.controller;

import com.jfast.core.interfaces.Often4Sql;

import com.jfinal.plugin.activerecord.Db;

import com.jfinal.plugin.activerecord.Record;

/**

* 增删改查


方便业务拦截

* @author 王维

* hi.wangwei@qq.com

* 2017年8月18日 下午12:03:07

* @param 

*/

public class JfastService implements Often4Sql{

public T delete(T c) {

c.result = Db.use(c.ds).delete(c.tableName, c.pk, c.record);

return c;

}

public T deletes(T c) {

boolean res = true;//操作结果

for(Record temp : c.recordList ){

boolean tempResult;//本次循环的结果

tempResult = Db.use(c.ds).delete(c.tableName, c.pk, temp);

res = res && tempResult;

}

c.result = res;

return c;

}

//……节约篇幅,其余方法省略

}

2、正如大家第一眼看到的,参数c是JfastController的子类,这个父类怎么定义的呢:package com.jfast.core.controller;

import java.util.ArrayList;

import java.util.List;

import com.jfast.core.interfaces.Often4Sql;

import com.jfinal.core.Controller;

import com.jfinal.plugin.activerecord.Record;

import com.jfinal.plugin.activerecord.SqlPara;

/**

* 配合业务拦截的控制器

* @author 王维

* hi.wangwei@qq.com

* 2017年8月18日 下午2:41:57

*/

public class JfastController extends Controller implements Often4Sql{

public Record record = new Record(); //待操作记录

/**

* 写操作时:待操作记录集合

* 读操作时:读取结果集合

*/

public List recordList = new ArrayList();

public String tableName = "";//数据库表名

public String ds = "";//数据源

public boolean result = true;//操作结果

public String pk = "";//主键名

public SqlPara sqlPara = new SqlPara();//数据库操作语句类

public List fieldList = new ArrayList(); //字段集合

@Override

public JfastController deletes(JfastController c) {

return null;

}

@Override

public JfastController delete(JfastController c) {

return null;

}

//……继续节省篇幅,省略其他方法

}

这个类的重点是对属性的定义,因为拦截器拦截到方法后,主要采用对类属性的“修改”达到拦截目的的。子类继承后,即得到被全局业务拦截的特性。

3、上面都省略了很多方法,但细心的观众一定注意到了,我写了一个接口Often4Sql。好吧,这里我就不省略了:package com.jfast.core.interfaces;

/**

*

* @author 王维

 
 

* 常用增删改查

* 试图配合全局业务拦截器

* @param 

*/

public interface Often4Sql {

T delete(T c);

T deletes(T c);

T find(T c);

T finds(T c);

T add(T c);

T adds(T c);

T update(T c);

T updates(T c);

}

哈哈,接口就是简洁,我就希望这类简洁的东西!是不是程序员都喜欢?

4、上面充其量是铺垫,下面来正主,继承JfastController这个父类,目的是获得拦截器需要的属性定义和方法,举例:(先贴原文,为了节省时间,可以先看后面的精简与解释)package com.jfast.core.controller;

import java.io.File;

import java.text.SimpleDateFormat;

import java.util.ArrayList;

import java.util.Date;

import java.util.HashMap;

import java.util.List;

import java.util.Locale;

import java.util.Map;

import com.alibaba.fastjson.JSONArray;

import com.alibaba.fastjson.JSONObject;

import com.jfast.core.exception.NullTableException;

import com.jfast.core.intercept.validate.AddValidate;

import com.jfast.core.intercept.validate.DeleteValidate;

import com.jfast.core.intercept.validate.EditValidate;

import com.jfast.core.intercept.validate.ImportsValidate;

import com.jfast.core.model.JfastField;

import com.jfast.core.model.JfastObject;

import com.jfast.util.XX;

import com.jfinal.aop.Before;

import com.jfinal.aop.Duang;

import com.jfinal.kit.HttpKit;

import com.jfinal.kit.Kv;

import com.jfinal.plugin.activerecord.Db;

import com.jfinal.plugin.activerecord.Record;

import com.jfinal.plugin.activerecord.SqlPara;

import com.jfinal.upload.UploadFile;

/**

* 数据的增删改查

* @author WangWei

* @param 

*

*/

public class MetaController extends JfastController {

private static JfastObject jfastObject;

private LinkSql data = new LinkSql(new HashMap<>());

/**

* 删除

* @return

*/

@Before(DeleteValidate.class)

public boolean deletes(){

String json = HttpKit.readData(getRequest());

JSONArray jsonArray = null;

try{

jsonArray = JSONArray.parseArray(json);

}catch (Exception e) {

renderJson(Kv.by("errCode", 1).set("msg", "传入的数据格式有误!"));

return false;

}

this.setJfastObject();

ds = jfastObject.getStr("data_source");

if(XX.isEmpty(ds)){

renderJson(Kv.by("errCode", 1).set("msg","数据库可能被本损坏或篡改ds"));

return false;

}

pk = jfastObject.getStr("pk_name");

if(XX.isEmpty(pk)){

renderJson(Kv.by("errCode", 1).set("msg","数据库可能被本损坏或篡改pk"));

return false;

}

boolean isDelete = true;

int success = 0;

int fail = 0;

for(int i=0;i

JSONObject jsonObject = JSONObject.parseObject(jsonArray.get(i).toString());

boolean temp = false;

try {

temp = this.deleteOne(jsonObject, pk);

} catch (NullTableException e) {

e.printStackTrace();

temp = false;

}

if(temp){

success++;

}else{

fail++;

}

isDelete = isDelete & temp;

}

if(isDelete){

renderJson(Kv.by("errCode", 0).set("msg", "删除成功"));

}else{

renderJson(Kv.by("errCode", 1).set("msg", "删除失败:成功"+success+",失败"+fail));

}

return true;

}

/**

* 删除元数据

* @param jsonObject

* @return

* @throws NullTableException

*/

private boolean deleteOne(JSONObject jsonObject ,String pkName) throws NullTableException{

String pkValue = jsonObject.getString(pkName);

tableName = jfastObject.getStr("table_name");

if(XX.isEmpty(tableName)){

tableName = jfastObject.getStr("view_table");

if(XX.isEmpty(tableName)){

throw new NullTableException("未查到相应数据表或视图");

}

}

record.set("code", jsonObject.get("code"));

record.set(pkName, pkValue);

JfastService js = Duang.duang(new JfastService<>());

MetaController mc = js.deletes(this);

return mc.result;

}

}

上面的一段代码是我的真实代码,作为思路的展示,看着还是有点费劲的,我抽丝剥茧,对上述代码进行了压缩:package com.jfast.core.controller;

import java.util.Map;//……继续省略导包行

/**

* 数据的增删该查

* @author WangWei

* @param 

*

*/

public class MetaController extends JfastController {

/**

* 删除

* @return

*/

@Before(DeleteValidate.class)

public void deletes(){

recordList = getData();//通过各种手段获得recordList,至于getData()里的内容,大家根据需要自行定义

ds = getDs();//同上,获得数据源ds

tableName = getTable(); //同上,不择手段地获取tableName

pk = getPk();//同上,获取pk

MetaController mc = this;

boolean temp = true;

for(Record r :recordList){

this.record = r;

mc = this.deleteOne(this);

temp = temp && mc.result;

}

if(temp){

renderJson(Kv.by("errCode", 0).set("msg", "删除成功"));

}else{

renderJson(Kv.by("errCode", 1).set("msg", "删除失败"));

}

}

/**

* 删除元数据

* @param MetaController

* @return

* @throws NullTableException

*/

private boolean deleteOne(MetaController mc) throws NullTableException{

JfastService js = Duang.duang(new JfastService<>());

MetaController mc = js.delete(this);

return mc.result;

}

}

6、Duang.duang已经给业务类套上,但是拦截器在哪?是谁?全局业务拦截器来也:JfastGlobeServiceInterceptor。package com.jfast.core.intercept;

import java.util.ArrayList;

import java.util.List;

import com.jfast.util.XX;

import com.jfinal.aop.Interceptor;

import com.jfinal.aop.Invocation;

import com.jfinal.core.Controller;

import com.jfinal.kit.Kv;

import com.jfinal.plugin.activerecord.Db;

import com.jfinal.plugin.activerecord.Record;

/**

* 全局业务拦截器

* @author 王维

* hi.wangwei@qq.com

* 2017年8月18日 下午2:44:32

* @param 

*/

public class JfastGlobeServiceInterceptor implements Interceptor{

private Interceptor[] inters;

private List interList;

protected final JfastGlobeServiceInterceptor addInterceptors(Interceptor... interceptors) {

if (interceptors == null || interceptors.length == 0)

throw new IllegalArgumentException("Interceptors can not be null");

if (interList == null)

interList = new ArrayList();

for (Interceptor ref : interceptors){

interList.add(ref);

}

inters = interList.toArray(new Interceptor[interList.size()]);

interList.clear();

interList = null;

return this;

}

public final void intercept(Invocation inv) {

Interceptor temp = getObjectInterceptor(inv);

if(XX.isEmpty(temp)){

inv.invoke();

}else{

addInterceptors(temp);

/*

* Jfinal源码中的InvocationWrapper不可见,

* 我只好自己复制了一个到项目中。

*/

new InvocationWrapper(inv, inters).invoke();

}

}

private  Interceptor getObjectInterceptor(Invocation inv){

Controller c = (Controller)inv.getArg(0);

Record r = Db.use("jfast").findFirst(Db.getSqlPara("objects.getObjectInterceptorByCode", Kv.by("code",c.getPara(0))));

Interceptor inter = null;

String myIntercept = r.getStr("biz_intercept");

if(!XX.isEmpty(myIntercept)){

try {

inter = (Interceptor) Class.forName(myIntercept).newInstance();

} catch (ClassNotFoundException e) {

e.printStackTrace();

} catch (InstantiationException e) {

e.printStackTrace();

} catch (IllegalAccessException e) {

e.printStackTrace();

}

}

return inter;

}

}

这里是重点之一。作为新手,这段代码也是我吃不准的地方,还请各位大神指教!!!

代码是根据 Jfinal 源码中的com.jfinal.aop.InterceptorStack改造的。

它是“特殊的拦截器”,因为它本身不拦截操作,只是动态的引入拦截器,由引入的拦截器执行拦截操作。

核心是从数据库中找到拦截器的记录,如果没有则返回null,最终不拦截;如果有,则引入后加入到拦截器队伍中。

这个拦截器要想生效,需要在配置类中定义:package com.jfast.core.config;

import com.jfast.core.intercept.JfastGlobeServiceInterceptor;

//……省略

public class JfastConfig extends JFinalConfig {

@Override

public void configInterceptor(Interceptors me) {

me.addGlobalServiceInterceptor(new JfastGlobeServiceInterceptor());

me.addGlobalActionInterceptor(new JfastGlobeExceptionInterceptor());

//……还可以有登录拦截器、权限拦截器等

}

//……

}

生效后,全局动态拦截的框架就搭好了。

用户每次触发被拦截的方法,JfastGlobeServiceInterceptor从数据库中查找与本次访问对应的拦截器,比如,某次访问查到有拦截器记录,且为com.jfast.core.ObjectInterceptor。

接下来就是该拦截器到底张啥样?

7、实际生效的拦截器ObjectInterceptor

以下是其代码和相关类的代码。package com.jfast.core.intercept;

import java.util.List;

import com.jfast.core.controller.JfastController;

import com.jfast.core.intercept.JfastServicesInterceptor;

import com.jfinal.kit.Kv;

import com.jfinal.plugin.activerecord.Db;

import com.jfinal.plugin.activerecord.Record;

import com.jfinal.plugin.activerecord.SqlPara;

/**

* 元对象操作拦截器

* @author 王维

* hi.wangwei@qq.com

* 2017年8月18日 下午4:49:02

*/

public class ObjectInterceptor extends JfastServicesInterceptor{

/**

* 删除关联字段列表

* @para JfastController 控制器

*/

@Override

public void afterDelete(JfastController jc){ //删除关联字段列表

String code = jc.record.get("code");

SqlPara sqlPara = Db.getSqlPara("field.selectForDeletes", Kv.by("objectCode", code));

List list = Db.use(jc.ds).find(sqlPara);

for(Record r : list){

Db.use(jc.ds).delete("jfast_field", r);

}

}

}package com.jfast.core.intercept;

import com.jfast.core.controller.JfastController;

import com.jfinal.aop.Invocation;

/**

* 全局业务拦截器

* @author 王维

* hi.wangwei@qq.com

* 2017年8月18日 下午2:44:32

* @param 

*/

public class JfastServicesInterceptor extends JfastServicesAbstractInterceptor{

@Override

public void intercept(Invocation inv) {

JfastController jc = (JfastController) inv.getArg(0);

String method = inv.getMethodName();

if(method.equals("find")){

beforeFind(jc);

inv.invoke();

afterFind(inv.getReturnValue());

}else if(method.equals("add")){

beforeAdd(jc);

inv.invoke();

afterAdd(inv.getReturnValue());

}else if(method.equals("delete")){

beforeDelete(jc);

inv.invoke();

afterDelete(inv.getReturnValue());

}else if(method.equals("update")){

beforeUpdate(jc);

inv.invoke();

afterUpdate(inv.getReturnValue());

}else if(method.equals("finds")){

beforeFinds(jc);

inv.invoke();

afterFinds(inv.getReturnValue());

}else if(method.equals("adds")){

beforeAdds(jc);

inv.invoke();

afterAdds(inv.getReturnValue());

}else if(method.equals("deletes")){

beforeDeletes(jc);

inv.invoke();

afterDeletes(inv.getReturnValue());

}else if(method.equals("updates")){

beforeUpdates(jc);

inv.invoke();

afterUpdates(inv.getReturnValue());

}

}

@Override

public void beforeFind(JfastController jc) {}

@Override

public void afterFind(JfastController c) {}

@Override

public void beforeAdd(JfastController c) {}

@Override

public void afterAdd(JfastController c) {}

@Override

public void beforeUpdate(JfastController c) {}

@Override

public void afterUpdate(JfastController c) {}

@Override

public void beforeDelete(JfastController c) {}

@Override

public void afterDelete(JfastController c) {}

@Override

public void beforeFinds(JfastController jc) {}

@Override

public void afterFinds(JfastController c) {}

@Override

public void beforeAdds(JfastController c) {}

@Override

public void afterAdds(JfastController c) {}

@Override

public void beforeUpdates(JfastController c) {}

@Override

public void afterUpdates(JfastController c) {}

@Override

public void beforeDeletes(JfastController c) {}

@Override

public void afterDeletes(JfastController c) {}

}package com.jfast.core.intercept;

import com.jfinal.aop.Interceptor;

import com.jfinal.aop.Invocation;

/**

* 全局业务拦截器

* @author 王维

* hi.wangwei@qq.com

* 2017年8月18日 下午12:44:32

* @param 

*/

public abstract class JfastServicesAbstractInterceptor implements Interceptor{

@Override

public void intercept(Invocation inv) {

inv.invoke();

}

public abstract void beforeFind(T jc);

public abstract void afterFind(T c);

public abstract void beforeFinds(T jc);

public abstract void afterFinds(T c);

public abstract void beforeAdd(T c);

public abstract void afterAdd(T c);

public abstract void beforeAdds(T c);

public abstract void afterAdds(T c);

public abstract void beforeUpdate(T c);

public abstract void afterUpdate(T c);

public abstract void beforeUpdates(T c);

public abstract void afterUpdates(T c);

public abstract void beforeDelete(T c);

public abstract void afterDelete(T c);

public abstract void beforeDeletes(T c);

public abstract void afterDeletes(T c);

}

至此,全局动态拦截的工作就结束了。

Tips:业务拦截器的触发不会打印在控制台。没关系,这个功能可以自己造:

我就喜欢在方法体里System.out.println("拦截器被触发");//不过看到有大神说尽量不要这样调试,不知道是何原因,哪位大神顺便告知下小弟?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值