當(dāng)前位置:
首頁(yè) >
前端技术
> javascript
>内容正文
javascript
Springboot 源码分析 —— @Endpoint 注解生效原理解析
生活随笔
收集整理的這篇文章主要介紹了
Springboot 源码分析 —— @Endpoint 注解生效原理解析
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
文章目錄
- 1 WebMvcEndpointManagementContextConfiguration
- 1.1 webEndpointServletHandlerMapping
- 1.2 ControllerEndpointHandlerMapping
- 2 EndpointAutoConfiguration
- 3 WebEndpointAutoConfiguration
- 4 請(qǐng)求生效流程
- 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 一文中,介紹了端點(diǎn)以及其使用方法
- 本文將介紹 @Endpoint 注解的生效原理,以對(duì) actuator 端點(diǎn)有一個(gè)更深入的了解
1 WebMvcEndpointManagementContextConfiguration
- 這里是向容器注入自定義的 HandlerMapping,供 DispatcherServlet 調(diào)用
- DispatcherServlet 根據(jù)各個(gè) HandlerMapping 做實(shí)際的請(qǐng)求分發(fā)
1.1 webEndpointServletHandlerMapping
@Bean @ConditionalOnMissingBean public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(...) { List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>();//獲取所有的 web 類型 endpoints(@Endpoint、@WebEndpoint 注解)//這里可能會(huì)觸發(fā) endpoints 的初始化,但是應(yīng)該是被 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);//當(dāng) bathPath 為空,且 endpoint 的端口和server 端口一樣,才不暴露boolean shouldRegisterLinksMapping = 是否暴露 /actuator 發(fā)現(xiàn)頁(yè)面;return new WebMvcEndpointHandlerMapping(...); }1.2 ControllerEndpointHandlerMapping
- 同 1.1
2 EndpointAutoConfiguration
@Configuration(proxyBeanMethods = false) public class EndpointAutoConfiguration {@Bean@ConditionalOnMissingBean public ParameterValueMapper endpointOperationParameterMapper(...) {//方法參數(shù):@EndpointConverter ObjectProvider<Converter<?, ?>> converters//方法參數(shù):@EndpointConverter ObjectProvider<GenericConverter> genericConverters//獲取容器中的 @EndpointConverter(Converter,GenericConverter),用于 @Endpoint 輸入?yún)?shù)的類型轉(zhuǎn)換//如果沒(méi)有,則使用默認(rèn)的 ApplicationConversionService//如果有,則使用 它們,來(lái)設(shè)置 ApplicationConversionService}@Bean@ConditionalOnMissingBean//返回可緩存的 endpoint 的緩存時(shí)間 //management.endpoint.endpointName.cache.time-to-live=xx 來(lái)配置 endpointName 的緩存時(shí)間public CachingOperationInvokerAdvisor endpointCachingOperationInvokerAdvisor(Environment environment) {return new CachingOperationInvokerAdvisor(new EndpointIdTimeToLivePropertyFunction(environment));} }3 WebEndpointAutoConfiguration
- 生成 endpoint 相關(guān)信息
4 請(qǐng)求生效流程
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();//可以自定義配置預(yù)處理方法,如鑒權(quán)等if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());applyDefaultViewName(processedRequest, mv);//調(diào)用后置處理器,如加密等mappedHandler.applyPostHandle(processedRequest, response, mv);processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); }4.1.1 getHandler(processedRequest)
@Nullable protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {//其中關(guān)鍵的mapping: WebMvcEndpointHandlerMapping、ControllerEndpointHandlerMapping、RequestMappingHandlerMappingif (this.handlerMappings != null) {for (HandlerMapping mapping : this.handlerMappings) {//獲取request對(duì)應(yīng)的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 {// 默認(rèn)提供多個(gè) handlerAdapterif (this.handlerAdapters != null) {for (HandlerAdapter adapter : this.handlerAdapters) { if (adapter.supports(handler)) {//返回第一個(gè)可以匹配的 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 內(nèi)部類 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) {//通過(guò) this.operation.invoke 最終調(diào)用實(shí)際方法//如果容器中注入了 CachingOperationInvokerAdvisor,則 invoke 方法會(huì)被 CachingOperationInvoker 攔截,做 cache 方面的操作return handleResult(this.operation.invoke(invocationContext), HttpMethod.resolve(request.getMethod())); }4.1.4 endpoint 走緩存的流程
//每一個(gè) operation 會(huì)對(duì)應(yīng)創(chuàng)建一個(gè) CachingOperationInvoker,具體操作在 DiscoveredOperationsFactory 中 //通過(guò) CachingOperationInvokerAdvisor 對(duì) operation 做的靜態(tài)代理 public class CachingOperationInvoker implements OperationInvoker {@Overridepublic Object invoke(InvocationContext context) {//如果請(qǐng)求中有 body 內(nèi)容或 parameter 或 配置了安全相關(guān)的準(zhǔn)則,則不走緩存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);//將結(jié)果緩存下來(lái)this.cachedResponse = new CachedResponse(response, accessTime);return response;}return cached.getResponse();} }5 PathMappedEndpoints
@Bean @ConditionalOnMissingBean public PathMappedEndpoints pathMappedEndpoints(Collection<EndpointsSupplier<?>> endpointSuppliers) {//通過(guò) basepath 和 所有的 endpointSuppliers,得到 對(duì)象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
5.2 supplier.getEndpoints()
- 得到一個(gè) supplier 所有的 endpoints
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.)),才能進(jìn)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");//獲取對(duì)應(yīng)的 filter,controller 對(duì)應(yīng)的是 ControllerEndpointFilterthis.filter = getFilter(this.bean.getClass()); }5.2.2 addExtensionBeans
- 將所有 endpointBean 對(duì)應(yīng)的 extensionBean 填充到其中(必須匹配)
5.2.2.1 addExtensionBean(endpointBean, extensionBean)
- 為 endpointBean 添加 extensionBean
5.2.2.1.1 isExtensionExposed(endpointBean, extensionBean)
5.2.2.1.2 isEndpointExposed(endpointBean)
- 判斷 endpointbean 是否匹配對(duì)應(yīng)的 filter
- 判斷此 bean 是否被 exclude 了!(配置文件配置)
- 判斷 bean 上的注解是否匹配當(dāng)前 supplier
5.2.3 convertToEndpoints
//EndpointDiscoverer private Collection<E> convertToEndpoints(Collection<EndpointBean> endpointBeans) {Set<E> endpoints = new LinkedHashSet<>();for (EndpointBean endpointBean : endpointBeans) {//這里就過(guò)濾掉不屬于當(dāng)前 supplier 的 endpoint 的了if (isEndpointExposed(endpointBean)) {endpoints.add(convertToEndpoint(endpointBean));}}//得到屬于自己的包裝了 對(duì)應(yīng)的 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 創(chuàng)建 operationsaddOperations(indexed, id, endpointBean.getBean(), false);//一個(gè) endpoint 只能有一個(gè) extensionif (endpointBean.getExtensions().size() > 1) {//報(bào)錯(cuò)}for (ExtensionBean extensionBean : endpointBean.getExtensions()) {//為 endpoint 對(duì)應(yīng)的 extension 添加 operationaddOperations(indexed, id, extensionBean.getBean(), true);}//判斷 operation 有沒(méi)有重復(fù)assertNoDuplicateOperations(endpointBean, indexed);List<O> operations = indexed.values().stream().map(this::getLast).filter(Objects::nonNull).collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));//創(chuàng)建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<>();// 創(chuàng)建此 endpoint 對(duì)應(yīng)的所有 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(…)
- 創(chuàng)建此 endpoint 對(duì)應(yīng)的所有 Operations
5.2.3.2.3 createEndpoint(…)
//不同的 Discoverer 有不同的實(shí)現(xiàn),以下是 WebEndpointDiscoverer @Override protected ExposableWebEndpoint createEndpoint(Object endpointBean, EndpointId id, boolean enabledByDefault,Collection<WebOperation> operations) {//默認(rèn)為 /actuatorString rootPath = PathMapper.getRootPath(this.endpointPathMappers, id);return new DiscoveredWebEndpoint(this, endpointBean, id, rootPath, enabledByDefault, operations); }參考
springboot 2.2.1.RELEASE
總結(jié)
以上是生活随笔為你收集整理的Springboot 源码分析 —— @Endpoint 注解生效原理解析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 软件成功实施注意要点
- 下一篇: Tencent_机器翻译_文本翻译