要了解JDK动态代理,首先要了解代理,以及代理的用途,下面就稍微简单说一下。
什么是代理???
先百度一下代理。。。
ps:当然没人不知道代理的意思,但是在了解一件事情的时候,必须搞清楚事情的概念,也就是定义,世界上的任何可知的事物都有定义,我们需要知道的是被绝大多数人认可的定义。扯远了,哈哈,回到正题!
java中的代理是指某个对象的一个替代者,该替代者暂时担任了被代理对象的职责,代理对象包含了原始对象的所有方法,而且还有可能会附加一些额外的方法。代理对象就是在不破坏原始对象的前提下发挥和原始对象相同甚至更多的功能,Java中的代理分为静态代理和动态代理两种。
静态代理:
在为某个对象生成代理对象之前,就已经知道代理对象要实现的功能或者发挥的作用,那么只需要重新写一个代理类,该类和原始类实现了相同的接口,然后在代理类的方法中加入某些附加的处理操作,这种代理类对象的行为是确定的,所以称为静态代理。
public interface Eatable{ void eat(); } class Person implements Eatable{ public void eat(){ System.out.println("吃饭"); } } class ProxyPerson implements Eatable{ private Person person; public ProxyPerson(Person person){ this.person = person; } public void eat(){ System.out.println("吃饭之前先要洗手!"); person.eat(); } }
上述代码中,先定义了一个接口Eatable.该接口中有一个eat方法,然后有一个原始类Person实现了该接口,但是此时我们不想用Person对象的eat()方法,因为这种Person在吃饭的时候总是不洗手,这种Person太恶心了!很明确,我们需要一个洗手的Person,这就出现了ProxyPerson,ProxyPerson中组合了Person,ProxyPerson的eat方法中先执行附加的操作,然后在执行真正的eat操作。这就是静态代理。
但是有一个问题,假如我们又要在吃饭之后洗碗,那又得重新写一个ProxyPerson2,包含一个eat方法,该方法先吃饭,然后在洗碗,如果又出现一个需求,我们要的这个Person既要能饭前洗手,还能饭后洗碗,那怎么办?再写一个ProxyPerson3?这样下去,随着我们的需求越来越多,我们的类就会膨胀到爆。这时候动态代理就发挥作用了。
动态代理:
动态代理生成的代理类也和原始类实现了相同的接口,所不同的是,动态代理类是在程序运行期间动态生成的,也就是所,它在eat方法上附加的操作不是固定的,而是由我们指定的,我们想让它怎么eat它就怎么eat,这下好了,妈妈再也不用担心我们乱eat了。
java库中提供了实现动态代理的接口,在java.lang.reflect包下,Proxy类和Invocationhandler类就是实现动态代理的两个主要的东东。下面我们来一步步实现一个动态代理的Person.
/*跟前面一样,先定义一个接口,供原始类和代理类去实现它*/ public interface Eatable{ void eat(); } /*再来定义一个Person类,靠,这个不洗手的东西又出现了。*/ public class Person implements Eatable{ public void eat(){ System,out.println("吃饭..."); } }
下面我们需要一个代理类,但是这个代理类不是我们写出来的,而是动态生成的,怎样才能生成这个代理类呢。我们可以使用
Proxy.newProxyInstance(ClassLoader loader,Class[]<?> interfaces,InvocationHandler h); loader:类加载器,目标类的加载器,这个参数确保我们生成的就是目标类的代理类, interfaces:代理类需要实现的接口列表, h:调用处理器,当调用代理类的方法时,比如eat方法,实际上会调用h.invoke()方法。
所以我们在创造一个代理类时需要者三个参数,loader我们使用目标类的ClassLoader就可以,interfaces我们使用Eatable接口。还有一个InvocationHandler,下面我们先来创建一个InvocationHandler,用来处理方法调用.
public class MyInvocationHandler implements InvocationHandler{ //这是我们需要代理的目标对象 private Eatable eater; //这是我们要附加执行的操作,我们以拦截器的形式实现 private Interceptor[] interceptors; public void setEater(Eatable eater){ this.eater = eater; } public void setInterceptor(Interceptor[] interceptors){ this.interceptors = interceptors; } public MuInvocationHandler(Eatable eater,Interceptor[] interceptors){ this.eater = eater; this.interceptors = interceptors; } /* *proxy:代理类对象 *method:正在执行的方法, *args:执行method需要的参数 */ public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{ if(interceptors != null && interceptors.length > 0){ for(int i = 0;i < interceptors.length;i++){ interceptors[i].before(); } method.invoke(eater,args); for(int j = interceptors.length - 1;j >=0;j--){ interceptors[j].after(); } }else{ method.invoke(eater,args); } } } /*接下来再定义Interceptor接口*/ public interface Interceptor{ void before(); void after(); } /*我们定义三个附加的操作,吃饭前得进入食堂,拿餐具,洗手,然后在吃饭*/ public class DoorInterceptor implements Interceptor{ public void before(){ System.out.println("进入食堂..."); } public void after(){ System.out.println("从食堂出来..."); } } public class GetBlowInterceptor implements Interceptor{ public void before(){ System.out.println("从橱柜拿盘子,碗等餐具..."); } public void after(){ System.out.println("把餐具放回残渣台..."); } } public class WashInterceptor implements Interceptor{ public void before(){ System.out.println("吃饭之前先洗手..."); } public void after(){ System.out.println("饭后应该洗碗..."); } } /*接下来我们写一个测试类,测试一下我们的动态代理*/ public class ProxyTest{ public static void main(String[] args){ Eatable target= new Person();//要代理的对象 Eatable proxyEater = (Eatable)Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new MyInvocationHandler( target, new Interceptors[]{ new DoorInterceptor(), new GetBlowInterceptor(), new WashInterceptor() } ); ); proxyEater.eat(); } }
程序运行结果: 进入食堂... 从橱柜拿盘子,碗等餐具... 吃饭之前先洗手... 吃饭... 饭后应该洗碗... 把餐具放回残渣台... 从食堂出来...
这样就在吃饭这个方法中附加了很多操作,这些操作不是固定的,可以根据需求自定义的添加,而且这样做的好处是被代理的对象也不是固定的,也可以来一个Animal类,也实现了Eatable接口,然后把Animal对象传递给InvocationHandler构造器,这样就会生成一个Animal类的代理类,这种方法有很强的灵活性,经常被用在框架的开发中,比如Spring的AOP,就是采用了jdk动态代理。
除了使用JDK动态代理之外,还可以用cglib实现动态代理,这里就不再赘述。
写了一个多小时了,赶紧喝口水休息一下,谢谢大家参考阅读!