以下是 MyBatis 框架中体现的 SOLID 设计模式七大原则 的分类、技术原理及代码示例:
1. 单一职责原则 (Single Responsibility Principle, SRP)
定义:一个类应该只有一个引起它变化的原因。
MyBatis 实现:通过 Mapper
接口和 XML 映射文件分离 SQL 操作与业务逻辑。
// Mapper 接口(单一职责:定义 SQL 操作)
public interface UserMapper {
User getUserById(Long id);
void insertUser(User user);
}
// 业务逻辑类(单一职责:处理业务)
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public User getUser(Long id) {
return userMapper.getUserById(id); // 调用 Mapper 接口
}
}
2. 开闭原则 (Open-Closed Principle, OCP)
定义:软件实体应对扩展开放,对修改关闭。
MyBatis 实现:通过插件机制(Interceptor
)扩展功能,无需修改核心代码。
// 自定义插件(扩展点)
@Intercepts({
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class LoggingInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) {
System.out.println("Executing SQL: " + ((MappedStatement) invocation.getArgs()[0]).getSql());
return invocation.proceed(); // 调用原始方法
}
}
// 配置插件(无需修改 MyBatis 核心代码)
@Configuration
public class MyBatisConfig {
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setPlugins(new Interceptor[]{new LoggingInterceptor()}); // 注册插件
return factoryBean.getObject();
}
}
3. 里氏替换原则 (Liskov Substitution Principle, LSP)
定义:子类必须能够替换其父类而不影响程序正确性。
MyBatis 实现:通过 TypeHandler
接口及其实现类保证类型替换性。
// 父类接口
public interface TypeHandler<T> {
void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType);
T getResult(ResultSet rs, String columnName);
}
// 子类实现(枚举类型处理器)
public class EnumTypeHandler<E extends Enum<E>> implements TypeHandler<E> {
private final Class<E> type;
public EnumTypeHandler(Class<E> type) {
this.type = type;
}
@Override
public void setParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) {
ps.setString(i, parameter.name()); // 枚举名称存入数据库
}
@Override
public E getResult(ResultSet rs, String columnName) {
String name = rs.getString(columnName);
return Enum.valueOf(type, name); // 从数据库读取枚举名称
}
}
// 客户端代码(可替换为其他 TypeHandler 实现)
@MappedTypes(UserStatus.class)
public class UserStatusTypeHandler extends EnumTypeHandler<UserStatus> {
public UserStatusTypeHandler(Class<UserStatus> type) {
super(type);
}
}
4. 接口隔离原则 (Interface Segregation Principle, ISP)
定义:客户端不应依赖它不需要的接口。
MyBatis 实现:通过细粒度接口(如 ResultSetHandler
、ParameterHandler
)替代臃肿接口。
// 细粒度接口(符合 ISP)
public interface ResultSetHandler {
Object handleResultSets(Statement stmt);
}
public interface ParameterHandler {
Object getParameterObject();
void setParameters(PreparedStatement ps);
}
// 违反 ISP 的反例:臃肿接口
public interface StatementHandler {
Statement prepare(Connection connection);
void parameterize(Statement statement);
Object handleResultSets(Statement stmt); // 客户端可能不需要结果集处理
}
5. 依赖倒置原则 (Dependency Inversion Principle, DIP)
定义:高层模块不应依赖低层模块,二者都应依赖抽象。
MyBatis 实现:通过 SqlSession
接口注入抽象,而非具体实现。
// 抽象层
public interface SqlSession {
<T> T selectOne(String statement, Object parameter);
void insert(String statement, Object parameter);
}
// 具体实现(由 MyBatis 内部实现)
public class DefaultSqlSession implements SqlSession {
// 实现 SQL 操作逻辑
}
// 高层模块依赖抽象
@Service
public class UserService {
private final SqlSession sqlSession;
@Autowired // 注入抽象而非具体实现
public UserService(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
public User getUser(Long id) {
return sqlSession.selectOne("getUserById", id);
}
}
6. 迪米特法则 (Law of Demeter, LoD)
定义:一个对象应对其他对象有尽可能少的了解。
MyBatis 实现:通过 Configuration
对象隐藏数据库配置细节。
// 业务代码无需了解配置细节
@Service
public class OrderService {
private final SqlSession sqlSession;
@Autowired
public OrderService(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
public void placeOrder(Order order) {
sqlSession.insert("insertOrder", order); // 仅知道需要插入订单
}
}
// 配置细节由 MyBatis 内部管理
@Configuration
public class MyBatisConfig {
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mappers/*.xml"));
return factoryBean.getObject();
}
}
7. 合成复用原则 (Composite Reuse Principle, CRP)
定义:优先使用对象组合而非类继承。
MyBatis 实现:通过 Configuration
对象组合 MapperRegistry
、InterceptorChain
等组件。
// 通过组合复用功能(符合 CRP)
public class Configuration {
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
protected final InterceptorChain interceptorChain = new InterceptorChain();
public void addMapper(Class<?> type) {
mapperRegistry.addMapper(type); // 组合 Mapper 注册表
}
public void addInterceptor(Interceptor interceptor) {
interceptorChain.addInterceptor(interceptor); // 组合拦截器链
}
}
// 违反 CRP 的反例:通过继承复用
public class BadConfiguration extends MapperRegistry {
private final InterceptorChain interceptorChain = new InterceptorChain();
public void addInterceptor(Interceptor interceptor) {
interceptorChain.addInterceptor(interceptor);
}
}
总结
MyBatis 框架通过以下方式践行 SOLID 原则:
- SRP:通过
Mapper
接口和 XML 映射文件分离 SQL 操作与业务逻辑。 - OCP:通过插件机制(
Interceptor
)扩展功能,无需修改核心代码。 - LSP:通过
TypeHandler
接口及其实现类保证类型替换性。 - ISP:通过细粒度接口(如
ResultSetHandler
)替代臃肿接口。 - DIP:通过
SqlSession
接口注入抽象,而非具体实现。 - LoD:通过
Configuration
对象隐藏数据库配置细节。 - CRP:通过
Configuration
对象组合MapperRegistry
、InterceptorChain
等组件。