【Spring】(6)静态代理

【Spring】(6)静态代理

一、代理设计模式

根据模式是用来完成什么工作来划分,这种方式可分为创建型模式、结构型模式和行为型模式 3 种。

代理设计模式属于三种的的:结构型模式(用于描述如何将类或对象按某种布局组成更大的结构)。

1.代理模式的定义

为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

2.组成

  • 抽象角色:通过接口或抽象类声明真实角色实现的业务方法。(一般是用接口或抽象类来解决)

  • 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。(代理真实角色,代理角色一般会做一些其他的操作)

  • 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。 (被代理的角色)

3.优点

  • 职责清晰:
    真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰。(可以让真实对象职责更明确!不去关注一些公共的业务)

  • 代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用。(公共的业务交给了代理角色,实现了业务的分工)

  • 高扩展性。(公共业务发生扩展时(加需求),方便集中管理)

二、租房案例

对象:房东(Renter )、中介(Proxy)

出租事件:Rent接口的rent()方法,用于出租。

关系:中介(代理对象)可以代理房东(真实对象)租房子。

在这里插入图片描述

1.抽象角色

抽象角色:代理角色(代理人)和真实角色可以实现)

这里抽象角色为一个接口:出租方法,用于出租房屋。

/** 房东Host和代理人中介Proxy都可以实现出租Rent这件事情(方法) */
public interface Rent {
    /** 出租房屋 */
    void rent();
}

2.真实角色

真实角色:真实的角色,可以实现业务方法.

真实角色房东,可以实现出租房屋。房东仅仅只用关注出租房屋。

/** 房东:可以出租房屋的人 */
public class Renter implements Rent {
    @Override
    public void rent() {
        System.out.println("房东出租房子。");
    }
}

3.代理角色

代理角色:是真实对象的代理,也可以实现业务方法。

代理角色中介,不仅可以实现代理角色出租房屋,他还可以在租房前做一些别的事情。比如看房,签合同

/** 代理人中介 */
public class Proxy implements Rent {
    private Renter host;
    public Proxy(Renter host) {
        this.host = host;
    }

    @Override
    public void rent() {
        before();
        host.rent();
        after();
    }

    private void before() {
        System.out.println("----- 中介带你看房 -----");
    }
    private void after() {
        System.out.println("----- 中介带你签合同 -----");
    }

}

4.测试

有一个租客需要租房子,租客找中介租到了房子。

中介代理了房东,帮房东出租了房子,还帮房东做一些别的事情,比如看房,签合同。职责明确。

同时,代理还保护了真实的房东信息(对目标对象进行保护)。

/** 租客:需要租房子的人 */
public class Tenant {
    /** 顾客去租房 */
    public static void main(String[] args) {
        // 找到中介(中介代理了某个房东)
        Proxy proxy = new Proxy(new Renter());
        // 找中介租房子
        proxy.rent();
    }
}

输出:

可以看到中介(代理对象)帮房东(真实对象)出租了房子,同时还帮房东解决了一些其他的问题。

----- 中介带你看房 -----
房东出租房子。
----- 中介带你签合同 -----

三、真实业务案例

需求:

增加业务日志输出功能:业务调用前需要输出调用的方法名,调用后需要输出返回值。

1.抽象角色

抽象角色:用户增删改查操作接口。

/** 用户数据访问层接口 */
public interface UserDao {
    void ins();
    void del();
    void upd();
    void sel();
}

2.真实角色

真实对象:用户增删改查操作接口的实现类。

public class UserDaoImpl implements UserDao {
    @Override
    public void ins() {
        System.out.println("insert命令");
    }

    @Override
    public void del() {
        System.out.println("delete命令");
    }

    @Override
    public void upd() {
        System.out.println("update命令");
    }

    @Override
    public void sel() {
        System.out.println("select命令");
    }
}

3.代理角色

代理对象:用于代理真实角色,让真实角色职责更清晰明确。

/** 增加业务日志打印:调用前需要输出调用的方法名,调用后需要输出返回值 */
public class UserDaoImplProxy implements UserDao {
    private UserDao userDao;

    public void setUserDaoImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void ins() {
        log("----- 执行了ins()方法 -----");
        userDao.ins();
        log("----- 无返回值 -----");
    }

    @Override
    public void del() {
        log("----- 执行了del()方法 -----");
        userDao.del();
        log("----- 无返回值 -----");
    }

    @Override
    public void upd() {
        log("----- 执行了upd()方法 -----");
        userDao.upd();
        log("----- 无返回值 -----");
    }

    @Override
    public void sel() {
        log("----- 执行了sel()方法 -----");
        userDao.sel();
        log("----- 无返回值 -----");
    }

    /** 日志 */
    private void log(String name) {
        System.out.println("[info]: "+name);
    }
}

4.测试

创建代理对象,放入需要代理的真实对象,调用业务方法。

public class Test {
    public static void main(String[] args) {
        // 代理对象,新增日志打印
        UserDaoImplProxy userDaoProxy = new UserDaoImplProxy();
        // 代理原本对象UserDaoImpl
        userDaoProxy.setUserDaoImpl(new UserDaoImpl());
        // 执行方法
        userDaoProxy.ins();
        userDaoProxy.del();
        userDaoProxy.upd();
        userDaoProxy.sel();
    }
}

输出:

可以看到真实对象UserDaoImpl被代理对象UserDaoImplProxy给代理了,并且实现了新增的业务。

[info]: ----- 执行了ins()方法 -----
insert命令
[info]: ----- 无返回值 -----
[info]: ----- 执行了del()方法 -----
delete命令
[info]: ----- 无返回值 -----
[info]: ----- 执行了upd()方法 -----
update命令
[info]: ----- 无返回值 -----
[info]: ----- 执行了sel()方法 -----
select命令
[info]: ----- 无返回值 -----

四、备注

通过上面,我们大概了解了静态代理,但是静态代理有个缺点。

静态代理缺点 :每一个真实对象会产生一个代理角色,代码量会翻倍,开发效率变低(静态代理是在编译期就已经确定代理类和真实类的关系,并且生成代理类的)。

但是有方法解决的!使用 动态代理 (动态代理是在运行期利用JVM的 反射 机制生成代理类,这里是直接生成类的字节码,然后通过类加载器载入JAVA虚拟机执行)!