一、使用的背景(也不能算是使用的背景,最多只能算是一个在什么条件下面我想到了使用动态代理实现AOP的拦截功能):因为在项目中程序的结构是使用SOAP调用JNI,因此在SOAP服务端里面没有任何实现代码,仅仅是new一个JNI的对象,然后调用JNI对应的方法。但是在调用JNI方法之前需要对传进JNI的JavaBean进行初始化,而且还需要记录日志。而SOAP服务端的代码是通过ant自动生成的,需要对他进行手工的修改,在修改过程中发现每一个方法里面都是相同的:记录进入方法的日志、初始化JavaBean和记录退出方法的日志,这写东西都是通过拷贝粘贴来完成的,想到如果以后再加一个什么功能的时候又得每一个方法进行拷贝粘贴,而且方法的数量还不少,所以觉得这样来实现是不科学的。示例代码如下:
public class SOAP{
private JniInterface jni = null;
private Log log = 。。。;
public SOAP(){
jni=new JniClass();
}
/**方法A**/
public JavaBeanA aMethod(JavaBeanA javaBeanA){
log.debug("进入A方法");
//初始化JavaBean
Init(javaBeanA);
//调用JNI对应的方法
JavaBeanA result = jni.aMethod(javaBeanA);
log.debug("退出A方法");
return result;
}
……………………………………
……………………………………
等等,很多这样的方法
……………………………………
……………………………………
}
从示例代码里面可以看出,除了调用JNI对应的方法不同之外,其他的都是相同的代码,把所有的东西进行拷贝复制是不合理的。每当对SOAP进行修改,就必须将所有的方法重新拷贝粘贴。为了省去拷贝粘贴这一工序,所以使用动态代理实现AOP拦截共能。
二、实现AOP拦截
1.定义Interceptor接口
public interface Interceptor {
//在调用之前调用该方法
public void before(InvokeJniInfo invInfo);
//在调用之后调用该方法
public void after(InvokeJniInfo invInfo);
//出现异常之后调用该方法
public void exceptionThrow(InvokeJniInfo invInfo);
}
2. 定义 InvokeJniInfo 类
在Interceptor接口中的InvokeJniInfo类,该类的定义如下:
public class InvokeJniInfo {
//被代理的对象
Object proxy;
//被调用的方法
Method method;
//被调用方法的参数列表
Object[] args;
//调用之后的结果
Object result;
//抛出的异常
Throwable exception;
public InvokeJniInfo(Object proxy,
Method method,
Object[] args,
Object result,
Throwable exception){
this.proxy = proxy;
this.method = method;
this.args = args;
this.result = result;
this.exception = exception;
}
…………………………………………………………
…………………………………………………………
所有成员的get/set方法
…………………………………………………………
…………………………………………………………
}
从该类的成员变量可以知道,这个类使用来将调用函数的基本信息如代理的对象,调用的方法,调用方法的参数等信息传递给Interceptor,使得在Interceptor 之中可以通过使用该对象作出相应的拦截。
3.实现一个抽象的拦截器AbstractInterceptor
该拦截器实现了Interceptor接口,它里面的方法全都是空的,其目的是当某些拦截器只是需要实现三个方法中的一个方法或者两个方法的时候,就可以继承该抽象类,覆盖需要的实现的方法就可以了。
4.实现日志记录拦截器LogInterceptor
该拦截器主要是实现在调用之前记录日志,调用之后记录日志和出现异常的时候记录日志。其代码如下:
public class LogInterceptor implements Interceptor {
private Log log = LogFactory.getLog(“初始化Log” );
public void before(InvokeJniInfo invInfo) {
//调用InvokeJniInfo对象的Method的getName方法获取方法名
log.debug("Enter the" + invInfo.getMethod().getName());
}
public void after(InvokeJniInfo invInfo) {
//调用InvokeJniInfo对象的Method的getName方法获取方法名
log.debug("Exit the" + invInfo.getMethod().getName());
}
public void exceptionThrow(InvokeJniInfo invInfo) {
//调用InvokeJniInfo对象的Method的getName方法获取方法名
log.error("Call the" + invInfo.getMethod().getName() + " has error!");
//调用InvokeJniInfo对象的Exception的getStackTrace方法获取具体异常并记录
log.error(invInfo.getException().getStackTrace());
}
}
5.实现初始化JavaBean拦截器InitParamsInterceptor
该类继承AbstractInterceptor,只需覆盖before方法即可。其代码如下:
public class InitParamsInterceptor extends AbstractInterceptor {
public void before(InvokeJniInfo invInfo) {
if(invInfo.getArgs().length0){
//初始化第一个参数
InitContainsObjectNullUtil.initContainsOutParameter(invInfo.getArgs()[0]);
}
}
}
6.实现动态代理处理器InterceptorHandler
该类实现了java.lang.reflect.InvocationHandler接口。
public class InterceptorHandler implements InvocationHandler {
private static Log log = LogFactory.getLog(InterceptorHandler.class);
//拦截器列表
private List interceptors = null;
//存放原始对象
private Object orginalObject;
//使用Proxy返回一个对象。注意这里传进去的对象的对象必须实现一个接口
public Object bind(Object obj) {
this.orginalObject = obj;
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
.getClass().getInterfaces(), this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
Throwable ex = null;
InvokeJniInfo invInfo = new InvokeJniInfo(proxy,method,args,result,ex);
log.debug("Invoking Before Intercepors!");
//实现方法调用之前进行拦截的方法
invokeInterceptorBefor(invInfo);
try{
log.debug("Invoking Proxy Method!");
//调用方法
result = method.invoke(orginalObject,args);
invInfo.setResult(result);
log.debug("Invoking After method!");
//实现方法调用之后进行拦截的方法
invokeInterceptorAfter(invInfo);
}catch(Throwable tr){
invInfo.setException(tr);
log.error("Invoking exceptionThrow method!");
//实现出现异常进行拦截的方法
invokeInterceptorExceptionThrow(invInfo);
}
return result;
}
//获取拦截器列表
private synchronized List getIntercetors(){
if(null == interceptors){
interceptors = new ArrayList();
//添加日志记录拦截器
interceptors.add(new LogInterceptor());
//添加初始化JavaBean拦截器
interceptors.add(new InitParamsInterceptor());
//如果需要添加其他功能,可以很方便的添加其他的拦截器实现功能
}
return interceptors;
}
private void invokeInterceptorBefor(InvokeJniInfo invInfo){
List interceptors = getIntercetors();
int len = interceptors.size();
//遍历所有拦截器,并调用拦截器的before方法
for(int i = 0;i((Interceptor)interceptors.get(i)).before(invInfo);
}
}
private void invokeInterceptorAfter(InvokeJniInfo invInfo){
List interceptors = getIntercetors();
int len = interceptors.size();
//遍历所有拦截器,并调用拦截器的after方法
for(int i = len - 1;i = 0;i--){
((Interceptor)interceptors.get(i)).after(invInfo);
}
}
private void invokeInterceptorExceptionThrow(InvokeJniInfo invInfo){
List interceptors = getIntercetors();
int len = interceptors.size();
//遍历所有拦截器,并调用拦截器的exceptionThrow方法
for(int i = len - 1;i = 0;i--){
((Interceptor)interceptors.get(i)).exceptionThrow(invInfo);
}
}
}
7.获取动态代理对象工厂InterceptorFactory
public class InterceptorFactory {
private static Log log = LogFactory.getLog(InterceptorFactory.class);
public static Object getClassInstance(String clzName) {
Class cls;
Object obj = null;
try {
cls = Class.forName(clzName);
obj = (Object) cls.newInstance();
} catch (Exception e) {
log.error(e.getStackTrace());
}
return obj;
}
public static Object getInterceptorProxyedObject(String clzName) {
InterceptorHandler aopHandler = new InterceptorHandler();
Object obj = getClassInstance(clzName);
return aopHandler.bind(obj);
}
}
8.修改以前的代码,使用动态代理实现
public class SOAP{
private JniInterface jni = null;
private Log log = 。。。;
public SOAP(){
jni=(JniInterface)InterceptorFactory.getInterceptorProxyedObject("JniClass");
}
/**方法A**/
public JavaBeanA aMethod(JavaBeanA javaBeanA){
return jni.aMethod(javaBeanA);
}
……………………………………
……………………………………
等等,很多这样的方法
……………………………………
……………………………………
}
从红色代码对比可以看出,省了很多代码。
三、总结
1.必须彻底贯彻针对接口编成这一编程思想。
2.明白了这个,是不是也明白了Spring的AOP的实现了?以及为什么要使用Spring的AOP的时候必须使用他的BeanFactory呢?
(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/bianchengyuyan/)