利用Java的反射与代理机制实现AOP
在上一篇文章中,我们讲述了利用Java的反射机制中实现Spring中的IOC,在本文中,我们将更进一步,讲述用Java的反射和动态代理机制来实现Spring的AOP。
一.AOP概述
AOP(Aspect Oriented Programing),即面向切面编程,它主要用于日志记录、性能统计、安全控制、事务处理、异常处理等方面。它的主要意图就要将日志记录,性能统计,安全控制、事务处理、异常处理等等代码从业务逻辑代码中清楚地划分出来。通过对这些行为的分离,我们希望可以将它们独立地配置到业务逻辑方法中,而要改变这些行为的时候也不需要影响到业务逻辑方法代码。
下面让我们来看一个利用AOP来实现日志记录的例子,在没有使用AOP之前,我们的代码如下面所讲述。
下面这段代码为业务的接口类代码:
package org.amigo.proxy;

/** *//**
*业务逻辑类接口.
*@author<a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
*Creationdate:2007-10-7-上午09:09:53
*/ 
publicinterface BusinessObj
{

/** *//**
*执行业务.
*/
publicvoid process();
}

BusinessObj接口的某个实现类代码如下:
package org.amigo.proxy;

/** *//**
* 业务逻辑对象实现类.
* @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
* Creation date: 2007-10-7 - 上午09:11:49
*/ 
public class BusinessObjImpl implements BusinessObj
{

/** *//**
* 执行业务.
*/ 
public void process()
{ 
try
{
System.out.println("before process");
System.out.println("执行业务逻辑");
System.out.println("after process"); 
} catch (Exception e)
{
System.err.println("发生异常:" + e.toString());
}
}
} 
在上例中我们可以看到,在执行业务方法前、执行业务方法后以及异常发生时的日志记录,我们都是通过在对应的类中写入记录日志的代码来实现的,当有这种日志记录需求的业务逻辑类不断增多时,将会给我们的维护带来很大困难,而且,在上面的例子中,日志代码和业务逻辑代码混合在一起,为日后的维护工作又抹上了一层“恐怖”色彩。
按照AOP的思想,我们首先需要寻找一个切面,在这里我们已经找到,即在业务逻辑执行前后以及异常发生时,进行相应的日志记录。我们需要将这部分日志代码放入一个单独的类中,以便为以后的修改提供方便。
我们在截获某个业务逻辑方法时,可以采用Java的动态代理机制来实现。在下节中我们将重点讲述Java的动态代理机制。
二.Java的动态代理机制
代理模式是常用的Java设计模式。代理类主要负责为委托类预处理消息、过滤信息、把消息转发给委托类,以及事后处理信息等。
动态代理类不仅简化了编程工作,而且提高了软件系统的扩展性和可维护性。我们可以通过实现java.lang.reflect.InvocationHandler接口提供一个执行处理器,然后通过java.lang.reflect.Proxy得到一个代理对象,通过这个代理对象来执行业务逻辑方法,在业务逻辑方法被调用的同时,自动调用会执行处理器。 下面让我们来创建一个日志拦截器类LogInterceptor.java文件,该类实现java.lang.reflect.InvocationHandler接口,其内容如下所示:
package org.amigo.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/** *//**
*日志拦截器,用来进行日志处理.
*@author<a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
*Creationdate:2007-10-7-上午09:31:44
*/ 
publicclass LogInterceptor implements InvocationHandler
{
private Object delegate;

/** *//**
*构造函数,设置代理对象.
*/ 
public LogInterceptor(Object delegate)
{
this.delegate = delegate;
}

/** *//**
*方法的调用.
*@paramproxy
*@parammethod对应的方法
*@paramargs方法的参信息
*@return返回操作结果对象
*@throwsThrowable
*/ 
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
Object result = null; 
try
{
System.out.println("before process" + method);
//调用代理对象delegate的method方法,并将args作为参数信息传入
result = method.invoke(delegate, args);
System.out.println("after process" + method); 
} catch (Exception e)
{
System.err.println("发生异常:" + e.toString());
}
return result;
}

/** *//**
*测试方法.
*@paramargs
*@throwsException
*/ 
publicstaticvoid main(String[] args) throws Exception
{
BusinessObj obj = new BusinessObjImpl();
//创建一个日志拦截器
LogInterceptor interceptor = new LogInterceptor(obj);
//通过Proxy类的newProxyInstance(
)方法来获得动态的代理对象
BusinessObj proxy = (BusinessObj) Proxy.newProxyInstance(
BusinessObjImpl.class.getClassLoader(),
BusinessObjImpl.class.getInterfaces(),
interceptor);
//执行动态代理对象的业务逻辑方法
proxy.process();
}
}
此时还需要对BusinessObj的实现类BusinessObjImpl类进行修改,去掉其日志记录等内容,修改后的文件如下:
package org.amigo.proxy; 
/** *//**
* 业务逻辑对象实现类.
* @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
* Creation date: 2007-10-7 - 上午09:11:49
*/ 
public class BusinessObjImpl implements BusinessObj
{ 
/** *//**
* 执行业务.
*/ 
public void process()
{
System.out.println("执行业务逻辑");
}
} 
运行LogInterceptor类我们可以发现,它实现了前面所需要的功能,但是很好的将业务逻辑方法的代码和日志记录的代码分离开来,并且所有的业务处理对象都可以利用该类来完成日志的记录,防止了重复代码的出现,增加了程序的可扩展性和可维护性,从而提高了代码的质量。那么Spring中的AOP的实现是怎么样的呢?接着让我们来对Spring的AOP进行探讨,了解其内部实现原理。
三.Spring中AOP的模拟实现
在学习了Java的动态代理机制后,我们在本节中将学习Java的动态代理机制在Spring中的应用。首先我们创建一个名为AopHandler的类,该类可生成代理对象,同时可以根据设置的前置或后置处理对象分别在方法执行前后执行一些另外的操作,该类的内容如下所示:
package org.amigo.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/** *//**
* AOP处理器.
* @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
* Creation date: 2007-10-7 - 上午10:13:28
*/ 
public class AopHandler implements InvocationHandler
{
//需要代理的目标对象
private Object target;
//方法前置顾问
Advisor beforeAdvisor;
//方法后置顾问
Advisor afterAdvisor;

/** *//**
* 设置代理目标对象,并生成动态代理对象.
* @param target 代理目标对象
* @return 返回动态代理对象
*/ 
public Object setObject(Object target)
{
//设置代理目标对象
this.target = target;
//根据代理目标对象生成动态代理对象
Object obj = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
return obj;
}

/** *//**
* 若定义了前置处理,则在方法执行前执行前置处理
* 若定义了后置处理,则在方法调用后调用后置处理.
* @param proxy 代理对象
* @param method 调用的业务方法
* @param args 方法的参数
* @return 返回结果信息
* @throws Throwable
*/ 
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
//进行业务方法的前置处理 
if (beforeAdvisor != null)
{
beforeAdvisor.doInAdvisor(proxy, method, args);
}
//执行业务方法
Object result = method.invoke(target, args);
//进行业务方法的后置处理 
if (afterAdvisor != null)
{
afterAdvisor.doInAdvisor(proxy, method, args);
}
//返回结果对象
return result;
}

/** *//**
* 设置方法的前置顾问.
* @param advisor 方法的前置顾问
*/ 
public void setBeforeAdvisor(Advisor advisor)
{
this.beforeAdvisor = advisor;
}

/** *//**
* 设置方法的后置顾问.
* @param advisor 方法的后置顾问
*/ 
public void setAfterAdvisor(Advisor advisor)
{
this.afterAdvisor = advisor;
}
} 
在上类中,前置和后置顾问对象都继承Advisor接口,接下来让我们来看看顾问接口类的内容,该类定义了doInAdvisor(Object proxy, Method method, Object[] args)方法,如下所示:
package org.amigo.proxy;
import java.lang.reflect.Method;

/** *//**
*顾问接口类.
*@author<a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
*Creationdate:2007-10-7-上午10:21:18
*/ 
publicinterface Advisor
{

/** *//**
*所做的操作.
*/
publicvoid doInAdvisor(Object proxy, Method method, Object[] args);
}BeforeMethodAdvisor和AfterMethodAdvisor都实现了Advisor接口,分别为方法的前置顾问和后置顾问类。
BeforeMethodAdvisor.java文件(前置顾问类)的内容如下所示:
package org.amigo.proxy;
import java.lang.reflect.Method;

/** *//**
*
*方法前置顾问,它完成方法的前置操作.
*@author<a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
*Creationdate:2007-10-7-上午10:19:57
*/ 
publicclass BeforeMethodAdvisor implements Advisor
{ 
/** *//**
*在方法执行前所进行的操作.
*/ 
publicvoid doInAdvisor(Object proxy, Method method, Object[] args)
{
System.out.println("before process " + method);
}
} 
AfterMethodAdvisor.java文件(后置顾问类)的内容如下所示:
package org.amigo.proxy;
import java.lang.reflect.Method;

/** *//**
*方法的后置顾问,它完成方法的后置操作.
*@author<a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
*Creationdate:2007-10-7-上午10:20:43
*/ 
publicclass AfterMethodAdvisor implements Advisor
{

/** *//**
*在方法执行后所进行的操作.
*/ 
publicvoid doInAdvisor(Object proxy, Method method, Object[] args)
{
System.out.println("after process " + method);
}
} 
这两个类分别在方法执行前和方法执行后做一些额外的操作。
对于在配置文件中对某个bean配置前置或后置处理器,我们可以在bean中增加两个属性aop和aopType,aop的值为对应的前置顾问类或后置顾问类的名称,aopType用于指明该顾问类为前置还是后置顾问,为before时表示为前置处理器,为after时表示为后置处理器,这时候我们需要修改上一篇文章中的BeanFactory.java这个文件,添加其对aop和aopType的处理,修改后的该文件内容如下:
; k < pd.length; k++)
{ 
if (pd[k].getName().equalsIgnoreCase(name.getText()))
{
mSet = pd[k].getWriteMethod();
//利用Java的反射极致调用对象的某个set方法,并将值设置进去
mSet.invoke(obj, value);
}
}
}
//为对象增加前置或后置顾问
obj = (Object) aopHandler.setObject(obj);
//将对象放入beanMap中,其中key为id值,value为对象
beanMap.put(id.getText(), obj);
} 
} catch (Exception e)
{
System.out.println(e.toString());
}
}

/** *//**
*通过bean的id获取bean的对象.
*@parambeanNamebean的id
*@return返回对应对象
*/ 
public Object getBean(String beanName)
{
Object obj = beanMap.get(beanName);
return obj;
}

/** *//**
*测试方法.
*@paramargs
*/ 
publicstaticvoid main(String[] args)
{
BeanFactory factory = new BeanFactory();
factory.init("config.xml");
BusinessObj obj = (BusinessObj) factory.getBean("businessObj");
obj.process();
}
}观察此类我们可以发现,该类添加了对bean元素的aop和aopType属性的处理。编写完该文件后,我们还需要修改src目录下的配置文件:config.xml文件,在该文件中添加名为businessObj的bean,我们为其配置了aop和aopType属性,增加了方法的前置顾问。增加的部分为:
<bean id="businessObj" class="org.amigo.proxy.BusinessObjImpl" aop="org.amigo.proxy.BeforeMethodAdvisor" aopType="before"/>
此时运行BeanFactory.java这个类文件,运行结果如下:
before process public abstract void org.amigo.proxy.BusinessObj.process()
执行业务逻辑
由运行结果可以看出,前置处理已经生效。
本节中的例子只是实现了Spring的AOP一小部分功能,即为某个bean添加前置或后置处理,在Spring中,考虑的比这多很多,例如,为多个bean配置动态代理等等。但是究其根源,Spring中AOP的实现,是基于Java中无比强大的反射和动态代理机制。
四.总结
本文讲述了AOP的概念等信息,并详细讲解了Java的动态代理机制,接着又通过一个简单的实例讲解了Spring中AOP的模拟实现,使得读者能够更好地学习Java的反射和代理机制。
通过这两篇文章,使得我们能够更加深入地理解Java的反射和动态代理机制,同时对Spring中盛行的IOC和AOP的后台实现原理有了更加清晰的理解,Java的反射和动态代理机制的强大功能在这两篇文章中可见一斑。有兴趣的朋友可以通过学习Spring框架的源码来进一步的理解Java的反射和动态代理机制,从而在实际的开发工作中更好地理解它。
利用Java的反射与代理机制实现AOP的更多相关文章
- Java反射机制详解(3) -java的反射和代理实现IOC模式 模拟spring
IOC(Inverse of Control) 可翻译为“控制反转”,但大多数人都习惯将它称为“依赖注入”.在Spring中,通过IOC可以将实现类.参数信息等配置在其对应的配置文件中,那么当 需要更 ...
- java中的动态代理机制
java中的动态代理机制 在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface).另一个则是 Proxy(Class),这一个类和接口是实现 ...
- Java的反射和代理以及注解
最近接触到java的反射和代理(接触的有点迟了...),还是有必要总结下 1. Java的反射 有的时候我们需要在程序运行的时候获取类.方法等信息用于动态运行,这个时候反射就能够帮我们找到类.方法.成 ...
- 深度剖析java中JDK动态代理机制
https://www.jb51.net/article/110342.htm 本篇文章主要介绍了深度剖析java中JDK动态代理机制 ,动态代理避免了开发人员编写各个繁锁的静态代理类,只需简单地指定 ...
- kettle job如何利用java的反射机制获取执行的sql语句
kettle job中的JavaScript如何获取同一个job中SQL步骤的执行语句并让执行语句记录在日志中呢?首先写日志需要用到job中JavaScript写日志的方法,其次是利用java反射机制 ...
- Java基础—反射与代理(新手向)
第1章 反射与代理 1.1 反射定义 一般情况下,需要一个功能的前提是遇到了某个问题,这里先列举一些问题,然后再通过反射是如何解决了这些问题,来引出反射的定义. 普通开发人员工作中最常见的问题:需要生 ...
- Java进阶 | Proxy动态代理机制详解
一.Jvm加载对象 在说Java动态代理之前,还是要说一下Jvm加载对象的过程,这个依旧是理解动态代理的基础性原理: Java类即源代码程序.java类型文件,经过编译器编译之后就被转换成字节代码.c ...
- 跟着刚哥梳理java知识点——反射和代理(十七)
反射机制是什么?反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有的属性和方法:对于任意一个对象,都能够调用他的一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为java语 ...
- Java中如何实现代理机制(JDK动态代理和cglib动态代理)
http://blog.csdn.net/skiof007/article/details/52806714 JDK动态代理:代理类和目标类实现了共同的接口,用到InvocationHandler接口 ...
随机推荐
- 【BZOJ 1485】[HNOI2009]有趣的数列 卡特兰数
这个题我是冲着卡特兰数来的所以就没有想到什么dp,当然也没有想到用卡特兰数的原因........... 你只要求出前几项就会发现是个卡特兰数,为什么呢:我们选择地时候要选择奇数位和偶数位,相邻(一对里 ...
- bzoj 2756 [SCOI2012]奇怪的游戏 二分+网络流
2756:[SCOI2012]奇怪的游戏 Time Limit: 40 Sec Memory Limit: 128 MBSubmit: 4926 Solved: 1362[Submit][Stat ...
- spring aop与aspectj
AOP:面向切面编程 简介 AOP解决的问题:将核心业务代码与外围业务(日志记录.权限校验.异常处理.事务控制)代码分离出来,提高模块化,降低代码耦合度,使职责更单一. AOP应用场景: 日志记录.权 ...
- 排序(bzoj 4552)
Description 在2016年,佳媛姐姐喜欢上了数字序列.因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题 ,需要你来帮助他.这个难题是这样子的:给出一个1到n的全排列,现在对这 ...
- [BZOJ1005]Prufer数列+排列组合
一棵树的Prufer数列 每次在剩下的树中找到标号最小的叶子节点(对于无根树而言即是度数为1的节点),删去. 同时将其父节点(即与其相连的唯一点)加入Prufer数列当中. 一个Prufer数列所对应 ...
- Chocolatey 使用
最近空了下来不干点什么就感觉脑袋热,可是出过的问题挖过的坑还是要自己去解决. 一直网络不好安装choco一直都是报错,今天又建立了chocolatey 在windows上来用,网络问题解决了,类似于m ...
- Mysql添加视图
有时候复杂的查询需要创建视图,可以简化查询.我们也可以将视图包装成对象,这样查询后在Java中也可以直接封装为对象. 原来的表结构
- Raspberry Pi 3b+ 配置摄像头
1.开启摄像头硬件接口 raspi-config > Interfacing Options > Camera 2.测试 raspistill -v -o test.jpg
- App云测试平台免费功能汇总
Wetest http://wetest.qq.com 阿里云测 https://mqc.aliyun.com/ Testbird https://www.testbird.com/ 百度 htt ...
- 基于java容器注解---基于泛型的自动装配
上面利用@Configuration和@Bean配置bean,中间利用@Autowired,指定s1和s2分别指向哪个实现类,下面利用@Autowired,指定s中只有Integer实现类 例子: 在 ...