代理及动态代理

xiaoxiao2021-02-27  195

    代理模式为常用的设计模式之一,代理模式为实际对象提供了一个代理类,用代理类来控制实际对象的访问(本地或是远程访问、起到权限或是对资源的控制),同时也起到隔离和屏蔽实际对象的具体实现细节。其UML图如下:

接口

public interface Subject { public void add(); }

实现类

public class RealSubject implements Subject { @Override public void add() { System.out.println("add method"); } }

代理类

public class SubjectAgent implements Subject { private Subject sell ; public SubjectAgent(Subject sell) { this.sell = sell; } @Override public void add() { sell.add(); } }代理类中依赖了接口的一个具体实现,来实现Subject接口的方法,屏蔽了Subject接口的具体实现细节。同时,代理类还具有很好的拓展性,可以在代理类里面添加其他一些跟具体业务无关的逻辑,例如:操作日志、权限的控制等。

静态代理特点:

1、代理类与实现类需要共同实现同一个接口

2、不修改实现对象功能的前提下,可以对目标功能做拓展

3、可能存在多个实现类,也会有多个代理类,一旦增加新的接口或方法,目标对象和代理对象都很难维护

使用动态代理就可以很方便的维护代理对象和目标方法。

1.代理对象,不需要实现目标接口 2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型) 3.动态代理也叫做:JDK代理,接口代理

动态代理

    动态代理是在运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象 。JDK 5中引入的动态代理机制,允许开发人员在运行时刻动态的创建出代理类及其对象。在运行时刻,可以动态创建出一个实现了多个接口的代理类。每个代理类的对象都会关联一个表示 内部处理逻辑的InvocationHandler接 口的实现 。当使用者调用了代理对象所代理的接口中的方法的时候,这个调用的信息会被传递给InvocationHandler的invoke方法。在 invoke方法的参数中可以获取到代理对象、方法对应的Method对象和调用的实际参数。invoke方法的返回值被返回给使用者,这种做法实际上相当于对方法调用进行了拦截,这样我们就可以在调用invoke的前后做自己想做的事。 public class DynamicProxy implements InvocationHandler { private Object targt; public Object GetInstance(Object target){ this.targt =target; return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(proxy.getClass().toString()); Field [] fields = proxy.getClass().getDeclaredFields(); for(Field f:fields){ System.out.println(f.getName()); } Method[] methods = proxy.getClass().getDeclaredMethods(); for(Method m :methods){ System.out.println(m.getName()); } System.out.println("before"); Object result = method.invoke(targt,args); System.out.println("after"); return result; } }

public class Test { public static void main(String[] args) { Subject proxy = (Subject) new DynamicProxy().GetInstance(new SubjectAgent(new RealSubject())); proxy.add(); } } 缺点是目标类需要实现接口,原因是JDK在生成代理类时需要传入接口。 Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this);

那么动态代理是怎么把代理对象跟目标对象联系起来的呢?换句话说,在调用目标对象的相关方法时是怎样跟代理对象的invoke方法联系起来的?

invoke方法中的参数proxy对象看似没有起到作用,实际上是通过该对象把代理对象和目标对象关联的。在编译动态代理类时会生成内部类class $Proxy0 ,也就是此处的invoke方法参数中的proxy类,该类继承自Proxy类并实现了Subject接口,其部分代码如下:

public final void add() { try { super.h.invoke(this, m3, null); return; } catch (Error e) { } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } 可以看到此处的目标对象的add()方法跟代理对象关联起来了,实际调用的是Proxy中 InvocationHandler的invoke方法。再看Proxy类中的关键代码 class Proxy{ InvocationHandler h=null; protected Proxy(InvocationHandler h) { this.h = h; } ... } 动态代理的不足之处就是目标对象必须实现接口,如果目标对象没有实现接口,那么就需要使用Cglib代理

Cglib代理

上面的静态代理和动态代理模式都是要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做: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,那么就不会被拦截,即不会执行目标对象额外的业务方法

目标对象

public class CglibSubject { public void add(){ System.out.println("add"); } }

动态代理类

public class CglibProxy implements MethodInterceptor { private Object target; public Object getProxyInstance(Object target){ this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable { // TODO Auto-generated method stub System.out.println("start"); Object result = arg1.invoke(target, arg2); System.out.println("end"); return result; } } 测试类:

public class Test { public static void main(String[] args) { new Test().testCglibProxy(); } public void testCglibProxy(){ CglibSubject proxy = (CglibSubject) new CglibProxy().getProxyInstance(new CglibSubject()); proxy.add(); } }

如果目标对象有实现接口,用JDK代理 如果目标对象没有实现接口,用Cglib代理

@姗姗来迟

转载请注明原文地址: https://www.6miu.com/read-11904.html

最新回复(0)