Java动态代理简述

一、代理简介

代理一词含义十分宽泛,例如金融领域的股票发行代理、营销领域的销售代理、以及计算机领域中的代理设计模式等。尽管代理一词被使用的领域如此广泛,但是代理一词的大致的抽象含义是相似的或者说是相同的。代理是一个被委托人委托其执行如下活动:参加活动、行驶权力、执行任务等。这样理解的话,计算机中某个对象或组件的代理就非常好理解了。

计算机领域中代理的概念是一个十分重要的概念,常见的有代理服务器代理设计模式等。在软件开发发展成熟的今天,每个工程的代码量也越来越庞大,带来的一个问题就是一次小小的需求修改就会引起很大的变化,从而可能引进新的BUG.
因此程序员对需求修改都深恶痛绝,而代理设计模式在某种程度上可以缓解这种问题。代理设计模式可以实现在不破坏原有代码的情况下,对原有代码添加额外的功能,从而实现以低的侵入完成原有系统功能的扩展,这种设计也符合里氏替换原则(对修改关闭,对扩展开放)。

二、Java语言的代理

编程语言中的代理分为静态代理动态代理

  • 静态代理:在编译时期就已经确定了静态代理的类型或者说是是在编译时期的时候生成代理的类(class)
  • 动态代理:在运行时期确定代理的类型或者是说在运行时期生成代理的类(class)

像大多数其他语言一样,Java可以轻松的实现静态代理。具体来讲有两种形式:

静态代理

  1. 匿名内部类的形式
abstract class AbstractProxy {AbstractProxy real;public AbstractProxy() {// TODO Auto-generated constructor stub}public AbstractProxy(AbstractProxy real) {this.real = real;};//被代理的方法public abstract void foolbar(String str);
}//某个被代理类
class RealClass extends AbstractProxy {public RealClass() {super();}public RealClass(AbstractProxy real) {super(real);// TODO Auto-generated constructor stub}@Overridepublic void foolbar(String str) {// TODO Auto-generated method stubSystem.out.println("out>>" + str);}}final AbstractProxy realObj = new RealClass();
AbstractProxy proxy = new AbstractProxy() {@Overridepublic void foolbar(String str) {// TODO Auto-generated method stubSystem.out.println("you are proxied by me!");realObj.foolbar(str);}
};

该形式可以实现一个代理,看似是在运行时生成的一个匿名内部类,但是通过测试发现匿名内部类是在编译时期生成的类,这个可以通过匿名内部类类文件来观察,因此其属于静态代理。这种形式的代理看起来不太正常,而且一个代理类只能代理一个接口或者一个抽象类,如果代理多个就必须新增加多个匿名内部类。

  1. 继承被代理类或者实现被代理接口
    这种形式的代理设计类似于设计模式中的装饰器模式或者适配器模式,具体看代码:

//被代理的接口
interface IReal {public void doSomeThing(String str);
}//某个被代理的类
class RealClass implements IReal {@Overridepublic void doSomeThing(String str) {// TODO Auto-generated method stubSystem.out.println("doSomeThing " + str);}}class ProxyClass implements IReal {IReal realObj;public ProxyClass(IReal realObj) {this.realObj = realObj; }@Overridepublic void doSomeThing(String str) {// TODO Auto-generated method stubSystem.out.println("you are proxied by me!");realObj.doSomething();}
}RealClass realObj = new RealClass();
RealClass proxy = new ProxyClass(realObj);

这种形式的代理类型需要在编译时期确定,因此属于静态类型。从某种程度上来看,这种形式和装饰器模式、适配器模式的设计思路相似。缺点同第一种形式一样,如果多个需要代理多个接口就需要重写代理类,让其实现多个被代理的接口;同时在类型转换的时候也会很麻烦。

动态代理

java的动态代理是运行时动态的根据需要被代理的接口列表interfaces生成一个代理类,该代理类实现了接口列表interfaces中的所有方法,然后在方法的内部实际是讲该方法的调用转发给了实现了InvocationHandler接口的对象,顾名思义,该对象让包含代理时被代理方法的代理逻辑。

用法举例:编写一个代理类实现拦截某个被代理方法
具体的使用代码:

interface IProxied1 {public void proxiedMethod1(String str);
}interface IProxied2 {public void proxiedMethod2(String str);
}class Proxied implements IProxied1, IProxied2 {@Overridepublic void proxiedMethod2(String str) {// TODO Auto-generated method stubSystem.out.println("proxiedMethod2 " + str);}@Overridepublic void proxiedMethod1(String str) {// TODO Auto-generated method stubSystem.out.println("proxiedMethod1 " + str);}}class Interceptor implements InvocationHandler {Object proxied;public Interceptor() {// TODO Auto-generated constructor stub}public Interceptor(Object proxied) {// TODO Auto-generated constructor stubthis.proxied = proxied;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// TODO Auto-generated method stubSystem.out.println("I am watching...");//判断是否拦截if(method.getName().equals("proxiedMethod1")) {System.out.println("you are intercepted!!!!!");return null;}return method.invoke(proxied, args);}}

执行代码


public class DynamicProxyDemo {public static void main(String[] str) {IProxied1 proxiedObj = new Proxied();Object proxyObj = Proxy.newProxyInstance(IProxied1.class.getClassLoader(), new Class<?>[]{IProxied1.class, IProxied2.class}, new Interceptor(proxiedObj));((IProxied1)proxyObj).proxiedMethod1("Hello, World!");System.out.println("-------");((IProxied2)proxyObj).proxiedMethod2("Hello, World!");}
}

输出如下:


I am watching...
you are intercepted!!!!!
-------
I am watching...
proxiedMethod2 Hello, World!

比较静态代理和动态代理

一般来讲,静态代理是硬编码去实现一个代理类,如果需要被代理的接口有变动,则需要重新编码代理类;静态绑定的过程将代理类的代理逻辑和代理类的生成绑定到一起了,
所以修改起来不是很方便(解耦不彻底)。其实我们最关注的是代理类的代理逻辑,因此如果将代理的生成自动化(因为代理类的生成的规则是general的,可以泛化。先实现被代理的接口、然后方法转发,就是这么简单。),
而将代理逻辑分离出来,所有的代理逻辑全部发生在这里;通过这样的解耦,代码可维护性会增强、侵入性会减小,这就是动态代理的思想。

具体来讲区别如下图:

静态代理


静态代理的代理类多处出现代理逻辑的代码,并且同时静态代理的代理类需要自己硬编码。

动态代理


动态代理的代理类类似于一个方法路由,对被代理对象的任何被代理方法的调用,都会被该路由转发InvocationHandler代理逻辑处理类中,从而将代理类的生成和代理类的代理逻辑分开。
Java动态代理生成的代理类是直接在内存中按照class文件格式生成了一个二进制文件,然后类加载器加载该二进制类文件,最后实例化一个代理类的实例。

三、Java动态代理源码分析

前面已经介绍了Java动态代理的基本用法,主要涉及到如下几个类和方法如下:(JDK7

  • java.lang.reflect.Proxy

    • public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
    • public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException
  • sun.misc.ProxyGenerator
    • public static byte[] generateProxyClass(final String name, Class[] interfaces)
  • java.lang.reflect.InvocationHandler
    • public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

具体源代码如下:

/*** Returns an instance of a proxy class for the specified interfaces* that dispatches method invocations to the specified invocation* handler.  This method is equivalent to:* <pre>*     Proxy.getProxyClass(loader, interfaces).*         getConstructor(new Class[] { InvocationHandler.class }).*         newInstance(new Object[] { handler });* </pre>** <p>{@code Proxy.newProxyInstance} throws* {@code IllegalArgumentException} for the same reasons that* {@code Proxy.getProxyClass} does.** @param   loader the class loader to define the proxy class* @param   interfaces the list of interfaces for the proxy class*          to implement* @param   h the invocation handler to dispatch method invocations to* @return  a proxy instance with the specified invocation handler of a*          proxy class that is defined by the specified class loader*          and that implements the specified interfaces* @throws  IllegalArgumentException if any of the restrictions on the*          parameters that may be passed to {@code getProxyClass}*          are violated* @throws  NullPointerException if the {@code interfaces} array*          argument or any of its elements are {@code null}, or*          if the invocation handler, {@code h}, is*          {@code null}*/public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{if (h == null) {throw new NullPointerException();}/** Look up or generate the designated proxy class.*/Class<?> cl = getProxyClass(loader, interfaces);/** Invoke its constructor with the designated invocation handler.*/try {Constructor cons = cl.getConstructor(constructorParams);return 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());}}
/*** Returns the {@code java.lang.Class} object for a proxy class* given a class loader and an array of interfaces.  The proxy class* will be defined by the specified class loader and will implement* all of the supplied interfaces.  If a proxy class for the same* permutation of interfaces has already been defined by the class* loader, then the existing proxy class will be returned; otherwise,* a proxy class for those interfaces will be generated dynamically* and defined by the class loader.** <p>There are several restrictions on the parameters that may be* passed to {@code Proxy.getProxyClass}:** <ul>* <li>All of the {@code Class} objects in the* {@code interfaces} array must represent interfaces, not* classes or primitive types.** <li>No two elements in the {@code interfaces} array may* refer to identical {@code Class} objects.** <li>All of the interface types must be visible by name through the* specified class loader.  In other words, for class loader* {@code cl} and every interface {@code i}, the following* expression must be true:* <pre>*     Class.forName(i.getName(), false, cl) == i* </pre>** <li>All non-public interfaces must be in the same package;* otherwise, it would not be possible for the proxy class to* implement all of the interfaces, regardless of what package it is* defined in.** <li>For any set of member methods of the specified interfaces* that have the same signature:* <ul>* <li>If the return type of any of the methods is a primitive* type or void, then all of the methods must have that same* return type.* <li>Otherwise, one of the methods must have a return type that* is assignable to all of the return types of the rest of the* methods.* </ul>** <li>The resulting proxy class must not exceed any limits imposed* on classes by the virtual machine.  For example, the VM may limit* the number of interfaces that a class may implement to 65535; in* that case, the size of the {@code interfaces} array must not* exceed 65535.* </ul>** <p>If any of these restrictions are violated,* {@code Proxy.getProxyClass} will throw an* {@code IllegalArgumentException}.  If the {@code interfaces}* array argument or any of its elements are {@code null}, a* {@code NullPointerException} will be thrown.** <p>Note that the order of the specified proxy interfaces is* significant: two requests for a proxy class with the same combination* of interfaces but in a different order will result in two distinct* proxy classes.** @param   loader the class loader to define the proxy class* @param   interfaces the list of interfaces for the proxy class*          to implement* @return  a proxy class that is defined in the specified class loader*          and that implements the specified interfaces* @throws  IllegalArgumentException if any of the restrictions on the*          parameters that may be passed to {@code getProxyClass}*          are violated* @throws  NullPointerException if the {@code interfaces} array*          argument or any of its elements are {@code null}*///根据一组接口列表interfaces,返回一个由类加载器loader加载的实现了所有interfaces的类对象//如果实现接口列表interfaces的类已经被加载过了;则直接返回缓存的类对象public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces)throws IllegalArgumentException{if (interfaces.length > 65535) {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];// for detecting duplicatesSet<Class<?>> interfaceSet = new HashSet<>();//验证接口列表的接口对于参数给定的类加载器loader是否是可见的;//同时检查接口列表interfaces的合法性(无重复的接口、必须是接口类型)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()) {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.*/List<String> key = Arrays.asList(interfaceNames);/** Find or create the proxy class cache for the class loader.*/Map<List<String>, Object> cache;synchronized (loaderToCache) {cache = loaderToCache.get(loader);if (cache == null) {cache = new HashMap<>();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 的key是接口名数组生成的list;value是一个代理类对象的弱引用Object value = cache.get(key);if (value instanceof Reference) {proxyClass = (Class<?>) ((Reference) value).get();}if (proxyClass != null) {// proxy class already generated: return itreturn proxyClass;//如果是一个标志符号,则说明有另外的线程正在创建代理类;则该线程挂起等待} else if (value == pendingGenerationMarker) {// proxy class being generated: wait for ittry {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;//被唤醒后;继续check是否存在类对象} 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.*///不存在代理类的时候,需要自己去生成代理类;生成代理类之前先置一个状态标志对象pendingGenerationMarkercache.put(key, pendingGenerationMarker);break;}} while (true);}try {String proxyPkg = null;     // package to define proxy class in/** 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++) {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) {     // if no non-public proxy interfaces,proxyPkg = "";          // use the unnamed package}{/** Choose a name for the proxy class to generate.*/long num;synchronized (nextUniqueNumberLock) {num = nextUniqueNumber++;}String proxyName = proxyPkg + proxyClassNamePrefix + num;/** Verify that the class loader hasn't already* defined a class with the chosen name.*//** Generate the specified proxy class.*///重点在这里;调用ProxyGenerator的静态方法去生成一个代理类,得到类文件的字节数组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());}}// add to set of all generated proxy classes, for isProxyClassproxyClasses.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<Class<?>>(proxyClass));} else {cache.remove(key);}cache.notifyAll();//不管创建代理类是否成功,唤醒在cache上面等待的线程}}return proxyClass;}
    /*** Generate a class file for the proxy class.  This method drives the* class file generation process.*///根据得到的方法信息、类信息;按照JVM的规范动态的生成一个代理类的二进制文件//该过程比较复杂涉及到许多JVM的指令private byte[] generateClassFile() {/* ============================================================* Step 1: Assemble ProxyMethod objects for all methods to* generate proxy dispatching code for.*//** Record that proxy methods are needed for the hashCode, equals,* and toString methods of java.lang.Object.  This is done before* the methods from the proxy interfaces so that the methods from* java.lang.Object take precedence over duplicate methods in the* proxy interfaces.*/addProxyMethod(hashCodeMethod, Object.class);addProxyMethod(equalsMethod, Object.class);addProxyMethod(toStringMethod, Object.class);/** Now record all of the methods from the proxy interfaces, giving* earlier interfaces precedence over later ones with duplicate* methods.*/for (int i = 0; i < interfaces.length; i++) {Method[] methods = interfaces[i].getMethods();for (int j = 0; j < methods.length; j++) {addProxyMethod(methods[j], interfaces[i]);}}/** For each set of proxy methods with the same signature,* verify that the methods' return types are compatible.*/for (List<ProxyMethod> sigmethods : proxyMethods.values()) {checkReturnTypes(sigmethods);}/* ============================================================* Step 2: Assemble FieldInfo and MethodInfo structs for all of* fields and methods in the class we are generating.*/try {methods.add(generateConstructor());for (List<ProxyMethod> sigmethods : proxyMethods.values()) {for (ProxyMethod pm : sigmethods) {// add static field for method's Method objectfields.add(new FieldInfo(pm.methodFieldName,"Ljava/lang/reflect/Method;",ACC_PRIVATE | ACC_STATIC));// generate code for proxy method and add itmethods.add(pm.generateMethod());}}methods.add(generateStaticInitializer());} catch (IOException e) {throw new InternalError("unexpected I/O Exception");}if (methods.size() > 65535) {throw new IllegalArgumentException("method limit exceeded");}if (fields.size() > 65535) {throw new IllegalArgumentException("field limit exceeded");}/* ============================================================* Step 3: Write the final class file.*//** Make sure that constant pool indexes are reserved for the* following items before starting to write the final class file.*/cp.getClass(dotToSlash(className));cp.getClass(superclassName);for (int i = 0; i < interfaces.length; i++) {cp.getClass(dotToSlash(interfaces[i].getName()));}/** Disallow new constant pool additions beyond this point, since* we are about to write the final constant pool table.*/cp.setReadOnly();ByteArrayOutputStream bout = new ByteArrayOutputStream();DataOutputStream dout = new DataOutputStream(bout);try {/** Write all the items of the "ClassFile" structure.* See JVMS section 4.1.*/// u4 magic;dout.writeInt(0xCAFEBABE);// u2 minor_version;dout.writeShort(CLASSFILE_MINOR_VERSION);// u2 major_version;dout.writeShort(CLASSFILE_MAJOR_VERSION);cp.write(dout);             // (write constant pool)// u2 access_flags;dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);// u2 this_class;dout.writeShort(cp.getClass(dotToSlash(className)));// u2 super_class;dout.writeShort(cp.getClass(superclassName));// u2 interfaces_count;dout.writeShort(interfaces.length);// u2 interfaces[interfaces_count];for (int i = 0; i < interfaces.length; i++) {dout.writeShort(cp.getClass(dotToSlash(interfaces[i].getName())));}// u2 fields_count;dout.writeShort(fields.size());// field_info fields[fields_count];for (FieldInfo f : fields) {f.write(dout);}// u2 methods_count;dout.writeShort(methods.size());// method_info methods[methods_count];for (MethodInfo m : methods) {m.write(dout);}// u2 attributes_count;dout.writeShort(0); // (no ClassFile attributes for proxy classes)} catch (IOException e) {throw new InternalError("unexpected I/O Exception");}return bout.toByteArray();}

通过反编译生成的动态代理类的文件,可以得到


final class $Proxy0extends Proxyimplements IReal
{private static Method m1;private static Method m3;private static Method m2;private static Method m0;public $Proxy0(InvocationHandler paramInvocationHandler){super(paramInvocationHandler);}public final boolean equals(Object paramObject){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 doSomeThing(String paramString){try{this.h.invoke(this, m3, new Object[] { paramString });//方法的转发;反射调用InvocationHandler的invoke方法return;}catch (Error|RuntimeException localError){throw localError;}catch (Throwable localThrowable){throw new UndeclaredThrowableException(localThrowable);}}public final String toString(){try{return (String)this.h.invoke(this, m2, null);}catch (Error|RuntimeException localError){throw localError;}catch (Throwable localThrowable){throw new UndeclaredThrowableException(localThrowable);}}public final int hashCode(){try{return ((Integer)this.h.invoke(this, m0, null)).intValue();}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") });m3 = Class.forName("trick.IReal").getMethod("doSomeThing", new Class[] { Class.forName("java.lang.String") });m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);return;}catch (NoSuchMethodException localNoSuchMethodException){throw new NoSuchMethodError(localNoSuchMethodException.getMessage());}catch (ClassNotFoundException localClassNotFoundException){throw new NoClassDefFoundError(localClassNotFoundException.getMessage());}}
}

java动态代理的应用在框架中十分广泛,例如Spring框架用动态代理来实现AOPStruts2框架利用动态代理实现拦截器。
AOP中的代理逻辑点又称为切面的切入点(cut point)。另外,实现AOP概念的方式是动态代理,但动态代理的形式有很多种,JDK提供的这种只是其中一种,还有涉及到类加载器加载类前、加载类后植入字节码等形式。

See Also

[1] 彻底理解JAVA动态代理

[2] JDK动态代理实现原理

[3]AOP动态代理的实现机制

转载于:https://www.cnblogs.com/Spground/p/8536155.html

Java动态代理简述相关推荐

  1. java动态代理【一】

    java动态代理的定义:为其他目标类的方法增加切面的逻辑,即在执行目标类方法的时候,先去执行一段如校验检测的逻辑代码.java通俗一点就是生成一个继承目标类的子类,并在每个调用方法都添加一段逻辑. 应 ...

  2. Java动态代理的实现

    动态代理作为代理模式的一种扩展形式,广泛应用于框架(尤其是基于AOP的框架)的设计与开发,本文将通过实例来讲解Java动态代理的实现过程. 友情提示:本文略有难度,读者需具备代理模式相关基础知识,. ...

  3. Java动态代理与Cglib代理

    为什么80%的码农都做不了架构师?>>>    最近又继续回来死磕Spring源码,以前看的也忘得差不多了,这次先把Spring使用的动态代理cglib看了一下,打好基础知识. cg ...

  4. JAVA 动态代理学习记录

    打算用JAVA实现一个简单的RPC框架,看完RPC参考代码之后,感觉RPC的实现主要用到了两个方面的JAVA知识:网络通信和动态代理.因此,先补补动态代理的知识.---多看看代码中写的注释 参考:Ja ...

  5. 初看Mybatis 源码 (二) Java动态代理类

    先抛出一个问题,用过Mybatis的都知道,我们只需要定义一个Dao的接口,在里面写上一些CRUD相关操作,然后配置一下sql映射文件,就可以达到调用接口中的方法,然后执行sql语句的效果,为什么呢? ...

  6. Java初学者如何迈出AOP第一步--使用Java 动态代理实现AOP

    Java初学者如何迈出AOP第一步--使用Java 动态代理实现AOP xBird 原创  (参与分:36,专家分:90)   发表:2004-9-3 上午9:37   版本:1.0   阅读:160 ...

  7. Java动态代理的应用

    先看一下代理模式,这个应该是设计模式中最简单的一个了,类图 代理模式最大的特点就是代理类和实际业务类实现同一个接口(或继承同一父类),代理对象持有一个实际对象的引用,外部调用时操作的是代理对象,而在代 ...

  8. java动态代理上是否能再进行一层代理

    CGLIB动态代理类 import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.p ...

  9. Java 动态代理机制分析及扩展--转

    http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/#icomments http://www.ibm.com/developerworks/c ...

最新文章

  1. thinkphp学习笔记7—多层MVC
  2. 视频分类/动作识别数据库研究现状
  3. 解决ImportError: cannot import name ‘imread‘ from ‘scipy.misc‘
  4. Intelli IDEA导入jar包
  5. hive创建分区表 指定分隔符_HIVE 对于分区表的操作
  6. 用Jenkins自动化搭建测试环境_jenkins基础搭建_入门试炼02
  7. Python爬虫扩展库BeautifulSoup4用法精要
  8. linux常用的日志分析脚本
  9. 单片机原理与应用技术(四)———矩阵键盘汇编语言
  10. 如何使用Javascript复制到剪贴板
  11. Android 多媒体框架stagefright
  12. 测度论与概率论基础(程士宏)学习笔记(二)
  13. oracle切割字符串函数,Oracle字符串分割函数
  14. 关于华为任正非裁员事件
  15. 从银行、保险到证券,揭开大数据在金融行业的应用
  16. 女性电子商务服装评论
  17. Learning Center Probability Map for Detecting Objects in Aerial Images 论文学习笔记
  18. 为什么要在单片机程序中使用结构体和指针
  19. ci框架 反向代理配置_docker-compose配置Nginx反向代理禅道
  20. Three.js - 光源使用详解1(环境光 AmbientLight、点光源 PointLint)

热门文章

  1. JavaScript如何实现字符串拼接操作
  2. 前端面试时面试官想要听到什么答案(关于一些Vue的问题)
  3. 小白转前端,学习哪些知识点才能不走弯路?
  4. 图解HTTP知识框架
  5. mysql -h 日志打印_mysql日志
  6. python的科学计算库有哪些_《用Python进行科学计算》——SciPy数值计算库
  7. LUNA16_Challange数据预处理4
  8. 【Linux】Linux命令大全----常用文件操作命令
  9. Scrapy框架的用法实例
  10. simplejson.scanner.JSONDecodeError: Extra data: line 1 column 22089 - line 1 column 22090