一个使用API创建代理的例子

在进入API分析前,我们先通过两个例子体会下如何使用API的方式来创建一个代理对象,对应示例如下:

  1. 定义通知
publicclassDmzAfterReturnAdviceimplementsAfterReturningAdvice{@OverridepublicvoidafterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target)throwsThrowable{
		System.out.println("after invoke method ["  method.getName()  "],aop afterReturning logic invoked");
	}
}publicclassDmzAroundAdviceimplementsMethodInterceptor{@OverridepublicObjectinvoke(MethodInvocation invocation)throwsThrowable{
		System.out.println("aroundAdvice invoked");returninvocation.proceed();
	}
}publicclassDmzBeforeAdviceimplementsMethodBeforeAdvice{@Overridepublicvoidbefore(Method method, Object[] args, Object target)throwsThrowable{
		System.out.println("before invoke method ["  method.getName()  "],aop before logic invoked");
	}
}publicclassDmzIntroductionAdviceextendsDelegatingIntroductionInterceptorimplementsRunnable{@Overridepublicvoidrun(){
		System.out.println("running!!!!");
	}
}

2.切点

publicclassDmzPointcutimplementsPointcut{@Override@NonNullpublic ClassFilter getClassFilter() {// 在类级别上不进行拦截returnClassFilter.TRUE;
	}

	@Override@NonNullpublicMethodMatchergetMethodMatcher() {returnnewStaticMethodMatcherPointcut() {@Overridepublic boolean matches(@NonNullMethod method, Class targetClass) {// 对于toString方法不进行拦截return!method.getName().equals("toString");
			}
		};
	}
}

3.目标类

publicclassDmzService{
	@OverridepublicStringtoString(){
		System.out.println("dmzService toString invoke");return"dmzService";
	}publicvoidtestAop(){
		System.out.println("testAop invoke");
	}
}

4.测试代码

publicclassMain{publicstaticvoidmain(String[] args){

		ProxyFactory proxyFactory =newProxyFactory();// 一个Advisor代表的是一个已经跟指定切点绑定了的通知// 在这个例子中意味着环绕通知不会作用到toString方法上Advisor advisor =newDefaultPointcutAdvisor(newDmzPointcut(),newDmzAroundAdvice());// 添加一个绑定了指定切点的环绕通知proxyFactory.addAdvisor(advisor);// 添加一个返回后的通知proxyFactory.addAdvice(newDmzAfterReturnAdvice());// 添加一个方法执行前的通知proxyFactory.addAdvice(newDmzBeforeAdvice());// 为代理类引入一个新的需要实现的接口--RunnableproxyFactory.addAdvice(newDmzIntroductionAdvice());// 设置目标类proxyFactory.setTarget(newDmzService());// 因为要测试代理对象自己定义的方法,所以这里启用cglib代理proxyFactory.setProxyTargetClass(true);// 创建代理对象Object proxy = proxyFactory.getProxy();// 调用代理类的toString方法,通过控制台查看代理逻辑的执行情况proxy.toString();if(proxyinstanceofDmzService) {
			((DmzService) proxy).testAop();
		}// 判断引入是否成功,并执行引入的逻辑if(proxyinstanceofRunnable) {
			((Runnable) proxy).run();
		}
	}
}

这里我就不将测试结果放出来了,大家可以先自行思考这段程序将输出什么。接下来我们就来分析上面这段程序中所涉及到的API,通过这些API的学习相信大家可以彻底理解上面这段代码。

API介绍

Pointcut(切点)

对应接口定义如下:

publicinterfacePointcut{// ClassFilter,在类级别进行过滤ClassFiltergetClassFilter();// MethodMatcher,在方法级别进行过滤MethodMatchergetMethodMatcher();// 一个单例对象,默认匹配所有Pointcut TRUE = TruePointcut.INSTANCE;

}

切点的主要作用是定义通知所要应用到的类跟方法,上面的接口定义也很明显的体现了这一点,我们可以将其拆分成为两个部分

  • ClassFilter,接口定义如下:
publicinterfaceClassFilter{booleanmatches(Class clazz);

	ClassFilter TRUE = TrueClassFilter.INSTANCE;

}

ClassFilter的主要作用是在类级别上对通知的应用进行一次过滤,如果它的match方法对任意的类都返回true的话,说明在类级别上我们不需要过滤,这种情况下,通知的应用,就完全依赖MethodMatcher的匹配结果。

  • MethodMatcher,接口定义如下:
publicinterfaceMethodMatcher{booleanmatches(Method method, @Nullable Class targetClass);booleanisRuntime();booleanmatches(Method method, @Nullable Class targetClass, Object... args);

	MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;

}

MethodMatcher中一共有三个核心方法

  • matches(Method method, @Nullable Class targetClass),这个方法用来判断当前定义的切点跟目标类中的指定方法是否匹配,它可以在创建代理的时候就被调用,从而决定是否需要进行代理,这样就可以避免每次方法执行的时候再去做判断
  • isRuntime(),如果这个方法返回true的话,意味着每次执行方法时还需要做一次匹配
  • matches(Method method, @Nullable Class targetClass, Object… args),当之前的isRuntime方法返回true时,会调用这个方法再次进行一次判断,返回false的话,意味这个不对这个方法应用通知

Advice(通知)

环绕通知(Interception Around Advice)

接口定义如下:

publicinterfaceMethodInterceptorextendsInterceptor{Objectinvoke(MethodInvocation invocation)throwsThrowable;
}

在上面接口定义的invoke方法中,MethodInvocation就是当前执行的方法,当我们调用invocation.proceed就是在执行当前的这个方法,基于此,我们可以在方法的执行前后去插入我们自定义的逻辑,比如下面这样

// 执行前的逻辑doSomeThingBefore();Objectvar= invocation.proceed;
doSomeThingAfter();// 执行后的逻辑retrunvar;

前置通知(Before Advice)

publicinterfaceMethodBeforeAdviceextendsBeforeAdvice{voidbefore(Method m, Object[] args, Object target)throwsThrowable;
}

跟环绕通知不同的是,这个接口中定义的方法的返回值是void,所以前置通知是无法修改方法的返回值的。

如果在前置通知中发生了异常,那么会直接终止目标方法的执行以及打断整个拦截器链的执行

后置通知(After Returning Advice)

publicinterfaceAfterReturningAdviceextendsAdvice{voidafterReturning(Object returnValue, Method m, Object[] args, Object target)throwsThrowable;
}

后置通知相比较于前置通知,主要有以下几点不同

  • 后置通知可以访问目标方法的返回值,但是不能修改
  • 后置通知是在方法执行完成后执行

异常通知(Throws Advice)

publicinterfaceThrowsAdviceextendsAfterAdvice{

}

异常通知中没有定义任何方法,它更像一个标记接口。我们在定义异常通知时需要实现这个接口,同时方法的签名也有要求

  1. 方法名称必须是afterThrowing
  2. 方法的参数个数必须是1个或者4个,如下:
publicclassOneParamThrowsAdviceimplementsThrowsAdvice{// 如果只有一个参数,那么这个参数必须是要进行处理的异常publicvoidafterThrowing(RemoteException ex)throwsThrowable{// Do something with remote exception}
}publicclassFourParamThrowsAdviceimplementsThrowsAdvice{// 如果定义了四个参数,那么这四个参数分别是// 1.m:目标方法// 2.args:执行目标方法所需要的参数// 3.target:目标对象// 4.ex:具体要处理的异常// 并且参数类型必须按照这个顺序定义publicvoidafterThrowing(Method m, Object[] args, Object target, ServletException ex){// Do something with all arguments}
}

我们可以在一个异常通知中定义多个方法,在后续的源码分析中我们会发现,这些方法最终会被注册成对应的异常的handler,像下面这样

publicstaticclassCombinedThrowsAdviceimplementsThrowsAdvice{publicvoidafterThrowing(RemoteException ex)throwsThrowable{// Do something with remote exception}publicvoidafterThrowing(Method m, Object[] args, Object target, ServletException ex){// Do something with all arguments}
}

引入通知(Introduction Advice)

引入通知的主要作用是可以让生成的代理类实现额外的接口。例如在上面的例子中,我们为DmzService创建一个代理对象,同时为其定义了一个引入通知

publicclassDmzIntroductionAdviceextendsDelegatingIntroductionInterceptorimplementsRunnable{@Overridepublicvoidrun(){
		System.out.println("running!!!!");
	}
}

在这个引入通知中,我们为其引入了一个新的需要实现的接口Runnable,同时通知本身作为这个接口的实现类。

通过这个引入通知,我们可以将生成的代理类强转成Runnable类型然后执行其run方法,同时,run方法也会被前面定义的前置通知,后置通知等拦截。

为了更好的了解引入通知,我们来需要了解下DelegatingIntroductionInterceptor这个类。见名知意,这个类就是一个委托引入拦截器,因为我们要为代理类引入新的接口,因为着我们要提供具体的实现的逻辑,而具体的实现的逻辑就可以被委托给这个DelegatingIntroductionInterceptor。

我们可以看看它的源码

publicclassDelegatingIntroductionInterceptorextendsIntroductionInfoSupportimplementsIntroductionInterceptor{// 实际实现了引入逻辑的类@NullableprivateObject delegate;// 对外提供了一个带参的构造函数,通过这个构造函数我们可以传入一个// 具体的实现类publicDelegatingIntroductionInterceptor(Object delegate){
		init(delegate);
	}// 对子类暴露了一个空参的构造函数,默认将自身作为实现了引入逻辑的委托类// 我们上面的例子中就是使用的这种方法protectedDelegatingIntroductionInterceptor(){
		init(this);
	}// 对这个类进行初始化,要通过实际的实现类来找到具体要实现的接口privatevoidinit(Object delegate){
		Assert.notNull(delegate,"Delegate must not be null");this.delegate = delegate;// 找到delegate所有实现的接口implementInterfacesOnObject(delegate);// 因为我们可能会将DelegatingIntroductionInterceptor本身作为委托者// Spring的设计就是不对外暴露这两个接口// 如果将其暴露,意味着我们可以将代理类强转成这种类型suppressInterface(IntroductionInterceptor.class);
		suppressInterface(DynamicIntroductionAdvice.class);
	}// 引入通知本身也是基于拦截器实现的,当执行一个方法时需要判断这个方法// 是不是被引入的接口中定义的方法,如果是的话,那么不能调用目标类的方法// 而要调用委托类的方法publicObjectinvoke(MethodInvocation mi)throwsThrowable{if(isMethodOnIntroducedInterface(mi)) {
			Object retVal = AopUtils.invokeJoinpointUsingReflection(this.delegate, mi.getMethod(), mi.getArguments());// 这里是处理一种特殊情况,方法的返回值是this的时候// 这里应该返回代理类if(retVal ==this.delegate 
1.本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2.分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3.不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4.本站提供的源码、模板、插件等其他资源,都不包含技术服务请大家谅解!
5.如有链接无法下载或失效,请联系管理员处理!
6.本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!