• 作者:老汪软件技巧
  • 发表时间:2024-10-03 17:00
  • 浏览量:

定义业务

以下结合 Java 业务场景来解释代理模式

一、静态代理

classDiagram
    class ProductService {
        +getProductDetails(): String
    }
    class RealProductService {
        +getProductDetails(): String
    }
    class ProductServiceProxy {
        -ProductService realService
        +ProductServiceProxy(ProductService realService)
        +getProductDetails(): String
    }
    class LoggingInvocationHandler {
        -Object target
        +LoggingInvocationHandler(Object target)
        +invoke(Object proxy, Method method, Object[] args): Object
    }
    ProductService <|.. RealProductService
    ProductService <|.. ProductServiceProxy
    RealProductService <-- ProductServiceProxy
    LoggingInvocationHandler --> ProductService

假设你正在开发一个在线购物系统,其中有一个接口ProductService代表商品服务,包含获取商品详情的方法。

interface ProductService {
    String getProductDetails();
}

有一个具体的实现类RealProductService来真正实现获取商品详情的功能。

class RealProductService implements ProductService {
    @Override
    public String getProductDetails() {
        return "Real product details";
    }
}

现在,出于安全或者性能考虑,你希望在获取商品详情之前和之后记录日志。可以创建一个静态代理类ProductServiceProxy。

class ProductServiceProxy implements ProductService {
    private ProductService realService;
    public ProductServiceProxy(ProductService realService) {
        this.realService = realService;
    }
    @Override
    public String getProductDetails() {
        System.out.println("Before getting product details.");
        String details = realService.getProductDetails();
        System.out.println("After getting product details.");
        return details;
    }
}

在使用时:

public class Main {
    public static void main(String[] args) {
        ProductService realService = new RealProductService();
        ProductService proxyService = new ProductServiceProxy(realService);
        String details = proxyService.getProductDetails();
        System.out.println(details);
    }
}

在这个场景中,代理对象ProductServiceProxy在不改变RealProductService的代码的情况下,为其添加了日志记录的功能。

二、动态代理还是上述购物系统,现在假设系统中有很多不同的服务,都需要在方法调用前后记录日志,使用动态代理可以更灵活地实现。

Java 的动态代理是通过java.lang.reflect.Proxy类来实现的。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
class LoggingInvocationHandler implements InvocationHandler {
    private Object target;
    public LoggingInvocationHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method invocation.");
        Object result = method.invoke(target, args);
        System.out.println("After method invocation.");
        return result;
    }
}

使用动态代理:

public class Main {
    public static void main(String[] args) {
        ProductService realService = new RealProductService();
        ProductService proxyService = (ProductService) Proxy.newProxyInstance(
                realService.getClass().getClassLoader(),
                realService.getClass().getInterfaces(),
                new LoggingInvocationHandler(realService));
        String details = proxyService.getProductDetails();
        System.out.println(details);
    }
}

在这个场景中,动态代理可以在运行时为任何实现了特定接口的对象创建代理,无需为每个具体的类编写单独的代理类,提高了代码的灵活性和可维护性

总之,在 Java 业务场景中,代理模式可以在不修改原始对象代码的情况下,为对象添加额外的功能,如日志记录、性能监控、安全检查等,有助于实现代码的解耦和可维护性。

框架中的使用spring

同上述业务代码

_对象请求代理中间件_对对象的访问权限进行控制

mybatis

在 MyBatis 框架中,代理模式被广泛应用,主要体现在 Mapper 接口的实现机制上。

MyBatis 通过动态代理模式,使得开发者在编写代码时只需要定义一个 Mapper 接口,而不需要为这个接口编写具体的实现类。MyBatis 在运行时会为这个接口生成一个代理对象,当调用这个代理对象的方法时,MyBatis 会根据配置文件中的 SQL 语句和映射关系,执行相应的数据库操作。

以下是具体的解释:

一、Mapper 接口定义

例如,定义一个用户 Mapper 接口:

public interface UserMapper {
    User getUserById(int id);
}

二、MyBatis 配置与映射文件在 MyBatis 的配置文件中,配置数据库连接信息以及指定映射文件。在映射文件中,定义 SQL 语句与 Mapper 接口方法的对应关系。

例如,对应的 XML 映射文件可能如下:

"1.0" encoding="UTF-8"?>
mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.UserMapper">
    <select id="getUserById" resultType="com.example.User">
        select * from users where id = #{id}
    select>
mapper>

三、代理对象生成与调用

在应用程序中,通过 SqlSession 对象获取 Mapper 接口的代理对象:

SqlSession sqlSession = MyBatisUtil.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.getUserById(1);
sqlSession.close();

MyBatis 在获取代理对象时,会使用 JDK 动态代理或者 CGLIB 动态代理来创建一个实现了 Mapper 接口的代理类。这个代理类在被调用方法时,会根据方法名、参数等信息,找到对应的 SQL 语句,并执行数据库操作,然后将结果返回给调用者。

这样的设计有以下几个好处:

解耦业务逻辑与数据库访问逻辑:开发者只需要关注业务逻辑,通过定义 Mapper 接口来描述数据库操作,而不需要关心具体的数据库访问实现。提高代码的可维护性:如果数据库结构或者 SQL 语句发生变化,只需要修改配置文件和映射文件,而不需要修改业务代码中的实现类。灵活性:可以方便地切换不同的数据库或者调整数据库配置,而不影响业务逻辑代码。总结

一、使用场景

远程代理:为一个位于不同地址空间的对象提供本地代表。比如在分布式系统中,客户端通过代理对象来访问远程服务器上的对象,代理对象负责处理网络通信等细节。虚拟代理:当一个对象的创建开销很大时,可以使用虚拟代理来延迟对象的创建,直到真正需要使用它的时候。例如,在加载大图片时,可以先显示一个占位图,当用户需要查看图片时,再真正加载图片对象。保护代理:基于权限控制对目标对象的访问。代理对象可以检查调用者的权限,决定是否允许调用真实对象的方法。

二、优点

职责清晰:真实对象和代理对象的职责明确,真实对象负责具体的业务逻辑,代理对象负责控制访问和添加额外功能。高扩展性:可以在不修改真实对象的情况下,通过添加不同类型的代理对象来实现不同的功能扩展。安全控制:可以对真实对象进行安全控制,防止未授权的访问。

三、缺点

可能会增加系统的复杂性,因为需要引入代理对象。代理对象可能会影响系统的性能,特别是在需要频繁创建代理对象或者代理对象的额外功能比较复杂的情况下。


上一条查看详情 +使用MyBatis缓存的简单案例
下一条 查看详情 +没有了