首先我们需要了解一下什么是代理
代理:就是一个角色代表另一角色完成某些特定的功能。
例如:汽车制造商,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动态代理详解 #### 一、代理模式概述 代理模式是一种软件设计模式,它在客户端和目标对象之间提供了一种间接层。这种模式的主要目的是控制客户端对目标对象的访问,并且可以在不修改原有...
在Java编程中,有时我们需要模拟用户的网络环境,例如设置IE(Internet Explorer)浏览器的代理以进行自动化测试或者数据抓取。这个"java操作IE代理"的主题涉及到如何在Java程序中控制IE的网络代理设置,这通常需要...
Lotus Domino 是一种基于Notes技术的服务器端应用程序, Lotus Domino Java 代理是指在 Domino 服务器上运行的 Java 代理程序,可以用来处理用户请求、提供数据服务等。今天,我们将讨论如何在 Lotus Domino Java ...
Lotus Domino是一个企业级的应用开发平台,而Java代理在Lotus Domino中被用来处理服务器端的任务,例如数据处理、业务逻辑或响应HTTP请求。在这个特定的案例中,`JQueryReportToolJavaAgent`是一个Java代理,它被...
Java代理服务器程序是一种用于转发网络请求的应用,它在客户端与目标服务器之间起到中介的作用。这样的设计使得用户可以通过代理服务器访问互联网,从而实现多种功能,如匿名浏览、负载均衡、缓存优化、网络监控等。...
Java代理机制是Java编程中一个重要的特性,它允许我们在不修改原有代码的基础上,为已有类增加额外的功能。本文将深入探讨两种主要的Java代理实现:JDK动态代理和CGLIB代理。 一、JDK动态代理 JDK动态代理基于接口...
在Java编程语言中,代理模式是一种设计模式,它允许我们为一个对象提供一个代理以控制对这个对象的访问。代理模式通常用于增加额外的功能或在访问原对象时进行额外的操作,比如日志记录、安全控制、性能度量等。Java...
Java 实现免费代理IP的获取方式 并动态实时校验是否有效,java文件项目内含有Jsoup的Jar包(Jsoup是加工过的,含请求),有2个主入口程序: 其一:用于请求代理IP,并立即校验是否是一个有效的代理IP,如果有效,...
当你创建一个Java代理时,你需要定义一个主方法(main method),这是代理运行的入口点。在这个方法中,你可以实现各种业务逻辑。 代理传值通常涉及到以下步骤: 1. **创建参数**:在代理的main方法中,你可以创建...
通过以上步骤,你可以构建一个基本的Java HTTP代理服务器。当然,实际的项目可能会涉及更多细节,如支持HTTPS、代理链、重定向处理、代理认证等。提供的压缩包文件可能包含有关此主题的教程、代码示例和相关资源,...
Java动态代理是Java编程中一个重要的特性,它允许我们在运行时创建代理对象,这些代理对象可以代表并增强原对象的功能。动态代理在很多场景下都非常有用,比如日志记录、性能监控、事务管理等。本示例将带你深入理解...
Java动态代理是Java语言提供的一种高级特性,它允许我们在运行时创建一个代理对象来代替某个接口或类的对象,这个代理对象能够对方法调用进行拦截和处理。在Java中,动态代理主要通过`java.lang.reflect.Proxy`类和`...
Java代理模式是一种设计模式,它在面向对象编程中扮演着重要的角色,主要目的是为了在不修改原有对象的基础上,为对象添加额外的功能或者控制对对象的访问。代理模式的核心思想是通过代理类来间接调用目标类的方法,...
在Java编程中,代理模式是一种常用的结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。代理模式通常用于增加额外的功能或在访问原始对象时进行额外的操作,比如日志记录、性能监控等。Java提供了两...
Java动态代理是Java语言提供的一种在运行时创建代理对象的技术,它允许我们为已存在的接口创建代理类,以便在调用真实目标对象的方法时添加额外的功能或行为。在这个实例中,我们将深入探讨Java动态代理的核心概念,...
Java提供了一个内置的机制来实现动态代理,即`java.lang.reflect.Proxy`类和`java.lang.reflect.InvocationHandler`接口。 1. **定义接口**:与静态代理相同,我们依然需要一个业务接口`Service`。 2. **实现...
Java静态代理模式是一种设计模式,它允许我们为一个对象提供一个代理,以便增强或扩展其功能,同时不改变原有对象的代码。在Java中,静态代理是通过在代理类中显式实现目标接口来实现的。下面将详细介绍静态代理模式...
Java动态代理机制是Java语言提供的一种强大的功能,它允许在运行时创建代理对象来实现特定接口,从而可以灵活地扩展或增强已有代码的功能。在Java中,动态代理主要通过两个类来实现:`java.lang.reflect.Proxy` 和 `...
在Java编程中,获取浏览器的代理设置是一项重要的任务,特别是在开发网络应用或者需要模拟用户网络环境的场景下。本文将详细讲解如何利用Java来获取浏览器的代理设置,并结合提供的文件`registry.jar`和`ICE_...