原文链接: http://blog.csdn.NET/zuoxiaolong8810/article/details/9026775
作者:zuoxiaolong8810(左潇龙)
我特意将本系列改了下名字,原名是《设计模式学习之路》,原因是因为之前写过一篇《spring源码学习之路》,但是我感觉本次写二十三种设计模式,更多的还是分享给各位自己的理解,所以感觉学习之路有点不合适,另外,从本章开始,正式启用本人称呼,LZ。
好了,废话至此,本章接着讨论第二种要介绍的设计模式,代理模式。
LZ不希望写的东西与网络上的资料千篇一律,所以这一系列不会像很多典型文章一章,只是列出这个模式的定义以及一堆适用的情况,然后就是一堆这个模式的各个角色,对于这种罗列LZ并不反对,相比之下会比较清晰,但是如果脱离了实际,就会导致看的人特别是初学者觉得设计模式很陌生很遥远。
LZ并不反对这种教学式的标准模式,但说实话,LZ本人看这种帖子从来都感觉收获不大,看一遍看一遍,到现在都没记住那些各个适用的情况与一堆乱七八糟的角色。
所以LZ探讨代理模式,不会再按这个步骤进行,而是跟着自己的思维进行。
首先代理模式,可以分为两种,一种是静态代理,一种是动态代理。
两种代理从虚拟机加载类的角度来讲,本质上都是一样的,都是在原有类的行为基础上,加入一些多出的行为,甚至完全替换原有的行为。
静态代理采用的方式就是我们手动的将这些行为换进去,然后让编译器帮我们编译,同时也就将字节码在原有类的基础上加入一些其他的东西或者替换原有的东西,产生一个新的与原有类接口相同却行为不同的类型。
说归说,我们来真实的去试验一下,实验的话需要找一个示例,就拿我们的数据库连接来做例子吧。
我们都知道,数据库连接是很珍贵的资源,频繁的开关数据库连接是非常浪费服务器的CPU资源以及内存的,所以我们一般都是使用数据库连接池来解决这一问题,即创造一堆等待被使用的连接,等到用的时候就从池里取一个,不用了再放回去,数据库连接在整个应用启动期间,几乎是不关闭的,除非是超过了最大闲置时间。
但是在程序员编写程序的时候,会经常使用connection.close()这样的方法,去关闭数据库连接,而且这样做是对的,所以你并不能告诉程序员们说,你们使用连接都不要关了,去调用一个其他的类似归还给连接池的方法吧。这是不符合程序员的编程思维的,也很勉强,而且具有风险性,因为程序员会忘的。
解决这一问题的办法就是使用代理模式,因为代理模式可以替代原有类的行为,所以我们要做的就是替换掉connection的close行为。
下面是connection接口原有的样子,我去掉了很多方法,因为都类似,全贴上来占地方。
- import java.sql.SQLException;
- import java.sql.Statement;
- import java.sql.Wrapper;
-
- public interface Connection extends Wrapper {
-
- Statement createStatement() throws SQLException;
-
- void close() throws SQLException;
-
- }
这里只贴了两个方法,但是我们代理的精髓只要两个方法就能掌握,下面使用静态代理,采用静态代理我们通常会使用组合的方式,为了保持对程序猿是透明的,我们实现Connection这个接口。
如下所示。
- import java.sql.SQLException;
- import java.sql.Statement;
-
-
- public class ConnectionProxy implements Connection{
-
- private Connection connection;
-
- public ConnectionProxy(Connection connection) {
- super();
- this.connection = connection;
- }
-
- public Statement createStatement() throws SQLException{
- return connection.createStatement();
- }
-
- public void close() throws SQLException{
- System.out.println("不真正关闭连接,归还给连接池");
- }
-
- }
我们在构造方法中让调用者强行传入一个原有的连接,接下来我们将我们不关心的方法,交给真正的Connection接口去处理,就像createStatement方法一样,而我们将真正关心的close方法用我们自己希望的方式去进行。
此处为了更形象,LZ给出一个本人写的非常简单的连接池,意图在于表明实现的思路。下面我们来看一下连接池的变化,在里面注明了变化点。
- import java.sql.Connection;
- import java.sql.DriverManager;
- import java.sql.SQLException;
- import java.util.LinkedList;
-
- public class DataSource {
-
- private static LinkedList<Connection> connectionList = new LinkedList<Connection>();
-
- static{
- try {
- Class.forName("com.mysql.jdbc.Driver");
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- }
- }
-
- private static Connection createNewConnection() throws SQLException{
- return DriverManager.getConnection("url","username", "password");
- }
-
- private DataSource(){
- if (connectionList == null || connectionList.size() == 0) {
- for (int i = 0; i < 10; i++) {
- try {
- connectionList.add(createNewConnection());
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
- }
- }
-
- public Connection getConnection() throws Exception{
- if (connectionList.size() > 0) {
-
-
- return new ConnectionProxy(connectionList.remove());
- }
- return null;
- }
-
- public void recoveryConnection(Connection connection){
- connectionList.add(connection);
- }
-
- public static DataSource getInstance(){
- return DataSourceInstance.dataSource;
- }
-
- private static class DataSourceInstance{
-
- private static DataSource dataSource = new DataSource();
-
- }
-
- }
连接池我们把它做成单例,所以假设是上述连接池的话,我们代理中的close方法可以再具体化一点,就像下面这样,用归还给连接池的动作取代关闭连接的动作。
- public void close() throws SQLException{
- DataSource.getInstance().recoveryConnection(connection);
- }
好了,这下我们的连接池返回的连接全是代理,就算程序员调用了close方法也只会归还给连接池了。
我们使用代理模式解决了上述问题,从静态代理的使用上来看,我们一般是这么做的。
1,代理类一般要持有一个被代理的对象的引用。
2,对于我们不关心的方法,全部委托给被代理的对象处理。
3,自己处理我们关心的方法。
这种代理是死的,不会在运行时动态创建,因为我们相当于在编译期,也就是你按下CTRL+S的那一刻,就给被代理的对象生成了一个不可动态改变的代理类。
静态代理对于这种,被代理的对象很固定,我们只需要去代理一个类或者若干固定的类,数量不是太多的时候,可以使用,而且其实效果比动态代理更好,因为动态代理就是在运行期间动态生成代理类,所以需要消耗的时间会更久一点。就像上述的情况,其实就比较适合使用静态代理。
下面介绍下动态代理,动态代理是JDK自带的功能,它需要你去实现一个InvocationHandler接口,并且调用Proxy的静态方法去产生代理类。
接下来我们依然使用上面的示例,但是这次该用动态代理处理,我们来试一下看如何做。
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- import java.sql.Connection;
-
-
- public class ConnectionProxy implements InvocationHandler{
-
- private Connection connection;
-
- public ConnectionProxy(Connection connection) {
- super();
- this.connection = connection;
- }
-
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-
- if (Connection.class.isAssignableFrom(proxy.getClass()) && method.getName().equals("close")) {
-
-
-
- DataSource.getInstance().recoveryConnection(connection);
- return null;
- }else {
- return method.invoke(connection, args);
- }
- }
-
- public Connection getConnectionProxy(){
- return (Connection) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Connection.class}, this);
- }
-
- }
上面是我们针对connection写的动态代理,InvocationHandler接口只有一个invoke方法需要实现,这个方法是用来在生成的代理类用回调使用的,关于动态代理的原理一会做详细的分析,这里我们先只关注用法。很显然,动态代理是将每个方法的具体执行过程交给了我们在invoke方法里处理。而具体的使用方法,我们只需要创造一个ConnectionProxy的实例,并且将调用getConnectionProxy方法的返回结果作为数据库连接池返回的连接就可以了。
上述便是我们针对connection做动态代理的方式,但是我们从中得不到任何好处,除了能少写点代码以外,因为这个动态代理还是只能代理Connection这一个接口,如果我们写出这种动态代理的方式的话,说明我们应该使用静态代理处理这个问题,因为它代表我们其实只希望代理一个类就好。从重构的角度来说,其实更简单点,那就是在你发现你使用静态代理的时候,需要写一大堆重复代码的时候,就请改用动态代理试试吧。
通常情况下,动态代理的使用是为了解决这样一种问题,就是我们需要代理一系列类的某一些方法,最典型的应用就是我们前段时间讨论过的springAOP,我们需要创造出一批代理类,切入到一系列类当中的某一些方法中。下面给出一个经常使用的动态代理方式。
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
-
-
- public class DynamicProxy implements InvocationHandler{
-
- private Object source;
-
- public DynamicProxy(Object source) {
- super();
- this.source = source;
- }
-
- public void before(){
- System.out.println("在方法前做一些事,比如打开事务");
- }
-
- public void after(){
- System.out.println("在方法返回前做一些事,比如提交事务");
- }
-
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
-
-
- if (method.getName().equals("toString")) {
- before();
- }
- Object result = method.invoke(source, args);
- if (method.getName().equals("toString")) {
- after();
- }
- return result;
- }
-
- public Object getProxy(){
- return Proxy.newProxyInstance(getClass().getClassLoader(), source.getClass().getInterfaces(), this);
- }
-
-
- }
上述我做了一些注释,其实已经说明一些问题,这个代理类的作用是可以代理任何类,因为它被传入的对象是Object,而不再是具体的类,比如刚才的Connection,这些产生的代理类在调用toString方法时会被插入before方法和after方法。
动态代理有一个强制性要求,就是被代理的类必须实现了某一个接口,或者本身就是接口,就像我们的Connection。
道理其实很简单,这是因为动态代理生成的代理类是继承Proxy类的,并且会实现被你传入newProxyInstance方法的所有接口,所以我们可以将生成的代理强转为任意一个代理的接口或者Proxy去使用,但是Proxy里面几乎全是静态方法,没有实例方法,所以转换成Proxy意义不大,几乎没什么用。假设我们的类没有实现任何接口,那么就意味着你只能将生成的代理类转换成Proxy,那么就算生成了,其实也没什么用,而且就算你传入了接口,可以强转,你也用不了这个没有实现你传入接口的这个类的方法。
你可能会说,假设有个接口A,那我将接口A传给newProxyInstance方法,并代理一个没实现接口A的类B,但类B与接口A有一样的方法可以吗?
答案是可以的,并且JDK的动态代理只认你传入的接口,只要你传入,你就可以强转成这个接口,这个一会解释,但是你无法在invoke方法里调用method.invoke方法,也就是说,你只能全部替换A接口的方法,而不能使用类B中原有与接口A方法描述相同的方法,这是因为invoke中传入的Method的class信息是接口A,而类B因为没实现接口A,所以无法执行传入的Method,会抛出非法参数异常。
下面我贴出测试代码,各位可以自己试一下,具体为何会这样是在后面解释的,这里不再多做解释。
先是一个普通接口。
- public interface TestInterface {
-
- void method1();
-
- void method2();
-
- void method3();
- }
然后是一个类,和接口一模一样的方法,但是就是没实现这个接口。
- public class TestClass{
-
- public void method1() {
- System.out.println("TestClass.method1");
- }
-
- public void method2() {
- System.out.println("TestClass.method2");
- }
-
- public void method3() {
- System.out.println("TestClass.method3");
- }
-
- }
下面是测试类。
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
-
-
- public class DynamicProxy implements InvocationHandler{
-
- private Object source;
-
- public DynamicProxy(Object source) {
- super();
- this.source = source;
- }
-
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- System.out.println("接口的方法全部变成这样了");
-
-
- return null;
- }
-
- public static void main(String[] args) {
-
- TestInterface object = (TestInterface) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{TestInterface.class}, new DynamicProxy(new TestClass()));
- object.method1();
- object.method2();
- object.method3();
- }
- }
上面我们运行就会发现接口的方法全部都只能输出一个很2的字符串了。如果是要继续使用TestClass的方法也不是不行,只要你确认你传入的类包括了所有你传入的接口的方法,只是没实现这些接口而已,那么你可以在invoke中这样使用。
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- System.out.println("before");
- Method sourceMethod = source.getClass().getDeclaredMethod(method.getName(), method.getParameterTypes());
- sourceMethod.setAccessible(true);
- Object result = sourceMethod.invoke(source, args);
- System.out.println("after");
- return result;
- }
这就与你实现接口的表现行为一致了,但是我们本来就只需要一句method.invoke就可以了,就因为没实现接口就要多写两行,所以这种突破JDK动态代理必须实现接口的行为就有点画蛇添足了。因为你本来就实现了该接口的方法,只是差了那一句implements而已。
上面写这个例子只是为了解释LZ当初的疑惑,因为LZ曾一度认为不实现接口就不能使用动态代理,现在想想那时候LZ有点2,呵呵。
好了,从现在开始,我们开始详细讲解动态代理的原理,这算是进阶篇,如果是新手的话,可以跳过下面的内容,因为现在还没必要知道这些,而且弄不好会越看越蒙,不过仅仅是LZ个人建议,你要有耐心,完全可以继续看下去。
接下来我们结合源码去看一下,代理类是如何产生的,首先当然就是要进入Proxy的newProxyInstance方法,这里是产生代理的入口,源码如下。
- public static Object newProxyInstance(ClassLoader loader,
- Class<?>[] interfaces,
- InvocationHandler h)
- throws IllegalArgumentException
- {
- if (h == null) {
- throw new NullPointerException();
- }
-
-
-
-
- Class cl = getProxyClass(loader, interfaces);
-
-
-
-
- try {
- Constructor cons = cl.getConstructor(constructorParams);
- return (Object) cons.newInstance(new Object[] { h });
- } catch (NoSuchMethodException e) {
- throw new InternalError(e.toString());
- } catch (IllegalAccessException e) {
- throw new InternalError(e.toString());
- } catch (InstantiationException e) {
- throw new InternalError(e.toString());
- } catch (InvocationTargetException e) {
- throw new InternalError(e.toString());
- }
- }
这个方法其实很简单,首先获取了代理类的运行时Class引用,然后调用了这个Class中的构造方法,这个构造方法只有一个参数,正是InvocationHandler接口,由此产生了一个代理类的实例。那么关键的地方就在于如何获取的代理类运行时的class信息的呢?我们进入getProxyClass方法看一下。为了方便起见,我直接加注释,这个方法需要解释的地方比较多。
- public static Class<?> getProxyClass(ClassLoader loader,
- Class<?>... interfaces) throws IllegalArgumentException {
-
- if (interfaces.length > 65535) {
- throw new IllegalArgumentException("interface limit exceeded");
- }
-
- Class proxyClass = null;
-
-
- String[] interfaceNames = new String[interfaces.length];
-
- Set interfaceSet = new HashSet();
-
- for (int i = 0; i < interfaces.length; i++) {
-
-
-
-
- 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");
- }
-
-
-
-
-
- if (!interfaceClass.isInterface()) {
- throw new IllegalArgumentException(interfaceClass.getName()
- + " is not an interface");
- }
-
-
-
-
-
- if (interfaceSet.contains(interfaceClass)) {
- throw new IllegalArgumentException("repeated interface: "
- + interfaceClass.getName());
- }
- interfaceSet.add(interfaceClass);
-
- interfaceNames[i] = interfaceName;
- }
-
-
-
-
-
-
-
-
-
- Object key = Arrays.asList(interfaceNames);
-
-
-
-
- Map cache;
- synchronized (loaderToCache) {
-
- cache = (Map) loaderToCache.get(loader);
- if (cache == null) {
- cache = new HashMap();
- loaderToCache.put(loader, cache);
- }
-
-
-
-
-
- }
-
-
-
-
-
-
-
-
-
-
- synchronized (cache) {
-
-
-
-
-
-
-
- do {
-
- Object value = cache.get(key);
- if (value instanceof Reference) {
- proxyClass = (Class) ((Reference) value).get();
- }
-
- if (proxyClass != null) {
-
- return proxyClass;
-
- } else if (value == pendingGenerationMarker) {
-
- try {
- cache.wait();
- } catch (InterruptedException e) {
-
-
-
-
-
- }
- continue;
-
- } else {
-
-
-
-
-
-
- cache.put(key, pendingGenerationMarker);
- break;
- }
- } while (true);
- }
-
- try {
- String proxyPkg = null;
-
-
-
-
-
-
-
- for (int i = 0; i < interfaces.length; i++) {
- int flags = interfaces[i].getModifiers();
- if (!Modifier.isPublic(flags)) {
- 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) {
- proxyPkg = "";
- }
-
- {
-
-
-
- long num;
- synchronized (nextUniqueNumberLock) {
- num = nextUniqueNumber++;
- }
-
- String proxyName = proxyPkg + proxyClassNamePrefix + num;
-
-
-
-
-
-
-
-
-
-
- byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
- proxyName, interfaces);
- try {
-
- proxyClass = defineClass0(loader, proxyName,
- proxyClassFile, 0, proxyClassFile.length);
- } catch (ClassFormatError e) {
-
-
-
-
-
-
- throw new IllegalArgumentException(e.toString());
- }
- }
-
-
- proxyClasses.put(proxyClass, null);
-
- } finally {
-
-
-
-
-
-
-
- synchronized (cache) {
- if (proxyClass != null) {
-
- cache.put(key, new WeakReference(proxyClass));
- } else {
-
- cache.remove(key);
- }
-
- cache.notifyAll();
- }
- }
- return proxyClass;
- }
上面基本上已经解释的很清楚了,下面就是去看一下byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces)这句话是如何处理的,也就是如何得到的代理类的class文件的,我们进去源码看一下,我依然会加上注释。
- public static byte[] generateProxyClass(String paramString, Class[] paramArrayOfClass) {
-
- ProxyGenerator localProxyGenerator = new ProxyGenerator(paramString, paramArrayOfClass);
-
- final byte[] arrayOfByte = localProxyGenerator.generateClassFile();
-
- if (saveGeneratedFiles) {
- AccessController.doPrivileged(new PrivilegedAction() {
- public Object run() {
- try {
- FileOutputStream localFileOutputStream = new FileOutputStream( ProxyGenerator.dotToSlash(this.val$name) + ".class");
- localFileOutputStream.write(arrayOfByte);
- localFileOutputStream.close();
- return null;
- } catch (IOException localIOException) {
- throw new InternalError( "I/O exception saving generated file: " + localIOException);
- }
- }
-
- });
- }
-
- return arrayOfByte;
- }
我们继续跟踪到localProxyGenerator.generateClassFile()这一句当中,依然会加上注释。
其实代理类的class文件并不复杂,还是有很多规律可循的,所以上述过程基本上可以让各位了解下JDK动态代理生成代理类时都生成了什么东西。
下面我们可以调用下JDK中生成Class文件的方法,并且写入到本地文件,然后使用反编译工具来看一下生成的代理类到底是什么样子的。下面是生成文件的测试类。我们暂且将生成的类名写成TestProxy,代理的接口就是我们上面的TestInterface。如下。
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
-
- import sun.misc.ProxyGenerator;
-
-
-
- public class CreateClassTest {
-
- public static void main(String[] args) throws IOException {
- byte[] classFile = ProxyGenerator.generateProxyClass("TestProxy", new Class[]{TestInterface.class});
- File file = new File("F:/TestProxy.class");
- FileOutputStream fos = new FileOutputStream(file);
- fos.write(classFile);
- fos.flush();
- fos.close();
- }
-
- }
生成后,我们反编译过来会是如下格式的JAVA文件。我加入了注释,大致说明了下文件中生成的部分与刚才分析的时候写入的过程的对应关系。
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- import java.lang.reflect.UndeclaredThrowableException;
-
-
- public final class TestProxy extends Proxy
- implements TestInterface
- {
-
- private static Method m1;
- private static Method m5;
- private static Method m3;
- private static Method m4;
- private static Method m0;
- private static Method m2;
-
- public TestProxy(InvocationHandler paramInvocationHandler)
- throws
- {
- super(paramInvocationHandler);
- }
-
- public final boolean equals(Object paramObject)
- throws
- {
- try
- {
- return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
- }
- catch (Error|RuntimeException localError)
- {
- throw localError;
- }
- catch (Throwable localThrowable)
- {
- throw new UndeclaredThrowableException(localThrowable);
- }
- }
-
- public final void method3()
- throws
- {
- try
- {
- this.h.invoke(this, m5, null);
- return;
- }
- catch (Error|RuntimeException localError)
- {
- throw localError;
- }
- catch (Throwable localThrowable)
- {
- throw new UndeclaredThrowableException(localThrowable);
- }
- }
-
- public final void method1()
- throws
- {
- try
- {
- this.h.invoke(this, m3, null);
- return;
- }
- catch (Error|RuntimeException localError)
- {
- throw localError;
- }
- catch (Throwable localThrowable)
- {
- throw new UndeclaredThrowableException(localThrowable);
- }
- }
-
- public final void method2()
- throws
- {
- try
- {
- this.h.invoke(this, m4, null);
- return;
- }
- catch (Error|RuntimeException localError)
- {
- throw localError;
- }
- catch (Throwable localThrowable)
- {
- throw new UndeclaredThrowableException(localThrowable);
- }
- }
-
- public final int hashCode()
- throws
- {
- try
- {
- return ((Integer)this.h.invoke(this, m0, null)).intValue();
- }
- catch (Error|RuntimeException localError)
- {
- throw localError;
- }
- catch (Throwable localThrowable)
- {
- throw new UndeclaredThrowableException(localThrowable);
- }
- }
-
- public final String toString()
- throws
- {
- try
- {
- return (String)this.h.invoke(this, m2, null);
- }
- catch (Error|RuntimeException localError)
- {
- throw localError;
- }
- catch (Throwable localThrowable)
- {
- throw new UndeclaredThrowableException(localThrowable);
- }
- }
-
- static
- {
- try
- {
- m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
- m5 = Class.forName("TestInterface").getMethod("method3", new Class[0]);
- m3 = Class.forName("TestInterface").getMethod("method1", new Class[0]);
- m4 = Class.forName("TestInterface").getMethod("method2", new Class[0]);
- m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
- m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
- return;
- }
- catch (NoSuchMethodException localNoSuchMethodException)
- {
- throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
- }
- catch (ClassNotFoundException localClassNotFoundException)
- {
- throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
- }
- }
- }
看到这里就知道invoke方法是干嘛的了,其实就是生成的代理类对每一个方法的处理就是回调invoke方法。从生成的代理类源文件中也可以发现,每一个Method除了hashCode,toString和equals外,都是与所属的接口绑定的,所以这也就解释了为什么我们不实现这个接口,只传入进入的话,不能直接使用method.invoke,而是要转成source对应的method才可以调用。
好了,代理模式就分析到这里了,这里讲的更多的是代理模式的原理,对于如何使用并没有讲述太多,是因为代理模式在平时工作中用的虽然很多,但我们大多是使用的现成的,原因很简单,就是因为spring的AOP已经给我们弄了一个很好的动态代理的框架,所以我们几乎不需要自己去写,只要明白其原理,知道动态代理和静态代理主要处理的问题是那种的,知道在何处用,也能够用起来得心应手就可以了,当然这只是LZ个人之见,仅供参考。