前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >代理模式

代理模式

作者头像
胖虎
发布2019-06-26 17:19:54
3440
发布2019-06-26 17:19:54
举报
文章被收录于专栏:晏霖

前言

代理模式让我评价就是一个笑面虎,结构超简单,实现比较复杂。大家都知道代理模式深入咱们都业务代码中,(就是代理类把接口都实现类引入进来,然后加以封装,再由其他类使用代理类)应用场景太多了,但是大多都是静态代理,如果真都实现一个动态代理我认为还是一个比较都事情,本文对动态代理这块写对也是比较简单,

参照文献

  • https://blog.csdn.net/qq_34178598/article/details/78630934
  • https://blog.csdn.net/maoyuanming0806/article/details/80186248
  • https://blog.csdn.net/yhl_jxy/article/details/80633194

如果大家想深入学习动态代理模式强烈建议去多看看其他人写多资料,我这个博客顶多算是一个入门介绍。

文献阅读提示:并不是他们的demo在你的本地都能跑通,同一个地方综合大家的说法理解成自己的,这样效果更好。

正文

首先介绍一下代理模式的种类,分为静态代理、jdk动态代理、cglib动态代理。

什么是代理模式?

代理模式的定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。举例说明,就是一个人或者一个机构,代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之前起到中介的作用。

用绘图方式举例

房屋中介这个角色就是代理模式的核心,中介可以做很多事,房屋加价,改造房屋等等,在代码中就是对被代理对象或方法的处理等等。

下面集合代码来讲解,由于本人能力不足,只能给出代理模式的大概思想,没有结合实际开发生产的场景讲解。

静态代理

静态代理是最好理解的,涉及到一个接口、一个接口实现类、还有一个修饰这个接口实现类的代理类,分别对应着租房的动作,房东,中介三者的关系。

由于很简单,三个类的代码都贴到类一起,大家一看便知。

代码语言:javascript
复制
public interface IRenting {
    //租房
    Integer letOut();
}


public class Landlady implements IRenting {
    //目标类,也可以说房东
    public Integer letOut() {
        System.out.println("房东同意租房,租金100元");
        return 100;
    }
}


public class Proxy implements IRenting {
    //代理类,可以看作中介

    public Integer letOut() {
        Landlady landlady = new Landlady();
        Integer integer = landlady.letOut();
        //现实中,代理类要处理很复杂的业务,甚至是调用其他系统操作一些事。
        System.out.println("中介把房东的房子出租给你并从中渔利100元");
        return integer + 100;

    }
}

下面来模拟调用者,也就是租客是怎么找中介租房的。

代码语言:javascript
复制
public class StaticProxyTest {
    public static void main(String[] args) {
        Proxy proxy = new Proxy();
        Integer integer = proxy.letOut();
        System.out.println("你一共话了" + integer +         "元成功租房,全程没有见过房东,中介把你们隔离开");
    }
}

调用方只需要把代理类注入到本类中就可以使用了。对于调用者来说,你只知道中介可以租给房子,至于他怎么操作的对你是隔离的。

JDK动态代理

jdk代理模式虽然是动态代理,但是也算比较好理解,只要你的电脑有jdk就可以玩。

其实动态代理和静态代理的思想是不变的,动态代理和静态代理的区别就是,动态代理不用我们去手编写代理类,在运行时,动态的在内存中生产代理类。

JDK动态代理的API:

在java.lang.reflect包中有一个代理类。

  • java.lang.reflect.Proxy

我们分别使用三个包就可以完成JDK动态代理。

import java.lang.reflect.InvocationHandler;

代码语言:javascript
复制
 import java.lang.reflect.Method;
      import java.lang.reflect.Proxy;

方便大家看到动态代理的输出过程,我讲被代理对象多写一个方法,思想还是一样的,接口、实现类、代理工厂三者。

代码语言:javascript
复制
public interface TargetInterface {
    //被代理的两个方法
    void method1();

    int method2(Integer i);
}public class Target implements TargetInterface {
    public void method1() {
        System.out.println("被代理的method1 running ...相当于做了一些列的业务");
    }

    public int method2(Integer i) {
        System.out.println("被代理的method2 running ...并对参数做了处理");
        return i + 10;
    }
}

与静态代理唯一有区别的地方就是这里,代理类。

代理类的作用是,无论被代理的类是什么返回值类型,什么参数类型,都可以通过我去调用到,对客户端实现隔离。 实际情况代理类需要做的事情远比我demo要复杂的多。

代码语言:javascript
复制
public class ProxyFactory {
    public static <T> Object getProxy(final T t) {
        
        //返回一个代理对象
        Object object = Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), new InvocationHandler() {
            //invoke()方法是因为new InvocationHandler()而重写的。
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 // proxy就是目标对象,method就是调用目标对象中方法,args就是调用目标对象中方法的参数。
 //比如说:代理对象.method1(),这时proxy就是目标类,method1就是method,args就是method1方法参数。
                System.out.println("执行方法前...");
 //执行了invoke方法就相当于把Target(目标类)的所有方法都交给代理类去调用,这里都t是类,args是参数,可以有很多参数。
                Object invoke = method.invoke(t, args);
                System.out.println("执行方法后...");
                return invoke;
            }
        });

        return object;
    }
}

使用的时候,返回类型是一个接口,然后使用工厂类调用即可。

代码语言:javascript
复制
public class JDKProxyTest {
    public static void main(String[] args) {
        Target target = new Target();
        TargetInterface proxy = (TargetInterface)ProxyFactory.getProxy(target);
        proxy.method1();
        System.out.println("-------------------------");
        int i = proxy.method2(100);
        System.out.println(i);

    }
}

输出结果

注意:JDK的Proxy方式实现的动态代理 目标对象必须有接口 没有接口不能实现jdk版动态代理!

下面来说一下这个方法Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h);

返回值:Object就是代理对象

参数:

loader:代表与目标对象相同的类加载器-------目标对象.getClass().getClassLoader()

interfaces:代表与目标对象实现的所有的接口字节码对象数组----数组因为目标类可以有多个接口

h:具体的代理的操作,InvocationHandler接口

Cglib动态代理

第三方代理技术--Cglib代理。

可以对任何类生成代理,代理的原理是可以对目标对象接口实现代理,也可以进行继承代理。

需要引入cglib的jar文件,但是Spring的核心包中已经包括了Cglib功能,所以直接引入spring-core包,由于我依赖了SpringBoot所以这点不用担心。

跟jdk动态代理区别,第一点是可以省略目标接口。 第二点代理工厂使用的是 org.springframework.cglib.proxy.Enhancer; 来帮助我们生成代理对象的,千万不用用net 的包,可能导致错误。

代码实现:

需要依赖springboot

代码语言:javascript
复制
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
        <version>2.0.0.RELEASE</version>
    </dependency>
</dependencies>

目标类,被代理对象

代码语言:javascript
复制
public class Target {
    public void method1() {
        System.out.println("被代理的method1 running ...相当于做了一些列的业务");
    }

    public int method2(Integer i) {
        System.out.println("被代理的method2 running ...并对参数做了处理");
        return i + 10;
    }
}

代理类

代码语言:javascript
复制
public class CglibFactory implements MethodInterceptor {
    public Object getProxy(Class clazz) {
        Enhancer enhancer = new Enhancer(); //帮我们生成代理对象
        //设置需要创建子类的类
        enhancer.setSuperclass(clazz);
        //设置要代理的目标类,就是当前类,所以this
        enhancer.setCallback(this);
        //通过字节码技术动态创建子类实例
        return enhancer.create();
    }

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("执行方法前dosomething。。。");
        Object invoke = methodProxy.invokeSuper(o, objects);
        System.out.println("执行方法后dosomething。。。");
        return invoke;
    }
}

让我们测试一下吧

代码语言:javascript
复制
public class CglibProxyTest {
    public static void main(String[] args) {
        CglibFactory cglibFactory=new CglibFactory();
        Target proxy= (Target) cglibFactory.getProxy(Target.class);
        proxy.method1();
        System.out.println(proxy.method2(100));

    }
}

输出结果

以上三种代理模式已经讲完,代码结构如下,索要加微信:15524579896

总结:

静态代理需要自己手动编写代理类和目标方法。

JDK动态代理就不需要自己手动实现代理类和目标方法,但动态代理的目标类要必须实现接口!

Cglib 代理的目标类可以实现接口也可以不实现,因为可以使用继承子类的方式代理。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-03-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 晏霖 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 正文
    • 静态代理
      • JDK动态代理
        • Cglib动态代理
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档