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

Posted 小样5411

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式之代理模式详解(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四种实现方式的阅读一下这个文章

以上是关于设计模式之代理模式详解(java)的主要内容,如果未能解决你的问题,请参考以下文章

JAVA基础——代理模式

java之 代理设计模式

设计模式之静态代理

设计模式之代理模式

Java静态代理与动态代理模式的实现

Java设计模式---代理模式---动态代理