代理模式

代理模式

一.静态代理

​ 首先, 定义接口和接口的实现类, 然后定义接口的代理对象, 将接口的实例注入到代理对象中, 然后通过代理对象去调用真正的实现类。

​ 目标情况:代理类较少且确定

​ 优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。

​ 缺点:我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相 应修改。

​ 创建步骤 1.创建服务类接口

​ 2.实现服务接口

​ 3.创建代理类

​ 代理类以及实现服务接口都需要实现服务类接口。

​ 代理类中要有目标类对象的引用,然后再代理类的方法里面执行目标类对象的方法。

// 委托接口
public interface IHelloService {

    /**
     * 定义接口方法
     * @param userName
     * @return
     */
    String sayHello(String userName);

}
// 委托类实现
public class HelloService implements IHelloService {

    @Override
    public String sayHello(String userName) {
        System.out.println("helloService" + userName);
        return "HelloService" + userName;
    }
}

// 代理类
public class StaticProxyHello implements IHelloService {

    private IHelloService helloService = new HelloService();

    @Override
    public String sayHello(String userName) {
        /** 代理对象可以在此处包装一下*/
        System.out.println("代理对象包装礼盒...");
        return helloService.sayHello(userName);
    }
}
// 测试静态代理类
public class MainStatic {
    public static void main(String[] args) {
        StaticProxyHello staticProxyHello = new StaticProxyHello();
        staticProxyHello.sayHello("isole");
    }
}

二.动态代理

​ 不再需要再手动的创建代理类,我们只需要编写一个动态处理器就可以了。真正的代理对象由JDK再运行时为我们动态的来创建。

​ JVM 的类加载机制中的加载阶段要做的三件事情
( 附 Java 中的类加载器 )

  1. 通过一个类的全名或其它途径来获取这个类的二进制字节流
  2. 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
  3. 在内存中生成一个代表这个类的 Class 对象, 作为方法区中对这个类访问的入口

​ 而我们要说的动态代理,主要就发生在第一个阶段, 这个阶段类的二进制字节流的来源可以有很多, 比如 zip 包、网络、运行时计算生成、其它文件生成 (JSP)、数据库获取。其中运行时计算生成就是我们所说的动态代理技术,在 Proxy 类中, 就是运用了 ProxyGenerator.generateProxyClass 来为特定接口生成形式为 *$Proxy 的代理类的二进制字节流。所谓的动态代理就是想办法根据接口或者目标对象计算出代理类的字节码然后加载进 JVM 中。

涉及类:java.lang.reflect.Proxy java.lang.reflect.InvocationHandler

// 委托类接口
public interface IHelloService {

    /**
     * 方法1
     * @param userName
     * @return
     */
    String sayHello(String userName);

    /**
     * 方法2
     * @param userName
     * @return
     */
    String sayByeBye(String userName);

}
// 委托类
public class HelloService implements IHelloService {

    @Override
    public String sayHello(String userName) {
        System.out.println(userName + " hello");
        return userName + " hello";
    }

    @Override
    public String sayByeBye(String userName) {
        System.out.println(userName + " ByeBye");
        return userName + " ByeBye";
    }
}
// 中间类
public class JavaProxyInvocationHandler implements InvocationHandler {

    /**
     * 中间类持有委托类对象的引用,这里会构成一种静态代理关系
     */
    private Object obj ;

    /**
     * 有参构造器,传入委托类的对象
     * @param obj 委托类的对象
     */
    public JavaProxyInvocationHandler(Object obj){
        this.obj = obj;

    }

    /**
     * 动态生成代理类对象,Proxy.newProxyInstance
     * @return 返回代理类的实例
     */
    public Object newProxyInstance() {
        return Proxy.newProxyInstance(
                //指定代理对象的类加载器
                obj.getClass().getClassLoader(),
                //代理对象需要实现的接口,可以同时指定多个接口
                obj.getClass().getInterfaces(),
                //方法调用的实际处理者,代理对象的方法调用都会转发到这里
                this);
    }


    /**
     *
     * @param proxy 代理对象
     * @param method 代理方法
     * @param args 方法的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("invoke before");
        Object result = method.invoke(obj, args);
        System.out.println("invoke after");
        return result;
    }
}
// 测试动态代理类
public class MainJavaProxy {
    public static void main(String[] args) {
        JavaProxyInvocationHandler proxyInvocationHandler = new JavaProxyInvocationHandler(new HelloService());
        IHelloService helloService = (IHelloService) proxyInvocationHandler.newProxyInstance();
        helloService.sayByeBye("paopao");
        helloService.sayHello("yupao");
    }

}

中间类要实现invocationHandler接口

中间类通过反射动态生成的代理类

代理类的任何方法都会先执行invoke

​ 虽然相对于静态代理,动态代理大大减少了我们的开发任务,同时减少了对业务接口的依赖,降低了耦合度。但是还是有一点点小小的遗憾之处,那就是它始终无法摆脱仅支持interface代理

三.CGLIB代理

​ JDK 动态代理依赖接口实现,而当我们只有类没有接口的时候就需要使用另一种动态代理技术 CGLIB 动态代理。首先 CGLIB 动态代理是第三方框架实现的,在 maven 工程中我们需要引入 cglib 的包, 如下:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2</version>
</dependency>

CGLIB 代理是针对类来实现代理的,原理是对指定的委托类生成一个子类并重写其中业务方法来实现代理。代理类对象是由 Enhancer 类创建的。CGLIB 创建动态代理类的模式是:

  1. 查找目标类上的所有非 final 的 public 类型的方法 (final 的不能被重写)
  2. 将这些方法的定义转成字节码
  3. 将组成的字节码转换成相应的代理的 Class 对象然后通过反射获得代理类的实例对象
  4. 实现 MethodInterceptor 接口, 用来处理对代理类上所有方法的请求
// 委托类,是一个简单类
public class CglibHelloClass {
    /**
     * 方法1
     * @param userName
     * @return
     */
    public String sayHello(String userName){
        System.out.println("目标对象的方法执行了");
        return userName + " sayHello";
    }

    public String sayByeBye(String userName){
        System.out.println("目标对象的方法执行了");
        return userName + " sayByeBye";
    }

}
/**
 * CglibInterceptor 用于对方法调用拦截以及回调
 *
 */
public class CglibInterceptor implements MethodInterceptor {
    /**
     * CGLIB 增强类对象,代理类对象是由 Enhancer 类创建的,
     * Enhancer 是 CGLIB 的字节码增强器,可以很方便的对类进行拓展
     */
    private Enhancer enhancer = new Enhancer();

    /**
     *
     * @param obj  被代理的对象
     * @param method 代理的方法
     * @param args 方法的参数
     * @param proxy CGLIB方法代理对象
     * @return  cglib生成用来代替Method对象的一个对象,使用MethodProxy比调用JDK自身的Method直接执行方法效率会有提升
     * @throws Throwable
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("方法调用之前");
        Object o = proxy.invokeSuper(obj, args);
        System.out.println("方法调用之后");
        return o;
    }


    /**
     * 使用动态代理创建一个代理对象
     * @param c
     * @return
     */
    public  Object newProxyInstance(Class<?> c) {
        /**
         * 设置产生的代理对象的父类,增强类型
         */
        enhancer.setSuperclass(c);
        /**
         * 定义代理逻辑对象为当前对象,要求当前对象实现 MethodInterceptor 接口
         */
        enhancer.setCallback(this);
        /**
         * 使用默认无参数的构造函数创建目标对象,这是一个前提,被代理的类要提供无参构造方法
         */
        return enhancer.create();
    }
}

//测试类
public class MainCglibProxy {
    public static void main(String[] args) {
        CglibProxy cglibProxy = new CglibProxy();
        CglibHelloClass cglibHelloClass = (CglibHelloClass) cglibProxy.newProxyInstance(CglibHelloClass.class);
        cglibHelloClass.sayHello("isole");
        cglibHelloClass.sayByeBye("sss");
    }
}

java动态代理和gclib代理的区别

java动态代理基于接口动态生成实现类

gclib代理基于继承动态生成子类

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值