Java静态代理、动态代理与CGLib代理
java的动态代理举足轻重,它同反射原理一直是许多框架的底层实现。今天唠一下。
一、代理模式
代理模式
是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。简言之,代理模式就是设置一个中间代理来控制访问原目标对象,以达到增强原对象的功能和简化访问方式。
代理分静态代理和动态代理两种。
示意图:
二、静态代理
我们平常去电影院看电影的时候,在电影开始的阶段是不是经常会放广告呢?
电影是电影公司委托给影院进行播放的,但是影院可以在播放电影的时候,产生一些自己的经济收益,比如卖爆米花、可乐等,然后在影片开始结束时播放一些广告。
现在用代码来进行模拟。
首先得有一个接口,通用的接口是代理模式实现的基础。这个接口我们命名为 Movie,代表电影播放的能力。
public interface Movie {void play();}
然后,我们要有一个真正的实现这个 Movie 接口的类,和一个只是实现接口的代理类。
public class RealMovie implements Movie {@Overridepublic void play() {// TODO Auto-generated method stubSystem.out.println("您正在观看电影 《肖申克的救赎》");}
}
这个表示真正的影片。它实现了 Movie 接口,play() 方法调用时,影片就开始播放。那么 Proxy 代理呢?
public class Cinema implements Movie { RealMovie movie;public Cinema(RealMovie movie) {super();this.movie = movie;}@Overridepublic void play() {guanggao(true); movie.play(); guanggao(false);} public void guanggao(boolean isStart){if ( isStart ) {System.out.println("电影马上开始了,爆米花、可乐、口香糖9.8折,快来买啊!");} else {System.out.println("电影马上结束了,爆米花、可乐、口香糖9.8折,买回家吃吧!");}} }
Cinema 就是 Proxy 代理对象,它有一个 play() 方法。不过调用 play() 方法时,它进行了一些相关利益的处理,那就是广告。现在,我们编写测试代码。
public class ProxyTest {public static void main(String[] args) {RealMovie realmovie = new RealMovie();Movie movie = new Cinema(realmovie);movie.play();}
}
然后观察结果:
电影马上开始了,爆米花、可乐、口香糖9.8折,快来买啊!
您正在观看电影 《肖申克的救赎》
电影马上结束了,爆米花、可乐、口香糖9.8折,买回家吃
现在可以看到,代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个类。
上面介绍的是静态代理的内容,为什么叫做静态呢?
因为它的类型是事先预定好的,比如上面代码中的 Cinema 这个类。
三、动态代理所涉及的一些接口、类及方法
java动态代理的主要类和接口有:
java.lang.reflect.Proxy
,java.lang.reflect.InvocationHandler
- java.lang.reflect.Proxy:
动态代理机制的主类,提供一组静态方法为一组接口动态的生成对象和代理类。
其包含以下重要方法:
1、public static InvocationHandler getInvocationHandler(Object var0) 该方法用于获取指定代理对象所关联的调用处理器
2、public static Class<?> getProxyClass(ClassLoader var0, Class… var1) 该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
3、public static Object newProxyInstance(ClassLoader var0, Class<?>[]var1, InvocationHandler var2) 方法用于为指定类装载器,一组接口及调用处理器生成动态代理类实例
- java.lang.reflect.InvocationHandler:
调用处理器接口,自定义invoke方法,用于实现对于真正委托类的代理访问
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
- java.lang.ClassLoader:类装载器类
将类的字节码装载到 Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy类与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中。
四、动态代理例子(基于反射技术实现的)
1.方法接口
public interface SayGoodbye {void sayGoodbye(String name);
}
public interface SayHello {void sayHello(String name);
}
2.接口实现
public class goodByeImpl implements SayGoodbye {@Overridepublic void sayGoodbye(String name) {System.out.println("GoodBye"+name);}
}
public class HelloImpl implements SayHello{@Overridepublic void sayHello(String name) {System.out.println("Hello"+name);}
}
3.代理实例类
public class ServiceProxy implements InvocationHandler {private Object target;public void InvokeBefore(){System.out.println("调用前");}public void InvokeAfter(){System.out.println("调用后");}public Object getInstance(Object target) {this.target = target;//target.getClass().getClassLoader() 类加载器,动态代理的类加载器必然和被代理的对象在同一个加载器,所以直接使用//target.getClass().getInterfaces() 实现的接口//this 该方法只负责实例化代理,至于代理业务的流程规范,由InvocationHandler(this)来定义return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this);}@Override//Object proxy代理类//Method method要拦截(优化)的方法//Object[] args方法的参数public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result;//反射方法前调用InvokeBefore();//反射执行方法 相当于调用target.sayHelllo;result = method.invoke(target,args);//反射方法后调用.InvokeAfter();return result;}
}
4.测试类
public class ProxyTest {public static void main(String[] args) {//生成proxy类ServiceProxy proxy = new ServiceProxy();//获取SayGoodbye代理对象,此处需要传入goodByeImpl实例给getInstance方法,最终被newProxyInstance调用SayGoodbye sgServiceProxy = (SayGoodbye) proxy.getInstance(new goodByeImpl());//通过代理对象调用方法sgServiceProxy.sayGoodbye(" lisi");//获取SayHello代理对象SayHello shServiceProxy=(SayHello)proxy.getInstance(new HelloImpl());//调用对象方法shServiceProxy.sayHello(" zhangsan");}
}
5.运行结果
调用前
GoodBye lisi
调用后
调用前
Hello zhangsan
调用后
总结:动态代理如果需求改变,只需修改接口方法以及实现类,而无需修改代理的实例
,实现了解耦。
五、动态代理源码解析
1.Proxy类的静态方法newProxyInstance方法
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException{//验证传入的InvocationHandler是否为空Objects.requireNonNull(h);//克隆代理类实现的接口final Class<?>[] intfs = interfaces.clone();//获得安全管理器final SecurityManager sm = System.getSecurityManager();//检查创建代理类所需的权限if (sm != null) {checkProxyAccess(Reflection.getCallerClass(), loader, intfs);}/** Look up or generate the designated proxy class.* 查找或者生成特定的代理类(如果缓存中存在,则直接获取)*/Class<?> cl = getProxyClass0(loader, intfs);/** Invoke its constructor with the designated invocation handler.*/try {//权限校验if (sm != null) {checkNewProxyPermission(Reflection.getCallerClass(), cl);}//获取参数类型是InvocationHandler.class的代理类构造器final Constructor<?> cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;//如果代理类是不可访问的, 就使用特权将它的构造器设置为可访问if (!Modifier.isPublic(cl.getModifiers())) {AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {cons.setAccessible(true);return null;}});}//传入InvocationHandler实例去构造一个代理类的实例,所有代理类都继承自Proxy,而Proxy构造方法需要InvocationHandler实例作为参数return cons.newInstance(new Object[]{h});} catch (IllegalAccessException|InstantiationException e) {throw new InternalError(e.toString(), e);} catch (InvocationTargetException e) {Throwable t = e.getCause();if (t instanceof RuntimeException) {throw (RuntimeException) t;} else {throw new InternalError(t.toString(), t);}} catch (NoSuchMethodException e) {throw new InternalError(e.toString(), e);}}
从newProxyInstance方法看出,产生代理类核心代码在getProxyClass0
2.Proxy类的静态方法getProxyClass0方法
private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {if (interfaces.length > 65535) {throw new IllegalArgumentException("interface limit exceeded");}//如果由实现给定接口的代理类存在,这将简单地返回缓存的副本;否则,将通过ProxyClassFactory创建代理类return proxyClassCache.get(loader, interfaces);}
getProxyClass0通过类加载器和接口集合去缓存里面获取,如果能找到代理类就直接返回,否则就会调用ProxyClassFactory这个工厂去生成一个代理类,下面我们看下Proxy的静态内部类ProxyClassFactory
3.ProxyClassFactory
1 private static final class ProxyClassFactory2 implements BiFunction<ClassLoader, Class<?>[], Class<?>>3 {4 // prefix for all proxy class names 代理类名称前缀5 private static final String proxyClassNamePrefix = "$Proxy";6 7 // next number to use for generation of unique proxy class names, 用原子类来生成代理类的序号, 保证序号唯一8 private static final AtomicLong nextUniqueNumber = new AtomicLong();9 10 @Override11 public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {12 13 Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);14 for (Class<?> intf : interfaces) {15 /*16 * Verify that the class loader resolves the name of this17 * interface to the same Class object.18 */19 Class<?> interfaceClass = null;20 try {21 interfaceClass = Class.forName(intf.getName(), false, loader);22 } catch (ClassNotFoundException e) {23 }24 //intf是否可以由指定的类加载进行加载25 if (interfaceClass != intf) {26 throw new IllegalArgumentException(27 intf + " is not visible from class loader");28 }29 /*30 * Verify that the Class object actually represents an31 * interface.32 * intf是否是一个接口33 */34 if (!interfaceClass.isInterface()) {35 throw new IllegalArgumentException(36 interfaceClass.getName() + " is not an interface");37 }38 /*39 * Verify that this interface is not a duplicate.40 * intf在数组中是否有重复41 */42 if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {43 throw new IllegalArgumentException(44 "repeated interface: " + interfaceClass.getName());45 }46 }47 // package to define proxy class in 生成代理类的包名48 String proxyPkg = null;49 // 代理类的访问标志, 默认是public final50 int accessFlags = Modifier.PUBLIC | Modifier.FINAL;51 52 /*53 * Record the package of a non-public proxy interface so that the54 * proxy class will be defined in the same package. Verify that55 * all non-public proxy interfaces are in the same package.56 * 验证所有非公共代理接口都在同一个包中57 */58 for (Class<?> intf : interfaces) {59 //获取接口的访问标志60 int flags = intf.getModifiers();61 //如果接口的访问标志不是public, 那么生成代理类的包名和接口包名相同62 if (!Modifier.isPublic(flags)) {63 //生成的代理类的访问标志设置改为final64 accessFlags = Modifier.FINAL;65 String name = intf.getName();66 int n = name.lastIndexOf('.');67 String pkg = ((n == -1) ? "" : name.substring(0, n + 1));68 if (proxyPkg == null) {69 proxyPkg = pkg;70 } else if (!pkg.equals(proxyPkg)) {71 //代理类如果实现不同包的接口, 并且接口都不是public的, 那么就会在这里报错72 throw new IllegalArgumentException(73 "non-public interfaces from different packages");74 }75 }76 }77 78 if (proxyPkg == null) {79 // if no non-public proxy interfaces, use com.sun.proxy package 如果没有非公共代理接口,那生成的代理类都放到默认的包下:com.sun.proxy80 proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";81 }82 83 /*84 * Choose a name for the proxy class to generate.85 * 生成代理类的全限定名, 包名+前缀+序号, 例如:com.sun.proxy.$Proxy086 */87 long num = nextUniqueNumber.getAndIncrement();88 String proxyName = proxyPkg + proxyClassNamePrefix + num;89 90 /*91 * Generate the specified proxy class.92 * 这里是核心, 用ProxyGenerator来生成字节码, 该类放在sun.misc包下93 */94 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(95 proxyName, interfaces, accessFlags);96 //根据二进制文件生成相应的Class实例97 try {98 return defineClass0(loader, proxyName,99 proxyClassFile, 0, proxyClassFile.length);
100 } catch (ClassFormatError e) {101 /*
102 * A ClassFormatError here means that (barring bugs in the
103 * proxy class generation code) there was some other
104 * invalid aspect of the arguments supplied to the proxy
105 * class creation (such as virtual machine limitations
106 * exceeded).
107 */
108 throw new IllegalArgumentException(e.toString());
109 }
110 }
111 }
大boss终于出现了,下面我们分ProxyGenerator.generateClassFile
5.ProxyGenerator.generateClassFile
1 private byte[] generateClassFile() {2 //1、将所有的方法组装成ProxyMethod对象3 //首先为代理类生成toString, hashCode, equals等代理方法4 this.addProxyMethod(hashCodeMethod, Object.class);5 this.addProxyMethod(equalsMethod, Object.class);6 this.addProxyMethod(toStringMethod, Object.class);7 Class[] var1 = this.interfaces;8 int var2 = var1.length;9 10 int var3;11 Class var4;12 //遍历每一个接口的每一个方法, 并生成ProxyMethod对象13 for(var3 = 0; var3 < var2; ++var3) {14 var4 = var1[var3];15 Method[] var5 = var4.getMethods();16 int var6 = var5.length;17 18 for(int var7 = 0; var7 < var6; ++var7) {19 Method var8 = var5[var7];20 this.addProxyMethod(var8, var4);21 }22 }23 24 Iterator var11 = this.proxyMethods.values().iterator();25 26 List var12;27 while(var11.hasNext()) {28 var12 = (List)var11.next();29 checkReturnTypes(var12);30 }31 32 //2、组装要生成的class文件的所有的字段信息和方法信息33 Iterator var15;34 try {35 //添加构造器方法36 this.methods.add(this.generateConstructor());37 var11 = this.proxyMethods.values().iterator();38 39 //遍历缓存中的代理方法40 while(var11.hasNext()) {41 var12 = (List)var11.next();42 var15 = var12.iterator();43 44 while(var15.hasNext()) {45 ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();46 //添加代理类的静态字段, 例如:private static Method m1;47 this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));48 //添加代理类的代理方法49 this.methods.add(var16.generateMethod());50 }51 }52 53 //添加代理类的静态字段初始化方法54 this.methods.add(this.generateStaticInitializer());55 } catch (IOException var10) {56 throw new InternalError("unexpected I/O Exception", var10);57 }58 59 if(this.methods.size() > '\uffff') {60 throw new IllegalArgumentException("method limit exceeded");61 } else if(this.fields.size() > '\uffff') {62 throw new IllegalArgumentException("field limit exceeded");63 } else {64 //3、写入最终的class文件65 //验证常量池中存在代理类的全限定名66 this.cp.getClass(dotToSlash(this.className));67 //验证常量池中存在代理类父类的全限定名68 this.cp.getClass("java/lang/reflect/Proxy");69 var1 = this.interfaces;70 var2 = var1.length;71 72 //验证常量池存在代理类接口的全限定名73 for(var3 = 0; var3 < var2; ++var3) {74 var4 = var1[var3];75 this.cp.getClass(dotToSlash(var4.getName()));76 }77 78 //接下来要开始写入文件了,设置常量池只读79 this.cp.setReadOnly();80 ByteArrayOutputStream var13 = new ByteArrayOutputStream();81 DataOutputStream var14 = new DataOutputStream(var13);82 83 try {84 //1.写入魔数85 var14.writeInt(-889275714);86 //2.写入次版本号87 var14.writeShort(0);88 //3.写入主版本号89 var14.writeShort(49);90 //4.写入常量池91 this.cp.write(var14);92 //5.写入访问修饰符93 var14.writeShort(this.accessFlags);94 //6.写入类索引95 var14.writeShort(this.cp.getClass(dotToSlash(this.className)));96 //7.写入父类索引, 生成的代理类都继承自Proxy97 var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));98 //8.写入接口计数值99 var14.writeShort(this.interfaces.length);
100
101 Class[] var17 = this.interfaces;
102 int var18 = var17.length;
103
104 //9.写入接口集合
105 for(int var19 = 0; var19 < var18; ++var19) {106 Class var22 = var17[var19];
107 var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
108 }
109 //10.写入字段计数值
110 var14.writeShort(this.fields.size());
111 var15 = this.fields.iterator();
112 //11.写入字段集合
113 while(var15.hasNext()) {114 ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
115 var20.write(var14);
116 }
117 //12.写入方法计数值
118 var14.writeShort(this.methods.size());
119 var15 = this.methods.iterator();
120 //13.写入方法集合
121 while(var15.hasNext()) {122 ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
123 var21.write(var14);
124 }
125 //14.写入属性计数值, 代理类class文件没有属性所以为0
126 var14.writeShort(0);
127 //转换成二进制数组输出
128 return var13.toByteArray();
129 } catch (IOException var9) {130 throw new InternalError("unexpected I/O Exception", var9);
131 }
132 }
133 }
动态代理的本质:通过类加载器获取类字节码,通过类实现的接口反射获得该类的属性,方法等,并生成新的字节码文件
6.代理类字节码文件分析 $Proxy0.class
1 package com.sun.proxy;2 3 import com.doubibi.framework.util.proxy.HelloService;4 import java.lang.reflect.InvocationHandler;5 import java.lang.reflect.Method;6 import java.lang.reflect.Proxy;7 import java.lang.reflect.UndeclaredThrowableException;8 9 public final class $Proxy0 extends Proxy10 implements HelloService11 {12 //equals方法13 private static Method m1;14 //HelloService 的sayHello方法15 private static Method m3;16 //toString方法17 private static Method m2;18 //hashCode方法19 private static Method m0;20 21 //构造方法22 public $Proxy0(InvocationHandler paramInvocationHandler)23 throws 24 {25 super(paramInvocationHandler);26 }27 28 public final boolean equals(Object paramObject)29 throws 30 {31 try32 {33 return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();34 }35 catch (Error|RuntimeException localError)36 {37 throw localError;38 }39 catch (Throwable localThrowable)40 {41 throw new UndeclaredThrowableException(localThrowable);42 }43 }44 45 //调用了invocationHandler的invoke方法,invoke执行了HelloService 的sayHello方法46 public final void sayHello()47 throws 48 {49 try50 {51 this.h.invoke(this, m3, null);52 return;53 }54 catch (Error|RuntimeException localError)55 {56 throw localError;57 }58 catch (Throwable localThrowable)59 {60 throw new UndeclaredThrowableException(localThrowable);61 }62 }63 64 public final String toString()65 throws 66 {67 try68 {69 return (String)this.h.invoke(this, m2, null);70 }71 catch (Error|RuntimeException localError)72 {73 throw localError;74 }75 catch (Throwable localThrowable)76 {77 throw new UndeclaredThrowableException(localThrowable);78 }79 }80 81 public final int hashCode()82 throws 83 {84 try85 {86 return ((Integer)this.h.invoke(this, m0, null)).intValue();87 }88 catch (Error|RuntimeException localError)89 {90 throw localError;91 }92 catch (Throwable localThrowable)93 {94 throw new UndeclaredThrowableException(localThrowable);95 }96 }97 98 static99 {100 try
101 {102 m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
103 m3 = Class.forName("com.doubibi.framework.util.proxy.HelloService").getMethod("sayHello", new Class[0]);
104 m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
105 m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
106 return;
107 }
108 catch (NoSuchMethodException localNoSuchMethodException)
109 {110 throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
111 }
112 catch (ClassNotFoundException localClassNotFoundException)
113 {114 throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
115 }
116 }
117 }
源码分析参考自
六、CGLib代理
七、CGLib原理粗略解析
CGLIB类库结构
先看cglib包里有一个主要的包proxy包如下:
在该包中的Enhancer类和MethodInterceptor接口是整个包的核心所在!
Enhancer就是“增强”的意思!主要用于生成动态子类以启用方法拦截,什么意思?这样子讲!cglib类代理的基本思想就是对被代理类生成一个新的类(proxy),该类是继承自被代理类的,并对被代理类方法执行前后执行一些操作,这些操作的通常就是一些回调操作,可以是MethodInterceptor,LazyLoader,CallbackFilter,其中MethodInterceptor是最常用的。
所有被Enhancer关联的对象默认都是实现Factory接口的,该接口提供了一组可以设置对象回调类型的方法,你可以通过调用setUseFactory(false)取消此特性!需要注意的是,cglib是无法代理final修饰的方法的,因为这是java语言规范决定的!
MethodInterceptor是一个提供环绕通知的通用回调接口!Aop中有这样的术语,那就是前置通知,后置通知,环绕通知,非常好理解,就是一个在方法执行前的通知,一个方法执行后的通知,另外一个就是方法执行前后都通知。该接口只有一个intercept()方法:
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,MethodProxy proxy) throws Throwable;
所有对被代理类方法的执行都会跳转到这个方法上面来,而原来的方法则通过反射得到的Method对象或者MethodProxy对象进行调用。
八、CGLib代理例子
- 有一个没有实现任何接口的实体类
- 实现一个MethodInterceptor,方法调用会被转发到该类的intercept()方法。
- 然后在需要使用实体类的时候,通过CGLIB动态代理获取代理对象。
实体类:
public class MyEntity {public void method() {System.out.println("method");}
}
实现一个MethodInterceptor:
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class MyMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) throws Throwable {System.out.println("before:"+method.getName());Object object = proxy.invokeSuper(obj, arg);System.out.println("after:"+method.getName());return object;}
}
测试类:
public class Testcglib {public static void main(String[] args) {//该设置用于输出cglib动态代理产生的类 //Enhancer用于操作底层字节码,生成虚拟子类//System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"D:\\proxy"); Enhancer enhancer = new Enhancer();//继承被代理类enhancer.setSuperclass(MyEntity .class);//设置回调enhancer.setCallback(new MyMethodInterceptor());//生成代理类对象MyEntity myEntity= (MyEntity)enhancer.create();//在调用代理类中方法时会被我们实现的方法拦截器进行拦截myEntity.method();}
}
详细的CGLib源码分析
九、JDK动态代理和CGLIB代理的区别
JDK动态代理模式只能代理接口,如果要代理类那么就不行了。
而CGLIB则是代理类。因此,Spring AOP 会这样子来进行切换,因为Spring AOP 同时支持 CGLIB、ASPECTJ、JDK动态代理,
当你的真实对象有实现接口时,Spring AOP会默认采用JDK动态代理,否则采用cglib代理。同时CGLIB也有其缺陷,那就是必须目标类必须是可以继承的,如果目标类不可继承,那么我们就无法使用CGLIB来增强该类,
我们可以称JDK动态代理实现的AOP为面向接口的动态增强,将CGLIB实现的AOP称为面向子类的动态增强。
在spring中:
- 如果目标对象实现了接口,默认情况下会采用JDK的动态代理去实现AOP
- 如果目标对象实现了接口,可以强制使用CGLIB实现AOP
- 如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
Java静态代理、动态代理与CGLib代理相关推荐
- 静态代理,JDK动态代理和CGLIB代理入门学习
之前面试时面试官问我:"你知道spring中有哪几种代理吗?" 啊?代理?啥子代理?VPN代理吗?嘿嘿,面试官你要种子直说啊......被刷下来了.好吧,入门学习下代理. 为什么需 ...
- Spring中AOP的两种代理方式(Java动态代理和CGLIB代理)
第一种代理即Java的动态代理方式上一篇已经分析,在这里不再介绍,现在我们先来了解下GCLIB代理是什么?它又是怎样实现的?和Java动态代理有什么区别? cglib(Code Generation ...
- java静态和动态的区别_Java 静态与动态的区别
Java 静态与动态的区别 1.静态的属性是类共同拥有的,而动态的属性是类各对象独立拥有的. 2.静态上内存空间上是固定的,动态中内存空间在各个附属类里面分配. 3.分配顺序不同,先分配静态对象的空间 ...
- SSM3==理解静态代理、动态代理Proxy.newProxyInstance、cglib代理==通过纯XML配置spring AOP,通过纯注解配置spring AOP
静态代理: 为什么要代理?在不改动原代码的基础上,丰富调用某个方法时实现的功能. 比如service类中原本update只会更新,但是通过代理类加上了判断权限和输出时间的功能. 其实这些功能也可以写在 ...
- 基于Spring AOP的JDK动态代理和CGLIB代理
一.AOP的概念 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的 ...
- 动态代理(JDK动态代理和CGLIB代理)
一.什么是代理? 代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问.代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理. 二 Jav ...
- 动态代理:JDK动态代理和CGLIB代理的区别
代理模式:代理类和被代理类实现共同的接口(或继承),代理类中存有指向被代理类的索引,实际执行时通过调用代理类的方法.实际执行的是被代理类的方法. 而AOP,是通过动态代理实现的. 一.简单来说: JD ...
- JDK动态代理和CGLIB代理的区别
https://www.cnblogs.com/waves-a/p/8036857.html 转载于:https://www.cnblogs.com/wangjing666/p/11357689.ht ...
- java静态和动态的区别是什么意思_Java中的动态和静态多态性有什么区别?
多态性 1.静态绑定/编译时绑定/早期绑定/方法重载(在同一类) 2.动态绑定/运行时绑定/后期绑定/方法覆盖(在不同的类). 重载示例: class Calculation { void sum(i ...
最新文章
- php 检测是否是微信浏览器,php判断是否是微信浏览器和是否是移动端代码
- 控件无法安装,windows已经阻止此软件因为无法验证发行者
- presonus studio one 5中文版
- 返回函数之循环变量问题
- python语言为什么叫python_Python为什么叫Python,Java又如何而来?
- python基础之“换行符”的应用
- CSS:个人常用按钮样式
- 通达oa 2013 php解密,通达OA漏洞学习 - 安全先师的个人空间 - OSCHINA - 中文开源技术交流社区...
- python+opencv读取文件夹图片并保存
- 骨骼的动作识别数据集_基于关节数据的人体动作识别
- 豆瓣api不能访问了的解决办法
- KeyError: 'data'
- HCIE-Cloud Computing v2.0 lab机考全讲解
- 从redis中取值 然后通过list进行分页查询
- 鸿蒙首推机型,华为Mate 40成首载鸿蒙机型,花粉终于如愿所偿!
- java 解析mp4_Java - 通过优酷网视频播放网址解析出Mp4格式的单个文件
- 倒计时工具类:PYContDownManager
- 数据库的这些你都知道吗?
- Ubuntu 配置第三方动态库的系统环境变量
- 中国边缘公有云市场份额,网心科技位居第四名
热门文章
- ActiveMQ集群
- Django 部署(Nginx+uwsgi)
- TimesTen Warnings and Errors - TT0400 to TT9999 [IDnbsp (
- 关于 mybatis-generator自定义注释生成 使用DefaultCommentGenerator重写来完成
- JQuery-Table斑马线
- Xming + PuTTY 在Windows下远程Linux主机使用图形界面的程序
- runat=server
- 现代谱估计:多窗口谱分析和显著性
- 【转】C 从函数返回数组
- 【Python】可遍历的数据类型有哪些?