代理模式

思考代理模式

代理模式是通过创建一个代理对象,用这个代理对象去代表真实的对象,客户端得到这个代理对象后,对客户端并没有什么影响,就跟得到了真实对象一样来使用。
当客户端操作这个代理对象的时候,实际上功能最终还是会由真实的对象来完成,只不过是通过代理操作的,也就是客户端操作代理,代理操作真正的对象。

1.代理模式的本质

代理模式的本质:控制对象访问。

代理模式通过代理目标对象,把代理对象插入到客户和目标对象之间,从而为客户和目标对象引入一定的间接性。正是这个间接性,给了代理对象很多的活动空间。代理对象可以在调用具体的目标对象前后,附加很多操作,从而实现新的功能或是扩展目标对象的功能。更狠的是,代理对象还可以不去创建和调用目标对象,也就是说,目标对象被完全代理掉了,或是被替换掉了。

2.何时选用代理模式

需要为一个对象在不同的地址空间提供局部代表的时候,可以使用远程代理。需要按照需要创建开销很大的对象的时候,可以使用虚代理。

  • 需要为一个对象在不同的地址空间提供局部代表的时候,可以使用远程代理。
  • 需要按照需要创建开销很大的对象的时候,可以使用虚代理。
  • 需要在访问对象执行一些附加操作的时候,可以使用智能指引代理。
  • 需要在访问对象执行一些附加操作的时候,可以使用智能指引代理。

3.代理的分类

  • 远程代理:隐藏了一个对象存在于不同的地址空间的事实,也即是客户通过远程代理去访问一个对象,根本就不关心这个对象在哪里,也不关心如何通过网络去访问到这个对象。从客户的角度来讲,它只是在使用代理对象而已。

  • 虚代理:可以根据需要来创建“大”对象,只有到必须创建对象的时候,虚代理才会创建对象,从而大大加快程序运行速度,并节省资源。通过虚代理可以对系统进行优化。

  • 保护代理:可以在访问一个对象的前后,执行很多附加的操作,除了进行权限控制之外,还可以进行很多跟业务相关的处理,而不需要修改被代理的对象。也就是说,可以通过代理来给目标对象增加功能。

  • 智能指引:和保护代理类似,也是允许在访问一个对象的前后,执行很多附加的操作,这样一来就可以做很多额外的事情,比如,引用计数等。

4.代理模式的结构

在这里插入图片描述

  • Proxy:代理对象,通常具有如下功能。
    实现与具体的目标对象一样的接口,这样就可以使用代理来代替具体的目标对象。保存一个指向具体目标对象的引用,可以在需要的时候调用具体的目标对象。
    可以控制对具体目标对象的访问,并可以负责创建和删除它。
  • Subject:目标接口,定义代理和具体目标对象的接口,这样就可以在任何使用具体目标对象的地方使用代理对象。
  • RealSubject:具体的目标对象,真正实现目标接口要求的功能。

5.实现

静态代理

1.创建抽象角色

/**
 * @description:租房接口
 */
public interface Renting {

    /**
     * 租房
     */
    void rentingHouse();
}

2.创建真实角色

/**
 * @description:房东
 */
public class Host implements Renting{

    @Override
    public void rentingHouse() {
        System.out.println("房东要出租房子");
    }
}

3.创建代理角色

/**
 * @description:中介
 */
public class Mediation implements Renting{

    //中介要代理房东这个角色,代理的是具体的实现类
    private Host host;

    public Mediation(Host host) {
        this.host = host;
    }

    /**
     * 可以干一些其他事情,比如看房子,签合同
     */
    @Override
    public void rentingHouse() {
        seeHouse();
        host.rentingHouse();
        qianhetong();
    }

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

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

4.用户测试

public class Test {
    public static void main(String[] args) {
    	//实例化一个房东对象
        Host host = new Host();
        //中介代理房东,所以将房东对象传给中介
        Mediation mediation = new Mediation(host);
        //中介调用租房子方法
        mediation.rentingHouse();
    }
}

5.结果
在这里插入图片描述

JDK动态代理

动态代理类

/**
 * @description:jdk动态代理类
 */
public class JDKProxy implements InvocationHandler {

    //代理的是接口
    private Renting renting;

    public JDKProxy(Renting renting) {
        this.renting = renting;
    }

    /**
     * @param proxy 被代理对象
     * @param method 目标对象中的方法对象
     * @param args 方法对象的参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //看房子
        System.out.println("看房子");
        //反射执行接口方法(租房)
        Object invoke = method.invoke(renting, args);
        //签合同
        System.out.println("签合同");
        return invoke;
    }
}

测试类

/**
 * @description:测试类
 */
public class Client {

    public static void main(String[] args) {
        //目标对象
        Renting target=new Host();
        JDKProxy jdkProxy = new JDKProxy(target);
        //生成代理对象
        Object proxyInstance = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), jdkProxy);
        Renting renting=(Renting)proxyInstance;
        renting.rentingHouse();
    }
}

效果
在这里插入图片描述

现在的动态代理类只能代理Renting 接口,将其改为Object,就能代理所有接口了,变成通用类,效果一样

/**
 * @description:jdk动态代理类
 */
public class JDKProxy implements InvocationHandler {

    //代理的是接口
    private Object object;

    public JDKProxy(Object object) {
        this.object = object;
    }

    /**
     * @param proxy 被代理对象
     * @param method 目标对象中的方法对象
     * @param args 方法对象的参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //看房子
        System.out.println("看房子");
        //反射执行接口方法(租房)
        Object invoke = method.invoke(object, args);
        //签合同
        System.out.println("签合同");
        return invoke;
    }
}
cglib动态代理

cglib代理类

/**
 * @description:cglib代理类
 */
public class CglibProxy implements MethodInterceptor {

    /**
     * @param proxy 代理对象
     * @param method 目标对象中的方法
     * @param args 目标对象中的参数
     * @param methodProxy 代理对象中的代理方法对象
     */
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //看房子
        System.out.println("cglib看房子");
        //反射执行接口方法(租房)
        Object rtnObj = methodProxy.invokeSuper(proxy, args);
        //签合同
        System.out.println("cglib签合同");
        return rtnObj;
    }
}

测试类

/**
 * @description:测试类
 */
public class Client {

    public static void main(String[] args) {
        // 创建空的字节码对象
        Enhancer enhancer = new Enhancer();
        // 设置字节码对象的父类也就是目标类(代理的是具体的类)
        enhancer.setSuperclass(Host.class);
        //创建回调对象
        Callback callback = new CglibProxy();
        // 设置字节码对象的回调方法
        enhancer.setCallback(callback);
        // 得到代理对象
        Host cglibProxyDemo = (Host) enhancer.create();
        // 调用方法
        cglibProxyDemo.rentingHouse();
    }
}

结果
在这里插入图片描述

Mybatis的Mapper接口代理

1.创建Mapper接口

  • 首先,定义一个Java接口,用于声明数据访问的方法。每个方法对应一个具体的数据库操作,如查询、插入、更新等。
  • 方法的名称和参数应与要执行的SQL语句相匹配。
public interface UserMapper {
    User getUserById(Long id);
    List<User> getAllUsers();
    void insertUser(User user);
    void updateUser(User user);
    void deleteUser(Long id);
}

2.创建Mapper映射文件

  • 在资源文件夹下创建一个与Mapper接口相对应的XML文件,用于定义与Mapper接口中方法对应的SQL语句。
  • 在XML文件中,使用、、、等标签定义具体的SQL语句,并使用id属性指定语句的唯一标识符。
<mapper namespace="com.example.mapper.UserMapper">
    <select id="getUserById" resultType="com.example.model.User">
        SELECT * FROM users WHERE id = #{id}
    </select>
    <select id="getAllUsers" resultType="com.example.model.User">
        SELECT * FROM users
    </select>
    <insert id="insertUser">
        INSERT INTO users (id, name) VALUES (#{id}, #{name})
    </insert>
    <update id="updateUser">
        UPDATE users SET name = #{name} WHERE id = #{id}
    </update>
    <delete id="deleteUser">
        DELETE FROM users WHERE id = #{id}
    </delete>
</mapper>

3.创建Mapper接口代理类

  • 创建一个代理类,实现InvocationHandler接口,并重写invoke方法。
  • 在invoke方法中,通过SqlSession执行底层的SQL语句,根据方法名动态匹配Mapper映射文件中的SQL语句,并将方法的参数传递给SQL语句。
  • 最后,通过Java的Proxy类的newProxyInstance方法创建Mapper接口的代理对象。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MapperProxy<T> implements InvocationHandler {
    private SqlSession sqlSession;
    private Class<T> mapperInterface;

    public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 获取Mapper接口方法的名称
        String methodName = method.getName();

        // 获取Mapper接口方法所在的类的名称
        String className = method.getDeclaringClass().getName();

        // 构造Mapper接口方法对应的Mapper映射文件中的statement id
        String statementId = className + "." + methodName;

        // 调用SqlSession的方法执行底层SQL
        if (args == null) {
            return sqlSession.selectOne(statementId);
        } else {
            return sqlSession.selectOne(statementId, args[0]);
        }
    }

    public T newInstance() {
        return (T) Proxy.newProxyInstance(
                mapperInterface.getClassLoader(),
                new Class[]{mapperInterface},
                this
        );
    }
}

4.使用Mapper接口代理:

  • 创建SqlSession实例,用于与数据库进行交互。
  • 创建Mapper接口代理对象,并调用其中的方法。
  • MyBatis框架会通过代理对象拦截方法的调用,并根据方法名动态执行Mapper映射文件中对应的SQL语句。
SqlSession sqlSession = ...; // 获取SqlSession实例

UserMapper userMapper = new MapperProxy<>(sqlSession, UserMapper.class).newInstance();

User user = userMapper.getUserById(1L);
List<User> users = userMapper.getAllUsers();

User newUser = new User();
newUser.setId(2L);
newUser.setName("John");
userMapper.insertUser(newUser);

User updatedUser = new User();
updatedUser.setId(2L);
updatedUser.setName("Updated John");
userMapper.updateUser(updatedUser);

userMapper.deleteUser(2L);

在上述示例中,通过创建MapperProxy的实例,并调用newInstance方法来获取代理对象。然后,可以使用代理对象调用Mapper接口中的方法,实际的SQL执行和结果返回由invoke方法中的逻辑完成。

这样,MyBatis框架通过动态代理的方式,将Mapper接口的方法与底层的SQL操作关联起来,使得开发者可以通过编写Mapper接口的方式进行数据库操作,而不必编写繁琐的SQL语句。同时,还能够利用MyBatis的各种特性,如缓存、参数映射等。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值