首先我们需要了解一下什么是代理
代理:就是一个角色代表另一角色完成某些特定的功能。
例如:汽车制造商,4S店,客户
客户购买汽车并不直接与汽车制造商打交道,也不用知道汽车是如何制造的,客户只与4S店打交道,而4S店可以对汽车进行一些包装,提供一些保修、保险等相关售后的服务。
下面我们根据实际的Java例子来说明代理
代理模式有三个角色: 1. 抽象主题角色 2. 代理主题角色 3. 实际被代理角色
其它类通过访问代理主题角色来访问实际被代理角色。
1. 静态代理
抽象主题角色
package com.staticproxy; /** * 抽象主题角色 * @author ljf * */ public interface StaticInterface { public void dosomething(); }代理主题角色
package com.staticproxy; /** * 代理主题角色 * @author ljf * */ public class StaticProxy implements StaticInterface { private StaticInterface staticInterface; public StaticProxy(StaticInterface staticInterface){ this.staticInterface = staticInterface; } public void dosomething() { System.out.println("我正在上班的路上...."); staticInterface.dosomething(); System.out.println("我正在下班的路上...."); } }实际被代理角色
package com.staticproxy; /** * 实际被代理角色 * @author ljf * */ public class StaticTarget implements StaticInterface { public void dosomething() { System.out.println("我正在上班工作呢>>>>>"); } }
静态代理测试
package com.staticproxy; public class TestStaticProxy { /** * @param args */ public static void main(String[] args) { StaticInterface staticTarget = new StaticTarget(); StaticProxy proxy = new StaticProxy(staticTarget); proxy.dosomething(); } }
测试结果
我正在上班的路上.... 我正在上班工作呢>>>>> 我正在下班的路上....
从上例可以看到代理主题角色:StaticProxy实现了对StaticInterface的dosomething()方法,而StaticProxy,Statictarget都实现了StaticInterface接口,通过调用StaticProxy的dosomething()方法我们可以实现对Statictarget的dosomething()方法的调用,而不用在Statictarget的dosomething()方法中作任何实现,这就是代理的作用。代理实现StaticProxy时,Statictarget必需实现StaticInterface接口。因此如果我要在Statictarget的dosomething()方法前后加入更多的功能就需要写更多的主题代理角色,代码会显得很臃肿。因此可以可以动态的生成代理主题来代理所有被代理的对象。这就是动态代理。
2. 动态代理
(1)、简单实现动态代理
下面我们先自己实现一个简单的动态代理功能
类图如下:
首先编写一个抽象代理主题功能的类:Proxy
package com.dynamic.proxy; import java.io.File; import java.io.FileWriter; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import javax.tools.JavaCompiler; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; import javax.tools.JavaCompiler.CompilationTask; /** * 实现抽象代理主题角色 * @author ljf * */ public class Proxy { public static Object newProxyIntenct(Class infac,MyInvocationHandler h) throws Exception{ String br ="\r\n"; String methString =""; Method[] method = infac.getMethods(); for(Method m: method){ methString = " @Override"+ br + " public void "+m.getName()+"() {"+ br + " try {" + br + " Method md ="+ infac.getName()+".class.getMethod(\""+m.getName()+"\");"+ br + " h.myInvoke(this,md);" + br + " }catch (Exception e){ "+ br+ " e.printStackTrace();" + br + " }" + br + " }"; } String src = "package com.dynamic.proxy;" + br + "import java.lang.reflect.Method;" + br + "public class $Proxy implements "+infac.getName()+"{" + br + " private com.dynamic.proxy.MyInvocationHandler h;" + br + " public $Proxy(MyInvocationHandler h) {" + br + " super();" + br + " this.h = h;" + br + " }" + br + br + methString +br + "}"; //创建java目录 MakDirectUtil.createDirect("C:/ljf/workspace/demo/src/com/dynamic/proxy"); //生成java文件 String fileName ="C:\\ljf\\workspace\\demo\\src\\com\\dynamic\\proxy\\$Proxy.java"; System.out.println(fileName); File file = new File(fileName); FileWriter fWriter = new FileWriter(file); fWriter.write(src); fWriter.flush(); fWriter.close(); //生成class文件,jdk6提供的工具类 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null); Iterable units = fileManager.getJavaFileObjects(fileName); CompilationTask task = compiler.getTask(null, fileManager, null, null, null, units); task.call(); fileManager.close(); //装载到内存,生成新对象 URL[] urls = new URL[]{new URL("file:/"+"C:\\ljf\\workspace\\demo\\src\\")}; URLClassLoader loader = new URLClassLoader(urls); Class c = loader.loadClass("com.dynamic.proxy.$Proxy"); //通过有参的构造器反射生成代理类的实例 Constructor ctr = c.getConstructor(MyInvocationHandler.class); Object obj = (Object) ctr.newInstance(h); return obj; } }
代理对象接口:MyInvocationHandler
package com.dynamic.proxy; import java.lang.reflect.Method; /** * 代理对象接口 * @author ljf * */ public interface MyInvocationHandler { void myInvoke(Object o,Method m); }
代理对象:TimeInvocationHandler
package com.dynamic.proxy; import java.lang.reflect.Method; /** * 代理对象 * @author ljf * */ public class TimeInvocationHandler implements MyInvocationHandler { //代理目标对象<Car> private Object target; public TimeInvocationHandler(Object target) { super(); this.target = target; } public void myInvoke(Object o, Method m) { long time1 = System.currentTimeMillis(); System.out.println("启动前时间="+time1); try { m.invoke(target); } catch (Exception e) { e.printStackTrace(); } long time2 = System.currentTimeMillis(); System.out.println("启动后时间"+time2); System.out.println("汽车的启动时间:"+(time2-time1)); } }
被代理对象接口:Moveable
package com.dynamic.proxy; /** * 被代理对象接口 * @author ljf * */ public interface Moveable { public void move(); }
被代理对象:Car
package com.dynamic.proxy; /** * 被代理对象 * @author ljf * */ public class Car implements Moveable { public void move() { int a = 5; int b = 6; int c = 0; for (int i = 0; i < 1000000; i++) { } c = ((a+b)/2)*12; System.out.println("Car moving..Car 的速度是"+c); } }
辅助创建目录功能类:MakDirectUtil
package com.dynamic.proxy; import java.io.File; import java.io.IOException; import java.util.StringTokenizer; /** * 创建目录 * @author ljf * */ public class MakDirectUtil { public static void createDirect(String pathstr) throws IOException{ //创建多级目录 String path = pathstr; //为指定字符串构造一个 string tokenizer。 "/"字符是分隔标记的分隔符。分隔符字符本身不作为标记。 StringTokenizer st = new StringTokenizer(path,"/"); String path1 = st.nextToken()+"/"; String path2 = path1; while(st.hasMoreTokens()) { path1 = st.nextToken()+"/"; path2 += path1; File inbox = new File(path2); if(!inbox.exists()) inbox.mkdir(); } } }
最后是测试类:
package com.dynamic.proxy; /** * 测试动态代理 * @author ljf * */ public class TestCar { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Car car = new Car(); Moveable moveable = null; try { moveable = (Moveable) Proxy.newProxyIntenct(Moveable.class,new TimeInvocationHandler(car)); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } moveable.move(); } }
运行结果:
C:\ljf\workspace\demo\src\com\dynamic\proxy\$Proxy.java 启动前时间=1414722152895 Car moving..Car 的速度是60 启动后时间1414722152895 汽车的启动时间:0
(2)、jdk动态代理
接下来我们了解一下jdk的动态代理是如何实现的。
先看看jdk的动态代理是怎么使用的:
代理关联对象:MyInvocationHandler
package com.jdk.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * 代理关联对象 * @author ljf * */ public class MyInvocationHandler implements InvocationHandler { //代理目标对象 private Object target; public Object bindProxy(Object o) { this.target = o; Object proxy = Proxy.newProxyInstance(o.getClass().getClassLoader(), o.getClass().getInterfaces(), this); return proxy; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("飞机正在跑道上准备起飞....."); Object result = method.invoke(target, args); System.out.println("飞机正在降落着陆的过程中...."); return result; } }被代理对象接口:IFly
package com.jdk.proxy; /** * 被代理对象接口 * @author ljf * */ public interface IFly { public void fly(); }被代理对象:Air
package com.jdk.proxy; /** * 被代理对象 * @author ljf * */ public class Air implements IFly { public void fly() { System.out.println("飞机正在天空中平稳的飞翔...."); } }代理测试:TestjdkProxy
package com.jdk.proxy; /** * jdk动态代理测试类 * @author ljf * */ public class TestjdkProxy { /** * @param args */ public static void main(String[] args) { IFly air = new Air(); MyInvocationHandler handler = new MyInvocationHandler(); IFly proxy = (IFly) handler.bindProxy(air); proxy.fly(); } }测试结果:
飞机正在跑道上准备起飞..... 飞机正在天空中平稳的飞翔.... 飞机正在降落着陆的过程中....用起来很方便吧,但是它究竟是怎么运作的呢,那我们接下来看看jdk源码是如何实现的。从上面看是使用的Proxy的静态方法newProxyInstance()。那我们就首先从它的源码来看:
/** * loader:类加载器 * interfaces:目标对象实现的接口 * h:InvocationHandler的实现类 */ public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { //InvocationHandler不能为空,因为代理对象的所有方法调用实际都是通过委托InvocationHandler的invoke方法 if (h == null) { throw new NullPointerException(); } /* * Look up or generate the designated proxy class. */ //这个是核心的地方,通过提供的ClassLoader和interface列表来产生代理类,具体的实现可以参考getProxyClass0这个方法的实现 Class<?> cl = getProxyClass0(loader, interfaces); // stack walk magic: do not refactor /* * Invoke its constructor with the designated invocation handler. */ //因为代理类继承了Proxy类.而Proxy中定义了构造函数protected Proxy(InvocationHandler h),所以可以反射得到Constructer实例 try { // 调用代理对象的构造方法(也就是$Proxy0(InvocationHandler h)) final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; SecurityManager sm = System.getSecurityManager(); if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) { // create proxy instance with doPrivilege as the proxy class may // implement non-public interfaces that requires a special permission return AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { return newInstance(cons, ih); } }); } else { return newInstance(cons, ih); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } }既然最核心的调用是getProxyClass0(loader, interfaces)方法,我们接着进入该方法源码看看:
/** * 使用特定的类加载器,加载某个类,从而得到此代理对象的Class */ private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { final int CALLER_FRAME = 3; // 0: Reflection, 1: getProxyClass0 2: Proxy 3: caller final Class<?> caller = Reflection.getCallerClass(CALLER_FRAME); final ClassLoader ccl = caller.getClassLoader(); checkProxyLoader(ccl, loader); ReflectUtil.checkProxyPackageAccess(ccl, interfaces); } if (interfaces.length > 65535) {//被代理对象实现的接口不可多于65535(好NB啊) throw new IllegalArgumentException("interface limit exceeded"); } Class proxyClass = null;//初始化代理对象 /* collect interface names to use as key for proxy class cache */ String[] interfaceNames = new String[interfaces.length]; Set interfaceSet = new HashSet(); // 避免接口重复 for (int i = 0; i < interfaces.length; i++) { /* * Verify that the class loader resolves the name of this * interface to the same Class object. */ String interfaceName = interfaces[i].getName();//得到被代理对象接口名称 Class interfaceClass = null; try { interfaceClass = Class.forName(interfaceName, false, loader);///使用类装载器加载类 } catch (ClassNotFoundException e) { } if (interfaceClass != interfaces[i]) { throw new IllegalArgumentException( interfaces[i] + " is not visible from class loader"); } /* * Verify that the Class object actually represents an * interface. */ if (!interfaceClass.isInterface()) {//不是接口抛出异常,jdk动态代理只能实现接口 throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } /* * Verify that this interface is not a duplicate. */ if (interfaceSet.contains(interfaceClass)) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } interfaceSet.add(interfaceClass); interfaceNames[i] = interfaceName; } /* * Using string representations of the proxy interfaces as * keys in the proxy class cache (instead of their Class * objects) is sufficient because we require the proxy * interfaces to be resolvable by name through the supplied * class loader, and it has the advantage that using a string * representation of a class makes for an implicit weak * reference to the class. */ Object key = Arrays.asList(interfaceNames); /* * Find or create the proxy class cache for the class loader. */ Map cache; synchronized (loaderToCache) { //同步类装载器的Cache cache = (Map) loaderToCache.get(loader); if (cache == null) { cache = new HashMap(); //该Cache以类装载器为key,value也为一个Cache loaderToCache.put(loader, cache); } /* * This mapping will remain valid for the duration of this * method, without further synchronization, because the mapping * will only be removed if the class loader becomes unreachable. */ } /* * Look up the list of interfaces in the proxy class cache using * the key. This lookup will result in one of three possible * kinds of values: * null, if there is currently no proxy class for the list of * interfaces in the class loader, * the pendingGenerationMarker object, if a proxy class for the * list of interfaces is currently being generated, * or a weak reference to a Class object, if a proxy class for * the list of interfaces has already been generated. */ synchronized (cache) { /* * Note that we need not worry about reaping the cache for * entries with cleared weak references because if a proxy class * has been garbage collected, its class loader will have been * garbage collected as well, so the entire cache will be reaped * from the loaderToCache map. */ do { //该Cache以Class数组为key以proxyClass为value Object value = cache.get(key); if (value instanceof Reference) { //保存在Cache中的value为Reference proxyClass = (Class) ((Reference) value).get(); } if (proxyClass != null) { // 代理对象已存在: 返回代理对象 return proxyClass; } else if (value == pendingGenerationMarker) { // 其他线程正在创建代理对象,本线程等待 try { cache.wait(); } catch (InterruptedException e) { /* * The class generation that we are waiting for should * take a small, bounded time, so we can safely ignore * thread interrupts here. */ } continue; } else { /* * No proxy class for this list of interfaces has been * generated or is being generated, so we will go and * generate it now. Mark it as pending generation. */ cache.put(key, pendingGenerationMarker); break; } } while (true); } try { String proxyPkg = null; // 代理对象所在的包 /* * 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 (int i = 0; i < interfaces.length; i++) {//遍历Class数组 int flags = interfaces[i].getModifiers();//得到class的修饰符 if (!Modifier.isPublic(flags)) {//修饰符为public的类 String name = interfaces[i].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) {//非public代理类使用sun默认的包路径 // if no non-public proxy interfaces, use sun.proxy package proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } { /* * Choose a name for the proxy class to generate. */ long num; synchronized (nextUniqueNumberLock) { num = nextUniqueNumber++; } //代理对象名称:包名.$Proxy0,$Proxy1等等 String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * Verify that the class loader hasn't already * defined a class with the chosen name. */ /* * 将指定名称代理类的.class文件转化为byte数组 */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces); try { //根据代理类的字节码生成代理类的实例 proxyClass = 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()); } } // 将新创建的ProxyClass放入Chche中 proxyClasses.put(proxyClass, null); } finally { /* * We must clean up the "pending generation" state of the proxy * class cache entry somehow. If a proxy class was successfully * generated, store it in the cache (with a weak reference); * otherwise, remove the reserved entry. In all cases, notify * all waiters on reserved entries in this cache. */ synchronized (cache) { if (proxyClass != null) { cache.put(key, new WeakReference(proxyClass)); } else { cache.remove(key); } cache.notifyAll(); } } return proxyClass; }接着我们再看看newInstance()方法:
private static Object newInstance(Constructor<?> cons, InvocationHandler h) { try { // 生成代理类的实例并把MyInvocationHandler的实例传给它的构造方法 return cons.newInstance(new Object[] {h} ); } catch (IllegalAccessException e) { throw new InternalError(e.toString()); } catch (InstantiationException e) { throw new InternalError(e.toString()); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString()); } } }最后我们再看一下Proxy里面的一些变量
/** 代理对象名称前缀 */ private final static String proxyClassNamePrefix = "$Proxy"; /** 代理对象实例化时需要的的构造参数 */ private final static Class[] constructorParams = { InvocationHandler.class }; /** 类转载器的cache */ private static Map loaderToCache = new WeakHashMap(); /** 表示某个代理对象正在被创建 */ private static Object pendingGenerationMarker = new Object(); /** 代理对象被创建的个数,初始化时为0 */ private static long nextUniqueNumber = 0; /** 此同步过程使代理被创建的数目唯一 */ private static Object nextUniqueNumberLock = new Object(); /** set of all generated proxy classes, for isProxyClass implementation */ private static Map proxyClasses = Collections.synchronizedMap(new WeakHashMap()); /** * the invocation handler for this proxy instance. * @serial */ protected InvocationHandler h;现在,JDK是怎样动态生成代理类的字节的原理我们已经基本了解了。有那么几个关键点1.目标对象必须实现接口;2.目标对象实现的接口数目不得超过65535;3.生成的代理对象都是以$Proxy+数字来命名。所以跟我平时老说的jdk动态代理必须要实现接口的说法一致了吧。
相关推荐
Java代理服务器的实现
Java 实现免费代理IP的获取方式 并动态实时校验是否有效,java文件项目内含有Jsoup的Jar包(Jsoup是加工过的,含请求),有2个主入口程序: 其一:用于请求代理IP,并立即校验是否是一个有效的代理IP,如果有效,...
一个比较完善的Java代理服务器程序,里面代码其全,结构清晰,值得研究。
多线程代理IP池,一直看到有关这方面的技术,最近实现了一个。简单的来说,启动后,会一直定时的获取代理ip,并自动检测代理ip的活跃度。运用多线程的技术,在极短的时间内获取大量的ip进行筛选。架构也比较清楚,...
说在前面:今天我们来聊一聊 Java 中的代理,先来聊聊故事背景: 小明想购买法国某个牌子的香水送给女朋友,但是在国内没有货源售卖,亲自去法国又大费周章了,而小红现在正在法国玩耍,她和小明是好朋友,可以帮...
JAVA设计模式之代理模式实例
一个简单的java动态代理的实例
代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。
这里提供了静态代理和动态代理的入门写法~一看即懂!
一个简单的网络爬虫,获取代理信息以及验证代理是否可以! GetAgentTask.java main 方法测试
而代理是一种基本的设计模式,它是一种为了提供额外的或不同的操作而插入到真 实对象中的某个对象。而Java的动态代理在代理上更进一步,既能动态的创建代理对象,又能动态的调用代理 方法。Java的反射和动态代理机制...
使用继承和组合的方式实现代理模式,对比分析了继承和组合在实现代理上面的不同,具体信息请看博客:http://blog.csdn.net/y_love_f/article/details/46291001
主要讲述Java反射机制与设计模式之一:代理模式的原理与应用;同时详细讲述了Java对代理模式的支持以及Java中动态代理的原理,应用与实践。
Java中的代理是一种常见的设计模式,它可以帮助我们在不改变原有代码逻辑的情况下,对现有对象进行增强或扩展。在代理模式中,主要有两种类型:静态代理和动态代理。 1.静态代理: 静态代理是指在编译期间就已经确定...
干脆用java自己写了个小程序,性能当然和squid、nginx之类的没法比,还好我们的系统负荷不是很重,运行了一个星期基本没问题。 附件是该 java 程序包。 使用方法: 1。 安装java 2。在命令行输入: java -jar ...
cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理
Java 动态代理 - Java 动态代理 - 代理模式 - 静态代理 - 动态代理 - JDK 动态代理 - CGLIB 动态代理 - Javassist 代理 - ASM 代理 这篇文章我们来聊一下 Java 中的动态代理。 动态代理在 Java 中有着广泛...
java设计模式【之】JDK动态代理【源码】【场景:帮爸爸买菜】.rar /** * 代理模式 * 在开发者的角度来看,创建一个代理对象,提供给用户使用,避免用户直接访问真正的对象 * 在用户角度来看,就是普通的类方法...
这是一个经典java动态代理和反射的例子、、这在里分享给大家、、希望对大家有用、、、
代理模式又叫Proxy模式(英文)。所谓的代理,就是一个人或者一个机构代替另一个人或者另一个机构去做一些事情。(类似于中介或者代理商)。