代理模式

用户头像
关注
发布于: 2020 年 07 月 22 日
代理模式

代理模式是一种设计模式,简单说即是在不改变源码的情况下,实现对目标对象功能扩展

1、静态代理

优点:

代理使客户端不需要知道实现类是什么,怎么做的,而客户端只需知道代理即可(解耦合),对于如上的客户端代码,newUserManagerImpl()可以应用工厂将它隐藏,如上只是举个例子而已。

缺点:

  • 1)代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

  • 2)代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。

     

 

2、JDK动态代理

底层实现过程:

  • 通过实现 InvocationHandler 接口创建自己的调用处理器;

  • 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;

  • 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;

  • 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

 

原理:

实现InvocationHandler 接口,调用Proxy类的静态方法newProxyInstance创建动态代理,通过反射的机制获取代理类,该类必须实现一个接口

调用Proxy类的静态方法newProxyInstance即可,该方法会返回代理类对象

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )

接收的三个参数依次为:

  • ClassLoader loader:指定当前目标对象使用类加载器,写法固定

  • Class<?>[] interfaces:目标对象实现的接口的类型,写法固定

  • InvocationHandler h:事件处理接口,需传入一个实现类,一般直接使用匿名内部类

 

调用:

UserServiceProcyFactory proxy = new UserServiceProcyFactory();

UserService user = (UserService) proxy.getProxy(UserServiceImpl.class);

 

package proxy.JDKProxy;

 

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

 

public class PracticeJDKProxyFactory implements InvocationHandler{

 

Object impl;

 

public <T> Object getProxy(Class<T> clz){

try {

//实例化对象

impl = clz.newInstance();

} catch (InstantiationException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IllegalAccessException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

//创建动态代理

Object proxy = Proxy.newProxyInstance(clz.getClassLoader(), clz.getInterfaces(), this);

return proxy;

}

 

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

System.out.println("之前");

//反射执行方法

Object invoke = method.invoke(impl, args);=

System.out.println("之后");

return invoke;

}

 

}

 

3、cglib动态代理

原理:

cglib是一个java字节码的生成工具,方式是继承它动态生成一个被代理类的子类,子类重写被代理的类,因此final标记的类,无法用CGLIB做动态代理。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。

 

目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法

 

调用:

1、实现MethodInterceptor接口生成方法拦截器:

 

public class HelloMethodInterceptor  implements MethodInterceptor{    @Override    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {        System.out.println("Before: "  + method.getName());        Object object = methodProxy.invokeSuper(o, objects);        System.out.println("After: " + method.getName());        return object;    } }

 

2、生成代理类对象并打印在代理类对象调用方法之后的执行结果:

 

public class Client {    public static void main(String[] args) {        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/zhanghao/Documents/toy/spring-framework-source-study/");        Enhancer enhancer = new Enhancer();        //继承被代理类        enhancer.setSuperclass(HelloServiceImpl.class);        //设置回调        enhancer.setCallback(new HelloMethodInterceptor());        //设置代理类对象        HelloServiceImpl helloService = (HelloServiceImpl) enhancer.create();        //在调用代理类中方法时会被我们实现的方法拦截器进行拦截        helloService.sayBey();    } }

 

Enhancer是CGLIB的字节码增强器,可以很方便的对类进行拓展。

创建代理类的过程:

  • 生成代理类的二进制字节码文件;

  • 加载二进制字节码,生成Class对象;

  • 通过反射机制获得实例构造,并创建代理类对象。

 

4、静态代理和动态代理的区别:

静态代理类:由程序员创建或由特定工具bai自动生成源代码,再对其编译。在du程序运行前,代理类的.class文件就已经存在了。

 

动态代理类:在程序运行时,运用反射机制动态创建而成。



用户头像

关注

everything will be alright 2020.04.06 加入

还未添加个人简介

评论

发布
暂无评论
代理模式