通过Spring源码学习适配器模式
- 📋 模式定义
- 🏗️ UML 类图解析
- 🌟 Spring 框架中的适配器模式实践
- 1. AOP 通知适配器:统一拦截器接口
- 2. Spring MVC 处理器适配:统一 Web 请求处理
- 3. ORM 集成适配:统一数据访问接口
- 4. 国际化适配:多区域解析策略
- 💻 实战应用:自定义适配器实现
- 示例1:数据表格适配器(后端分页与前端表格的桥梁)
- 示例2:HTTP客户端适配器(统一多客户端接口)
- 示例3:过滤器到拦截器的适配
- 📊 适配器模式在 Spring 中的应用总结
- 🎯 Spring 适配器模式的设计哲学
- 1. 接口统一原则
- 2. 开闭原则支持
- 3. 最小侵入设计
- 4. 组合优于继承
- 5. 关注点分离
- 🔧 适配器模式的最佳实践
- 1. 何时使用适配器模式
- 2. Spring 中的适配器实现要点
- 3. 常见陷阱与解决方案
- 🧪 练习与思考
- 练习 1:分析 Spring 源码中的适配器
- 练习 2:实现自定义的 MyBatis 适配器
- 📈 性能与扩展性考虑
- 1. 适配器模式的性能影响
- 2. 适配器模式的扩展性
- 🆚 适配器模式与相关模式对比
- 📚 深入阅读与扩展
- 下一步学习方向
- 推荐阅读
- 总结
- 参考资源
Spring框架中的适配器模式:接口转换的艺术
📋 模式定义
适配器模式是一种结构型设计模式,它允许接口不兼容的类能够协同工作。适配器通过将一个类的接口转换为客户端期望的另一个接口,解决了组件间的接口不兼容问题。
❝
Spring框架的核心应用场景:AOP中的
MethodInterceptor适配、HandlerAdapter请求处理、AdvisorAdapter通知适配。
🏗️ UML 类图解析

classDiagram
class Target {
<<interface>>
+request() void
}
class Adapter {
-adaptee: Adaptee
+request() void
}
class Adaptee {
+specificRequest() void
}
Target <|.. Adapter : 实现
Adapter --> Adaptee : 组合持有
Adapter ..> Adaptee : 委托调用
图示:适配器模式包含三个核心角色。Target是目标接口,定义客户端期望的方法;Adaptee是被适配者,拥有实际功能但接口不兼容;Adapter是适配器,实现Target接口并持有Adaptee引用,在request()方法中将调用转发给Adaptee.specificRequest()。
🌟 Spring 框架中的适配器模式实践
1. AOP 通知适配器:统一拦截器接口
/**
* Spring AOP 的核心设计:将不同类型的通知(Advice)适配为统一的拦截器(Interceptor)。
* 这样无论用户实现哪种通知接口,最终都能以统一的方式被AOP框架处理。
*/
// Target接口:Spring AOP框架期望的统一拦截器接口
publicinterfaceMethodInterceptorextendsInterceptor{
Object invoke(MethodInvocation invocation)throws Throwable;
}
// Adaptee:用户可能实现的各种通知接口
publicinterfaceBeforeAdvice{
voidbefore(Method method, Object[] args, Object target)throws Throwable;
}
publicinterfaceAfterReturningAdvice{
voidafterReturning(Object returnValue, Method method, Object[] args, Object target)
throws Throwable;
}
publicinterfaceThrowsAdvice{
voidafterThrowing(Method method, Object[] args, Object target, Exception ex);
}
// Adapter:将BeforeAdvice适配为MethodInterceptor
publicclassMethodBeforeAdviceInterceptorimplementsMethodInterceptor{
privatefinal BeforeAdvice advice;
publicMethodBeforeAdviceInterceptor(BeforeAdvice advice){
this.advice = advice;
}
@Override
public Object invoke(MethodInvocation invocation)throws Throwable {
// 在方法调用前执行通知逻辑
this.advice.before(
invocation.getMethod(),
invocation.getArguments(),
invocation.getThis()
);
// 继续执行拦截链
return invocation.proceed();
}
}
// Adapter:将AfterReturningAdvice适配为MethodInterceptor
publicclassAfterReturningAdviceInterceptorimplementsMethodInterceptor{
privatefinal AfterReturningAdvice advice;
publicAfterReturningAdviceInterceptor(AfterReturningAdvice advice){
this.advice = advice;
}
@Override
public Object invoke(MethodInvocation invocation)throws Throwable {
// 先执行目标方法
Object returnValue = invocation.proceed();
// 在方法返回后执行通知逻辑
this.advice.afterReturning(
returnValue,
invocation.getMethod(),
invocation.getArguments(),
invocation.getThis()
);
return returnValue;
}
}
// Spring AOP适配器注册中心
publicclassDefaultAdvisorAdapterRegistryimplementsAdvisorAdapterRegistry{
privatefinal List<AdvisorAdapter> adapters = new ArrayList<>();
publicDefaultAdvisorAdapterRegistry(){
// 注册内置适配器
registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
registerAdvisorAdapter(new AfterReturningAdviceAdapter());
registerAdvisorAdapter(new ThrowsAdviceAdapter());
}
@Override
public MethodInterceptor[] getInterceptors(Advisor advisor) {
List<MethodInterceptor> interceptors = new ArrayList<>();
Advice advice = advisor.getAdvice();
// 遍历所有适配器,找到能处理当前Advice的适配器
for (AdvisorAdapter adapter : this.adapters) {
if (adapter.supportsAdvice(advice)) {
interceptors.add(adapter.getInterceptor(advisor));
}
}
return interceptors.toArray(new MethodInterceptor[0]);
}
}
2. Spring MVC 处理器适配:统一 Web 请求处理
/**
* Spring MVC 的核心机制:将使用不同注解的控制器适配为统一的请求处理器。
* 这样无论开发者使用@Controller、@RestController还是其他注解,Spring都能统一处理。
*/
// Target接口:Spring MVC期望的处理器接口
publicinterfaceHandlerAdapter{
booleansupports(Object handler);
ModelAndView handle(HttpServletRequest request, HttpServletResponse response,
Object handler)throws Exception;
}
// Adaptee:使用不同注解的控制器
@Controller
publicclassTraditionalController{
@GetMapping("/traditional")
public String traditionalMethod(){
return"view";
}
}
@RestController
publicclassRestfulController{
@GetMapping("/restful")
public ResponseEntity<User> restfulMethod(){
return ResponseEntity.ok(new User());
}
}
// Adapter:处理@RequestMapping注解的适配器
publicclassRequestMappingHandlerAdapterimplementsHandlerAdapter{
@Override
publicbooleansupports(Object handler){
return handler instanceof HandlerMethod;
}
@Override
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response,
Object handler)throws Exception {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 1. 数据绑定:将HTTP参数绑定到方法参数
Object[] args = getMethodArgumentValues(request, response, handlerMethod);
// 2. 执行控制器方法
Object returnValue = invokeHandlerMethod(handlerMethod, args);
// 3. 处理返回值:适配不同类型的返回值
ModelAndView mav = handleReturnValue(returnValue, request, response, handlerMethod);
return mav;
}
private Object invokeHandlerMethod(HandlerMethod handlerMethod, Object[] args)
throws Exception {
// 通过反射调用控制器方法
return handlerMethod.getMethod().invoke(
handlerMethod.getBean(), args
);
}
private ModelAndView handleReturnValue(Object returnValue,
HttpServletRequest request,
HttpServletResponse response,
HandlerMethod handlerMethod){
// 根据返回类型进行适配
if (returnValue instanceof String) {
// 返回视图名称
returnnew ModelAndView((String) returnValue);
} elseif (returnValue instanceof ResponseEntity) {
// 返回HTTP响应实体
ResponseEntity<?> responseEntity = (ResponseEntity<?>) returnValue;
response.setStatus(responseEntity.getStatusCodeValue());
// 设置响应头、响应体...
returnnull; // RESTful请求通常不返回ModelAndView
} elseif (returnValue instanceof ModelAndView) {
// 已经是ModelAndView类型
return (ModelAndView) returnValue;
}
// 其他类型处理...
returnnull;
}
}
// Spring MVC 配置
@Configuration
@EnableWebMvc
publicclassWebMvcConfigimplementsWebMvcConfigurer{
@Override
publicvoidconfigureHandlerAdapters(List<HandlerAdapter> adapters){
// 注册各种处理器适配器
adapters.add(new RequestMappingHandlerAdapter());
adapters.add(new HttpRequestHandlerAdapter()); // 处理HttpRequestHandler
adapters.add(new SimpleControllerHandlerAdapter(); // 处理Controller接口
}
}
3. ORM 集成适配:统一数据访问接口
/**
* Spring 对 Hibernate 的集成:将 Hibernate 原生 API 适配为 Spring 风格的数据访问模板。
* 这样开发者可以使用统一的、异常无关的API,而底层仍是Hibernate的实现。
*/
// Target接口:Spring风格的数据访问模板
publicclassHibernateTemplate{
private SessionFactory sessionFactory;
publicHibernateTemplate(SessionFactory sessionFactory){
this.sessionFactory = sessionFactory;
}
// 核心模板方法:处理会话生命周期和异常转换
public <T> T execute(HibernateCallback<T> action){
// 获取Session(支持事务传播)
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
try {
// 执行用户回调
T result = action.doInHibernate(session);
// 必要时刷新会话
flushIfNecessary(session, false);
return result;
} catch (HibernateException ex) {
// 将Hibernate异常转换为Spring的DataAccessException
throw convertHibernateAccessException(ex);
} finally {
// 释放资源
SessionFactoryUtils.releaseSession(session, sessionFactory);
}
}
// 适配后的便捷方法
public <T> T get(Class<T> entityClass, Serializable id){
return execute(session -> session.get(entityClass, id));
}
publicvoidsave(Object entity){
execute(session -> {
session.save(entity);
returnnull;
});
}
publicvoidupdate(Object entity){
execute(session -> {
session.update(entity);
returnnull;
});
}
}
// 回调接口:将Hibernate操作适配为Spring模板的回调
@FunctionalInterface
publicinterfaceHibernateCallback<T> {
T doInHibernate(Session session)throws HibernateException;
}
// 使用示例
@Repository
publicclassUserRepositoryextendsHibernateDaoSupport{
public User findById(Long id){
// 使用适配后的简洁API
return getHibernateTemplate().get(User.class, id);
}
public List<User> findByUsername(String username){
// 使用HQL查询
return getHibernateTemplate().execute(session ->
session.createQuery("FROM User WHERE username = :username", User.class)
.setParameter("username", username)
.list()
);
}
}
4. 国际化适配:多区域解析策略
/**
* Spring MVC 国际化支持:适配不同的区域(Locale)解析策略。
* 开发者可以根据需求选择从Session、Cookie或HTTP头中解析用户区域设置。
*/
// Target接口:统一的区域解析器
publicinterfaceLocaleResolver{
Locale resolveLocale(HttpServletRequest request);
voidsetLocale(HttpServletRequest request, HttpServletResponse response,
Locale locale);
}
// Adaptee 1:基于Session的区域解析器
publicclassSessionLocaleResolverextendsAbstractLocaleContextResolver{
publicstaticfinal String LOCALE_SESSION_ATTRIBUTE_NAME =
SessionLocaleResolver.class.getName() + ".LOCALE";
@Override
public Locale resolveLocale(HttpServletRequest request){
// 1. 尝试从Session中获取
HttpSession session = request.getSession(false);
if (session != null) {
Locale locale = (Locale) session.getAttribute(LOCALE_SESSION_ATTRIBUTE_NAME);
if (locale != null) {
return locale;
}
}
// 2. 返回默认区域
return determineDefaultLocale(request);
}
@Override
publicvoidsetLocale(HttpServletRequest request, HttpServletResponse response,
Locale locale){
// 将区域设置保存到Session
request.getSession().setAttribute(LOCALE_SESSION_ATTRIBUTE_NAME, locale);
}
}
// Adaptee 2:基于Cookie的区域解析器
publicclassCookieLocaleResolverextendsCookieGeneratorimplementsLocaleResolver{
publicstaticfinal String LOCALE_REQUEST_ATTRIBUTE_NAME =
CookieLocaleResolver.class.getName() + ".LOCALE";
publicstaticfinal String DEFAULT_COOKIE_NAME = "org.springframework.web.servlet.i18n.CookieLocaleResolver.LOCALE";
@Override
public Locale resolveLocale(HttpServletRequest request){
// 1. 尝试从请求属性中获取(避免重复解析)
Locale locale = (Locale) request.getAttribute(LOCALE_REQUEST_ATTRIBUTE_NAME);
if (locale != null) {
return locale;
}
// 2. 从Cookie中解析
Cookie cookie = WebUtils.getCookie(request, getCookieName());
if (cookie != null) {
locale = StringUtils.parseLocaleString(cookie.getValue());
if (locale != null) {
request.setAttribute(LOCALE_REQUEST_ATTRIBUTE_NAME, locale);
return locale;
}
}
// 3. 返回默认区域
return determineDefaultLocale(request);
}
}
// Adaptee 3:基于HTTP Accept-Language头的解析器
publicclassAcceptHeaderLocaleResolverimplementsLocaleResolver{
@Override
public Locale resolveLocale(HttpServletRequest request){
// 直接从HTTP头的Accept-Language字段解析
return request.getLocale();
}
@Override
publicvoidsetLocale(HttpServletRequest request, HttpServletResponse response,
Locale locale){
thrownew UnsupportedOperationException(
"Cannot change HTTP accept header - use a different locale resolution strategy"
);
}
}
// 配置:根据需求选择合适的解析器
@Configuration
publicclassLocaleConfigimplementsWebMvcConfigurer{
@Bean
public LocaleResolver localeResolver(){
// 根据需要选择合适的实现
// return new SessionLocaleResolver(); // 基于Session
// return new CookieLocaleResolver(); // 基于Cookie
returnnew AcceptHeaderLocaleResolver(); // 基于HTTP头
}
@Bean
public LocaleChangeInterceptor localeChangeInterceptor(){
LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
interceptor.setParamName("lang"); // 通过lang参数切换语言
return interceptor;
}
@Override
publicvoidaddInterceptors(InterceptorRegistry registry){
registry.addInterceptor(localeChangeInterceptor());
}
}
💻 实战应用:自定义适配器实现
示例1:数据表格适配器(后端分页与前端表格的桥梁)
/**
* 实际场景:前端使用DataTables等表格插件,需要特定的JSON格式。
* 而后端通常返回简单的分页数据,需要适配为前端期望的格式。
*/
// Target接口:前端表格期望的数据结构
publicinterfaceDataTableResponse<T> {
intgetDraw(); // 请求标识(DataTables用)
longgetRecordsTotal(); // 总记录数
longgetRecordsFiltered(); // 过滤后的记录数
List<T> getData(); // 当前页数据
String getError(); // 错误信息(可选)
}
// Adaptee:后端分页查询结果
publicclassPageResult<T> {
private List<T> content; // 当前页数据
privatelong totalElements; // 总记录数
privateint totalPages; // 总页数
privateint pageNumber; // 当前页码
privateint pageSize; // 每页大小
// 构造器、getter、setter...
}
// Adapter:将PageResult适配为DataTableResponse
@Component
publicclassDataTableAdapter{
public <T> DataTableResponse<T> adapt(PageResult<T> pageResult, int draw){
returnnew SimpleDataTableResponse<>(
draw,
pageResult.getTotalElements(),
pageResult.getTotalElements(), // 假设无额外过滤
pageResult.getContent(),
null// 无错误
);
}
public <T> DataTableResponse<T> adapt(PageResult<T> pageResult, int draw,
long recordsFiltered){
returnnew SimpleDataTableResponse<>(
draw,
pageResult.getTotalElements(),
recordsFiltered,
pageResult.getContent(),
null
);
}
public <T> DataTableResponse<T> adaptWithError(int draw, String errorMessage){
returnnew SimpleDataTableResponse<>(
draw,
0,
0,
Collections.emptyList(),
errorMessage
);
}
}
// 适配器实现类
publicclassSimpleDataTableResponse<T> implementsDataTableResponse<T> {
privatefinalint draw;
privatefinallong recordsTotal;
privatefinallong recordsFiltered;
privatefinal List<T> data;
privatefinal String error;
publicSimpleDataTableResponse(int draw, long recordsTotal,
long recordsFiltered, List<T> data, String error){
this.draw = draw;
this.recordsTotal = recordsTotal;
this.recordsFiltered = recordsFiltered;
this.data = data != null ? data : Collections.emptyList();
this.error = error;
}
// 实现DataTableResponse接口的方法
@OverridepublicintgetDraw(){ return draw; }
@OverridepubliclonggetRecordsTotal(){ return recordsTotal; }
@OverridepubliclonggetRecordsFiltered(){ return recordsFiltered; }
@Overridepublic List<T> getData(){ return data; }
@Overridepublic String getError(){ return error; }
}
// 控制器中使用适配器
@RestController
@RequestMapping("/api/users")
publicclassUserController{
@Autowired
private UserService userService;
@Autowired
private DataTableAdapter dataTableAdapter;
@GetMapping("/datatable")
public DataTableResponse<User> getUsersForDataTable(
@RequestParam int draw,
@RequestParam(required = false) String search,
@RequestParam(defaultValue = "0")int start,
@RequestParam(defaultValue = "10")int length) {
try {
// 1. 调用服务层获取分页数据
PageResult<User> pageResult = userService.findUsers(
search, start / length, length
);
// 2. 通过适配器转换为DataTables期望的格式
return dataTableAdapter.adapt(pageResult, draw);
} catch (Exception e) {
// 3. 发生错误时返回错误响应
return dataTableAdapter.adaptWithError(draw,
"查询用户数据时发生错误: " + e.getMessage());
}
}
}
示例2:HTTP客户端适配器(统一多客户端接口)
/**
* 实际场景:应用需要支持多种HTTP客户端(RestTemplate、WebClient、Apache HttpClient等)。
* 通过适配器模式,可以统一接口,方便切换底层实现。
*/
// Target接口:统一的HTTP客户端
publicinterfaceHttpClient{
<T> ResponseEntity<T> exchange(String url, HttpMethod method,
HttpEntity<?> requestEntity,
Class<T> responseType,
Map<String, ?> uriVariables)throws HttpClientException;
<T> T getForObject(String url, Class<T> responseType, Object... uriVariables);
<T> ResponseEntity<T> postForEntity(String url, Object request,
Class<T> responseType, Object... uriVariables);
}
// Adaptee 1:Spring的RestTemplate适配
publicclassRestTemplateHttpClientimplementsHttpClient{
privatefinal RestTemplate restTemplate;
publicRestTemplateHttpClient(RestTemplate restTemplate){
this.restTemplate = restTemplate;
}
@Override
public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
HttpEntity<?> requestEntity,
Class<T> responseType,
Map<String, ?> uriVariables){
return restTemplate.exchange(url, method, requestEntity, responseType, uriVariables);
}
@Override
public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables){
return restTemplate.getForObject(url, responseType, uriVariables);
}
@Override
public <T> ResponseEntity<T> postForEntity(String url, Object request,
Class<T> responseType, Object... uriVariables){
return restTemplate.postForEntity(url, request, responseType, uriVariables);
}
}
// Adaptee 2:Spring WebClient适配(响应式)
publicclassWebClientHttpClientimplementsHttpClient{
privatefinal WebClient webClient;
publicWebClientHttpClient(WebClient webClient){
this.webClient = webClient;
}
@Override
public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
HttpEntity<?> requestEntity,
Class<T> responseType,
Map<String, ?> uriVariables){
// WebClient是响应式的,需要阻塞获取结果
T body = webClient.method(method)
.uri(url, uriVariables)
.headers(headers -> headers.putAll(requestEntity.getHeaders()))
.bodyValue(requestEntity.getBody())
.retrieve()
.bodyToMono(responseType)
.block(); // 阻塞等待结果
returnnew ResponseEntity<>(body, HttpStatus.OK);
}
// 其他方法实现类似...
}
// Adaptee 3:Apache HttpClient适配
publicclassApacheHttpClientimplementsHttpClient{
privatefinal CloseableHttpClient httpClient;
privatefinal ObjectMapper objectMapper;
publicApacheHttpClient(CloseableHttpClient httpClient, ObjectMapper objectMapper){
this.httpClient = httpClient;
this.objectMapper = objectMapper;
}
@Override
public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
HttpEntity<?> requestEntity,
Class<T> responseType,
Map<String, ?> uriVariables){
try {
// 构建Apache HttpClient请求
HttpRequestBase request = createApacheRequest(url, method, requestEntity, uriVariables);
// 执行请求
try (CloseableHttpResponse response = httpClient.execute(request)) {
// 解析响应
T responseBody = parseResponse(response, responseType);
HttpStatus status = HttpStatus.valueOf(response.getStatusLine().getStatusCode());
returnnew ResponseEntity<>(responseBody, status);
}
} catch (Exception e) {
thrownew HttpClientException("HTTP请求失败: " + url, e);
}
}
private HttpRequestBase createApacheRequest(String url, HttpMethod method,
HttpEntity<?> requestEntity,
Map<String, ?> uriVariables){
// 构建URI(处理路径变量)
String expandedUrl = expandUrl(url, uriVariables);
// 根据HTTP方法创建对应的请求对象
switch (method) {
case GET:
returnnew HttpGet(expandedUrl);
case POST:
HttpPost post = new HttpPost(expandedUrl);
if (requestEntity.getBody() != null) {
String json = objectMapper.writeValueAsString(requestEntity.getBody());
post.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON));
}
return post;
// 处理其他HTTP方法...
default:
thrownew UnsupportedOperationException("不支持的HTTP方法: " + method);
}
}
private <T> T parseResponse(CloseableHttpResponse response, Class<T> responseType)
throws IOException {
HttpEntity entity = response.getEntity();
if (entity == null) {
returnnull;
}
String content = EntityUtils.toString(entity, StandardCharsets.UTF_8);
return objectMapper.readValue(content, responseType);
}
}
// 配置:根据环境选择不同的HTTP客户端实现
@Configuration
publicclassHttpClientConfig{
@Bean
@ConditionalOnProperty(name = "http.client.type", havingValue = "resttemplate", matchIfMissing = true)
public HttpClient restTemplateHttpClient(RestTemplate restTemplate){
returnnew RestTemplateHttpClient(restTemplate);
}
@Bean
@ConditionalOnProperty(name = "http.client.type", havingValue = "webclient")
public HttpClient webClientHttpClient(WebClient.Builder webClientBuilder){
WebClient webClient = webClientBuilder.build();
returnnew WebClientHttpClient(webClient);
}
@Bean
@ConditionalOnProperty(name = "http.client.type", havingValue = "apache")
public HttpClient apacheHttpClient(ObjectMapper objectMapper){
CloseableHttpClient httpClient = HttpClients.custom()
.setConnectionTimeToLive(30, TimeUnit.SECONDS)
.setMaxConnTotal(100)
.setMaxConnPerRoute(20)
.build();
returnnew ApacheHttpClient(httpClient, objectMapper);
}
}
// 使用统一接口调用HTTP服务
@Service
publicclassExternalApiService{
privatefinal HttpClient httpClient;
@Autowired
publicExternalApiService(HttpClient httpClient){
this.httpClient = httpClient;
}
public User getUserFromExternalApi(String userId){
String url = "https://api.example.com/users/{userId}";
// 无论底层是哪种HTTP客户端,调用方式都一样
return httpClient.getForObject(url, User.class, userId);
}
public ResponseEntity<User> createUser(User user){
String url = "https://api.example.com/users";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<User> request = new HttpEntity<>(user, headers);
return httpClient.postForEntity(url, request, User.class);
}
}
示例3:过滤器到拦截器的适配
/**
* 实际场景:将传统的Servlet Filter适配为Spring的HandlerInterceptor,
* 使得Filter可以享受Spring的依赖注入等特性。
*/
// Target接口:Spring的处理器拦截器
publicinterfaceHandlerInterceptor{
booleanpreHandle(HttpServletRequest request, HttpServletResponse response,
Object handler)throws Exception;
voidpostHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView)throws Exception;
voidafterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex)throws Exception;
}
// Adaptee:Servlet过滤器
publicinterfaceFilter{
voidinit(FilterConfig filterConfig)throws ServletException;
voiddoFilter(ServletRequest request, ServletResponse response,
FilterChain chain)throws IOException, ServletException;
voiddestroy();
}
// Adapter:将Filter适配为HandlerInterceptor的抽象基类
publicabstractclassFilterToInterceptorAdapterimplementsFilter, HandlerInterceptor{
@Override
publicvoidinit(FilterConfig filterConfig)throws ServletException {
// 可选的初始化逻辑
}
@Override
publicvoiddoFilter(ServletRequest request, ServletResponse response,
FilterChain chain)throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
try {
// 1. 执行preHandle(拦截器前置处理)
boolean continueProcessing = preHandle(httpRequest, httpResponse, null);
if (!continueProcessing) {
// 如果preHandle返回false,中断请求处理
return;
}
// 2. 继续过滤器链
chain.doFilter(request, response);
// 3. 执行postHandle(拦截器后置处理)
// 注意:这里无法获取ModelAndView,因为Filter执行时Spring MVC还未处理
// postHandle(httpRequest, httpResponse, null, null);
} catch (Exception e) {
// 4. 执行afterCompletion(完成处理,包括异常情况)
afterCompletion(httpRequest, httpResponse, null, e);
throw e;
}
// 5. 正常完成后的afterCompletion
afterCompletion(httpRequest, httpResponse, null, null);
}
@Override
publicvoiddestroy(){
// 可选的清理逻辑
}
// 抽象方法,子类只需实现这些拦截器方法
@Override
publicabstractbooleanpreHandle(HttpServletRequest request,
HttpServletResponse response, Object handler)
throws Exception;
@Override
publicvoidpostHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView)throws Exception {
// 默认空实现,子类可覆盖
}
@Override
publicvoidafterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex)throws Exception {
// 默认空实现,子类可覆盖
}
}
// 具体实现:日志过滤器/拦截器
@Component
@Order(1)
publicclassLoggingFilterInterceptorextendsFilterToInterceptorAdapter{
privatestaticfinal Logger logger = LoggerFactory.getLogger(LoggingFilterInterceptor.class);
@Override
publicbooleanpreHandle(HttpServletRequest request, HttpServletResponse response,
Object handler)throws Exception {
long startTime = System.currentTimeMillis();
request.setAttribute("startTime", startTime);
logger.info("收到请求: {} {}, 参数: {}, IP: {}",
request.getMethod(),
request.getRequestURI(),
request.getParameterMap(),
request.getRemoteAddr());
returntrue; // 继续处理
}
@Override
publicvoidafterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex)throws Exception {
Long startTime = (Long) request.getAttribute("startTime");
if (startTime != null) {
long duration = System.currentTimeMillis() - startTime;
logger.info("请求完成: {} {}, 状态: {}, 耗时: {}ms, 异常: {}",
request.getMethod(),
request.getRequestURI(),
response.getStatus(),
duration,
ex != null ? ex.getMessage() : "无");
}
}
}
// 配置:注册适配后的过滤器
@Configuration
publicclassFilterConfig{
@Bean
public FilterRegistrationBean<LoggingFilterInterceptor> loggingFilter(){
FilterRegistrationBean<LoggingFilterInterceptor> registration =
new FilterRegistrationBean<>();
registration.setFilter(new LoggingFilterInterceptor());
registration.addUrlPatterns("/*");
registration.setName("loggingFilter");
registration.setOrder(1);
return registration;
}
// 也可以将过滤器注册为Spring拦截器
@Configuration
publicclassInterceptorConfigimplementsWebMvcConfigurer{
@Autowired
private LoggingFilterInterceptor loggingInterceptor;
@Override
publicvoidaddInterceptors(InterceptorRegistry registry){
registry.addInterceptor(loggingInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/static/**", "/error");
}
}
}
📊 适配器模式在 Spring 中的应用总结
|
|
|
|
|
|
|---|---|---|---|---|
| AOP通知适配 | MethodInterceptor |
MethodBeforeAdviceInterceptor |
BeforeAdvice |
|
| MVC处理器适配 | HandlerAdapter |
RequestMappingHandlerAdapter |
@Controller |
|
| 数据访问适配 | HibernateTemplate |
HibernateCallback |
Session |
|
| 国际化适配 | LocaleResolver |
SessionLocaleResolver |
Session |
|
| HTTP客户端适配 | HttpClient |
RestTemplateHttpClient |
RestTemplate |
|
🎯 Spring 适配器模式的设计哲学
1. 接口统一原则
Spring 框架通过定义统一的接口(Target),让不同的实现(Adaptee)能够协同工作。这种设计降低了系统各部分的耦合度。
2. 开闭原则支持
适配器模式支持开闭原则:当需要新的 Adaptee 时,只需要添加新的 Adapter,而不需要修改现有代码。
3. 最小侵入设计
Spring 的适配器设计尽可能减少对用户代码的侵入。用户通常不需要知道适配器的存在,只需按照自己的方式编写代码。
4. 组合优于继承
Spring 中的适配器大多采用对象适配器(通过组合实现),而非类适配器(通过继承实现)。这提供了更大的灵活性。
5. 关注点分离
适配器模式帮助分离了接口转换逻辑和业务逻辑,使代码更加清晰和可维护。
🔧 适配器模式的最佳实践
1. 何时使用适配器模式
-
集成遗留代码:需要将旧系统集成到新系统中 -
统一多个类库:应用中使用多个类似功能的第三方库 -
接口版本兼容:新接口需要向后兼容旧接口 -
系统重构:逐步重构系统,需要新旧接口并存
2. Spring 中的适配器实现要点
// 1. 明确Target接口
publicinterfaceTargetInterface{
ResultType targetMethod(ParamType param);
}
// 2. 创建适配器类
publicclassConcreteAdapterimplementsTargetInterface{
privatefinal Adaptee adaptee;
publicConcreteAdapter(Adaptee adaptee){
this.adaptee = adaptee;
}
@Override
public ResultType targetMethod(ParamType param){
// 3. 实现适配逻辑
AdapteeParam adaptedParam = convertParam(param);
AdapteeResult adapteeResult = adaptee.adapteeMethod(adaptedParam);
return convertResult(adapteeResult);
}
private AdapteeParam convertParam(ParamType param){
// 参数转换逻辑
}
private ResultType convertResult(AdapteeResult adapteeResult){
// 结果转换逻辑
}
}
// 4. 通过配置或工厂管理适配器
@Configuration
publicclassAdapterConfig{
@Bean
public TargetInterface targetService(Adaptee adaptee){
returnnew ConcreteAdapter(adaptee);
}
}
3. 常见陷阱与解决方案
-
过度适配:不要为每个小差异都创建适配器,考虑重构接口 -
性能损耗:适配器可能带来额外的方法调用开销,对性能敏感场景需评估 -
复杂性增加:过多的适配器会使系统更难理解,保持适度 -
双向适配:尽量避免双向适配,这通常表明接口设计有问题
🧪 练习与思考
练习 1:分析 Spring 源码中的适配器
/**
* 学习任务:阅读并分析Spring框架中的适配器实现
*
* 1. 找到并分析以下类:
* - org.springframework.aop.framework.adapter.MethodBeforeAdviceAdapter
* - org.springframework.web.servlet.HandlerAdapter
* - org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
*
* 2. 回答以下问题:
* - 每个适配器的Target接口是什么?
* - 每个适配器适配了哪些Adaptee?
* - 适配过程中做了哪些转换工作?
* - 为什么Spring选择使用适配器模式?
*
* 3. 画出这些适配器的类图
*/
练习 2:实现自定义的 MyBatis 适配器
/**
* 实现一个适配器,将MyBatis的Mapper接口适配为Spring Data的Repository接口
*/
// Spring Data Repository接口(Target)
publicinterfaceSpringDataRepository<T, ID> {
Optional<T> findById(ID id);
List<T> findAll();
T save(T entity);
voiddeleteById(ID id);
longcount();
}
// MyBatis Mapper接口(Adaptee)
publicinterfaceUserMapper{
User selectById(@Param("id") Long id);
List<User> selectAll();
intinsert(User user);
intupdate(User user);
intdeleteById(@Param("id") Long id);
Long countAll();
}
// 适配器实现
publicclassMyBatisRepositoryAdapter<T, ID> implementsSpringDataRepository<T, ID> {
privatefinal SqlSessionTemplate sqlSessionTemplate;
privatefinal Class<T> entityClass;
privatefinal Class<?> mapperClass;
publicMyBatisRepositoryAdapter(SqlSessionTemplate sqlSessionTemplate,
Class<T> entityClass){
this.sqlSessionTemplate = sqlSessionTemplate;
this.entityClass = entityClass;
this.mapperClass = resolveMapperClass(entityClass);
}
@Override
public Optional<T> findById(ID id){
Object mapper = sqlSessionTemplate.getMapper(mapperClass);
try {
Method method = mapperClass.getMethod("selectById", id.getClass());
T result = (T) method.invoke(mapper, id);
return Optional.ofNullable(result);
} catch (Exception e) {
thrownew DataAccessException("查询失败", e);
}
}
@Override
public List<T> findAll(){
Object mapper = sqlSessionTemplate.getMapper(mapperClass);
try {
Method method = mapperClass.getMethod("selectAll");
return (List<T>) method.invoke(mapper);
} catch (Exception e) {
thrownew DataAccessException("查询失败", e);
}
}
@Override
public T save(T entity){
Object mapper = sqlSessionTemplate.getMapper(mapperClass);
try {
// 判断是新增还是更新
Method getIdMethod = entityClass.getMethod("getId");
Object id = getIdMethod.invoke(entity);
if (id == null) {
// 新增
Method insertMethod = mapperClass.getMethod("insert", entityClass);
insertMethod.invoke(mapper, entity);
return entity;
} else {
// 更新
Method updateMethod = mapperClass.getMethod("update", entityClass);
updateMethod.invoke(mapper, entity);
return entity;
}
} catch (Exception e) {
thrownew DataAccessException("保存失败", e);
}
}
// 其他方法实现...
private Class<?> resolveMapperClass(Class<T> entityClass) {
// 根据实体类名解析对应的Mapper接口
String mapperClassName = entityClass.getSimpleName() + "Mapper";
String packageName = entityClass.getPackage().getName();
try {
return Class.forName(packageName + "." + mapperClassName);
} catch (ClassNotFoundException e) {
thrownew IllegalArgumentException("找不到对应的Mapper接口: " + mapperClassName, e);
}
}
}
// 配置和使用
@Configuration
publicclassMyBatisAdapterConfig{
@Bean
public SpringDataRepository<User, Long> userRepository(
SqlSessionTemplate sqlSessionTemplate){
returnnew MyBatisRepositoryAdapter<>(
sqlSessionTemplate, User.class
);
}
}
@Service
publicclassUserService{
@Autowired
private SpringDataRepository<User, Long> userRepository;
public User getUser(Long id){
return userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException(id));
}
}
📈 性能与扩展性考虑
1. 适配器模式的性能影响
适配器模式通常会引入额外的间接调用层,这可能带来轻微的性能开销。在大多数应用中,这种开销可以忽略不计,但在高性能场景下需要考虑:
// 性能优化:缓存适配结果
publicclassCachingAdapterimplementsTargetInterface{
privatefinal Adaptee adaptee;
privatefinal Cache cache;
publicCachingAdapter(Adaptee adaptee, Cache cache){
this.adaptee = adaptee;
this.cache = cache;
}
@Override
public ResultType targetMethod(ParamType param){
// 1. 尝试从缓存获取
String cacheKey = generateCacheKey(param);
ResultType cachedResult = cache.get(cacheKey);
if (cachedResult != null) {
return cachedResult;
}
// 2. 调用适配器逻辑
ResultType result = adapt(adaptee, param);
// 3. 放入缓存
cache.put(cacheKey, result);
return result;
}
}
2. 适配器模式的扩展性
适配器模式天然支持扩展,可以通过以下方式增强:
// 可扩展的适配器工厂
publicclassAdapterFactory{
privatefinal Map<Class<?>, AdapterCreator<?>> adapterCreators = new HashMap<>();
public <T> voidregisterAdapter(Class<T> targetType, AdapterCreator<T> creator){
adapterCreators.put(targetType, creator);
}
@SuppressWarnings("unchecked")
public <T> T createAdapter(Object adaptee, Class<T> targetType){
AdapterCreator<T> creator = (AdapterCreator<T>) adapterCreators.get(targetType);
if (creator == null) {
thrownew IllegalArgumentException("未注册的适配器类型: " + targetType);
}
return creator.create(adaptee);
}
@FunctionalInterface
publicinterfaceAdapterCreator<T> {
T create(Object adaptee);
}
}
// 使用扩展适配器工厂
AdapterFactory factory = new AdapterFactory();
factory.registerAdapter(TargetInterface.class, adaptee ->
newConcreteAdapter((Adaptee) adaptee));
TargetInterface adapter = factory.createAdapter(adapteeInstance, TargetInterface.class);
🆚 适配器模式与相关模式对比
|
|
|
|
|
|---|---|---|---|
| 桥接模式 |
|
|
PlatformTransactionManager
|
| 装饰器模式 |
|
|
ProxyFactory |
| 外观模式 |
|
|
JdbcTemplate
|
| 代理模式 |
|
|
|
📚 深入阅读与扩展
下一步学习方向
掌握了适配器模式后,建议继续学习:
👉 09_bridge.md – 学习如何分离抽象与实现,使它们可以独立变化。
推荐阅读
-
Spring 官方文档:https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-handlermapping -
Spring 源码: org.springframework.aop.framework.adapter包 -
经典著作:《设计模式:可复用面向对象软件的基础》- GoF -
Spring 相关:《Spring 源码深度解析》- 对 Spring 中设计模式的深入分析
总结
适配器模式是 Spring 框架中广泛应用的核心设计模式之一,它体现了 Spring 框架的几个重要设计哲学:
-
接口统一:通过适配器,Spring 能够将各种不同的技术、框架和实现统一到一致的编程模型下。
-
向后兼容:适配器模式使得 Spring 能够集成遗留系统和第三方库,而无需修改现有代码。
-
关注点分离:适配器将接口转换逻辑与业务逻辑分离,提高了代码的可维护性和可测试性。
-
开闭原则:通过添加新的适配器而不是修改现有代码,Spring 框架能够轻松扩展对新技术的支持。
在 Spring 框架中,适配器模式的应用无处不在:从 AOP 的通知适配、MVC 的处理器适配,到数据访问的模板适配,再到国际化的区域解析适配。深入理解适配器模式,不仅有助于更好地使用 Spring 框架,也能够在自己的项目中设计出更加灵活、可扩展的架构。
参考资源
-
🌐 https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-handlermapping -
🌐 https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/aop/framework/adapter/package-summary.html -
📖 Design Patterns: Elements of Reusable Object-Oriented Software – Erich Gamma 等 -
📖 Spring in Action – Craig Walls -
📖 Expert One-on-One J2EE Development Without EJB – Rod Johnson
夜雨聆风