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

代理模式(静态代理和动态代理) JAVA

2017-11-14 21:46 507 查看
转载地址:http://blog.csdn.net/goskalrie/article/details/52458773代理模式 JAVA     代理模式的现实例子就是——中介,很贴切,它的定义:给某个对象提供一个代理,并由代理对象控制对象对原对象的引用。    代理模式包含如下角色:    (1)抽象主题角色:是一个接口,该接口是对象和它的代理共用的接口。    (2)真实主题角色:是实现抽象主题接口的类。    (3)代理角色:内部含有对真实对象的引用,从而可以操作真实对象。代理对象提供与真实对象相同的接口,一边在任何时刻都能代替真实对象。同时,地阿里对象可以在执行真实对象操作时,附加其他操作,相当于对真实对象进行封装。      代理模式有几种:虚拟代理,计数代理,远程代理,动态代理。主要分为:静态代理和动态代理。静态代理比较简单,是有程序员编写的代理类,并在程序运行前就编译好了,而不是由程序动态产生代理类,这就是静态代理。      实现动态代理的关键技术就是反射。静态代理的实现      适用场景:管理员在网站上执行操作,在生成操作结果的同时需要记录操作日志,这是很常见的,此时不需要讲日志语句加到某个操作代码中,这样会污染代码。这种时候就是代理模式派用场的时候了。代理模式可以通过聚合和继承两种实现方式(如下代码所示):public class StaticProxyDemo {public static void main(String[] args) {//测试代码Admin admin = new Admin();Manager m = new AdminProxy(admin);m.doSomething();}}/** 聚合式静态代理*//** 抽象主题接口*/interface Manager {void doSomething();}/** 真实主题类*/class Admin implements Manager {public void doSomething() {System.out.pr4000intln("真实类正在进行操作。。。");}}/** 以聚合方式实现的代理类*/class AdminProxy implements Manager {private Admin admin;public AdminProxy(Admin admin) {super();this.admin = admin;}public void doSomething( ) {System.out.println("代理类开始日志操作");admin.doSomething();System.out.println("代理类结束日志操作");}}继承式代理模式
/*
* 继承式静态代理
*/
/*
* 代理类
*/
class AdminProxy1 extends Admin {
@Override
public void doSomething() {
System.out.println("代理类开始日志操作");
super.doSomething();
System.out.println("代理类结束日志操作");
}
}
public static void main(String[] args) {
//测试代码
AdminProxy1 a = new AdminProxy1();
a.doSomething();
}
上面就是静态代理的两种写法。       聚合实现方式中代理类聚合了被代理类,且代理类及被代理类都实现了同一个接口,可实现灵活多变。继承式的实现方式则不够灵活。动态代理——不需要编写代理类,需要编写事务处理类     静态代理模式的特点就是一个主题类与一个代理类————对应。但是也存在这样的情况,有n个主题类,但是代理类中的“前处理,后处理”都是一样的,进调用主题不一样。也就是说,多个主题类对应一个代理类,共享“前处理,后处理”功能,动态调用所需主题,大大减小了程序规模,这就是动态代理模式的优势所在,什么意思呢,就是指设计一个代理类给所有主题类使用。不用静态代理那样一类主题类建一个主题代理类,n类主题类建n个主题代理类,这样代码的冗余度太高了。    下面我们看看jdk动态代理是如何实现的吧:import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.Random;public class DynamicProxyDemo {public static void main(String[] args) throws Exception {Car car = new Car();InvocationHandler h = new TimeHandler(car);Class<?> cls = car.getClass();/** loader 类加载器* interfaces 实现接口* h InvocationHandler*/Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h);m.move();}}//抽象主题interface Moveable {void move() throws Exception;}//真是主题class Car implements Moveable {@Overridepublic void move() throws Exception{Thread.sleep(new Random().nextInt(1000));System.out.println("汽车行驶中。。。");}}//事务处理器class TimeHandler implements InvocationHandler {private Object target;public TimeHandler(Object target) {super();this.target = target;}public Object invoke(Object proxy, Method method, Object[] args)throws Throwable{long startTime = System.currentTimeMillis();System.out.println("汽车开始行驶...");method.invoke(target, args);long stopTime = System.currentTimeMillis();System.out.println("汽车结束行驶。。。。汽车行驶时间:" + (stopTime - startTime));return null;}}   在测试代码中,Proxy.newProxyInstance()方法有三个参数:类加载器(要进行代理的类)、被代理类实现的接口、事务处理器。所以先实例化Car,实例化InvocationHandler的子类TimeHandler,将各参数传入Proxy的静态方法newProxyInstance()即可获得Car的代理类,前面的静态代理,代理类是我们编写好的,而动态代理则不需要我们去编写代理类,是在程序中动态生成的。 总结一下:     jdk动态代理的步骤:      (1)创建一个实现invocationHandler接口的类,他必须实现invoke()方法      (2)创建被代理的类及接口      (3)调用Proxy的静态方法,创建一个代理类      (4) 通过代理调用方法    你肯定会疑惑,Proxy和InvocationHandler有什么用,直接看源码比较好,我建议为了更深层次的了解jdk动态代理,一定要看以下内容,毕竟要做一个合格的java攻城狮,啥都要理解!!!     public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{Objects.requireNonNull(h);final SecurityManager sm = System.getSecurityManager();if (sm != null) {checkProxyAccess(Reflection.getCallerClass(), loader, interfaces);}/** Look up or generate the designated proxy class.查看或者生成制定的代理类*/Class<?> cl = getProxyClass0(loader, interfaces);/** Invoke its constructor with the designated invocation handler.
* 用指定的调用处理程序调用它的构造函数
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}

final Constructor<?> cons = cl.getConstructor(constructorParams);//获得类的构造函数
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {//当需要代理的类实现一个非public的接口时,因为这样的接口需要特殊的权限,因此调用doPrivilege(native修
饰的方法)创建代理实例
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
可以看到关键代码是Class<?>cl = getProxyClass0(loader,intfs);然后由cl获得构造函数的。那我们来看看getProxyClass0是什么,请看下面的源码:private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {if (interfaces.length > 65535) {throw new IllegalArgumentException("interface limit exceeded");}// If the proxy class defined by the given loader implementing// the given interfaces exists, this will simply return the cached copy;// otherwise, it will create the proxy class via the ProxyClassFactoryreturn proxyClassCache.get(loader, interfaces);}还是没有看到代理类是怎么生成的,只知道代理类是从proxyClassCache中取得的,这个变量是与缓存相关的一个对象,查看该变量的声明与初始化:/*** a cache of proxy classes*/private static final WeakCache<ClassLoader, Class<?>[], Class<?>>proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());可以看到 proxyClassCache 是一个用来缓存代理类的变量,大家知道类变量的特点:在一个虚拟机中类只有一个,一个虚拟机中的类变量也只有一个,且在此处,在Proxy类被加载的时候就赋值了,在赋值操作中有ProxyClassFactory()这么一个构造函数,这个动态代理中的而关键:生成代理类的类文件字节码。继续追代码,找到代理类的生成之处:/*** A factory function that generates, defines and returns the proxy class given* the ClassLoader and array of interfaces.根据给定的类加载器和接口数组生成代理类的工厂类*/private static final class ProxyClassFactoryimplements BiFunction<ClassLoader, Class<?>[], Class<?>>{// prefix for all proxy class names所有代理类名称的前缀private static final String proxyClassNamePrefix = "$Proxy";// next number to use for generation of unique proxy class names用于生成唯一代理类名称的下一个序号private static final AtomicLong nextUniqueNumber = new AtomicLong();@Overridepublic Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);for (Class<?> intf : interfaces) {/** Verify that the class loader resolves the name of this* interface to the same Class object.验证类加载器将此接口的名称解析为实际对象的名称*/Class<?> interfaceClass = null;try {interfaceClass = Class.forName(intf.getName(), false, loader);} catch (ClassNotFoundException e) {}if (interfaceClass != intf) {throw new IllegalArgumentException(intf + " is not visible from class loader");}/** Verify that the Class object actually represents an* interface.验证类对象确实是一个接口*/if (!interfaceClass.isInterface()) {throw new IllegalArgumentException(interfaceClass.getName() + " is not an interface");}/** Verify that this interface is not a duplicate.确保接口唯一*/if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {throw new IllegalArgumentException("repeated interface: " + interfaceClass.getName());}}String proxyPkg = null;     // package to define proxy class in代理类包名int accessFlags = Modifier.PUBLIC | Modifier.FINAL;/** Record the package of a non-public proxy interface so that the* proxy class will be defined in the same package.  Verify that* all non-public proxy interfaces are in the same package.
	     * 记录非公开代理接口的包, 以便将代理类定义在同一个包中。确认所有非公共代理接口都在同一个包中。*/for (Class<?> intf : interfaces) {int flags = intf.getModifiers();if (!Modifier.isPublic(flags)) {accessFlags = Modifier.FINAL;String name = intf.getName();int n = name.lastIndexOf('.');String pkg = ((n == -1) ? "" : name.substring(0, n + 1));if (proxyPkg == null) {proxyPkg = pkg;} else if (!pkg.equals(proxyPkg)) {throw new IllegalArgumentException("non-public interfaces from different packages");}}}if (proxyPkg == null) {// if no non-public proxy interfaces, use com.sun.proxy packageproxyPkg = ReflectUtil.PROXY_PACKAGE + ".";}/** Choose a name for the proxy class to generate.生成代理类名的序号*/long num = nextUniqueNumber.getAndIncrement();String proxyName = proxyPkg + proxyClassNamePrefix + num;//射生成全类名/** Generate the specified proxy class.生成代理类字节码*/byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);try {return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);} catch (ClassFormatError e) {/** A ClassFormatError here means that (barring bugs in the* proxy class generation code) there was some other* invalid aspect of the arguments supplied to the proxy* class creation (such as virtual machine limitations* exceeded).*/throw new IllegalArgumentException(e.toString());}}}
在ProxyClassFactory中,可以看到产生代理类的具体逻辑,大致上是,根据传递的被代理类及其实现的接口生成代理类的字节码加载到缓存中,但是加载到缓存中只是一个.java文件也不能用,所以底层还有编译等操作。到这里,可以大致的看清JDK中动态代理的面孔了,实现的步骤为:1.      创建代理类的源码;2.      对源码进行编译成字节码;3.      将字节码加载到内存;4.      实例化代理类对象并返回给调用者;底层的代码我们看不到,但是我们可以查看其生成的字节码:import java.io.FileOutputStream;import java.io.IOException;import sun.misc.ProxyGenerator;public class GenerateByteCode {public static void main(String[] args) {byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy1", Car.class.getInterfaces());//生成字节码FileOutputStream out = null;try {out = new FileOutputStream(System.getProperty("user.dir")+"\\$Proxy1.class");//会在项目目录中生成字节码文件“user.dir”不用改out.write(classFile);out.flush();} catch (Exception e) {e.printStackTrace();} finally {try {out.close();} catch (IOException e) {e.printStackTrace();}}}}这边 说明一下,spring中也用到了动态代理是cglib动态代理,具体的过程在这边就不做解释了,下次在更新本次小结在ProxyClassFacto动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理。在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样对每一个方法或方法组合进行处理。Proxy 很美很强大,但是仅支持 interface 代理。Java 的单继承机制注定了这些动态代理类们无法实现对 class 的动态代理。好在有cglib为Proxy提供了弥补。class与interface的区别本来就模糊,在java8中更是增加了一些新特性,使得interface越来越接近class,当有一日,java突破了单继承的限制,动态代理将会更加强大。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: