Java中三种代理
Java的三种代理模式
1.代理模式
代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.
这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法
举个例子来说明代理的作用:假设我们想邀请一位明星,那么并不是直接连接明星,而是联系明星的经纪人,来达到同样的目的.明星就是一个目标对象,他只要负责活动中的节目,而其他琐碎的事情就交给他的代理人(经纪人)来解决.这就是代理思想在现实中的一个例子
用图表示如下:

代理模式的关键点是:代理对象与目标对象.代理对象是对目标对象的扩展,并会调用目标对象
1.1.静态代理(类似于装饰者模式)
静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类.
下面举个案例来解释:
模拟保存动作,定义一个保存动作的接口:IUserDao.java,然后目标对象实现这个接口的方法UserDao.java,此时如果使用静态代理方
式,就需要在代理对象(UserDaoProxy.java)中也实现IUserDao接口.调用的时候通过调用代理对象的方法来调用目标对象.
需要注意的是,代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法
代码示例:
接口:IUserDao.java
/** * 接口 */ public interface IUserDao { void save(); }
目标对象:UserDao.java
/** * 接口实现 * 目标对象 */ public class UserDao implements IUserDao { public void save() { System.out.println("----已经保存数据!----"); } }
代理对象:UserDaoProxy.java
/** * 代理对象,静态代理 */ public class UserDaoProxy implements IUserDao{ //接收保存目标对象 private IUserDao target; public UserDaoProxy(IUserDao target){ this.target=target; } public void save() { System.out.println("开始事务..."); target.save();//执行目标对象的方法 System.out.println("提交事务..."); } }
测试类:App.java
/** * 测试类 */ public class App { public static void main(String[] args) { //目标对象 UserDao target = new UserDao(); //代理对象,把目标对象传给代理对象,建立代理关系 UserDaoProxy proxy = new UserDaoProxy(target); proxy.save();//执行的是代理的方法 } }
静态代理总结:
1.可以做到在不修改目标对象的功能前提下,对目标功能扩展.
2.缺点:
- 因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.
如何解决静态代理中的缺点呢?答案是可以使用动态代理方式
1.2.动态代理
动态代理有以下特点:
1.代理对象,不需要实现接口
2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
3.动态代理也叫做:JDK代理,接口代理
JDK中生成代理对象的API
代理类所在包:java.lang.reflect.Proxy
JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:
ClassLoader loader,:指定当前目标对象使用类加载器,获取加载器的方法是固定的Class<?>[] interfaces,:目标对象实现的接口的类型,使用泛型方式确认类型InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
代码示例:
接口类IUserDao.java以及接口实现类,目标对象UserDao是一样的,没有做修改.在这个基础上,增加一个代理工厂类
(ProxyFactory.java),将代理类写在这个地方,然后在测试类(需要使用到代理的代码)中先建立目标对象和代理对象的联系,然后代用代理
对象的中同名方法
接口和默认实现:
package aop; public interface IUserDao { void save(); void delete(); void saveAndDelete(); }
实现类:
package aop; public class UserDao implements IUserDao { public void save() { System.out.println("save"); } public void delete() { System.out.println("delete"); } public void saveAndDelete() { save(); delete(); } }
代理工厂类:ProxyFactory.java
package aop; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * 创建动态代理对象 * 动态代理不需要实现接口,但是需要指定接口类型 */ public class ProxyFactory { //维护一个目标对象 private Object target; public ProxyFactory(Object target) { this.target = target; } //给目标对象生成代理对象 public Object getProxyInstance() { return Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("开始事务2"); //运用反射执行目标对象方法 Object returnValue = method.invoke(target, args); System.out.println("提交事务2"); return returnValue; } } ); } }
测试类:
package aop; /** * @author: 乔利强 * @date: 2020/12/11 19:55 * @description: */ public class Client { public static void main(String[] args) { // 目标对象 UserDao target = new UserDao(); // class aop.UserDao System.out.println(target.getClass()); // 给目标对象,创建代理对象 IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance(); // class $Proxy0 内存中动态生成的代理对象 System.out.println(proxy.getClass()); proxy.save(); System.out.println("===1==="); proxy.delete(); System.out.println("===2==="); proxy.saveAndDelete(); } }
结果:
class aop.UserDao class com.sun.proxy.$Proxy0 开始事务2 save 提交事务2 ===1=== 开始事务2 delete 提交事务2 ===2=== 开始事务2 save delete 提交事务2
可以看出saveAndDelete 内部调用save和delete方法没有走拦截到的方法,这个很好理解,内部调用直接走的方法调用,没用代理对象。
如果我们想查看生产的代理类:
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true" );
总结:
代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理
1.3.Cglib代理(基于继承的方式实现)

pom如下:
<!-- https://mvnrepository.com/artifact/cglib/cglib --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.12</version> </dependency>
上面的静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理
Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.
- JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.
- Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)
- Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.
Cglib子类代理实现方法:
1.需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,所以直接引入pring-core-3.2.5.jar即可.
2.引入功能包后,就可以在内存中动态构建子类
3.代理的类不能为final,否则报错
4.目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.
5.如果方法为static,private则无法进行代理。
代码:
dao和接口同上面JDK代理。
Cglib代理工厂
package aop; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * @author: 乔利强 * @date: 2020/12/11 20:05 * @description: */ public class CglibProxyFactory implements MethodInterceptor { public CglibProxyFactory() { } /** * 1、代理对象;2、委托类方法;3、方法参数;4、代理方法的MethodProxy对象。 * * @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 { System.out.println("事务开始......" + method.getName()); Object o1 = methodProxy.invokeSuper(o, objects); System.out.println("事务结束......." + method.getName()); return o1; } }
测试类:
package aop; import net.sf.cglib.core.DebuggingClassWriter; import net.sf.cglib.proxy.Enhancer; /** * @author: 乔利强 * @date: 2020/12/11 20:07 * @description: */ public class CglibClient { public static void main(String[] args) { //在指定目录下生成动态代理类,我们可以反编译看一下里面到底是一些什么东西 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "G:/proxy"); CglibProxyFactory cglibProxy = new CglibProxyFactory(); //jdk需要提供接口,cglib需要是非私有类,且不能处理final关键字修饰的方法 Enhancer enhancer = new Enhancer(); //设置父类 enhancer.setSuperclass(UserDao.class); //设置回调对象 enhancer.setCallback(cglibProxy); UserDao proxy = (UserDao) enhancer.create(); proxy.save(); System.out.println("===1==="); proxy.delete(); System.out.println("===2==="); proxy.saveAndDelete(); } }
结果: 可以看出通过方法内部调用也走了代理。这个很好理解相当于是调用的是继承类的方法。
事务开始......save
save
事务结束.......save
===1===
事务开始......delete
delete
事务结束.......delete
===2===
事务开始......saveAndDelete
事务开始......save
save
事务结束.......save
事务开始......delete
delete
事务结束.......delete
事务结束.......saveAndDelete
关于cglib动态代理中invokeSuper和invoke的区别参考:https://blog.csdn.net/z69183787/article/details/106878203/
补充:在Spring的AOP中自调用AOP无效。简单研究了下,是调用invoke的效果,如下:
1.修改代理方法
package aop; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; /** * @Author: qlq * @Description * @Date: 20:23 2021/1/24 */ public class CglibProxyFactory implements MethodInterceptor { private Object target; public CglibProxyFactory(Object target) { super(); this.target = target; } /** * 1、代理对象;2、委托类方法;3、方法参数;4、代理方法的MethodProxy对象。 * * @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 { System.out.println("事务开始......" + method.getName()); Object o1 = methodProxy.invoke(target, objects); System.out.println("事务结束......." + method.getName()); return o1; } }
2.测试类如下:
package aop; import net.sf.cglib.core.DebuggingClassWriter; import net.sf.cglib.proxy.Enhancer; /** * @Author: qlq * @Description * @Date: 20:26 2021/1/24 */ public class CglibClient { public static void main(String[] args) { //在指定目录下生成动态代理类,我们可以反编译看一下里面到底是一些什么东西 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "G:/proxy"); UserDao userDao = new UserDao(); CglibProxyFactory cglibProxy = new CglibProxyFactory(userDao); //jdk需要提供接口,cglib需要是非私有类,且不能处理final关键字修饰的方法 Enhancer enhancer = new Enhancer(); //设置父类 enhancer.setSuperclass(UserDao.class); //设置回调对象 enhancer.setCallback(cglibProxy); UserDao proxy = (UserDao) enhancer.create(); proxy.save(); System.out.println("===1==="); proxy.delete(); System.out.println("===2==="); proxy.saveAndDelete(); } }
结果: 可以看到方法内部调用没有走代理
事务开始......save save 事务结束.......save ===1=== 事务开始......delete delete 事务结束.......delete ===2=== 事务开始......saveAndDelete save delete 事务结束.......saveAndDelete
简单理解,invoke方法调用的对象没有增强过(target 模板对象),invokeSuper方法调用的对象已经是增强了的(proxy 代理对象),所以会再走一遍 MethodInterceptor的 interceptor方法,如果是个拦截器链条,就会重新在走一次拦截器链。
而且在invoke和invokeSuper中,this指代的对象不同,
(1) methodProxy.invokeSuper(o, objects); 中this是代理对象: aop.UserDao$$EnhancerByCGLIB$$86ae564d@6a1aab78
(2) methodProxy.invoke(target, objects); 中this指向target对象: aop.UserDao@eafc191
补充:Spring的AOP调用也是用的invoke的套路,我们在AOP中打印this可以看出是目标target对象还是代理proxy对象。结果看出是目标target对象
com.xm.ggn.test.aop.TestServiceImpl@330fff8e
也可以简单查看Spring代理:org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept
@Override @Nullable public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object oldProxy = null; boolean setProxyContext = false; Object target = null; TargetSource targetSource = this.advised.getTargetSource(); try { if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } // Get as late as possible to minimize the time we "own" the target, in case it comes from a pool... target = targetSource.getTarget(); Class<?> targetClass = (target != null ? target.getClass() : null); List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); Object retVal; // Check whether we only have one InvokerInterceptor: that is, // no real advice, but just reflective invocation of the target. if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) { // We can skip creating a MethodInvocation: just invoke the target directly. // Note that the final invoker must be an InvokerInterceptor, so we know // it does nothing but a reflective operation on the target, and no hot // swapping or fancy proxying. Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = methodProxy.invoke(target, argsToUse); } else { // We need to create a method invocation... retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed(); } retVal = processReturnType(proxy, target, method, retVal); return retVal; } finally { if (target != null && !targetSource.isStatic()) { targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } }

// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package org.springframework.aop.framework; import java.io.Serializable; import java.lang.reflect.Proxy; import org.springframework.aop.SpringProxy; public class DefaultAopProxyFactory implements AopProxyFactory, Serializable { public DefaultAopProxyFactory() { } public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) { return new JdkDynamicAopProxy(config); } else { Class<?> targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation."); } else { return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config)); } } } private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) { Class<?>[] ifcs = config.getProxiedInterfaces(); return ifcs.length == 0 || ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0]); } }
Spring中强制使用Cglib代理
<aop:aspectj-autoproxy proxy-target-class="true" />
SpringBoot中强制使用Cglib代理

如果加入容器的目标对象有实现接口,用JDK代理
如果目标对象没有实现接口,用Cglib代理


浙公网安备 33010602011771号