在日常的生活中,有如下情况,你提出了借钱的请求,这个请求需要处理,200块以下你的同学就能帮你处理,200以上500一下你的好友能够帮你处理,500以上你的父母帮你处理,50000以上银行帮你处理。这有四个对象,他们都有一个共同的方法,就是借钱给你。
这时你可能会有4个if - else语句来解决你的需求,然而在日常的开发生活中,对象不见得一定就是这4位,比如领导现在也能问你借钱了,那就要添加一个if-else再调用方法,这是相当不合理的,将我这个本体和其他5个对象都耦合在了一起,而且修改成本太大,并且我只是借钱却还要计算我的数值来判断向谁去借钱,完成了非必要需求。
这里引入职责链设计模式,简单来讲,将上述5个对象放在一个链表上,遍历时查看是否满足要求,满足就直接处理了,后面的对象也就不用管了。这样我这个本体就不用处理逻辑了,我只要提出借钱的请求就可以了。
职责链模式:避免将一个请求的发送者和请求的处理者耦合在一起,让多个对象都有处理请求的机会。将接受请求的对象连成一条线链并且沿着这条链传递请求,直到有一个对象能够处理它为止。
类图如下:
1、Handle()、抽象处理类在类中定义了一个success的属性,抽象的处理方法。success是对下家的引用,因为不同的接受对象处理方法不同所以用抽象方法设计处理方法。通过对下个处理对象的引用行出了一条链。这里对应了上述的5个借钱对象总体的抽象类,应该有个指向下一个对象的指针,然后他们又都要实现这个抽象类,给个抽象类的链表就可以了。当然还需要一个处理借钱的方法,让实现类去实现。
2、ConcreteHandle(),具体的处理类,通过继承或实现抽象处理类,并且实现的对应的请求方法。当请求发送到具体的处理类时,首先进行判断是否自己能够处理该请求,如果可以则执行处理方法,否则将该请求转发给下一个具体处理类。形成一条链!对应上面的例子,实现方法当然是这个钱是多少来判断是我自己解决还是给下一个处理者解决。
说了这么多,写个demo看一下吧。我们写个模拟SpringAOP的小案例,AOP的前置拦截,后置拦截其实和上面情况很像处理方法是调用了拦截方法,针对前置和后置还有一些要求,不满足的话我们就给下一个处理者,直到完成整个链的遍历。
/**
* 前置拦截职责链
*/
public class BeforeChain {
private BeforeChain next;//指向后一个拦截节点
public Object dealRequest(InterceptionDefination interception,Object[] args){
Method method = interception.getMethod();
Object orginObject = interception.getObject();
try {
Boolean result = (Boolean) method.invoke(orginObject);
//返回false表示此拦截之后的拦截方法不再进行
if(result==false){
return result;
}
//该节点并非最后一个节点,交给下一个节点再次处理。
if(this.next!=null){
this.next.dealRequest(interception,args);
}else{
return result;
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
后置拦截职责链链
public class AfterChain {
private AfterChain next;
public Object dealAfterMethod(InterceptionDefination interceptionDefination,Object oldResult){
Object orginObject = interceptionDefination.getObject();
Method method = interceptionDefination.getMethod();
try {
Object newObject = method.invoke(orginObject,oldResult);
if(newObject==null){
return newObject;
}
this.next.dealAfterMethod(interceptionDefination,newObject);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return oldResult;
}
}
其他的职责链类似,不在此赘述。
下面就是管理我们拦截器链的数据结构了,因为是针对于一个方法来进行拦截利用Map,键是我们的方法定义,值就是我们指责链的头结点。
/**
* describe:拦截器工厂
*
* @author gary
* @date 2019/1/17
*/
public class IntercepterFactory {
//前置拦截
private static final Map<IntercepterTargetDefinition, BeforeChain> beforeMap;
//后置拦截
private static final Map<IntercepterTargetDefinition, AfterChain> afterMap;
错误拦截
private static final Map<IntercepterTargetDefinition, ExceptionChain> exceptionMap;
static {
beforeMap = new HashMap<>();
afterMap = new HashMap<>();
exceptionMap = new HashMap<>();
}
protected IntercepterFactory() {
}
private void addIntercepter(Map<IntercepterTargetDefinition, Chain> map,
IntercepterTargetDefinition interTarget,
IntercepterMethodDefinition intercepter) {
Chain chain = map.get(interTarget);
if (chain == null) {
synchronized (IntercepterFactory.class) {
if (chain == null) {
chain = new Chain(intercepter);
map.put(interTarget, chain);
}
}
return ;
}
chain.setNextChain(new Chain(intercepter));
}
protected void addBeforeIntercepter(IntercepterTargetDefinition interTarget,
IntercepterMethodDefinition intercepter) {
addIntercepter(beforeMap, interTarget, intercepter);
}
protected void addAfterIntercepter(IntercepterTargetDefinition interTarget,
IntercepterMethodDefinition intercepter) {
addIntercepter(afterMap, interTarget, intercepter);
}
protected void addExceptionIntercepter(IntercepterTargetDefinition interTarget,
IntercepterMethodDefinition intercepter) {
addIntercepter(exceptionMap, interTarget, intercepter);
}
//下面的方法通过IntercepterTargetDefinition在对应map找到拦截器链。
protected Chain getBeforeIntercepterList(
IntercepterTargetDefinition interTarget) {
return beforeMap.get(interTarget);
}
protected Chain getAfterIntercepterList(
IntercepterTargetDefinition interTarget) {
return afterMap.get(interTarget);
}
protected Chain getExceptionIntercepterList(
IntercepterTargetDefinition interTarget) {
return exceptionMap.get(interTarget);
}
}
接下来就是我们的包扫描那些带有注解的方法了。
//对后置方法应该进行检查
// 前置拦截器方法的返回值必须是boolean
// 前置拦截器方法的参数必须是Object[],事实是相关方法的实参,就是说前置拦截方法的参数就是被拦截方法的参数
// 若不满足上述要求,应该抛出异常。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface After {
Class<?> tagetClass();
String methodName();
Class<?>[] paramterTypes();
}
//这是带有拦击方法的类的注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Aspect {
}
//对前置方法应该进行检查
// 后置拦截器方法的返回值必须是Object
// 后置拦截器方法的参数必须是Object,事实是相关方法的实参
// 若不满足上述要求,应该抛出异常。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Before {
Class<?> tagetClass();
String methodName();
}
这里重点说下前置拦截,因为前置拦截需要准确的锁定一个方法,仅方法名是无法确定重载方法的,因此需要用户提供方法参数类型列表。
下面就是包扫描了。就是把带了注解的方法给装到对应的职责链里面去就好了,那我们在调用方法时就直接在map里找到头结点,调用处理方法就好了,职责链对象就会自动的去调用拦截方法,达到解耦。
/**
* describe:扫描拦截器
*
* @author gary
* @date 2019/01/17
*/
public class IntercepterScan {
public static void intercepterScan(String packageName) {
new PackageScanner() {
@Override
public void dealClass(Class<?> klass) {
if (!klass.isAnnotationPresent(Aspect.class)) {
return ;
}
try {
Object object = klass.newInstance();
Method[] methods = klass.getDeclaredMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(Before.class)) {
Before before = method.getAnnotation(Before.class);
//TODO 处理before拦截方法
dealBeforeIntercepter(klass, object, method, before);
} else if (method.isAnnotationPresent(After.class)) {
After after = method.getAnnotation(After.class);
dealAfterIntercepter(klass, object, method, after);
} else if (method.isAnnotationPresent(ThrowException.class)) {
ThrowException throwException = method.getAnnotation(ThrowException.class);
dealThrowExceptionIntercepter(klass, object, method, throwException);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}.packageScan(packageName);
}
private static void dealThrowExceptionIntercepter(Class<?> klass, Object object, Method method, ThrowException throwException) throws Exception {
Class<?> intercepterReturnType = method.getReturnType();
if (!intercepterReturnType.equals(void.class)) {
//TODO 抛出异常 拦截器返回值只能是void
}
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length != 1 || Throwable.class.isAssignableFrom(parameterTypes[0])) {
//TODO 抛出异常 拦截器参数只能是Throwable或者Throwable子类
}
Class<?> targetKlass = throwException.klass();
Method targetMethod = targetKlass.getMethod(throwException.method(), throwException.parameterTypes());
IntercepterMethodDefinition imd = new IntercepterMethodDefinition(klass, method, object);
IntercepterTargetDefinition itd = new IntercepterTargetDefinition(targetKlass, targetMethod);
IntercepterFactory intercepterFactory = new IntercepterFactory();
intercepterFactory.addExceptionIntercepter(itd, imd);
}
private static void dealAfterIntercepter(Class<?> klass, Object object, Method method, After after) throws Exception {
Class<?> intercepterReturnType = method.getReturnType();
Class<?> targetKlass = after.klass();
Method targetMethod = targetKlass.getMethod(after.method(), after.parameterTypes());
Class<?> targetReturnType = targetMethod.getReturnType();
if (!intercepterReturnType.equals(targetReturnType)) {
//TODO 抛出异常 后置拦截器返回值类型与被拦截方法返回值类型不同
}
IntercepterMethodDefinition imd = new IntercepterMethodDefinition(klass, method, object);
IntercepterTargetDefinition itd = new IntercepterTargetDefinition(targetKlass, targetMethod);
IntercepterFactory intercepterFactory = new IntercepterFactory();
intercepterFactory.addAfterIntercepter(itd, imd);
}
private static void dealBeforeIntercepter(Class<?> klass, Object object, Method method, Before before) throws Exception {
Class<?> returnType = method.getReturnType();
if (!returnType.equals(boolean.class)) {
//TODO 抛出异常 前置拦截返回值类型只能是boolean
}
Class<?> targetKlass = before.klass();
Method targetMethod = targetKlass.getMethod(before.method(), method.getParameterTypes());
IntercepterMethodDefinition imd = new IntercepterMethodDefinition(klass, method, object);
IntercepterTargetDefinition itd = new IntercepterTargetDefinition(targetKlass, targetMethod);
IntercepterFactory intercepterFactory = new IntercepterFactory();
intercepterFactory.addBeforeIntercepter(itd, imd);
}
}
最后在代理对象生成前后加上调用职责链的处理方法就好。先通过api在Map中找到你这个方法的职责链头指针,调用一下dealXXX方法,把参数放对就好了。
综上所诉
优点;
职责链模式使得一个对象无需知道是哪一个对象处理其请求,对象仅需要知道该请求会被处理即可,降低了系统的耦合度。
请求处理对象仅需要位置一个指向其后继者的引用。
再给对象分派职责时,职责链可以更多的灵活性。
缺点:
由于一个请求没有明确的接受者,那么就比呢能够保证它一定被处理,该请求可能到最后的末端也不会被处理啦。
对于比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一个定的影响。
如果建链不当,可能会导致循环调用。陷入死循环。
职责链的使用场景
有多个对象处理同一个请求。
在不明确接收者的情况下对多个接收者发出请求。
可动态的指定一组对象为接收者,自己创建审批的人。
上述特点摘自https://blog.youkuaiyun.com/qq_40709468/article/details/82696147