SpringMVC DispatcherServlet源码(2) 扫描Controller创建HandlerMapping流程

news2024/9/28 7:25:04

Spring MVC向容器注册一个RequestMappingInfoHandlerMapping组件,他会扫描容器中的Controller组件,创建RequestMappingInfo并注册HandlerMethod映射关系。

本文将阅读Spring MVC源码分析上述流程。

RequestMappingHandlerMapping组件

Creates RequestMappingInfo instances from type and method-level @RequestMapping annotations in @Controller classes.

实现HandlerMapping接口

public interface HandlerMapping {

	HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

getHandler方法:

  • Return a handler and any interceptors for this request. The choice may be made on request URL, session state, or any factor the implementing class chooses.
  • The returned HandlerExecutionChain contains a handler Object, rather than even a tag interface, so that handlers are not constrained in any way. For example, a HandlerAdapter could be written to allow another framework’s handler objects to be used.
  • Returns null if no match was found. This is not an error. The DispatcherServlet will query all registered HandlerMapping beans to find a match, and only decide there is an error if none can find a handler.

功能就是根据当前请求获取执行处理器,HandlerExecutionChain封装着处理器和拦截器:

public class HandlerExecutionChain {

	private final Object handler;

	private HandlerInterceptor[] interceptors;

	private List<HandlerInterceptor> interceptorList;

	private int interceptorIndex = -1;
}

后续分析分发请求流程时会介绍这个方法。

实现InitializingBean接口

public void afterPropertiesSet() {
	this.config = new RequestMappingInfo.BuilderConfiguration();
	this.config.setUrlPathHelper(getUrlPathHelper());
	this.config.setPathMatcher(getPathMatcher());
	this.config.setSuffixPatternMatch(useSuffixPatternMatch());
	this.config.setTrailingSlashMatch(useTrailingSlashMatch());
	this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
	this.config.setContentNegotiationManager(getContentNegotiationManager());

	super.afterPropertiesSet();
}

AbstractHandlerMethodMapping类afterPropertiesSet方法实现:

public void afterPropertiesSet() {
	initHandlerMethods();
}

AbstractHandlerMethodMapping类是一个抽象类,泛型T需要子类实现去指定,比如RequestMappingHandlerMapping子类指定就是RequestMappingInfo类型,表示其内部管理和注册的是RequestMappingInfo类型对象:

public abstract class AbstractHandlerMethodMapping<T> 
    	extends AbstractHandlerMapping implements InitializingBean {

    // 这个类也支持泛型
    private final MappingRegistry mappingRegistry = new MappingRegistry();
}

扫描Controller创建RequestMappingInfo

/**
 * Scan beans in the ApplicationContext, detect and register handler methods.
 */
protected void initHandlerMethods() {
    // 首先从spring容器获取所有的beanName集
	for (String beanName : getCandidateBeanNames()) {
        // 过滤掉scoped proxy的beanName
		if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
            // 这里解析bean
			processCandidateBean(beanName);
		}
	}
}

protected void processCandidateBean(String beanName) {
	Class<?> beanType = null;
	try {
		beanType = obtainApplicationContext().getType(beanName);
	} catch (Throwable ex) {}
    // 这里判断bean被RequestMapping或Controller注解标注
    // 之后被这两个注解标注的bean才进行进一步解析
	if (beanType != null && isHandler(beanType)) {
		detectHandlerMethods(beanName);
	}
}

protected void detectHandlerMethods(Object handler) {
    // 这里的handler就是Controller的beanName
    // 所以需要从容器里面获取一下bean的类型
	Class<?> handlerType = (handler instanceof String ?
			obtainApplicationContext().getType((String) handler) : handler.getClass());

	if (handlerType != null) {
        // 获取一下原始类型,如果是cglib类,需要解析父类获取原始类型
		Class<?> userType = ClassUtils.getUserClass(handlerType);
        // 泛型T是RequestMappingInfo类型
        // RequestMappingInfo实现了RequestCondition接口
        // 封装了一系列的RequestCondition用于匹配请求,这个后续再介绍
        // 这个method保存的就是Controller方法 -> RequestMappingInfo对象的映射关系
		Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
				(MethodIntrospector.MetadataLookup<T>) method -> {
					try {
                        // 这里使用Controller方法、Controller类型创建RequestMappingInfo对象
                        // 抽象方法,需要子类实现
                        // RequestMappingHandlerMapping子类创建的是RequestMappingInfo对象
						return getMappingForMethod(method, userType);
					} catch (Throwable ex) {
						throw new IllegalStateException("xx");
					}
				});

        // 注册HandlerMethod
		methods.forEach((method, mapping) -> {
			Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
			registerHandlerMethod(handler, invocableMethod, mapping);
		});
	}
}

// Uses method and type-level @RequestMapping annotations to create the RequestMappingInfo.
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
	RequestMappingInfo info = createRequestMappingInfo(method);
	if (info != null) {
		RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
		if (typeInfo != null) {
			info = typeInfo.combine(info);
		}
		String prefix = getPathPrefix(handlerType);
		if (prefix != null) {
			info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
		}
	}
	return info;
}

protected RequestMappingInfo createRequestMappingInfo(
		RequestMapping requestMapping, RequestCondition<?> customCondition) {

    // 创建过程就是在创建一系列的RequestCondition
	RequestMappingInfo.Builder builder = RequestMappingInfo
			.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
			.methods(requestMapping.method())
			.params(requestMapping.params())
			.headers(requestMapping.headers())
			.consumes(requestMapping.consumes())
			.produces(requestMapping.produces())
			.mappingName(requestMapping.name());
	if (customCondition != null) {
		builder.customCondition(customCondition);
	}
	return builder.options(this.config).build();
}

RequestMappingInfo类和RequestCondition接口

封装着Controller处理方法相关的匹配条件,用于匹配进来的请求。

public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {

	private final String name;

	private final PatternsRequestCondition patternsCondition;

	private final RequestMethodsRequestCondition methodsCondition;

	private final ParamsRequestCondition paramsCondition;

	private final HeadersRequestCondition headersCondition;

	private final ConsumesRequestCondition consumesCondition;

	private final ProducesRequestCondition producesCondition;

	private final RequestConditionHolder customConditionHolder;
}

RequestCondition接口:

/*
 * Contract for request mapping conditions.
 * Request conditions can be combined via combine(Object), matched to a request via 
 * getMatchingCondition(HttpServletRequest), and compared to each other via compareTo(Object, 
 * HttpServletRequest) to determine which is a closer match for a given request.
 */
public interface RequestCondition<T> {

	/**
	 * Combine this condition with another such as conditions from a type-level 
	 * and method-level @RequestMapping annotation.
	 */
	T combine(T other);

	/**
	 * Check if the condition matches the request returning a potentially new instance 
	 * created for the current request. For example a condition with multiple URL patterns 
	 * may return a new instance only with those patterns that match the request.
	 * If a condition cannot be matched to a pre-flight request it should return 
	 * an instance with empty content thus not causing a failure to match.
	 */
	T getMatchingCondition(HttpServletRequest request);

	/**
	 * Compare this condition to another condition in the context of a specific request.
	 * This method assumes both instances have been obtained via 
	 * getMatchingCondition(HttpServletRequest) to ensure they have content 
	 * relevant to current request only.
	 */
	int compareTo(T other, HttpServletRequest request);
}

注册HandlerMethod

// 注册HandlerMethod
methods.forEach((method, mapping) -> {
    // 判断并返回一个可以被调用的Method
    // 1. method不是私有的、不是静态的
    // 2. method所属的Class是userType类型
    // 3. 等等
	Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
    // 注册HandlerMethod,三个参数:
    // handler: Controller BeanName
    // invocableMethod: 接口处理方法
    // mapping: 上文创建的RequestMappingInfo对象
	registerHandlerMethod(handler, invocableMethod, mapping);
});

注册HandlerMethod流程:

protected void registerHandlerMethod(Object handler, Method method, T mapping) {
	this.mappingRegistry.register(mapping, handler, method);
}

mappingRegistry是MappingRegistry对象:

class MappingRegistry {

	private final Map<T, MappingRegistration<T>> registry = new HashMap<>();

    // RequestMappingInfo -> HandlerMethod
	private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();

    // url -> RequestMappingInfo
	private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();

    // RequestMappingName -> HandlerMethod集
	private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();

	private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();

	private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    // 这个泛型T是RequestMappingInfo
	public void register(T mapping, Object handler, Method method) {
		this.readWriteLock.writeLock().lock();
		try {
            // 创建HandlerMethod
            // 封装接口处理方法信息,包括:
            // Controller实例、Controller类型、接口处理方法、处理方法参数列表等信息
			HandlerMethod handlerMethod = createHandlerMethod(handler, method);
			validateMethodMapping(handlerMethod, mapping);
            // 添加RequestMappingInfo -> HandlerMethod映射
			this.mappingLookup.put(mapping, handlerMethod);

            // 从RequestMappingInfo中查找没有占位符/变量的url集,添加url -> RequestMappingInfo映射
			List<String> directUrls = getDirectUrls(mapping);
			for (String url : directUrls) {
				this.urlLookup.add(url, mapping);
			}

			String name = null;
			if (getNamingStrategy() != null) {
				name = getNamingStrategy().getName(handlerMethod, mapping);
				addMappingName(name, handlerMethod);
			}

			CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
			if (corsConfig != null) {
				this.corsLookup.put(handlerMethod, corsConfig);
			}

            // 创建MappingRegistration并添加映射
			this.registry.put(
                mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
		} finally {
			this.readWriteLock.writeLock().unlock();
		}
	}
}

DispatcherServlet初始化

DispatcherServlet初始化入口

DispatcherServlet在启动时从spring容器加载HandlerMapping集和HandlerAdapter集,以及其他MVC组件,此处重点介绍HandlerMapping集和HandlerAdapter集:

@Override
protected void onRefresh(ApplicationContext context) {
	initStrategies(context);
}

// Initialize the strategy objects that this servlet uses
protected void initStrategies(ApplicationContext context) {
    // Initialize the MultipartResolver used by this class
	initMultipartResolver(context);
    // Initialize the LocaleResolver used by this class
	initLocaleResolver(context);
    // Initialize the ThemeResolver used by this class
	initThemeResolver(context);
    // 加载HandlerMapping集
	initHandlerMappings(context);
    // 加载HandlerAdapter集
	initHandlerAdapters(context);
    // 加载HandlerExceptionResolver异常处理器集
	initHandlerExceptionResolvers(context);
    // Initialize the RequestToViewNameTranslator used by this servlet instance
	initRequestToViewNameTranslator(context);
    // 加载ViewResolver视图解析器集
	initViewResolvers(context);
	initFlashMapManager(context);
}

加载HandlerMapping集

private void initHandlerMappings(ApplicationContext context) {
	this.handlerMappings = null;

	if (this.detectAllHandlerMappings) {
		// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
        // 从容器查找所有的HandlerMapping
		Map<String, HandlerMapping> matchingBeans =
				BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
		if (!matchingBeans.isEmpty()) {
            // 创建HandlerMapping集
			this.handlerMappings = new ArrayList<>(matchingBeans.values());
			// We keep HandlerMappings in sorted order.
            // 排序,之前介绍过
			AnnotationAwareOrderComparator.sort(this.handlerMappings);
		}
	} else {
		try {
			HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
			this.handlerMappings = Collections.singletonList(hm);
		} catch (NoSuchBeanDefinitionException ex) {
			// Ignore, we'll add a default HandlerMapping later.
		}
	}

    // 略
	// Ensure we have at least one HandlerMapping, by registering
	// a default HandlerMapping if no other mappings are found.
	if (this.handlerMappings == null) {
		this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
	}
}

加载HandlerAdapter集

private void initHandlerAdapters(ApplicationContext context) {
	this.handlerAdapters = null;

	if (this.detectAllHandlerAdapters) {
		// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
		Map<String, HandlerAdapter> matchingBeans =
				BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
		if (!matchingBeans.isEmpty()) {
			this.handlerAdapters = new ArrayList<>(matchingBeans.values());
			// We keep HandlerAdapters in sorted order.
			AnnotationAwareOrderComparator.sort(this.handlerAdapters);
		}
	}
	else {
		try {
			HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
			this.handlerAdapters = Collections.singletonList(ha);
		} catch (NoSuchBeanDefinitionException ex) {
			// Ignore, we'll add a default HandlerAdapter later.
		}
	}

    // 略
	// Ensure we have at least some HandlerAdapters, by registering
	// default HandlerAdapters if no other adapters are found.
	if (this.handlerAdapters == null) {
		this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
	}
}

加载HandlerExceptionResolver异常处理器集

HandlerMapping集

在这里插入图片描述

  1. springfox.documentation.spring.web.PropertySourcedRequestMappingHandlerMapping

  2. org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping

    A custom HandlerMapping that makes web endpoints available over HTTP using Spring MVC.

  3. org.springframework.boot.actuate.endpoint.web.reactive.ControllerEndpointHandlerMapping

    HandlerMapping that exposes @ControllerEndpoint and @RestControllerEndpoint annotated endpoints over Spring WebFlux.

  4. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping

    Creates RequestMappingInfo instances from type and method-level @RequestMapping annotations in @Controller classes.

  5. org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping

    Implementation of the org.springframework.web.servlet.HandlerMapping interface that maps from URLs to beans with names that start with a slash (“/”), similar to how Struts maps URLs to action names.

  6. org.springframework.web.servlet.function.support.RouterFunctionMapping

    HandlerMapping implementation that supports RouterFunctions. If no RouterFunction is provided at construction time, this mapping will detect all router functions in the application context, and consult them in order.

  7. org.springframework.web.servlet.handler.SimpleUrlHandlerMapping

    Implementation of the org.springframework.web.servlet.HandlerMapping interface that maps from URLs to request handler beans. Supports both mapping to bean instances and mapping to bean names; the latter is required for non-singleton handlers.

  8. org.springframework.boot.autoconfigure.web.servlet.WelcomePageHandlerMapping

    An AbstractUrlHandlerMapping for an application’s welcome page. Supports both static and templated files. If both a static and templated index page are available, the static page is preferred.

HandlerAdapter集

在这里插入图片描述

  1. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

    Extension of AbstractHandlerMethodAdapter that supports @RequestMapping annotated HandlerMethods

  2. org.springframework.web.servlet.function.support.HandlerFunctionAdapter

    HandlerAdapter implementation that supports HandlerFunctions.

  3. org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter

    Adapter to use the plain HttpRequestHandler interface with the generic org.springframework.web.servlet.DispatcherServlet. Supports handlers that implement the LastModified interface. This is an SPI class, not used directly by application code.

  4. org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter

    Adapter to use the plain Controller workflow interface with the generic org.springframework.web.servlet.DispatcherServlet. Supports handlers that implement the LastModified interface. This is an SPI class, not used directly by application code.

HandlerExceptionResolver异常处理器集

在这里插入图片描述

Spring MVC注入RequestMappingHandlerMapping组件

@EnableWebMvc注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
// 导入了DelegatingWebMvcConfiguration配置类
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

DelegatingWebMvcConfiguration配置类

继承WebMvcConfigurationSupport类。

WebMvcConfigurationSupport会向容器里面注入RequestMappingHandlerMapping和RequestMappingHandlerAdapter组件:

@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping(
		@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
		@Qualifier("mvcConversionService") FormattingConversionService conversionService,
		@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {

	RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
	mapping.setOrder(0);
	mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
	mapping.setContentNegotiationManager(contentNegotiationManager);
	mapping.setCorsConfigurations(getCorsConfigurations());

	PathMatchConfigurer configurer = getPathMatchConfigurer();

	Boolean useSuffixPatternMatch = configurer.isUseSuffixPatternMatch();
	if (useSuffixPatternMatch != null) {
		mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
	}
	Boolean useRegisteredSuffixPatternMatch = configurer.isUseRegisteredSuffixPatternMatch();
	if (useRegisteredSuffixPatternMatch != null) {
		mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
	}
	Boolean useTrailingSlashMatch = configurer.isUseTrailingSlashMatch();
	if (useTrailingSlashMatch != null) {
		mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
	}

	UrlPathHelper pathHelper = configurer.getUrlPathHelper();
	if (pathHelper != null) {
		mapping.setUrlPathHelper(pathHelper);
	}
	PathMatcher pathMatcher = configurer.getPathMatcher();
	if (pathMatcher != null) {
		mapping.setPathMatcher(pathMatcher);
	}
	Map<String, Predicate<Class<?>>> pathPrefixes = configurer.getPathPrefixes();
	if (pathPrefixes != null) {
		mapping.setPathPrefixes(pathPrefixes);
	}

	return mapping;
}

@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
		@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
		@Qualifier("mvcConversionService") FormattingConversionService conversionService,
		@Qualifier("mvcValidator") Validator validator) {

	RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
	adapter.setContentNegotiationManager(contentNegotiationManager);
	adapter.setMessageConverters(getMessageConverters());
	adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(conversionService, validator));
	adapter.setCustomArgumentResolvers(getArgumentResolvers());
	adapter.setCustomReturnValueHandlers(getReturnValueHandlers());

	if (jackson2Present) {
		adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));
		adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
	}

	AsyncSupportConfigurer configurer = new AsyncSupportConfigurer();
	configureAsyncSupport(configurer);
	if (configurer.getTaskExecutor() != null) {
		adapter.setTaskExecutor(configurer.getTaskExecutor());
	}
	if (configurer.getTimeout() != null) {
		adapter.setAsyncRequestTimeout(configurer.getTimeout());
	}
	adapter.setCallableInterceptors(configurer.getCallableInterceptors());
	adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());

	return adapter;
}

Spring Boot注入RequestMappingHandlerMapping组件

WebMvcAutoConfiguration自动装配类

org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.EnableWebMvcConfiguration内部类继承WebMvcConfigurationSupport类,自动注入RequestMappingHandlerMapping和RequestMappingHandlerAdapter组件。

小结

经过本文的源码分析,我们了解了Spring扫描Controller注册HandlerMethod的过程、以及Spring MVC和Spring Boot环境下注入RequestMappingHandlerMapping组件的过程:

  1. Spring MVC、Spring Boot自动注入RequestMappingHandlerMapping组件
  2. RequestMappingHandlerMapping组件扫描Controller创建RequestMappingInfo对象
  3. 封装注册HandlerMethod

后续我们将继续阅读DispatcherServlet源码,分析请求分发的流程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/186720.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java ssm校园兼职发布与互动平台的设计与实现

该系统基于B/S即所谓浏览器/服务器模式&#xff0c;应用JSP技术&#xff0c;选择MySQL作为后台数据库。系统主要包括个人中心、用户管理、企业管理、企业信息管理、兼职信息管理、职位申请管理、职位类型管理、交流中心、留言反馈、系统管理等功能模块。 使用校园兼职发布与互动…

JavaWeb:会话技术之Session

Cookie已经能完成一次会话多次请求之间的数据共享&#xff0c;之前我们还提到过Session也可以实现&#xff0c;那么&#xff1a; 什么是Session&#xff1f;Session如何来使用&#xff1f;Session是如何实现的&#xff1f;Session的使用注意事项有哪些&#xff1f; 1. Sessio…

SpringCloud学习笔记 - 熔断降级 - Sentinel

1. Sentinel熔断降级概述 1.1. 熔断降级要解决的问题 除了流量控制以外&#xff0c;对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块&#xff0c;可能是另外的一个远程服务、数据库&#xff0c;或者第三方 API 等。例如&…

海康Visionmaster-VM权限设置、软件设置、方案设置和运行策略

权限设置 权限设置可设置是否配置管理员、技术员和操作员权限和配置密码&#xff0c;并设置不同角色人员的权限。 权限导入导出可对当前软件权限设置模块的配置以txt格式文档进行导入或导出。 打开启用加密时&#xff0c;需设置管理员密码。设置完成后&#xff0c;软件以管理员…

如何把Node项目部署到服务器上

1. 如何合理选购一台服务器 对于服务器的选择&#xff0c;我们主要有以下几种选择&#xff1a; 1. 阿里云&#xff1b; 2. 腾讯云&#xff1b; 3. 华为云&#xff1b; 4. 亚马逊云&#xff1b; 国内用户如果没有特殊需求可以选择前三种&#xff0c;这里我阿里云举例&…

【Vue】vue-devtools调试工具安装和配置

1. 安装 vue-devtools 调试工具vue 官方提供的 vue-devtools 调试工具&#xff0c;能够方便开发者对 vue 项目进行调试与开发。Chrome 浏览器在线安装 vue-devtools &#xff1a;https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpdFi…

RPA自动办公02——Uibot界面元素选择

继续RPA-Uibot的简单使用&#xff0c;本次带来RPA自动点击网页。 当然官方教程更加详细&#xff1a;界面元素自动化_UiBot开发者指南 按照书上的教程&#xff0c;点击一个表格&#xff0c;在右边拖拽命令&#xff0c;然后选择元素就可以了&#xff1a; 这个表格在官方文档上有…

为什么要开发SpringBoot?

Spring配置繁琐 虽然Spring的组件代码是轻量级的&#xff0c;但它的配置却是重量级的。一开始&#xff0c;Spring用XML配置&#xff0c;而且是很多 XML配置。Spring 2.5引入了基于注解的组件扫描&#xff0c;这消除了大量针对应用程序自身组件的显式XML配置。Spring 3.0引入了基…

计算机图形学 第4章 多边形填充

目录前驱知识多边形的扫描转换有效边表填充算法原理边界像素处理原则怎么算交点有效边桶表与边表桶表表示法边缘填充算法填充过程在这里插入图片描述区域填充算法/种子填充算法种子填充算法扫描线种子填充算法 &#xff08;更有效&#xff09;前驱知识 了解扫描转换的基本概念…

九龙证券|兔年行情将打响,A股2月上涨概率最高

兔年行情行将正式打响&#xff0c;迎接投资者的会否是 “兔”飞猛进的行情呢&#xff1f;证券时报数据宝带您一起透视兔年股票上涨概率。 A股新年后上扬概率高 从近10年A股新年后的市场体现来看&#xff0c;头几个买卖日大概率上涨。 数据宝统计&#xff0c;曩昔10年&#xf…

国内在线图表工具,你能说出几个?

之前写过很多篇在线图表、数据分析处理类工具的内容&#xff0c;但都是针对单个问题写的&#xff0c;没有将其整合起来&#xff0c;今天就借着这个问题&#xff0c;做个国内在线图表工具的合集。 一共5大类&#xff0c;每一类各介绍一个代表性工具&#xff0c;全文较长&#x…

【c语言进阶】文件操作知识大全上

&#x1f680;write in front&#x1f680; &#x1f4dc;所属专栏&#xff1a;> c语言学习 &#x1f6f0;️博客主页&#xff1a;睿睿的博客主页 &#x1f6f0;️代码仓库&#xff1a;&#x1f389;VS2022_C语言仓库 &#x1f3a1;您的点赞、关注、收藏、评论&#xff0c;是…

15. XML解析

1. 什么是 XML&#xff1f; XML 指可扩展标记语言&#xff08;EXtensible Markup Language&#xff09;。 a. XML 是一种很像HTML的标记语言。 b. XML 的设计宗旨是传输数据&#xff0c;而不是显示数据。 c. XML 标签没有被预定义。您需要自行定义标签。 d. XML 被设计为具有自…

Tomcat详解

Tomcat 是一个 HTTP 服务器. 前面我们已经学习了 HTTP 协议, 知道了 HTTP 协议就是 HTTP 客户端和 HTTP 服务器之间的交互数据的格式. 同时也通过 ajax 和 Java Socket 分别构造了 HTTP 客户端. HTTP 服务器我们也同样可以通过 Java Socket 来实现. 而 Tomcat 就是基于 Java 实…

星戈瑞--磺化CY7标记活性脂Sulfo CY7-NHS注意事项你知道那些?

Sulfo-Cy7 NHS酯是一种磺化、亲水性和高度水溶性染料。该试剂允许制备Cy7标记的生物分子&#xff0c;特别适用于标记脆弱的蛋白质和易变性的蛋白质。染料标记的分子可用于各种研究相关的实验。近红外荧光成像利用了生物组织在特定波长范围内的透明度。在活生物体中&#xff0c;…

2. Kimball的DW/BI架构解析

文章目录Kimball的DW/BI架构1. 业务系统&#xff08;数据源系统&#xff09;2. 获取-转换-加载&#xff08;ETL&#xff09;系统3. 用于支持BI&#xff08;商业智能&#xff09;决策的展现区4. 商业智能应用引言&#xff1a;DW&#xff0c;即Data Warehouse&#xff0c;数据仓库…

LLA: Loss-aware Label Assignment for Dense Pedestrian Detection 原理与代码解析

paper&#xff1a;LLA: Loss-aware Label Assignment for Dense Pedestrian Detectioncode&#xff1a;https://github.com/Megvii-BaseDetection/LLA背景标签分配由于对检测器的性能影响很大&#xff0c;在通用目标检测中得到了广泛的研究&#xff0c;但是密集行人检测中的标签…

【SpringCloud复习巩固】Gateway

目录 一.统一网关Gateway 1.1为什么需要网关 1.2网关的技术实现 1.3网关的作用 1.4搭建网关服务 1.5路由断言工厂Route Predicate Factory 1.6路由过滤器GatewayFilter 1.7全局过滤器GlobalFilter 1.8过滤器执行顺序 1.9跨域问题解决 一.统一网关Gateway 1.1为什么需…

《流浪地球2》里的硬核科技,IT人带入了

《流浪地球2》不负期待&#xff0c;上映后口碑和热度一路高歌猛进&#xff0c;霸坐春节档&#xff0c;稳居票房TOP2。硬核科技惊喜不断&#xff0c;如喷出冲天蓝光的行星发动机、洞悉一切的量子计算机、高耸入云的太空电梯…… 逼真的特效质感以及浓厚的重工业美学掀起科技新热…

UE4中修改模型的中心点

UE4系列文章目录 文章目录UE4系列文章目录前言一、增加scenen空组件前言 在UE4中的模型中心点一般都在模型的中点&#xff0c;如果是门这样的模型&#xff0c;我们就不能绕中心点旋转了&#xff0c;不然会觉得很奇怪。但是默认的静态网格体中心点都在物体中心&#xff0c;那我…