您的位置:首页 > 编程语言 > Java开发

第一章 Java基础之JDK动态代理

2016-03-25 21:36 453 查看
          最近在找工作,许多面试官都比较喜欢问Spring AOP,

          面试官:Spring AOP的原理是什么?

          答:动态代理。

          面试官:动态代理的是怎么实现的?

          答:......................

          工资还想到12K,做梦吧!

          回去后鄙人痛定思痛,冥思苦想一朝悟得何为“动态代理”。各位道友请听我一一道来

          =========================================================================================================================

      何为代理?代理的作用是啥?

          代理其实就是将自己实现的功能交给A,A对这个功能做了一些包装,当B在调用这个功能其实最终还是调用到了自己的。代理的作用就是就是在原有的功能上加上其他的功能。

          何为动态代理?

          提到动态代理之前先说一下静态代理,静态代理就是在代码编译之前代理类已经写好了,就是已经存在Java源文件了;动态代理类是不需要你去手动书写Java源文件,是在程序运行的时候根据你所传参数生成的类生成相应的字节码,再通过字节码去创建代理对象。我们一般写代码就是如果想实现某个功能,就得新建一个java文件,在这个java文件中创建类,在类里面声明属性和方法。但是动态代理却不需要我们去新建java文件就能创建出一个类,在这个类里面就能实现我们想要的功能。

      在动态代理中有两个很重要的接口和类

          接口InvocationHandler,它是代理处理器类,被代理类的方法和增强效果就是在实现了这个接口的类中执行的,在InvocationHandler中有个方法

Object invoke(Object proxy, Method method, Object[] args) throws Throwable


          

这个方法的作用就是通过反射调用被代理对象的方法  ,  proxy就是创建动态代理实例的对象,proxy在这里感觉没啥作用。method就是被代理类要执行的方法,args就是这个方法的参数。

         类Proxy,它的作用就是创建动态代理实例。我们主要调用Proxy的newProxyInstance方法来创建动态代理实例。

          

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException


          参数loader就是被代理类的类加载器,加载代理对象;参数interfaces,就是被代理对象锁实现的接口,proxy创建的代理对象也要实现这个接口;参数h代理处理器类,将增强的方法传给proxy,让proxy创建出的动态代理对象里面有增强的效果。

       动态代理代码实现

 被代理接口          
/**
* 照相机接口
* @author user
*
*/
public interface Camera {
public void photo();

}


被代理类
/**
* 索尼相机
* @author user
*
*/
public class CameraImp implements Camera{
/**
* 照相
*/
public void photo() {
System.out.println("给如花照了一张像");

}
}


代理处理器
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
* 自动ps 其实就是给被代理对象的一些方法进行增强处理
* @author user
*
*/
public class AutoPs implements InvocationHandler{
private Object camera =  null;
public AutoPs(Object camera){
this.camera = camera;
}
/**
* 当Camera的实例类在调用photo方法时,
* 实际上是将photo方法中功能是由invoke这个方法来完成的,
* 在完成的时候还带上了附加功能
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("自动给照片美白,使得每张照片都貌美如花"+method.getName());
method.invoke(camera, args);
return null;
}
}


测试类
/**
* 照相机代理,在照相的同时将美白功能加上
* @author user
*
*/
public class CameraProxyTest {

public static void main(String[] args){
//被代理对象
CameraImp camera = new CameraImp();
//代理对象处理器,动态代理对象调执行方法的时候,实际就是调用处理器的invoke方法
AutoPs  ps = new AutoPs(camera);
/*
* 生成动态代理对象cameraPs,创建的这个动态代理实例实现了被代理接口
* 被代理接口就是别代理对象实现了的接口,在这里就是Camera接口
*/
Camera cameraPs = (Camera)Proxy.newProxyInstance(camera.getClass().getClassLoader(), camera.getClass().getInterfaces(), ps);
/*
* 动态代理对象由于实现了被代理接口
*  动态代理对象在执行方法的时候是由代理对象处理器的invoke方法在执行
*  invoke方法里面不仅执行了被代理对象的方法,而且还带上了一些额外功能。
*/
cameraPs.photo();

}
}


输出结果
自动给照片美白,使得每张照片都貌美如花photo
给如花照了一张像


Camera cameraPs = (Camera)Proxy.newProxyInstance(camera.getClass().getClassLoader(), camera.getClass().getInterfaces(), ps);
这段代码就是创建出动态代理对象,这个对象继承了Proxy和实现了Carema接口。我们其实可以通过通过反编译工具查看cameraPs的代码的,这里我就模拟写出一个caremaPs的主要代码。
public class CameraPs  extends Proxy implements Camera{
private static Method m;
protected CameraPs(InvocationHandler h) {
super(h);
}

public void photo() {
try {
super.h.invoke(this, m, null);
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

static
{
try
{
m = Class.forName("Camera").getMethod("photo", new Class[0]);
}
catch(NoSuchMethodException nosuchmethodexception)
{
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
}
catch(ClassNotFoundException classnotfoundexception)
{
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}
上面这段代码我调用里面的photo方法会出错,晕,这个类是为了更好地理解proxy创建的动态代理对象是什么样的啊

总结

1.创建被代理接口Camera和被代理对象CamearaImp;
2.创建代理处理器AutoPs,AutoPs实现了InvocationHandler接口,并实现了invoke方法,在这个方法中有附加效果和执行Carema的方法
3代理创建器Proxy的newProxyInstance方法创建动态的代理对象,newProxyInstance中要传入被代理类加载器,被代理接口和代理处理器。
创建的动态代理对象实现了被代理接口,实现接口的方法中调的是AutoPs的invoke方法。

上面所讲的就是我们在Spring AOP中所用的JDK代理,看起来屌屌的,但是有个缺点就是被代理对象必须要实现接口,cglib代理就不需要这么做。明天再看看cglib的实现原理是什么样的。看完动态代理就用一种迫不及待的想看Spring AOP源码了,这对理解Spring MVC中的HandlerAdapter这块应该有很大的帮助,以前看SpringMVC HandlerAdapter这一块不是特别清楚。

要想更深入理解动态代理请看:http://www.cnblogs.com/flyoung2008/archive/2013/08/11/3251148.html

感觉离12K的薪资期望又近了一点,屌屌的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: