日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Springboot 源码分析 —— @Endpoint 注解生效原理解析

發布時間:2024/1/1 javascript 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Springboot 源码分析 —— @Endpoint 注解生效原理解析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 1 WebMvcEndpointManagementContextConfiguration
    • 1.1 webEndpointServletHandlerMapping
    • 1.2 ControllerEndpointHandlerMapping
  • 2 EndpointAutoConfiguration
  • 3 WebEndpointAutoConfiguration
  • 4 請求生效流程
    • 4.1 doDispatch(request, response)
      • 4.1.1 getHandler(processedRequest)
      • 4.1.2 getHandlerAdapter(Object handler)
      • 4.1.3 ha.handle()
      • 4.1.4 endpoint 走緩存的流程
  • 5 PathMappedEndpoints
    • 5.1 getEndpoints
    • 5.2 supplier.getEndpoints()
      • 5.2.1 createEndpointBeans
        • 5.2.1.1 createEndpointBean(beanName)
      • 5.2.2 addExtensionBeans
        • 5.2.2.1 addExtensionBean(endpointBean, extensionBean)
          • 5.2.2.1.1 isExtensionExposed(endpointBean, extensionBean)
          • 5.2.2.1.2 isEndpointExposed(endpointBean)
      • 5.2.3 convertToEndpoints
        • 5.2.3.1 convertToEndpoint(endpointBean)
          • 5.2.3.2.1 addOperations()
          • 5.2.3.2.2 createOperations(...)
          • 5.2.3.2.3 createEndpoint(...)
  • 參考

  • 在 Springboot 中文文檔 —— Actuator 一文中,介紹了端點以及其使用方法
  • 本文將介紹 @Endpoint 注解的生效原理,以對 actuator 端點有一個更深入的了解

1 WebMvcEndpointManagementContextConfiguration

  • 這里是向容器注入自定義的 HandlerMapping,供 DispatcherServlet 調用
  • DispatcherServlet 根據各個 HandlerMapping 做實際的請求分發
@ManagementContextConfiguration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass(DispatcherServlet.class) @ConditionalOnBean({ DispatcherServlet.class, WebEndpointsSupplier.class }) @EnableConfigurationProperties(CorsEndpointProperties.class) public class WebMvcEndpointManagementContextConfiguration {//1.1 @Bean@ConditionalOnMissingBeanpublic WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(...) {//向容器注入 WebMvcEndpointHandlerMapping,由 DispatcherServlet 調用,轉發請求到真實的 endpoint 中的 特定 operation 中}//1.2 @Bean@ConditionalOnMissingBeanpublic ControllerEndpointHandlerMapping controllerEndpointHandlerMapping(...) {//向容器注入 ControllerEndpointHandlerMapping,由 DispatcherServlet 調用,轉發請求到真實的 endpoint} }

1.1 webEndpointServletHandlerMapping

@Bean @ConditionalOnMissingBean public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(...) { List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>();//獲取所有的 web 類型 endpoints(@Endpoint、@WebEndpoint 注解)//這里可能會觸發 endpoints 的初始化,但是應該是被 5 給搶先了Collection<ExposableWebEndpoint> webEndpoints = webEndpointsSupplier.getEndpoints();allEndpoints.addAll(webEndpoints);//獲取所有 servlet 類型 endpoints(@ServletEndpoint 注解)allEndpoints.addAll(servletEndpointsSupplier.getEndpoints());//獲取所有 controller 類型 endpoints(@ControllerEndpoint 注解)allEndpoints.addAll(controllerEndpointsSupplier.getEndpoints());//web endpoint 的 base pathString basePath = webEndpointProperties.getBasePath();EndpointMapping endpointMapping = new EndpointMapping(basePath);//當 bathPath 為空,且 endpoint 的端口和server 端口一樣,才不暴露boolean shouldRegisterLinksMapping = 是否暴露 /actuator 發現頁面;return new WebMvcEndpointHandlerMapping(...); }

1.2 ControllerEndpointHandlerMapping

  • 同 1.1
@Bean @ConditionalOnMissingBean public ControllerEndpointHandlerMapping controllerEndpointHandlerMapping(...) {EndpointMapping endpointMapping = new EndpointMapping(webEndpointProprties.getBasePath());return new ControllerEndpointHandlerMapping(...); }

2 EndpointAutoConfiguration

@Configuration(proxyBeanMethods = false) public class EndpointAutoConfiguration {@Bean@ConditionalOnMissingBean public ParameterValueMapper endpointOperationParameterMapper(...) {//方法參數:@EndpointConverter ObjectProvider<Converter<?, ?>> converters//方法參數:@EndpointConverter ObjectProvider<GenericConverter> genericConverters//獲取容器中的 @EndpointConverter(Converter,GenericConverter),用于 @Endpoint 輸入參數的類型轉換//如果沒有,則使用默認的 ApplicationConversionService//如果有,則使用 它們,來設置 ApplicationConversionService}@Bean@ConditionalOnMissingBean//返回可緩存的 endpoint 的緩存時間 //management.endpoint.endpointName.cache.time-to-live=xx 來配置 endpointName 的緩存時間public CachingOperationInvokerAdvisor endpointCachingOperationInvokerAdvisor(Environment environment) {return new CachingOperationInvokerAdvisor(new EndpointIdTimeToLivePropertyFunction(environment));} }

3 WebEndpointAutoConfiguration

  • 生成 endpoint 相關信息
@Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication @AutoConfigureAfter(EndpointAutoConfiguration.class) @EnableConfigurationProperties(WebEndpointProperties.class) public class WebEndpointAutoConfiguration {@Bean//health,info 等路徑的重定義,如://management.endpoints.path-mapping.health=healthcheck//management.endpoints.path-mapping.info=myInfopublic PathMapper webEndpointPathMapper() {return new MappingWebEndpointPathMapper(this.properties.getPathMapping());}@Bean@ConditionalOnMissingBeanpublic EndpointMediaTypes endpointMediaTypes() {return EndpointMediaTypes.DEFAULT;}@Bean@ConditionalOnMissingBean(WebEndpointsSupplier.class)public WebEndpointDiscoverer webEndpointDiscoverer(...) {//方法參數如下: //1. ParameterValueMapper,2.EndpointMediaTypes,3.PathMapper:info->Myinfo//4. OperationInvokerAdvisor,5.EndpointFilter<ExposableWebEndpoint>//通過如上參數創建 discoverer 類return new WebEndpointDiscoverer(...);}@Bean@ConditionalOnMissingBean(ControllerEndpointsSupplier.class)public ControllerEndpointDiscoverer controllerEndpointDiscoverer(...) {//同上 return new ControllerEndpointDiscoverer(...);}@Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.SERVLET) static class WebEndpointServletConfiguration {@Bean@ConditionalOnMissingBean(ServletEndpointsSupplier.class)ServletEndpointDiscoverer servletEndpointDiscoverer(...) {//同 WebEndpointDiscovererreturn new ServletEndpointDiscoverer(...);} }@Bean//5@ConditionalOnMissingBeanpublic PathMappedEndpoints pathMappedEndpoints(Collection<EndpointsSupplier<?>> endpointSuppliers) {//通過 basepath 和 所有的 endpointSuppliers,得到 所有 endpoint 對象 和其對應的 operationreturn new PathMappedEndpoints(this.properties.getBasePath(), endpointSuppliers);}@Beanpublic ExposeExcludePropertyEndpointFilter<ExposableWebEndpoint> webExposeExcludePropertyEndpointFilter() {//從 WebEndpointProperties 中得到 WebEndpointProperties.ExposureWebEndpointProperties.Exposure exposure = this.properties.getExposure();//使用參數,如果沒有 include 則使用默認的 info/health,如果有 include 則默認的失效 return new ExposeExcludePropertyEndpointFilter<>(ExposableWebEndpoint.class, exposure.getInclude(),exposure.getExclude(), "info", "health");}@Beanpublic ExposeExcludePropertyEndpointFilter<ExposableControllerEndpoint> controllerExposeExcludePropertyEndpointFilter() {WebEndpointProperties.Exposure exposure = this.properties.getExposure();//同上,這里沒有默認配置return new ExposeExcludePropertyEndpointFilter<>(...);} }

4 請求生效流程

4.1 doDispatch(request, response)

//DispatcherServlet protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;// Determine handler for the current request.mappedHandler = getHandler(processedRequest);// Determine handler adapter for the current request.HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// Process last-modified header, if supported by the handler.String method = request.getMethod();//可以自定義配置預處理方法,如鑒權等if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());applyDefaultViewName(processedRequest, mv);//調用后置處理器,如加密等mappedHandler.applyPostHandle(processedRequest, response, mv);processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); }

4.1.1 getHandler(processedRequest)

@Nullable protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {//其中關鍵的mapping: WebMvcEndpointHandlerMapping、ControllerEndpointHandlerMapping、RequestMappingHandlerMappingif (this.handlerMappings != null) {for (HandlerMapping mapping : this.handlerMappings) {//獲取request對應的handler,并處理為 chain HandlerExecutionChain handler = mapping.getHandler(request);if (handler != null) {return handler;}}}return null; }

4.1.2 getHandlerAdapter(Object handler)

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {// 默認提供多個 handlerAdapterif (this.handlerAdapters != null) {for (HandlerAdapter adapter : this.handlerAdapters) { if (adapter.supports(handler)) {//返回第一個可以匹配的 adapter //這里返回的是:RequestMappingHandlerAdapterreturn adapter;}}}throw new ServletException("No adapter for handler [" + handler +"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler"); }

4.1.3 ha.handle()

//AbstractWebMvcEndpointHandlerMapping 內部類 private final class OperationHandler {@ResponseBodyObject handle(HttpServletRequest request, @RequestBody(required = false) Map<String, String> body) {//最終走到這 return this.operation.handle(request, body);} } @Override public Object handle(HttpServletRequest request, @RequestBody(required = false) Map<String, String> body) {//通過 this.operation.invoke 最終調用實際方法//如果容器中注入了 CachingOperationInvokerAdvisor,則 invoke 方法會被 CachingOperationInvoker 攔截,做 cache 方面的操作return handleResult(this.operation.invoke(invocationContext), HttpMethod.resolve(request.getMethod())); }

4.1.4 endpoint 走緩存的流程

//每一個 operation 會對應創建一個 CachingOperationInvoker,具體操作在 DiscoveredOperationsFactory 中 //通過 CachingOperationInvokerAdvisor 對 operation 做的靜態代理 public class CachingOperationInvoker implements OperationInvoker {@Overridepublic Object invoke(InvocationContext context) {//如果請求中有 body 內容或 parameter 或 配置了安全相關的準則,則不走緩存if (hasInput(context)) {return this.invoker.invoke(context);}long accessTime = System.currentTimeMillis();CachedResponse cached = this.cachedResponse;if (cached == null || cached.isStale(accessTime, this.timeToLive)) {Object response = this.invoker.invoke(context);//將結果緩存下來this.cachedResponse = new CachedResponse(response, accessTime);return response;}return cached.getResponse();} }

5 PathMappedEndpoints

@Bean @ConditionalOnMissingBean public PathMappedEndpoints pathMappedEndpoints(Collection<EndpointsSupplier<?>> endpointSuppliers) {//通過 basepath 和 所有的 endpointSuppliers,得到 對象return new PathMappedEndpoints(this.properties.getBasePath(), endpointSuppliers); }public PathMappedEndpoints(String basePath, Collection<EndpointsSupplier<?>> suppliers) {Assert.notNull(suppliers, "Suppliers must not be null");this.basePath = (basePath != null) ? basePath : "";//3.1.1this.endpoints = getEndpoints(suppliers); }

5.1 getEndpoints

  • 獲取所有類型的 endpoint
private Map<EndpointId, PathMappedEndpoint> getEndpoints(Collection<EndpointsSupplier<?>> suppliers) {Map<EndpointId, PathMappedEndpoint> endpoints = new LinkedHashMap<>();suppliers.forEach((supplier) -> {supplier.getEndpoints().forEach((endpoint) -> {if (endpoint instanceof PathMappedEndpoint) {endpoints.put(endpoint.getEndpointId(), (PathMappedEndpoint) endpoint);}});});return Collections.unmodifiableMap(endpoints); }

5.2 supplier.getEndpoints()

  • 得到一個 supplier 所有的 endpoints
//EndpointDiscoverer @Override public final Collection<E> getEndpoints() {if (this.endpoints == null) {this.endpoints = discoverEndpoints();}return this.endpoints; }private Collection<E> discoverEndpoints() {//5.2.1 得到所有的 endpointCollection<EndpointBean> endpointBeans = createEndpointBeans();//5.2.2 將 extension 加入所有 extension id 對應的 endpoint id 的 endpointBean(filter 必須匹配,否則不填充)//目前有:healthEndpointWebExtension、environmentEndpointWebExtensionaddExtensionBeans(endpointBeans);//5.2.3 最終處理 endpointreturn convertToEndpoints(endpointBeans); }

5.2.1 createEndpointBeans

//EndpointDiscoverer private Collection<EndpointBean> createEndpointBeans() {Map<EndpointId, EndpointBean> byId = new LinkedHashMap<>();//得到所有 @Endpoint 及其子注解的類名String[] beanNames = BeanFactoryUtils.beanNamesForAnnotationIncludingAncestors(this.applicationContext,Endpoint.class);for (String beanName : beanNames) {//不是被代理的類(!beanName.startsWith(scopedTarget.)),才能進if (!ScopedProxyUtils.isScopedTarget(beanName)) {//5.2.1.1EndpointBean endpointBean = createEndpointBean(beanName);EndpointBean previous = byId.putIfAbsent(endpointBean.getId(), endpointBean);}}return byId.values(); }

5.2.1.1 createEndpointBean(beanName)

private EndpointBean createEndpointBean(String beanName) {Object bean = this.applicationContext.getBean(beanName);return new EndpointBean(this.applicationContext.getEnvironment(), beanName, bean); }EndpointBean(Environment environment, String beanName, Object bean) {MergedAnnotation<Endpoint> annotation = MergedAnnotations.from(bean.getClass(), SearchStrategy.TYPE_HIERARCHY).get(Endpoint.class);String id = annotation.getString("id");this.beanName = beanName;this.bean = bean;this.id = EndpointId.of(environment, id);this.enabledByDefault = annotation.getBoolean("enableByDefault");//獲取對應的 filter,controller 對應的是 ControllerEndpointFilterthis.filter = getFilter(this.bean.getClass()); }

5.2.2 addExtensionBeans

  • 將所有 endpointBean 對應的 extensionBean 填充到其中(必須匹配)
//EndpointDiscoverer private void addExtensionBeans(Collection<EndpointBean> endpointBeans) {Map<EndpointId, EndpointBean> byId = endpointBeans.stream().collect(Collectors.toMap(EndpointBean::getId, Function.identity()));//得到所有 @EndpointExtension 及其子注解的類名//注意 @EndpointExtension 上的 id 對應 @Endpoint 上的 id//說明,此 EndpointExtension 是對 對應的 Endpoint 類的擴展String[] beanNames = ...;//for (String beanName : beanNames) {ExtensionBean extensionBean = createExtensionBean(beanName);//得到 ExtensionBean 對應的 EndpointBeanEndpointBean endpointBean = byId.get(extensionBean.getEndpointId());//5.2.2.1addExtensionBean(endpointBean, extensionBean);} }

5.2.2.1 addExtensionBean(endpointBean, extensionBean)

  • 為 endpointBean 添加 extensionBean
//EndpointDiscoverer private void addExtensionBean(EndpointBean endpointBean, ExtensionBean extensionBean) {//只有 endpointBean、extensionBean、對應的 supllier 匹配,才能將 extensionBean 加入 endpointBeanif (isExtensionExposed(endpointBean, extensionBean)) {endpointBean.addExtension(extensionBean);} }
5.2.2.1.1 isExtensionExposed(endpointBean, extensionBean)
//EndpointDiscoverer private boolean isExtensionExposed(EndpointBean endpointBean, ExtensionBean extensionBean) {return isFilterMatch(extensionBean.getFilter(), endpointBean) && isExtensionExposed(extensionBean.getBean()); } //isFilterMatch:判斷 extensionBean 對應的 filter 和 endpointBean 對應的是否一樣 //isExtensionExposed:一般為 true
5.2.2.1.2 isEndpointExposed(endpointBean)
  • 判斷 endpointbean 是否匹配對應的 filter
  • 判斷此 bean 是否被 exclude 了!(配置文件配置)
  • 判斷 bean 上的注解是否匹配當前 supplier
//isFilterMatch(endpointBean.getFilter(), endpointBean):判斷 bean 是否匹配對應的 filter,具體可查看:isExposed(ExposableEndpoint<?> endpoint)、isExcluded(ExposableEndpoint<?> endpoint) 方法 //isEndpointFiltered(endpointBean):判斷此 bean 是否被 exclude 了!(配置文件配置) //isEndpointExposed(endpointBean.getBean()):判斷 bean 上的注解是否匹配當前 supplier //其中@Endpoint、@WebEndpoint 都只適用于 WebDiscoverer!原因可跟蹤一下方法源碼 private boolean isEndpointExposed(EndpointBean endpointBean) {return isFilterMatch(endpointBean.getFilter(), endpointBean) && !isEndpointFiltered(endpointBean)&& isEndpointExposed(endpointBean.getBean()); }

5.2.3 convertToEndpoints

//EndpointDiscoverer private Collection<E> convertToEndpoints(Collection<EndpointBean> endpointBeans) {Set<E> endpoints = new LinkedHashSet<>();for (EndpointBean endpointBean : endpointBeans) {//這里就過濾掉不屬于當前 supplier 的 endpoint 的了if (isEndpointExposed(endpointBean)) {endpoints.add(convertToEndpoint(endpointBean));}}//得到屬于自己的包裝了 對應的 extendendpoint 的 endpointreturn Collections.unmodifiableSet(endpoints); }

5.2.3.1 convertToEndpoint(endpointBean)

//EndpointDiscoverer private E convertToEndpoint(EndpointBean endpointBean) {MultiValueMap<OperationKey, O> indexed = new LinkedMultiValueMap<>();EndpointId id = endpointBean.getId();//為 endpoint 創建 operationsaddOperations(indexed, id, endpointBean.getBean(), false);//一個 endpoint 只能有一個 extensionif (endpointBean.getExtensions().size() > 1) {//報錯}for (ExtensionBean extensionBean : endpointBean.getExtensions()) {//為 endpoint 對應的 extension 添加 operationaddOperations(indexed, id, extensionBean.getBean(), true);}//判斷 operation 有沒有重復assertNoDuplicateOperations(endpointBean, indexed);List<O> operations = indexed.values().stream().map(this::getLast).filter(Objects::nonNull).collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));//創建return createEndpoint(endpointBean.getBean(), id, endpointBean.isEnabledByDefault(), operations); }
5.2.3.2.1 addOperations()
//EndpointDiscoverer private void addOperations(MultiValueMap<OperationKey, O> indexed, EndpointId id, Object target,boolean replaceLast) {Set<OperationKey> replacedLast = new HashSet<>();// 創建此 endpoint 對應的所有 OperationsCollection<O> operations = this.operationsFactory.createOperations(id, target);for (O operation : operations) {OperationKey key = createOperationKey(operation);O last = getLast(indexed.get(key));if (replaceLast && replacedLast.add(key) && last != null) {indexed.get(key).remove(last);}indexed.add(key, operation);} }
5.2.3.2.2 createOperations(…)
  • 創建此 endpoint 對應的所有 Operations
Collection<O> createOperations(EndpointId id, Object target) {return MethodIntrospector.selectMethods(target.getClass(), (MetadataLookup<O>) (method) -> createOperation(id, target, method)).values(); } //最終會走到不同 Discoverer 的各自實現中 @Override protected WebOperation createOperation(...) {//根據 pathMapping 中的值替換 endpoint id 對應的值(如果有的話)String rootPath = PathMapper.getRootPath(this.endpointPathMappers, endpointId);WebOperationRequestPredicate requestPredicate = this.requestPredicateFactory.getRequestPredicate(rootPath,operationMethod);return new DiscoveredWebOperation(endpointId, operationMethod, invoker, requestPredicate); }
5.2.3.2.3 createEndpoint(…)
//不同的 Discoverer 有不同的實現,以下是 WebEndpointDiscoverer @Override protected ExposableWebEndpoint createEndpoint(Object endpointBean, EndpointId id, boolean enabledByDefault,Collection<WebOperation> operations) {//默認為 /actuatorString rootPath = PathMapper.getRootPath(this.endpointPathMappers, id);return new DiscoveredWebEndpoint(this, endpointBean, id, rootPath, enabledByDefault, operations); }

參考

springboot 2.2.1.RELEASE

總結

以上是生活随笔為你收集整理的Springboot 源码分析 —— @Endpoint 注解生效原理解析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。