乐于分享
好东西不私藏

通过Spring源码学习适配器模式

通过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> 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> 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{
doInHibernate(Session session)throws HibernateException;
}

// 使用示例
@Repository
publicclassUserRepositoryextendsHibernateDaoSupport{

public User findById(Long id){
// 使用适配后的简洁API
return getHibernateTemplate().get(User.classid);
    }

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<TimplementsDataTableResponse<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> 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> 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> 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.classuserId);
    }

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的抽象基类
publicabstractclassFilterToInterceptorAdapterimplementsFilterHandlerInterceptor{

@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, nullnull);
    }

@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 中的应用总结

应用场景
Target接口
Adapter类
Adaptee对象
作用
AOP通知适配 MethodInterceptor MethodBeforeAdviceInterceptor BeforeAdvice
统一AOP拦截接口
MVC处理器适配 HandlerAdapter RequestMappingHandlerAdapter @Controller
统一Web请求处理
数据访问适配 HibernateTemplate HibernateCallback
Hibernate Session
统一ORM操作接口
国际化适配 LocaleResolver SessionLocaleResolver
HTTP Session
统一区域解析
HTTP客户端适配 HttpClient RestTemplateHttpClient RestTemplate
统一HTTP调用

🎯 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<TID{
Optional<T> findById(ID id);
List<T> findAll();
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<TIDimplementsSpringDataRepository<TID{
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> 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{
create(Object adaptee);
    }
}

// 使用扩展适配器工厂
AdapterFactory factory = new AdapterFactory();
factory.registerAdapter(TargetInterface.classadaptee -> 
newConcreteAdapter((Adapteeadaptee))
;

TargetInterface adapter = factory.createAdapter(adapteeInstance, TargetInterface.class);

🆚 适配器模式与相关模式对比

模式
目的
与适配器模式的关系
在Spring中的应用
桥接模式
分离抽象与实现
都涉及接口转换,但桥接是预先设计,适配器是事后补救
PlatformTransactionManager

 与不同事务实现的桥接
装饰器模式
动态添加功能
装饰器增强功能,适配器转换接口
Spring AOP 中的 ProxyFactory
外观模式
简化复杂子系统
外观提供统一接口,适配器转换不兼容接口
JdbcTemplate

 封装了复杂的JDBC操作
代理模式
控制对象访问
代理控制访问,适配器转换接口
Spring AOP 的动态代理

📚 深入阅读与扩展

下一步学习方向

掌握了适配器模式后,建议继续学习:

👉 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 框架的几个重要设计哲学:

  1. 接口统一:通过适配器,Spring 能够将各种不同的技术、框架和实现统一到一致的编程模型下。

  2. 向后兼容:适配器模式使得 Spring 能够集成遗留系统和第三方库,而无需修改现有代码。

  3. 关注点分离:适配器将接口转换逻辑与业务逻辑分离,提高了代码的可维护性和可测试性。

  4. 开闭原则:通过添加新的适配器而不是修改现有代码,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