JAVA反射机制的应用(2)
Spring中的AOP(面向切面的编程)
在MVC中,业务层对象扮演了相当重要的作用,它的方法代表了核心业务逻辑,但是可能还有一些附加的操作,比如写日志等其它操作也会包含在其中,那么可能会带来几个问题:
- 冗余代码的出现
- 破坏了面向对象的思想。各个方法应该各司其职,只做它应该做的那一部分工作,而这里这些辅助操作的引入会破坏这个特性。
这里我们把这些附加操作(比如写日志操作)看成一个切面,并且把它从业务方法中抽离出来,然后把它植入到各个业务方法中,这样维护这些辅助操作是相当容易了,而且也不会出现冗余代码。那么这种思想怎么实现呢?
这里把上面提到的业务对象称为目标对象,再引入一个委托对象(下面会讲到两种委托对象),我们不直接对目标对象进行操作,而是对委托对象进行方法调用,在委托对象的方法中可以完成切面操作,接着再把方法调用转交到相应的目标对象上。
AOP的两种实现方式:
(1)CgLib 运行时代码增强工具,它是一个第三方的工具,运行时使用cglib可以 动态地给一个类生成一个子类 ,并使用子类对象作为父类对象的委托。 CGLIB包对代理那些没有实现接口的类非常有用。 它是通过动态的生成一个子类去覆盖所要代理类的不是final的方法,并设置好callback ,setCallback的参数是一个MethodInterceptor接口的实现类实例,则原有类的每个方法调用就会转变成调用用户在 MethodInterceptor 自定义的拦截方法(intercept)。 在这个拦截方法中,我们可以在intercept方法中首先处理我们要辅助的逻辑,完了之后再使用 proxy.invokeSuper(o, os)来调用父类中的相应 方法来处理 。
步骤:首先需要一个cglib的jar包,并配置到classPath中,cglib有一个核心接口:MethodInterceptor接口,我们需要创建类来实现这个接口,并实现其中的方法:
public Object intercept(Object o, Method m, Object[] os, MethodProxy proxy)
该方法中我们可以定义一些特殊的逻辑,比如写日志等操作,这些操作完了之后再调用父类中的相关方法。
除此之外,在该类中还要创建一个Enhancer对象,参考以下内容 :
用Enhancer生成一个原有类的子类,并且设置好callback , 则 原有类的每个方法调用都会转成调用实现了MethodInterceptor接口的proxy的intercept() 函数 : public Object intercept(Object o,Method method,Object[] args,MethodProxy proxy)
在intercept()函数里,你可以执行Object result=proxy.invokeSuper(o,args)来执行原有函数,在执行前后加入自己的东西,改变它的参数,也可以瞒天过海,完全干别 的。说白了,就是AOP中的around advice。
public class TestCglibProxy {
public static void main(String[] args) {
AProxy ap=new AProxy();
A a=(A)ap.getProxy(A.class); //利用cglib创建A类的子类对象(代理对象)
a.method1(); //调用父对象的method1方法时会会调用代理对象的intercept方法
a.method2();
}
}
class A{
public void method1(){
System.out.println("A method1");
}
public void method2(){
System.out.println("A method2");
}
}
class AProxy implements MethodInterceptor{
private Logger log=Logger.getLogger("A");
private Enhancer en=new Enhancer();
public Object getProxy(Class c){ //c是所要代理的父类的Class对象
en.setCallback(this); //表示调用父对象的方法时,回调当前对象的intercept方法
en.setSuperclass(c); //设置父类对象
return en.create(); //创建一个代理类的实例
}
public Object intercept(Object o, Method m, Object[] os, MethodProxy proxy) throws Throwable {
log.info("invoke method "+m.getName());
Object result=proxy.invokeSuper(o, os);
return result;
}
}
(2)动态代理方式
目标类T和代理类P都实现同样一个接口I , 其中 P类对象中包含了一个T类的对象 ,在P和T实现接口I的同一个方法M时,可以在P中的M中调用T的M 方法 , 同时在调用T的M之前或者之后进行一些自定义的操作。
一个简单的代理模式实现
Interface I{
void method1();
}
class T implements I{
public void method() {
...
}
}
class P implements I {
private T t;
public P(T t) {
this.t = t;
}
public void methid() {
//do something
t.method();
//do something
}
}
上面的代码相信大家都能看懂 ,这就是一个代理模式的简单实现。
下面来介绍一下JAVA中已有的动态代理框架,三个关键类:Proxy,InvocationHandler,Method对象,首先我们需要创建目标类动态代理对象,怎么创建呢?调用:
newProxyInstance ( ClassLoader loader, Class <?>[] interfaces, InvocationHandler h)
前面讲过了代理类和目标类实现共同的接口,因此需要将目标类所实现的接口(这里是一个Class数组)传给Proxy,除此之外还需要传 递目标类的类加载器和InvocationHandler实例,类加载器当然是用来加载生成的代理类的class文件,而这里的 InvocationHandler是什么呢?顾名思义,调用处理器,当对代理类调用相应的方法时(这里的方法其实是我们想对目标类调用的),该方法调用 会被拦截,并回调InvocationHandler实现类的invoke方法,我们来简单看看invoke方法:
invoke ( Object proxy, Method method, Object [] args)
invoke方法中又包含了三个参数,第一个是代理对象proxy,第二个是Method对象,第三个是一个Object数组,这里简单介绍下这三个参 数:proxy对象大家应该都知道了,即代理对象 , 上面利用Proxy的静态newProxyInstance方法创建的 ;method对象,即方法对象,这里的方法对象是什么呢?刚才说到了对代理对象调用方法时会被拦截,而这里的method就是被拦截的方法对象 ; 最后一个是args,相信大家对这个变量不会陌生,这里表示的是对代理对象调用方法时所传递的参数,参数可能为一个,两个或者多个,当然也可能没有,因此这里用一个数组表示。
肯定有人会问我在这个方法里写什么呢?爱写什么你写什么,呵呵,开个玩笑。 按照常理来说,这里面肯定会调用目标对象的method方法了,不然这代理还有什么意义!!!那怎么调用呢?相信有java反射基础的人大家应该都知道吧,不知道或者不熟练就查查API:
invoke ( Object obj, Object ... args)
第 一个对象是目标对象,第二个是参数数组,这里又涉及问题,怎么把目标对象传递到这儿呢?我这里介绍两种方法,其实大家应该都能想到,在定义 InvocationHandler实现类时,把目标类作为它的成员变量,通过构造函数把目标对象赋值给它,那么在invoke方法中当然就可以直接使用 了;另外一种更简单的方法,InvocationHandler的实例使用匿名内部类创建:
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) {
...
method,invoke(target, args);
...
}
}
这里的target可以来自方法参数,但一定要是final的。
public class TestProxy {
public static void main(String[] args){
/*
I target=new Target();
I proxy=(I)ProxyFactory.getProxy(target);
proxy.method();
*/
List list=new ArrayList();
List listProxy=(List)ProxyFactory.getProxy(list);
listProxy.add("abc");
listProxy.add("def");
listProxy.add(0,"123");
listProxy.remove(1);
listProxy.size();
for(Object o:list){
System.out.println(o);
}
}
}
interface I{
void method();
}
class Target implements I{
public void method(){
System.out.println("Target method");
}
}
class TargetProxy implements I{
I target;
public TargetProxy(I target){
this.target=target;
}
public void method(){
System.out.println("do sth in proxy");
target.method();
}
}
class ProxyFactory{
public static Object getProxy(final Object target){
Class c=target.getClass();
ClassLoader loader=c.getClassLoader();
Class[] is=c.getInterfaces();
return Proxy.newProxyInstance(loader,is,new InvocationHandler(){
public Object invoke(Object proxy, Method m, Object[] os) throws Throwable {
System.out.println("invoke "+m.getName());
return m.invoke(target, os);
}
});
}
}
以上内容部分来自:http://nucchenyibin.iteye.com/blog/667733
http://www.zx2010.com/program/java-cglib-proxy.asp
本文探讨了Spring框架中的面向切面编程(AOP),详细介绍了如何通过CgLib和Java动态代理解决业务代码中重复的日志记录等问题,实现代码解耦。
1197

被折叠的 条评论
为什么被折叠?



