深入剖析代理模式

何为代理模式

代理模式是指,在调用方与被调用方之间加一层代理,用来控制对被调用方的访问。属于结构型模式。

为何需要代理模式

某些特殊情况,调用方不适合直接访问被调用方,通过加一层代理,可以隔绝被调用方的部分细节,同时还可以对被调用方的部分功能做增强。

静态代理

假设有个人叫张三,他想买一套房子,但是他没有渠道,他的朋友赵六人脉广资源多,于是张三就请求赵六帮忙物色房源,代理张三购房。

/**
 * 张三
 */
public class Zhangsan {

    public void buyHouse() {
        System.out.println("张三购买一套200平的独栋别墅,带地下室和车库!");
    }

}
/**
 * 赵六
 */
public class Zhaoliu {

    private Zhangsan zhangsan;
    // 引用被代理对象
    public Zhaoliu(Zhangsan zhangsan) {
        this.zhangsan = zhangsan;
    }

    public void buyHouse() {
        before();
        zhangsan.buyHouse();
        after();
    }

    /**
     * 被代理方法执行之前的增强
     */
    private void before() {
        System.out.println("赵六替张三物色房源!");
    }

    /**
     * 被代理方法执行之后的增强
     */
    private void after() {
        System.out.println("房屋交易完成!");
    }
}
public class Test {
    public static void main(String[] args) {
        Zhaoliu zhaoliu = new Zhaoliu(new Zhangsan());
        zhaoliu.buyHouse();
    }
}

执行结果:

赵六替张三物色房源!
张三购买一套200平的独栋别墅,带地下室和车库!
房屋交易完成!

动态代理

通过上面的实例,大家有没有发现静态代理的弊端,假设现在又有一个李四和王五也有购房的需求,但是他们不像张三一样有一个神通广大的朋友赵六,那他们如果想买房该找谁呢?于是乎,现在需要一个更加通用的解决方案,便是房产中介,不管你是谁,只要是一个人,只要有购房的需求都可以找房产中介。

JDK实现动态代理

/**
 * 人的抽象
 */
public interface IPerson {

    /**
     * 购买房子
     */
    public void buyHouse();

}
/**
 * 李四
 */
public class Lisi implements IPerson {
    @Override
    public void buyHouse() {
        System.out.println("李四想要购买一个500平的大平层!");
    }
}
/**
 * 房产中介:链家
 */
public class Lianjia implements InvocationHandler {

    // 被代理对象
    private IPerson target;

    /**
     * 获取一个代理类的实例(可以理解为链家为您动态分配了一个房产经纪)
     * @param target
     * @return
     */
    public IPerson getInstance(IPerson target) {
        this.target = target;
        return (IPerson) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    /**
     * 代理方法执行时调用的方法
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(target, args);
        after();
        return result;
    }

    private void before() {
        System.out.println("链家为您物色房源信息!");
    }

    private void after() {
        System.out.println("交易结束!");
    }
}
public class Test {
    public static void main(String[] args) throws Exception {
        IPerson person = new Lianjia().getInstance(new Lisi());
        person.buyHouse();
    }
}

执行结果:

链家为您物色房源信息!
李四想要购买一个500平的大平层!
交易结束!

JDK动态代理实现原理刨析

动态代理之所以是动态的,其主要原因是动态的生成了一个代理类,该代理类一般是$Proxyn($Proxy是固定的,n代表一个数字)这样格式的,我们现在用一段程序将该代理类输出到磁盘并反编译,看一下它的庐山真面目。

public class Test {
    public static void main(String[] args) throws Exception {
        // 生成代理类
        IPerson person = new Lianjia().getInstance(new Lisi());
        person.buyHouse();

        // 获取代理类$Proxy0的字节文件
        byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{IPerson.class});
        // 将代理类输出至磁盘D://$Proxy0.class
        FileOutputStream fos = new FileOutputStream("D://$Proxy0.class");
        fos.write(bytes);
        fos.close();
    }
}
public final class $Proxy0 extends Proxy implements IPerson {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) {
        super(var1);
    }

    public final boolean equals(Object var1) {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void buyHouse() {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.test.proxy.IPerson").getMethod("buyHouse");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

通过分析代理类源码,可以得出结论:
1、代理类是继承了JDK的Proxy类,并实现了被代理类的接口;
2、代理类内部持有一个InvocationHandler(触发管理类)对象,方法的调用其实最终会调用InvocationHandler的invoke()方法。

动手实现自己的动态代理

知道了动态代理的实现原理,我们可以手动实现一套属于自己的动态代理API,话不多说,直接上代码

public interface MyInvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable;
}
public class MyProxy {

    private static final String ln = "\r\n";

    protected MyInvocationHandler h;

    protected MyProxy(MyInvocationHandler h) {
        this.h = h;
    }

    public static Object newProxyInstance(MyClassLoader loader,
                                          Class<?>[] interfaces,
                                          MyInvocationHandler h) {
        try {
            //动态生成.java源码文件
            String src = generate(interfaces);
            //将.java源码文件输出至磁盘的指定目录
            String basePath = MyProxy.class.getResource("").getFile();
            String filePath = basePath + "/$Proxy0.java";
            File file = new File(filePath);
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(src.getBytes());
            fos.flush();
            fos.close();
            //将.java源码文件编译成.class字节码文件
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager standardFileManager = compiler.getStandardFileManager(null, null, null);
            Iterable<? extends JavaFileObject> iterable = standardFileManager.getJavaFileObjects(file);
            JavaCompiler.CompilationTask task = compiler.getTask(null, standardFileManager, null, null, null, iterable);
            task.call();
            standardFileManager.close();
            file.delete();
            //将.class字节码文件加载至JVM中
            Class proxyClass = loader.findClass("$Proxy0");
            //返回新生成的代理对象
            Constructor constructor = proxyClass.getConstructor(MyInvocationHandler.class);
            return constructor.newInstance(h);
        } catch (Exception e) {
            e.printStackTrace();
        }
      return null;
    }

    private static String generate(Class<?>[] interfaces) {
        StringBuffer srcBuffer = new StringBuffer();
        srcBuffer.append(MyProxy.class.getPackage() + ";" + ln);
        //拼接导入包
        for (Class<?> interfaceClass : interfaces) {
            String interfaceName = interfaceClass.getName();
            srcBuffer.append("import " + interfaceName + ";" + ln);
        }
        srcBuffer.append("import com.test.proxy.mydynamicproxy.MyInvocationHandler;" + ln);
        srcBuffer.append("import com.test.proxy.mydynamicproxy.MyProxy;" + ln);
        srcBuffer.append("import java.lang.reflect.*;" + ln);

        //拼接类的声明
        srcBuffer.append("public final class $Proxy0 extends MyProxy ");
        for (Class<?> interfaceClass : interfaces) {
            String interfaceSimpleName = interfaceClass.getSimpleName();
            srcBuffer.append("implements " + interfaceSimpleName);
        }
        srcBuffer.append(" {" + ln);

        //拼接方法声明
        Set<Method> methodSet = new LinkedHashSet<Method>();
        for (Class<?> interfaceClass : interfaces) {
            Method[] methods = interfaceClass.getMethods();
            for (Method method : methods) {
                methodSet.add(method);
            }
        }
        for (int i = 0;i < methodSet.size();i++) {
            srcBuffer.append("private static Method m" + i + ";" + ln);
        }
        //拼接构造函数
        srcBuffer.append("public $Proxy0(MyInvocationHandler h) {" + ln);
        srcBuffer.append("super(h);" + ln);
        srcBuffer.append("}" + ln);

        //拼接方法
        int i = 0;
        Iterator<Method> iterator = methodSet.iterator();
        while (iterator.hasNext()) {
            Method method = iterator.next();
            srcBuffer.append("public final "+method.getReturnType().getSimpleName()+" "+method.getName()+"(");
            Class<?>[] parameterTypes = method.getParameterTypes();
            for (int j = 0;j < parameterTypes.length;j++) {
                if (j == parameterTypes.length - 1) {
                    srcBuffer.append(parameterTypes[j].getSimpleName() + "var"+j);
                } else {
                    srcBuffer.append(parameterTypes[j].getSimpleName() + "var"+j+",");
                }
            }
            srcBuffer.append(") {" + ln);
            srcBuffer.append("try {" + ln);
            if (method.getReturnType() != Void.class && method.getReturnType() != void.class) {
                srcBuffer.append("return (" + method.getReturnType().getSimpleName() + ")");
            }
            srcBuffer.append("super.h.invoke(this, m"+i+", new Object[]{");
            for (int j = 0;j < parameterTypes.length;j++) {
                if (j == parameterTypes.length - 1) {
                    srcBuffer.append("var"+j);
                } else {
                    srcBuffer.append("var"+j+",");
                }
            }
            srcBuffer.append("});" + ln);
            srcBuffer.append("} catch (RuntimeException | Error e) {" + ln);
            srcBuffer.append("throw e;" + ln);
            srcBuffer.append("} catch (Throwable t) {" + ln);
            srcBuffer.append("throw new UndeclaredThrowableException(t);" + ln);
            srcBuffer.append("}" + ln);
            srcBuffer.append("}" + ln);
            i++;
        }

        //拼接静态代码块
        srcBuffer.append("static {" + ln);
        srcBuffer.append("try {" + ln);
        i = 0;
        iterator = methodSet.iterator();
        while (iterator.hasNext()) {
            Method method = iterator.next();
            srcBuffer.append("m"+i+" = Class.forName(\""+method.getDeclaringClass().getName()+"\").getMethod(\""+method.getName()+"\"");
            Parameter[] parameters = method.getParameters();
            if (parameters != null && parameters.length > 0) {
                for (Parameter parameter : parameters) {
                    srcBuffer.append(", Class.forName(\""+parameter.getType().getName()+"\")");
                }
            }
            srcBuffer.append("");
            srcBuffer.append(");" + ln);
        }
        srcBuffer.append("} catch (NoSuchMethodException e) {" + ln);
        srcBuffer.append("throw new NoSuchMethodError(e.getMessage());" + ln);
        srcBuffer.append("} catch (ClassNotFoundException e) {" + ln);
        srcBuffer.append("throw new NoClassDefFoundError(e.getMessage());" + ln);
        srcBuffer.append("}" + ln);
        srcBuffer.append("}" + ln);

        srcBuffer.append("}");

        return srcBuffer.toString();
    }
}
public class MyClassLoader extends ClassLoader{

    public Class<?> findClass(String name) {
        String className = MyClassLoader.class.getPackage().getName() + "." + name;
        String basePath = MyClassLoader.class.getResource("").getFile();
        String filePath = basePath + "/" + name + ".class";
        File file = new File(filePath);
        byte[] buffer = null;
        FileInputStream fis = null;
        ByteArrayOutputStream baos = null;
        try {
            fis = new FileInputStream(file);
            baos = new ByteArrayOutputStream();
            byte[] n = new byte[1024];
            int len;
            while ((len = fis.read(n)) != -1) {
                baos.write(n, 0, len);
            }
            buffer = baos.toByteArray();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (fis != null) {
                    fis.close();
                }
                if (baos != null) {
                    baos.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return defineClass(className, buffer, 0, buffer.length);
    }
}
public class MyLianjia implements MyInvocationHandler {

    private IPerson person;

    public MyLianjia(IPerson person) {
        this.person = person;
    }

    public IPerson getInstance() {
        return (IPerson) MyProxy.newProxyInstance(new MyClassLoader(), person.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(person, args);
        after();
        return result;
    }

    private void after() {
        System.out.println("交易结束!!!");
    }

    private void before() {
        System.out.println("中介开始物色房源!!!");
    }

}
public class Test {
    public static void main(String[] args) {
        IPerson person = new MyLianjia(new Lisi()).getInstance();
        person.buyHouse();
    }
}

运行结果:

中介开始物色房源!!!
李四想要购买一个500平的大平层!
交易结束!!!

CGLIB实现动态代理

首先引入Cglib的依赖:

<dependency>
	<groupId>cglib</groupId>
	<artifactId>cglib</artifactId>
	<version>2.2.2</version>
</dependency>
/**
 * Cglib中介类,用于生成代理类对象以及实现增强方法
 * @param <T>
 */
public class CglibIntermediary<T> implements MethodInterceptor {

    public T getInstance(Class<? extends T> clazz) {
        //设置一个增强者对象
        Enhancer enhancer = new Enhancer();
        //设置被代理对象的类型(创建的代理类实际是被代理类的子类)
        enhancer.setSuperclass(clazz);
        //设置回调(最终会回调MethodInterceptor类的interceptor方法)
        enhancer.setCallback(this);
        //生成并返回代理类对象
        return (T) enhancer.create();
    }

    /**
     * 回调的方法,完成对原有方法的增强
     * @param o
     * @param method
     * @param objects
     * @param methodProxy
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        before();
        Object result = methodProxy.invokeSuper(o, objects);
        after();

        return result;
    }

    private void after() {
        System.out.println("Cglib中介交易结束!!!");
    }

    private void before() {
        System.out.println("Cglib中介物色房源!!!");
    }

}

编写测试类:

public class Test {
    public static void main(String[] args) {

        Zhangsan proxy = new CglibIntermediary<Zhangsan>().getInstance(Zhangsan.class);
        proxy.buyHouse();
    }
}

测试结果:

Cglib中介物色房源!!!
张三购买一套200平的独栋别墅,带地下室和车库!
Cglib中介交易结束!!!

CGLIB动态代理实现原理分析

测试代码中加入下面一句话,可以看到cglib动态生成的代码

public class Test {
    public static void main(String[] args) {
        //将cglib动态生成的.class文件输出至D://cglib_proxy_class/目录下
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D://cglib_proxy_class/");

        Zhangsan proxy = new CglibIntermediary<Zhangsan>().getInstance(Zhangsan.class);
        proxy.buyHouse();
    }
}

可以看到其主要生成了以下3个类:

Zhangsan$$EnhancerByCGLIB$$4dde1686:动态代理类,其继承了被代理类
Zhangsan$$FastClassByCGLIB$$3571adf0:被代理类的fastclass类
Zhangsan$$EnhancerByCGLIB$$4dde1686$$FastClassByCGLIB$$92f3b06a:代理类的fastclass类

fastclass类,其内部为每一个方法维护了一个索引(int类型的index),可以快速的找到要执行的方法,不用反射获取,效率更高。

我们从cglib动态代理的执行过程,看一下其执行的原理:

1、创建代理类
代理的入口是我们自己编写的CglibIntermediary类的getInstance()方法,通过调用enhancer.create()生成代理类
Zhangsan$$EnhancerByCGLIB$$4dde1686,其内部会维护一个MethodInterceptor的引用CGLIB$CALLBACK_0,
初始化时会执行静态代码块中的CGLIB$STATICHOOK1()方法
	static {
        CGLIB$STATICHOOK1();
    }
static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("com.test.proxy.staticproxy.Zhangsan$$EnhancerByCGLIB$$4dde1686");
        Class var1;
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$finalize$1$Method = var10000[0];
        CGLIB$finalize$1$Proxy = MethodProxy.create(var1, var0, "()V", "finalize", "CGLIB$finalize$1");
        CGLIB$equals$2$Method = var10000[1];
        CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
        CGLIB$toString$3$Method = var10000[2];
        CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
        CGLIB$hashCode$4$Method = var10000[3];
        CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4");
        CGLIB$clone$5$Method = var10000[4];
        CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
        CGLIB$buyHouse$0$Method = ReflectUtils.findMethods(new String[]{"buyHouse", "()V"}, (var1 = Class.forName("com.test.proxy.staticproxy.Zhangsan")).getDeclaredMethods())[0];
        CGLIB$buyHouse$0$Proxy = MethodProxy.create(var1, var0, "()V", "buyHouse", "CGLIB$buyHouse$0");
    }
2、创建MethodProxy对象
//我们主要关注CGLIB$STATICHOOK1()方法的最后一步创建buyHouse()方法的代理,var1是被代理类,var0为代理类
CGLIB$buyHouse$0$Method = ReflectUtils.findMethods(new String[]{"buyHouse", "()V"}, (var1 = Class.forName("com.test.proxy.staticproxy.Zhangsan")).getDeclaredMethods())[0];
        CGLIB$buyHouse$0$Proxy = MethodProxy.create(var1, var0, "()V", "buyHouse", "CGLIB$buyHouse$0");
   
/**
 * 创建方法的代理
 * c1传入的被代理类,c2传入的是代理类
 */
public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
        MethodProxy proxy = new MethodProxy();
        //被代理方法
        proxy.sig1 = new Signature(name1, desc);
        //代理方法
        proxy.sig2 = new Signature(name2, desc);
        //将代理类和被代理类的引用封装到CreateInfo中
        proxy.createInfo = new MethodProxy.CreateInfo(c1, c2);
        //返回方法的代理
        return proxy;
    }
方法的调用

我们在测试类中调用buyHouse()方法时,实际是调用的代理类的buyHouse()方法

public final void buyHouse() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }
		//判断其持有的MethodInterceptor对象是否为空
		//若不为空,则执行其interceptor方法
		//若为空,则执行原有被代理对象的buyHouse()方法
        if (var10000 != null) {
            var10000.intercept(this, CGLIB$buyHouse$0$Method, CGLIB$emptyArgs, CGLIB$buyHouse$0$Proxy);
        } else {
            super.buyHouse();
        }
    }

来到我们自己编写的实现了MethodInterceptor接口的intercetor()方法的CglibIntermediary类

	/**
     * 回调的方法,完成对原有方法的增强
     * @param o
     * @param method
     * @param objects
     * @param methodProxy
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //方法执行前的操作
        before();
        //调用代理方法
        Object result = methodProxy.invokeSuper(o, objects);
        //方法执行后的操作
        after();

        return result;
    }

调用MethodProxy类的invokeSuper()方法

public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
        	//初始化fastClass
            this.init();
            MethodProxy.FastClassInfo fci = this.fastClassInfo;
            //执行方法的调用
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        }
    }
private void init() {
        if (this.fastClassInfo == null) {
            synchronized(this.initLock) {
                if (this.fastClassInfo == null) {
                    //获取缓存的代理类和被代理类
                    MethodProxy.CreateInfo ci = this.createInfo;
                    MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
                    //生成fastClass
                    fci.f1 = helper(ci, ci.c1);
                    fci.f2 = helper(ci, ci.c2);
                    //获取方法
                    fci.i1 = fci.f1.getIndex(this.sig1);
                    fci.i2 = fci.f2.getIndex(this.sig2);
                    this.fastClassInfo = fci;
                    this.createInfo = null;
                }
            }
        }

    }
/**
 * 动态生成fastClass类
 */
private static FastClass helper(MethodProxy.CreateInfo ci, Class type) {
        Generator g = new Generator();
        g.setType(type);
        g.setClassLoader(ci.c2.getClassLoader());
        g.setNamingPolicy(ci.namingPolicy);
        g.setStrategy(ci.strategy);
        g.setAttemptLoad(ci.attemptLoad);
        return g.create();
    }

至此,我们便分析完成了CGLIB动态代理的全过程。

JDK动态代理和CGLIB动态代理的区别

1、JDK动态代理是java源码中自带的方式,不需要引入额外的依赖。CGLIB需要引入第三方的依赖;
2、JDK动态代理直接生成class文件,CGLIB通过ASM框架生成字节码且生成逻辑复杂,生成代理类的效率比JDK要低;
3、JDK动态代理是通过反射调用的,CGLIB通过fastclass机制调用,其方法的调用效率比JDK要高;
4、JDK的动态代理需要被代理类有实现的接口,代理类也是基于其实现的接口来创建的,被代理的方法也仅限于接口声明的方法。CGLIB的动态代理类是通过继承被代理类的方式创建的,对被代理类的要求更低;
5、若被代理类有实现的接口,或者被代理的就是一个接口没有实现类,可以用JDK的动态代理。若被代理类没有实现的接口,可以用CGLIB的动态代理。

动态代理的实际应用

1、SpringAop,若类符合切点表达式,则Spring在动态创建类对象的时候,会为其床建一个代理对象,方法执行的时候,会依据切面类中定义的方法,对原有方法做增强;
2、MyBatis的Mapper类,MyBatis中的Mapper类只有接口的声明,并没有实现类,MyBatis会自动为当前接口创建一个代理类,并实现其增删改查的方法;
3、MyBatis的插件,MyBatis为了实现功能的扩展,预留了插件的机制,若MyBatis的配置文件中配置了插件类,MyBatis在运行时会为插件可以拦截的类(Executor、StatementHandler、ParameterHandler、ResultSetHandler)动态创建代理对象,并将被代理类的方法调用引入实现了Interceptor接口的intercept()方法中,从而实现功能的动态扩展。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值