Skip to content

框架设计

为什么需要框架

在软件开发中,框架(Framework)是经过高度抽象和设计的半成品软件,它为特定领域的问题提供了通用解决方案。当一个团队反复面对相似的技术挑战时,框架的价值就凸显出来:

  • 复用与效率:避免重复造轮子。框架将经过验证的最佳实践沉淀为可复用的组件,开发者只需关注业务逻辑。
  • 统一规范:约束团队按照统一模式编码,降低沟通成本,新人也能快速上手。
  • 质量保障:框架通常由资深开发者设计,经过大量项目验证,其稳定性和性能远超临时拼凑的工具类。
  • 关注点分离:框架将横切关注点(日志、安全、事务、缓存)从业务代码中剥离,让代码更干净。
  • 可扩展性:好的框架通过 SPI、插件机制等设计,允许在不修改核心代码的情况下进行扩展。
没有框架时:                        有框架时:
┌──────────────────────┐          ┌──────────────────────┐
│  业务逻辑 + 基础设施   │          │      业务逻辑         │
│  (混杂在一起)          │          │  (只关注业务)         │
│  · 连接池管理          │          ├──────────────────────┤
│  · 事务控制            │          │      框架层           │
│  · 日志记录            │          │  · 连接池管理         │
│  · 异常处理            │          │  · 事务控制           │
│  · 安全校验            │          │  · 日志记录           │
│  · 真正业务逻辑        │          │  · 安全校验           │
└──────────────────────┘          └──────────────────────┘

框架 vs 库:谁调用谁?

框架和库的关键区别在于控制反转(IoC, Inversion of Control)

维度框架(Framework)库(Library)
控制权框架控制流程,调用你的代码你控制流程,调用库的 API
关系好莱坞原则:"Don't call us, we'll call you"你主动调用,库被动响应
典型例子Spring、MyBatis、NettyApache Commons、Guava、SLF4J
代码结构你需要遵循框架规定的接口和生命周期你自由组织代码,按需调用
侵入性高,代码依赖框架低,可随时替换
java
// 使用库:你控制流程
public void doSomething() {
    // 你主动调用 Guava 的工具类
    List<String> result = Splitter.on(',')
        .trimResults()
        .omitEmptyStrings()
        .splitToList(input);
    // 你决定何时、如何使用
}

// 使用框架:框架控制流程
@RestController  // 框架要求你使用注解
public class UserController {
    @GetMapping("/users/{id}")
    public User getUser(@PathVariable Long id) {
        // 框架决定了何时调用这个方法(HTTP 请求到达时)
        return userService.findById(id);
    }
}

框架设计原则

1. 可扩展性——SPI 机制

好的框架必须允许用户在不修改源码的情况下扩展功能。Java SPI(Service Provider Interface)是基石:

java
// ===== 框架方定义接口 =====
// 文件:connector-api.jar
public interface DatabaseConnector {
    String getName();
    Connection connect(String url, String user, String password);
    boolean supports(String url);
}

// ===== 框架方的加载逻辑 =====
public class ConnectorLoader {
    public List<DatabaseConnector> loadConnectors() {
        List<DatabaseConnector> connectors = new ArrayList<>();
        // 通过 ServiceLoader 加载所有实现
        ServiceLoader<DatabaseConnector> loader = 
            ServiceLoader.load(DatabaseConnector.class);
        for (DatabaseConnector connector : loader) {
            connectors.add(connector);
        }
        return connectors;
    }
}

// ===== 扩展方实现接口 =====
// 文件:META-INF/services/com.example.DatabaseConnector
// 内容:com.mycompany.MySQLConnector

public class MySQLConnector implements DatabaseConnector {
    @Override
    public String getName() { return "MySQL"; }
    
    @Override
    public Connection connect(String url, String user, String password) {
        // MySQL 连接实现
        return DriverManager.getConnection(url, user, password);
    }
    
    @Override
    public boolean supports(String url) {
        return url.startsWith("jdbc:mysql:");
    }
}

2. Spring Boot 自动配置——SPI 的进阶应用

Spring Boot 的自动配置是基于 SPI 思想的高级实现:

java
// spring-boot-autoconfigure.jar 中的 spring.factories
// 位置:META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
  org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
  org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
  ...

// 自动配置条件注解示例
@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class, ... })
public class DataSourceAutoConfiguration {
    
    @Configuration
    @ConditionalOnMissingBean(DataSource.class)
    static class EmbeddedDatabaseConfiguration {
        @Bean
        public DataSource dataSource(DataSourceProperties properties) {
            // 创建内嵌数据源
            return createEmbeddedDataSource(properties);
        }
    }
}

3. 约定优于配置

Spring Boot 通过"约定优于配置"大幅减少样板代码:

java
// 约定:spring.datasource 前缀的属性自动映射到 DataSource
// 配置方只需在 application.yml 中填写:
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: root
    password: 123456

// 框架内部自动完成映射,无需手动创建 DataSource Bean
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties {
    private String url;
    private String username;
    private String password;
    // getter / setter
}

4. 可配置性

框架需要提供灵活但不繁琐的配置方式:

配置方式优先级适用场景
代码配置(@Bean)最高需要编程逻辑的复杂配置
YAML/Properties环境相关配置
注解声明式配置
默认值最低开箱即用

框架设计中的核心设计模式

1. 模板方法模式(Template Method)

定义算法骨架,将可变步骤延迟到子类。

java
// 抽象父类定义算法骨架
public abstract class AbstractDataProcessor {
    
    // 模板方法:定义处理流程的骨架
    public final void process() {
        validate();
        beforeProcess();
        doProcess();
        afterProcess();
        onSuccess();
    }
    
    protected void validate() {
        // 默认实现,子类可覆盖
    }
    
    protected void beforeProcess() {
        // 钩子方法
    }
    
    // 抽象方法:子类必须实现
    protected abstract void doProcess();
    
    protected void afterProcess() {
        // 钩子方法
    }
    
    protected void onSuccess() {
        // 钩子方法
    }
}

// 具体实现
public class ExcelDataProcessor extends AbstractDataProcessor {
    @Override
    protected void doProcess() {
        System.out.println("处理 Excel 数据");
    }
    
    @Override
    protected void validate() {
        System.out.println("校验 Excel 格式");
    }
}

框架中的经典应用

  • Spring 的 AbstractApplicationContext.refresh()
  • MyBatis 的 BaseExecutor
  • Spring 的 JdbcTemplateRestTemplate

2. 策略模式(Strategy)

定义一系列算法,让它们可以相互替换。

java
// 策略接口
public interface LoadBalanceStrategy {
    Server select(List<Server> servers);
}

// 具体策略
public class RoundRobinStrategy implements LoadBalanceStrategy {
    private int index = 0;
    @Override
    public Server select(List<Server> servers) {
        return servers.get(index++ % servers.size());
    }
}

public class RandomStrategy implements LoadBalanceStrategy {
    @Override
    public Server select(List<Server> servers) {
        return servers.get(ThreadLocalRandom.current().nextInt(servers.size()));
    }
}

// 上下文
public class LoadBalancer {
    private LoadBalanceStrategy strategy;
    
    public LoadBalancer(LoadBalanceStrategy strategy) {
        this.strategy = strategy;
    }
    
    public Server route(List<Server> servers) {
        return strategy.select(servers);
    }
}

3. 责任链模式(Chain of Responsibility)

将请求沿链传递,直到被处理。

java
// 拦截器接口
public interface Interceptor {
    Object intercept(Invocation invocation) throws Throwable;
}

// 责任链
public class InterceptorChain {
    private final List<Interceptor> interceptors;
    
    public Object proceed(Object target) {
        // 构建调用链
        Invocation invocation = new Invocation(target, interceptors, 0);
        return invocation.proceed();
    }
}

// 调用对象
public class Invocation {
    private final Object target;
    private final List<Interceptor> interceptors;
    private int index;
    
    public Object proceed() {
        if (index >= interceptors.size()) {
            return target; // 链尾,执行目标
        }
        Interceptor interceptor = interceptors.get(index++);
        return interceptor.intercept(this);
    }
}

框架中的经典应用

  • Spring AOP 的拦截器链
  • Spring MVC 的 HandlerInterceptor
  • MyBatis 的插件机制
  • Netty 的 ChannelPipeline

4. 观察者模式(Observer)

定义一对多依赖,当对象状态改变时通知所有依赖者。

java
// 事件对象
public class ApplicationEvent {
    private final Object source;
    private final long timestamp;
    // 构造函数、getter...
}

// 监听器接口
public interface ApplicationListener<E extends ApplicationEvent> {
    void onApplicationEvent(E event);
}

// 事件广播器
public class SimpleEventMulticaster {
    private final List<ApplicationListener<?>> listeners = new ArrayList<>();
    
    public void addListener(ApplicationListener<?> listener) {
        listeners.add(listener);
    }
    
    public void publishEvent(ApplicationEvent event) {
        for (ApplicationListener listener : listeners) {
            // 类型匹配后调用
            listener.onApplicationEvent(event);
        }
    }
}

框架中的经典应用

  • Spring 的事件机制(ApplicationEvent + ApplicationListener
  • Spring Boot 的 ApplicationRunnerCommandLineRunner

5. 装饰器模式(Decorator)

动态地给对象添加额外职责。

java
// 组件接口
public interface Cache {
    Object get(String key);
    void put(String key, Object value);
}

// 基础实现
public class SimpleCache implements Cache {
    private final Map<String, Object> map = new ConcurrentHashMap<>();
    public Object get(String key) { return map.get(key); }
    public void put(String key, Object value) { map.put(key, value); }
}

// 装饰器:增加日志
public class LoggingCache implements Cache {
    private final Cache delegate;
    public LoggingCache(Cache delegate) { this.delegate = delegate; }
    
    public Object get(String key) {
        System.out.println("Cache get: " + key);
        return delegate.get(key);
    }
    public void put(String key, Object value) {
        System.out.println("Cache put: " + key);
        delegate.put(key, value);
    }
}

// 装饰器:增加 LRU 淘汰
public class LRUCache implements Cache {
    private final Cache delegate;
    private final int maxSize;
    private final LinkedHashMap<String, Object> accessOrder;
    // ...
}

框架中的经典应用

  • MyBatis 的二级缓存(CachingExecutor 装饰 BaseExecutor
  • Java IO 流(BufferedInputStream 装饰 InputStream
  • Spring 的 TransactionAwareCacheDecorator

实战:设计一个简单的 ORM 框架

核心流程

注解解析 → SQL 生成 → 执行 → 结果映射

第一步:定义注解

java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Table {
    String name() default "";
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Column {
    String name() default "";
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Id {
    boolean autoIncrement() default true;
}

第二步:实体类元数据解析

java
public class EntityMeta {
    private String tableName;
    private Class<?> entityClass;
    private Field idField;
    private List<Field> columns;
    // getter / setter...
}

public class EntityParser {
    public static EntityMeta parse(Class<?> clazz) {
        EntityMeta meta = new EntityMeta();
        meta.setEntityClass(clazz);
        
        // 解析 @Table 注解
        Table table = clazz.getAnnotation(Table.class);
        meta.setTableName(table != null && !table.name().isEmpty() 
            ? table.name() : clazz.getSimpleName().toLowerCase());
        
        // 解析字段
        List<Field> columns = new ArrayList<>();
        for (Field field : clazz.getDeclaredFields()) {
            if (field.isAnnotationPresent(Id.class)) {
                meta.setIdField(field);
            }
            if (field.isAnnotationPresent(Column.class)) {
                columns.add(field);
            }
        }
        meta.setColumns(columns);
        return meta;
    }
}

第三步:SQL 生成器

java
public class SqlGenerator {
    
    public static String generateInsert(EntityMeta meta) {
        StringBuilder sql = new StringBuilder("INSERT INTO ");
        sql.append(meta.getTableName()).append(" (");
        
        List<Field> columns = meta.getColumns();
        for (int i = 0; i < columns.size(); i++) {
            Column col = columns.get(i).getAnnotation(Column.class);
            String colName = col.name().isEmpty() 
                ? columns.get(i).getName() : col.name();
            sql.append(colName);
            if (i < columns.size() - 1) sql.append(", ");
        }
        sql.append(") VALUES (");
        for (int i = 0; i < columns.size(); i++) {
            sql.append("?");
            if (i < columns.size() - 1) sql.append(", ");
        }
        sql.append(")");
        return sql.toString();
    }
    
    public static String generateSelectById(EntityMeta meta) {
        return "SELECT * FROM " + meta.getTableName() + " WHERE id = ?";
    }
    
    public static String generateSelectAll(EntityMeta meta) {
        return "SELECT * FROM " + meta.getTableName();
    }
}

第四步:结果映射器

java
public class ResultMapper {
    
    public static <T> T map(ResultSet rs, Class<T> clazz) throws Exception {
        T instance = clazz.getDeclaredConstructor().newInstance();
        ResultSetMetaData rsMeta = rs.getMetaData();
        
        for (int i = 1; i <= rsMeta.getColumnCount(); i++) {
            String columnName = rsMeta.getColumnLabel(i);
            Object value = rs.getObject(i);
            
            // 通过反射设置字段值
            for (Field field : clazz.getDeclaredFields()) {
                Column col = field.getAnnotation(Column.class);
                String fieldColName = (col != null && !col.name().isEmpty()) 
                    ? col.name() : field.getName();
                if (fieldColName.equalsIgnoreCase(columnName)) {
                    field.setAccessible(true);
                    field.set(instance, value);
                    break;
                }
            }
        }
        return instance;
    }
}

第五步:整合——核心 Session

java
public class MiniSession {
    private final DataSource dataSource;
    private final Map<Class<?>, EntityMeta> metaCache = new ConcurrentHashMap<>();
    
    public MiniSession(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    
    public <T> void insert(T entity) throws Exception {
        EntityMeta meta = getMeta(entity.getClass());
        String sql = SqlGenerator.generateInsert(meta);
        
        try (Connection conn = dataSource.getConnection();
             PreparedStatement ps = conn.prepareStatement(sql)) {
            
            List<Field> columns = meta.getColumns();
            for (int i = 0; i < columns.size(); i++) {
                columns.get(i).setAccessible(true);
                ps.setObject(i + 1, columns.get(i).get(entity));
            }
            ps.executeUpdate();
        }
    }
    
    public <T> T findById(Class<T> clazz, Object id) throws Exception {
        EntityMeta meta = getMeta(clazz);
        String sql = SqlGenerator.generateSelectById(meta);
        
        try (Connection conn = dataSource.getConnection();
             PreparedStatement ps = conn.prepareStatement(sql)) {
            ps.setObject(1, id);
            ResultSet rs = ps.executeQuery();
            if (rs.next()) {
                return ResultMapper.map(rs, clazz);
            }
        }
        return null;
    }
    
    private EntityMeta getMeta(Class<?> clazz) {
        return metaCache.computeIfAbsent(clazz, EntityParser::parse);
    }
}

第六步:使用示例

java
@Table(name = "users")
public class User {
    @Id
    private Long id;
    @Column
    private String name;
    @Column(name = "email_addr")
    private String email;
    // getter / setter...
}

// 使用
MiniSession session = new MiniSession(dataSource);
User user = new User();
user.setName("张三");
user.setEmail("[email protected]");
session.insert(user);

User found = session.findById(User.class, 1L);

实战:设计一个简单的 RPC 框架

核心流程

客户端代理 → 序列化 → 网络传输 → 反序列化 → 服务端调用 → 序列化返回

第一步:服务接口定义

java
// 公共接口,客户端和服务端共享
public interface HelloService {
    String sayHello(String name);
}

第二步:客户端动态代理

java
public class RpcClientProxy {
    
    @SuppressWarnings("unchecked")
    public static <T> T create(Class<T> interfaceClass, String host, int port) {
        return (T) Proxy.newProxyInstance(
            interfaceClass.getClassLoader(),
            new Class<?>[]{interfaceClass},
            new RpcInvocationHandler(host, port, interfaceClass.getName())
        );
    }
    
    static class RpcInvocationHandler implements InvocationHandler {
        private final String host;
        private final int port;
        private final String serviceName;
        
        RpcInvocationHandler(String host, int port, String serviceName) {
            this.host = host;
            this.port = port;
            this.serviceName = serviceName;
        }
        
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 1. 封装 RPC 请求
            RpcRequest request = new RpcRequest();
            request.setServiceName(serviceName);
            request.setMethodName(method.getName());
            request.setParameterTypes(method.getParameterTypes());
            request.setArguments(args);
            
            // 2. 序列化 + 发送 + 接收
            try (Socket socket = new Socket(host, port);
                 ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                 ObjectInputStream ois = new ObjectInputStream(socket.getInputStream())) {
                
                oos.writeObject(request);
                oos.flush();
                
                // 3. 读取响应
                RpcResponse response = (RpcResponse) ois.readObject();
                if (response.getException() != null) {
                    throw response.getException();
                }
                return response.getResult();
            }
        }
    }
}

第三步:服务端——接收请求并调用

java
public class RpcServer {
    private final int port;
    private final Map<String, Object> serviceMap = new ConcurrentHashMap<>();
    private volatile boolean running = true;
    
    public RpcServer(int port) {
        this.port = port;
    }
    
    public void registerService(Class<?> interfaceClass, Object implementation) {
        serviceMap.put(interfaceClass.getName(), implementation);
    }
    
    public void start() throws IOException {
        try (ServerSocket serverSocket = new ServerSocket(port)) {
            System.out.println("RPC Server started on port " + port);
            
            while (running) {
                Socket socket = serverSocket.accept();
                new Thread(() -> handleRequest(socket)).start();
            }
        }
    }
    
    private void handleRequest(Socket socket) {
        try (ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
             ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream())) {
            
            // 1. 反序列化请求
            RpcRequest request = (RpcRequest) ois.readObject();
            
            // 2. 查找服务并反射调用
            Object service = serviceMap.get(request.getServiceName());
            Method method = service.getClass().getMethod(
                request.getMethodName(), request.getParameterTypes());
            Object result = method.invoke(service, request.getArguments());
            
            // 3. 封装并返回响应
            RpcResponse response = new RpcResponse();
            response.setResult(result);
            oos.writeObject(response);
            oos.flush();
            
        } catch (Exception e) {
            // 异常处理
        }
    }
}

第四步:通信协议对象

java
public class RpcRequest implements Serializable {
    private String serviceName;      // 服务接口全限定名
    private String methodName;       // 方法名
    private Class<?>[] parameterTypes; // 参数类型
    private Object[] arguments;      // 参数值
    // getter / setter...
}

public class RpcResponse implements Serializable {
    private Object result;           // 返回值
    private Exception exception;     // 异常信息
    // getter / setter...
}

第五步:使用示例

java
// ===== 服务端 =====
RpcServer server = new RpcServer(8080);
server.registerService(HelloService.class, new HelloServiceImpl());
server.start(); // 阻塞

// ===== 客户端 =====
HelloService helloService = RpcClientProxy.create(
    HelloService.class, "localhost", 8080);
String result = helloService.sayHello("World");
System.out.println(result); // "Hello, World!"

RPC 框架演进方向

演进阶段核心能力新增组件
V1.0 基础版动态代理 + Socket + 序列化代理、网络、序列化
V2.0 高性能版Netty 替代 BIOReactor 线程模型
V3.0 服务治理服务发现、负载均衡注册中心、LB 策略
V4.0 企业级熔断、限流、监控熔断器、限流器、链路追踪

面试常见问题

1. 框架和库的区别是什么?

答:核心区别在于控制权。框架通过 IoC 控制程序流程,你的代码被框架调用("Don't call us, we'll call you");库则是一组工具函数,你主动调用库的 API。框架提供了一套完整的解决方案并约束了代码结构(如 Spring),库则提供特定功能的集合(如 Guava)。

2. 如何设计一个插件化/可扩展的框架?

答:核心是面向接口编程 + SPI 机制。框架定义接口规范,扩展方实现接口并通过 META-INF/services 文件或注解声明。框架通过 ServiceLoader 或自定义类扫描器发现并加载实现。关键设计要点:接口职责单一且稳定、提供清晰的扩展点文档、考虑插件间隔离和依赖管理。

3. Spring Boot 自动配置的原理是什么?

答:Spring Boot 通过 @EnableAutoConfiguration 注解触发自动配置,核心是 AutoConfigurationImportSelector 类。它读取 META-INF/spring.factories 文件中配置的所有自动配置类,然后根据 @Conditional 系列注解(@ConditionalOnClass@ConditionalOnMissingBean 等)进行条件判断,只加载满足条件的配置类。这种方式基于 Java SPI 机制,但增加了条件过滤能力。

4. 模板方法模式在框架中有哪些典型应用?

答:Spring 的 AbstractApplicationContext.refresh() 定义了容器启动的 12 步模板,其中 postProcessBeanFactory()onRefresh() 是留给子类的扩展点;MyBatis 的 BaseExecutor 定义了查询、更新的模板骨架,具体执行由子类实现;Spring 的 JdbcTemplate 封装了 JDBC 的操作模板(获取连接、执行 SQL、处理异常、释放连接),用户只需提供 SQL 和参数。

5. 设计一个 ORM 框架需要关注哪些核心问题?

答:1) 对象-关系映射(注解 vs XML 配置映射规则);2) SQL 生成(CRUD 自动化、动态 SQL 支持);3) 结果集映射(类型转换、关联查询映射);4) 缓存机制(一级缓存、二级缓存);5) 事务管理(与 Spring 事务集成);6) 连接池管理;7) 性能优化(批量操作、延迟加载、预编译 SQL 复用)。