设计模式之代理模式详解(java)

一、代理模式

代理模式很重要,它是SpringAOP的底层实现!!!代理模式分为两类:静态代理和动态代理。其中动态就是用到反射。

参考视频(狂神说Java):代理模式

1.1 静态代理

举一个经典案例说明:房东与租客
描述:房东不想贴小广告费力找租客,带租客看房,租客也不想跟无头苍蝇一样找合适房子,于是就诞生了一个代理中介,房东只要把钥匙交给中介,然后带人看房,签合同的事情都中介搞定,房东不用操心这些,这就是代理。
在这里插入图片描述
首先我们先写一个理想情况,租客可以很容易的找到房东并租到房
在这里插入图片描述

//租房
public interface Rent {
    void rent();
}
//租客
public class Customer {
    public static void main(String[] args) {
        //租客找房东租房
        Landlord landlord = new Landlord();
        landlord.rent();
        System.out.println("租客找到房东,租了房子");
    }
}
//房东
public class Landlord implements Rent{
    @Override
    public void rent() {
        System.out.println("房东要出租房子");
    }
}

在这里插入图片描述
但是理想是理想,租客往往都需要找中介,因为中介房源多,可以挑选的多,所以就出现代理(Proxy类)

package com.yx.proxy;

package com.yx.proxy;

public class Proxy {

    private Landlord landlord;

    public Proxy(){

    }

    public Proxy(Landlord landlord) {
        this.landlord = landlord;
    }
    //中介拿着不同房东的房子钥匙,带租客看房
    public void rent(){
        seeHouse();
        landlord.rent();
        contract();
    }
    //中介带租客看多套房
    public void seeHouse(){
        System.out.println("中介带着租客看房");
    }
    //中介代理房东签合同
    public void contract(){
        System.out.println("中介代理房东签合同");
    }
}
//租客
public class Customer {
    public static void main(String[] args) {
        Landlord landlord = new Landlord();
        //代理房东租房
        Proxy proxy = new Proxy(landlord);
        proxy.rent();
    }
}

在这里插入图片描述
总结:何为代理模式?
代理模式就是你看不见房东,通过代理中介就能租到房。实际开发也是,直接通过代理对象就能获取到结果,而不用去接触真实对象,也就是说,真实对象的操作更加纯粹,繁琐操作交给代理。你会发现被代理的真实角色房东类里面一直就只有rent方法没有变过,不会改变原有代码。比如这里房东只想把房子出租出去收钱,那么带人看房,签合同这些麻烦事就给代理去做,自己就做最基本的收租工作。

再举一个例子,用户常做的操作就是增删改查,但是此时我们想在每个增删改查前都加一个打印日志功能,这时候就可以用代理,加入新功能,不改变原有功能,这就像Spring AOP中的切面,切入新功能,Spring AOP底层就是用代理模式。

在这里插入图片描述

//用户
public class Client {
    public static void main(String[] args) {
        UserServiceImpl userService = new UserServiceImpl();

        UserServiceProxy proxy = new UserServiceProxy();
        proxy.setUserService(userService);
        proxy.add();
    }
}
//用户接口
public interface UserService {

    void add();

    void delete();

    void update();

    void query();
}
//用户接口
public interface UserService {

    void add();

    void delete();

    void update();

    void query();
}
public class UserServiceProxy implements UserService{

    private UserServiceImpl userService;

    //Spring中赋值推荐用set
    public void setUserService(UserServiceImpl userService) {
        this.userService = userService;
    }


    @Override
    public void add() {
        log("add");
        userService.add();
    }

    @Override
    public void delete() {
        log("delete");
        userService.delete();
    }

    @Override
    public void update() {
        log("update");
        userService.update();
    }

    @Override
    public void query() {
        log("query");
        userService.query();
    }

    public void log(String msg){
        System.out.println("[Debug]使用了"+msg+"方法");
    }
}

运行Client
在这里插入图片描述
这样代理就没有改变原来的基本增删改查操作,而且增加了新操作(扩展操作),改动原有代码是大忌,因为可能导致公司原本正常执行的代码被你一改,大家都运行出错了,开发要符合开闭原则,对修改关闭,对新增开放。

上面还是自己手动写log方法,开发中肯定不能这么死板(写死了),要动态灵活,动态自动生成,所以下面讲动态代理

1.2 动态代理

先说明,动态代理底层都是反射、反射、反射!!!

动态代理分两类:
1、基于JDK的动态代理(接口)
2、基于Cglib的动态代理(类)

java.lang.reflect下有个代理类,叫Proxy,动态自动生成代理类还需要一个叫InvocationHandler接口,用该接口中唯一的invoke方法执行代理实例,并返回结果,这里有点蒙?举个例子就懂了

同样租房子案例,这次我们不写静态代理,写会自己创建代理的动态代理
在这里插入图片描述

//房东
public class Landlord implements Rent {
    @Override
    public void rent() {
        System.out.println("房东要出租房子");
    }
}
//租房
public interface Rent {
    void rent();
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {

    private Rent rent;//被代理的类

    public void setRent(Rent rent) {
        this.rent = rent;
    }

    //生成得到代理类
    public Object getProxy(){
        //创建代理实例(通过反射获取参数):类加载器、被代理的接口、InvocationHandler接口
        Object obj = Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
        return obj;
    }

    //执行代理实例,并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        seeHouse();
        Object res = method.invoke(rent, args);//invoke就是执行对应接口方法,所以这里讲rent放进去,类似AOP中的切点
        contract();
        return res;
    }

    public void seeHouse() {
        System.out.println("中介带看房子");
    }

    public void contract(){
        System.out.println("签合同");
    }
}

注意:这里还是先写了Rent接口,作为代理接口,如果要改成万能代理,只要把private Rent rent改成private Object object即可

public class Client {
    public static void main(String[] args) {
        Landlord landlord = new Landlord();//真实角色:房东

        //代理角色:还没有,可通过ProxyInvocationHandler创建出来,以前自己写代理,现在可以通过这个类动态创建
        ProxyInvocationHandler handler = new ProxyInvocationHandler();
        handler.setRent(landlord);//设置代理的真实角色
        Rent proxy =(Rent) handler.getProxy();//getProxy动态生成代理类
        proxy.rent();//就是执行invoke
    }
}

运行结果如下
在这里插入图片描述

抽象出万能代理类,也就是代码的封装复用

//自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {

    private Object target;//被代理的类

    public void setObject(Object target) {
        this.target = target;
    }

    //生成得到代理类
    public Object getProxy(){
        //创建代理实例(通过反射获取参数):类加载器、被代理的接口、InvocationHandler接口
        Object obj = Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
        return obj;
    }

    //执行代理实例,并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object res = method.invoke(target, args);
        return res;
    }

}

在这里插入图片描述
运行后
在这里插入图片描述
换成其他真是角色,比如之前的UserServiceImpl
在这里插入图片描述
运行后
在这里插入图片描述
这只是证明可以用动态代理调用基本操作,如果需要新增扩展操作呢?比如加一个日志
在这里插入图片描述
运行Client
在这里插入图片描述
如果换成调用proxy.delete()
在这里插入图片描述
这样就做到万能通用,且不会改动之前写好的代码,都是新增的

总结动态代理:
动态代理就是通过重写InvocationHandler接口中invoke方法,并且用Proxy创建代理实例来创建动态代理的,这两个点是最重要的,一个创建一个执行

看完上面什么是代理,非常推荐没有阅读过Spring AOP四种实现方式的阅读一下这个文章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小样x

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值