涉及模式 - 代理模式(Proxy)-- 为其他对象提供代理

tech2023-02-14  107

代理模式(Proxy)

类结构图静态代理动态代理JDK ProxyCGLibFastClass机制 JDK、CGLib对比源码中的应用ProxyFactoryBeanSpring AOP

为其他对象提供代理,控制对这个对象的访问。 在客户端和目标对象之间起到中介作用。

类结构图

Subject:顶层接口 RealSubject:被代理对象(真实对象) Proxy:代理对象,持有被代理对象的引用Subject,并在被代理对象的request()前后增加处理。

静态代理

定义一个接口 Person

public interface Person { void findLove(); }

实现类 Son

public class Son implements Person{ public void findLove(){ System.out.println("儿子要求:肤白貌美大长腿"); } }

实现类 Father,持有Son的对象应用,达到代理的目的

public class Father implements Person { private Son person; public Father(Son person){ this.person = person; } public void findLove(){ System.out.println("父亲物色对象"); this.person.findLove(); System.out.println("双方父母同意,确立关系"); } }

测试类

public class FatherProxyTest { public static void main(String[] args) { Father father = new Father(new Son()); father.findLove(); } }

动态代理

从1.3的版本开始引入动态代理

JDK Proxy

继承InvocationHandler接口,实现invoke方法。 Proxy通过字节码重组(Proxy.newProxyInstance)生成一个新的对象。

步骤: 1.通过反射拿到被代理对象的引用,并获取他的所有接口。 2.Proxy生成一个新的类,此类要实现被代理类实现的所有接口。 3.动态生成.java文件 4.编译.java文件,生成.class文件。(JDK规范中,Classpath下$开头的class文件一般都是自动生成的) 5.重新加载.class文件到jvm运行。

public class JDKMeipo implements InvocationHandler { private Object target; public Object getInstance(Object person) throws Exception{ // 构建代理对象 this.target = person; Class<?> clazz = target.getClass(); return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); Object obj = method.invoke(this.target,args); after(); return obj; } private void before(){ System.out.println("我是媒婆,我要给你找对象,现在已经确认你的需求"); System.out.println("开始物色"); } private void after(){ System.out.println("OK的话,准备办事"); } } public class Test { public static void main(String[] args) { Person person = (Person) new JdkProxy().getInstance(new Person_2Impl()); person.work(); //实际跑的是proxy的invoke方法 } }

CGLib

继承MethodInterceptor 接口,实现intercept方法。 通过enhancer.create()获取对象。 内许多AOP框架使用(如:spring AOP),为他们提供方法拦截(interception)

被代理的对象

public class Customer { public void findLove(){ System.out.println("儿子要求:肤白貌美大长腿"); } }

代理对象

public class CGlibMeipo implements MethodInterceptor { public Object getInstance(Class<?> clazz) throws Exception{ // enhancer 构建代理对象 Enhancer enhancer = new Enhancer(); //增强类对象 enhancer.setSuperclass(clazz); //设置增强类的类型 enhancer.setCallback(this); //设置代理方法,当前类intercept方法 return enhancer.create(); } public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { before(); Object obj = methodProxy.invokeSuper(o,objects); after(); return obj; } private void before(){ System.out.println("我是媒婆,我要给你找对象,现在已经确认你的需求"); System.out.println("开始物色"); } private void after(){ System.out.println("OK的话,准备办事"); } }

测试类

public class CglibTest { public static void main(String[] args) { try { System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERY, "E://cglib_proxy_classes"); Customer obj = (Customer) new CGlibMeipo().getInstance(Customer.class); System.out.println(obj); obj.findLove(); } catch (Exception e) { e.printStackTrace(); } } }

FastClass机制

CGlib动态代理之所以效率高,是因为采用的FastClass机制:为代理类和被代理类 各生成一个class,然后各自的class会为自己的方法分配一个index(int类型)。这个index 可以帮助FastClass直接定位要调用的方法,并直接调用,这样就省去了反射。 FastClass是在第一个执行MethodProxy invoke/invokeSuper时生成的并放在缓存中。

JDK、CGLib对比

1.生成一个新类时,JDK采用读取接口信息,所以必须要有一个接口实现。CGLib是覆盖父类方法,可以代理任意普通类。 2.JDK Proxy每次都要动态反射调用,效率略低。CGLib生成一个包含所有逻辑的FastClass,不需要反射,效率更高。 3.CGLib有个坑,不能代理final方法。

源码中的应用

ProxyFactoryBean

在getObject()方法中,主要调用 getSingletonInstance()和 newPrototypeInstance()。 在Spring 的配置中,如果不做任何设置,那么 Spring 代理生成的 Bean 都是单例对象。

Spring AOP

Spring AOP就是利用动态代理实现的。实现类JdkDynamicAopProxy、CglibAopProxy。 当被代理对象有接口时,Spring选择JdkDynamicAopProxy,否则选择CglibAopProxy。

可以使用配置强制使用CglibAopProxy,配置如下: <aop:aspectj-autoproxy proxy-target-class=“true”/>

最新回复(0)