使用JDK中的Proxy技术实现AOP功能

来学习下AOP技术,AOP技术在企业的开发中或多或少也被使用到,最主要的应用场合是在做权限的时候。都知道在做权限的时候我们要粗粒度的权限控制和碎粒度的权限控制,对于碎粒度的权限控制我们一般是对方法进行拦截,然后拦截到方法之后判断用户是否有权限,如果有权限就允许用户执行被拦截的方法,在我们的例子当中将模拟实际的业务需求,在不使用任何AOP框架的情况下,我们对业务bean里面的所有方法实现拦截,并且在拦截到方法后判断用户是否有调用方法的权限。。
现在就建一个AOP的项目,在这个项目里面不打算使用任何的AOP框架,我们看一下传统我们要实现AOP的话要怎么做。。

先提供一个需要被拦截的业务bean,先是接口
PersonService.java

package cn.itcast.service; 
 
public interface PersonService { 
    public void save(String name); 
    public void update(String name, Integer personid); 
    public String getPersonName(Integer personid); 
}



PersonServiceBean.java

package cn.itcast.service.impl; 
 
import cn.itcast.service.PersonService; 
 
public class PersonServiceBean implements PersonService{ 
    private String user = null;//提供一个用户,用String类型代表 
 
    public String getUser() { 
        return user; 
    } 
 
    public PersonServiceBean(){} 
 
    public PersonServiceBean(String user){ 
        this.user = user; 
    } 
 
    public String getPersonName(Integer personid) { 
        System.out.println("我是getPersonName()方法"); 
        return "xxx"; 
    } 
 
    public void save(String name) { 
        System.out.println("我是save()方法"); 
    } 
 
    public void update(String name, Integer personid) { 
        System.out.println("我是update()方法"); 
    } 
 
}


业务需求是这样的:要对PersonServiceBean这个业务bean里面的所有方法实施拦截,拦截到这个方法之后,我们判断用户是否为null,如果为null,就没有权限去调用里面的业务方法;如果用户不为null,则代表有权限去调用里面的方法。
现在我们要做的第一步是:
1.拦截所有业务方法
2.判断用户是否有权限,有权限就允许他执行业务方法,没有权限不允许他执行业务方法。(是否有权限是根据user是否为null作为判断依据)

如果让大家做,怎么做呢? 估计有些同学很快就想到一个非常简单的方法,比如下面这样写:

 
public String getPersonName(Integer personid) { 
    if(user != null){ 
        System.out.println("我是getPersonName()方法"); 
    } 
    return "xxx"; 
} 


看下这种实现好不好啊?肯定不行。因为这样的话,如果我们要控制更多业务bean里面的业务方法的话,那么我们的这些代码会出现在各个业务bean方法里面,这样这些代码就写死了,不灵活。如果你的权限判断依据一旦发生了改变之后呢,恐怕所有业务方法里面的判断方法都要做修改,显然这种方式是不行的。

还有其他方法吗? 有同学说用过滤器,这里能用过滤器吗?这里不是web应用,没办法用过滤器。
那么我们有没有更好的实现方案呢?以前学习的设计模式里面,曾经有一个代理,看一下我们使用代理技术的话,我们怎样实现拦截,并且在拦截到了之后呢做一下处理。

【客户端】 ---------> 【代理对象】 -----------> 【目标对象】
客服端在调用实际的目标对象之前呢(也就是说调用PersonServiceBean这个业务bean对象之前呢),它先经过代理对象,也就是说客户端调用的是代理对象,这个代理对象实现了目标对象的所有接口,所以通过代理对象的调用,他能把方法的调用委派给目标对象,更形象一下的话,大家可以想象在代理对象的方法内部,它调用了目标对象的方法,那么采用这种方案的话,我们不需要再在目标对象里面编写控制代码,而我们尽需要在代理对象里面对目标对象的方法在执行前做一个权限的判断就可以了。如果有权限,那么在代理对象里面我们才调用目标对象里面的方法。当然如果没有权限的话,就不调用目标对象里的方法。

现在怎么创建这个代理对象呢?代理对象的创建有两种实现方案,一种是静态代理,一种是动态代理,静态代理基本上在企业开发中很少用上,大部分是动态代理。动态代理就是说我们不需要再为目标对象去编写静态代理类了,我们只需要通过JDK或者第三方框架来动态生成代理对象的字节码就可以了。
那么以前在学习代理模式的时候,曾经学习到一个类,这个类是专门用于为目标对象动态创建代理对象,这个类是Proxy
现在就使用JDK提供的代理类来创建代理对象,想想在使用Proxy这个类的时候前提在于什么呢?它的应用前提是目标对象韩国阿朵cc霜必须要实现接口,也就是说目标对象必须面向接口的时候,
我们才能使用JDK提供的Proxy代理类来创建代理对象。如果目标对象它没有实现接口的话,Proxy类是不能使用的,它是有使用条件的。 现在学习下怎样通过代理类来创建代理对象。那么在创建代理对象之前呢,有必要说下前面的概念,前面提到了要拦截这些方法,拦截到方法后再对他们做处理,实际上我们所思考的这些步骤都属
于“横切性关注点”,也就是说对哪些方法实行拦截啊,然后拦截到后做什么工作啊,像这些思考的步骤我们都可以称为横切性关注点。 现在采用代理类Proxy创建代理对象 JDKProxyFactory.java
package cn.itcast.aop; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class JDKProxyFactory implements InvocationHandler { private Object targetObject; // 返回为目标对象创建好的代理对象 public Object createProxyIntance(Object targetObject) { this.targetObject = targetObject; // 第二个参数:代理对象要实现的接口是哪些? // 第三个参数:InvocationHandler,handler在C++是句柄的意思,在这,一般看到这个后缀实际上它是一个回调 // InvocationHandler这个类是一个接口,这里面的回调。。就是说拦截到方法的时候呢,可以触发哪个类里面的拦截方法 // 这里写this,就代表JDKProxyFactory这个类实例本身。。。这就要求这个类必须实现InvocationHandler接口 // 接口里面有个invoke方法 return Proxy.newProxyInstance(this.targetObject.getClass() .getClassLoader(), this.targetObject.getClass().getInterfaces(), this); } // 参数1:代理对象 // 参数2:被拦截到的方法 // 参数3:方法的输入参数 // 当我们对代理对象的业务方法进行调用的时候,这个调用操作会被上面的this拦截到,拦截到后会执行invoke方法 // 也就是说我们客户端调用代理对象的方法的时候,会调用this里面InvocationHandler这个接口决定的invoke方法 // 在invoke方法里面,如果我们要访问目标对象的话,我们就必须把代理对象方法的调用委派给目标对象, // 这里就要把方法的调用委派给method public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = method.invoke(targetObject, args); return result; } /* 在这里就实现了为目标类创建代理对象的代码,它的执行过程是这样的:当客户端调用代理对象的业务方法的时候,那么 代理对象就会执行invoke方法,在这方面里面,再把方法的调用委派给目标对象,也就相当于调用目标对象的method方法, 当然也要把方法的输入参数args输入进去 */ } 现在已经实现了方法的拦截,但是我们还没有判断用户是否有权限......能否执行业务方法.... JDKProxyFactory.java package cn.itcast.aop; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import cn.itcast.service.impl.PersonServiceBean; public class JDKProxyFactory implements InvocationHandler { private Object targetObject; // 返回为目标对象创建好的代理对象 public Object createProxyIntance(Object targetObject) { this.targetObject = targetObject; // 第二个参数:代理对象要实现的接口是哪些? // 第三个参数:InvocationHandler,handler在C++是句柄的意思,在这,一般看到这个后缀实际上它是一个回调 // InvocationHandler这个类是一个接口,这里面的回调。。就是说拦截到方法的时候呢,可以触发哪个类里面的拦截方法 // 这里写this,就代表JDKProxyFactory这个类实例本身。。。这就要求这个类必须实现InvocationHandler接口 // 接口里面有个invoke方法 return Proxy.newProxyInstance(this.targetObject.getClass() .getClassLoader(), this.targetObject.getClass().getInterfaces(), this); } // 参数1:代理对象 // 参数2:被拦截到的方法 // 参数3:方法的输入参数 // 当我们对代理对象的业务方法进行调用的时候,这个调用操作会被上面的this拦截到,拦截到后会执行invoke方法 // 也就是说我们客户端调用代理对象的方法的时候,会调用this里面InvocationHandler这个接口决定的invoke方法 // 在invoke方法里面,如果我们要访问目标对象的话,我们就必须把代理对象方法的调用委派给目标对象, // 这里就要把方法的调用委派给method public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { PersonServiceBean bean = (PersonServiceBean)this.targetObject; Object result = null; if(bean.getUser() != null){ result = method.invoke(targetObject, args); } return result; } /* 在这里就实现了为目标类创建代理对象的代码,它的执行过程是这样的:当客户端调用代理对象的业务方法的时候,那么 代理对象就会执行invoke方法,在这方面里面,再把方法的调用委派给目标对象,也就相当于调用目标对象的method方法, 当然也要把方法的输入参数args输入进去 */ } 我们通过JDKProxyFactory这个工厂类可以创建代理对象,为目标对象创建代理类。当代理对象的方法执行的时候,它会回调这个方法,也就是说invoke这个方法会被调用,在invoke方法里面,首先取得用户名,如果用户名不为null,就代表有权限,我们就让它执行目标对象的业务方法,没权限的话就不让它执行。 现在就在客户端执行下我们的应用,看下当我们不使用任何AOP框架的时候,我们是否也能实现AOP功能,建个单元测试 AOPTest.java package junit.test; import org.junit.BeforeClass; import org.junit.Test; import cn.itcast.aop.JDKProxyFactory; import cn.itcast.service.PersonService; import cn.itcast.service.impl.PersonServiceBean; public class AOPTest { @BeforeClass public static void setUpBeforeClass() throws Exception { } @Test public void proxyTest() { JDKProxyFactory factory = new JDKProxyFactory(); PersonService service = (PersonService) factory.createProxyIntance(new PersonServiceBean("xxx")); /*注意:这个代理对象,我们应该采用接口来引用它,采用return Proxy.newProxyInstance(this.targetObject .getClass().getClassLoader(),this.targetObject.getClass().getInterfaces(), this); 这里面出现的接口引用这个代理对象,因为代理对象是实现了这个接口的 */ service.save("888"); //调用这个方法,invoke就会被调用 } } 运行单元测试,控制台输出:我是save()方法 加入上面代码改动一句如下:PersonService service = (PersonService) factory.createProxyIntance(new PersonServiceBean("")); 运行单元测试代码,没用任何东西输出 可以看到,我们采用动态代理创建技术就可以实现业务需求

 

http://www.java63.com/spring/use_jdk_proxy_implement_aop.html

 

posted @ 2012-10-26 16:19  haiwei.sun  阅读(207)  评论(0编辑  收藏  举报
返回顶部