【Spring】(7)JDK 动态代理

【Spring】(7)JDK 动态代理

一、动态代理

  • 动态代理和静态代理角色都一样(抽象角色、真实角色、代理角色)。

  • 动态代理的代理类是动态生成的,不是我们直接写好的。(静态代理是我们自己写了代理类,但是动态代理就不需要我们自己的写代理类了。)

  • 动态代理分为:

    • 基于接口的:JDK 动态代理
    • 基于类:cglib
    • 基于Java 字节码:Javassist

我们主要学习JDK 的动态代理(Spring AOP默认使用JDK动态代理)。

需要在JDK的API文档中去看看 ==InvocationHandler(接口)、Proxy(类)== 。(在反射包reflect下)

二、租房案例

还是使用上一篇静态代理中的案例,不过这次使用的是动态代理!

对象:房东(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.创建调用处理器

创建一个类并且实现InvocationHandler接口。

用这个类来生成代理实例。

/** 调用处理类实例:(每个代理实例都有一个关联的调用处理程序) */
public class MyInvocationHandler implements InvocationHandler {
    /** 被代理的接口 */
    private Rent rent;
    public void setRent(Rent rent) {
        this.rent = rent;
    }

    /** 真正生成代理类的方法:返回rent接口的代理类实例 */
    public Object getProxy() {
        // 参数解释:loader:类加载器来定义代理类。interfaces :代理类实现的接口列表。h:调度方法调用的调用处理函数(因为实现了InvocationHandler所以直接填this)
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
    }

    /**
     * 处理代理实例,并返回结果。(处理代理实例上的方法调用并返回结果。)
     * proxy:调用该方法的代理实例(代理类代理的真实代理对象com.sun.proxy.$Proxy0(生成的代理对象)
     * method:我们所要调用某个对象真实的方法的Method对象
     * args:指代代理对象方法传递的参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        // 方法调用
        Object result = method.invoke(rent, args);
        after();
        return result;
    }

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

4.测试

创建调用处理器,set置入需要代理的对象(房东),获取代理对象(中介)。最后代理对象(中介)执行出租方法。

/** 租客:需要租房子的人 */
public class Tenant {
    /** 顾客去租房 */
    public static void main(String[] args) {
        // 创建调用处理器
        MyInvocationHandler ih = new MyInvocationHandler();
        // set 置入需要代理的对象(房东)
        ih.setRent(new Renter());
        // 获取代理对象(中介)
        Rent proxy = (Rent) ih.getProxy();
        // 代理对象,执行出租方法。
        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 MyInvocationHandler implements InvocationHandler {
    private UserDao userDao;
    public void setUserDaoImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    /** 获取代理对象 */
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), userDao.getClass().getInterfaces(), this);
    }

    /** 处理代理实例,并返回结果。 */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log(method.getName());
        Object result = method.invoke(userDao, args);
        logReturnType(method.getReturnType().getName());
        return result;
    }
    /** 日志 */
    private void log(String name) {
        System.out.println("[info]: ----- "+name + "()方法 -----");
    }
    /** 日志 */
    private void logReturnType(String name) {
        System.out.println("[info]: ----- 返回类型:"+name + " -----");
    }
}

4.测试

创建调用处理类实例,set注入需要代理的对象(UserDaoImpl),获取代理对象,最后执行代理对象的插入方法

public class Test {
    public static void main(String[] args) {
        // 创建调用处理类实例
        MyInvocationHandler ih = new MyInvocationHandler();
        // set 置入需要代理的对象(UserDaoImpl)
        ih.setUserDaoImpl(new UserDaoImpl());
        // 获取代理对象(UserDaoImpl)
        UserDao proxy = (UserDao) ih.getProxy();
        // 代理对象,执行插入方法。
        proxy.ins();
    }
}

输出:

[info]: ----- ins()方法 -----
insert命令
[info]: ----- 返回类型:void -----

四、封装成通用类

将刚刚使用的两个MyInvocationHandler给封装成通用类。至此i,JDK的动态代理已结束,开始学习Spring 的AOP。

/** 调用处理类实例:(每个代理实例都有一个关联的调用处理程序) */
public class MyInvocationHandler implements InvocationHandler {
    /** 目标:被代理的接口 */
    private Object target;
    public void setTarget(Object target) {
        this.target = target;
    }

    /** 返回代理类实例 */
    public Object getProxy() {
        // 参数解释:loader:类加载器来定义代理类。interfaces :代理类实现的接口列表。h:调度方法调用的调用处理函数(因为实现了InvocationHandler所以直接填this)
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    /** 处理代理实例,并返回结果。(处理代理实例上的方法调用并返回结果。) */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 方法调用
        Object result = method.invoke(target, args);
        return result;
    }
}