springmvc組件中的HandlerMapping解析
HandlerMapping
HandlerMapping在Spring MVC框架的jar包下面,他是處理映射器,為用戶發(fā)送的請求找到合適的Handler Adapter,它將會把請求映射為HandlerExecutionChain對象(包含一個Handler處理器(頁面控制器)對象、多個HandlerInterceptor攔截器)對象,同時通過這種策略模式,很容易添加新的映射策略。SpringMVC在請求到handler處理器的分發(fā)這步就是通過HandlerMapping模塊解決的,handlerMapping還處理攔截器,同時Spring MVC也提供了一系列HandlerMapping的實現(xiàn),根據(jù)一定的規(guī)則選擇controller。
public interface HandlerMapping { // 返回請求的一個處理程序handler和攔截器interceptors @Nullable HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception; }
HandlerMapping執(zhí)行流程如下:
- 初始化流程:DispatcherServlet——>initHandlerMappings(context)
private void initHandlerMappings(ApplicationContext context) { this.handlerMappings = null; //默認為true,可通過DispatcherServlet的init-param參數(shù)進行設置 if (this.detectAllHandlerMappings) { //在ApplicationContext中找到所有的handlerMapping, 包括父級上下文 Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<>(matchingBeans.values()); //排序,可通過指定order屬性進行設置,order的值為int型,數(shù)越小優(yōu)先級越高 AnnotationAwareOrderComparator.sort(this.handlerMappings); } } else { try { //從ApplicationContext中取id(或name)="handlerMapping"的bean,此時為空 HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); // 將hm轉(zhuǎn)換成list,并賦值給屬性handlerMappings this.handlerMappings = Collections.singletonList(hm); } catch (NoSuchBeanDefinitionException ex) { } } //如果沒有自定義則使用默認的handlerMappings //默認的HandlerMapping在DispatcherServlet.properties屬性文件中定義, // 該文件是在DispatcherServlet的static靜態(tài)代碼塊中加載的 // 默認的是:BeanNameUrlHandlerMapping和RequestMappingHandlerMapping if (this.handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);//默認策略 if (logger.isTraceEnabled()) { logger.trace("No HandlerMappings declared for servlet '" + getServletName() + "': using default strategies from DispatcherServlet.properties"); } } }
- 如果緩存中存在,則從緩存中獲取,并進行排序
- 從ApplicationContext中取id(或name)="handlerMapping"的bean
- 如果沒有自定義則使用默認的handlerMappings
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) { String key = strategyInterface.getName();//HandlerMappings //獲取HandlerMappings為key的value值,defaultStrategies在static塊中讀取DispatcherServlet.properties String value = defaultStrategies.getProperty(key); if (value != null) { //逗號,分割成數(shù)組 String[] classNames = StringUtils.commaDelimitedListToStringArray(value); List<T> strategies = new ArrayList<>(classNames.length); for (String className : classNames) { try { //反射加載并存儲strategies Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader()); //通過容器創(chuàng)建bean 觸發(fā)后置處理器 Object strategy = createDefaultStrategy(context, clazz); strategies.add((T) strategy); } 。。。 } return strategies; } else { return new LinkedList<>(); } } protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) { //通過容器創(chuàng)建bean return context.getAutowireCapableBeanFactory().createBean(clazz); }
- 獲取HandlerMappings為key的value值,defaultStrategies在static塊中讀取DispatcherServlet.properties
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\ org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\ org.springframework.web.servlet.function.support.RouterFunctionMapping
- 執(zhí)行service方法流程:DispatcherServlet——>doDispatch()——>getHandler(request)——>getHandlerExecutionChain(handler, request)——>new HandlerExecutionChain(handler))——>chain.addInterceptor(interceptor)
HandlerMapping是處理器映射器,根據(jù)請求找到處理器Handler,但并不是簡單的返回處理器,而是將處理器和攔截器封裝,形成一個處理器執(zhí)行鏈(HandlerExecuteChain)。
AbstractHandlerMapping
獲取處理器執(zhí)行鏈的過程在AbstractHandlerMapping中的getHandler方法
- 先看看初始化過程
protected void initApplicationContext() throws BeansException { //模板方法,用于給子類提供一個添加Interceptors的入口。 extendInterceptors(this.interceptors); //將SpringMvc容器和父容器中所有的MappedInterceptor類型的Bean添加到mappedInterceptors的屬性 detectMappedInterceptors(this.mappedInterceptors); //用于初始化Interceptor,將Interceptors屬性里所包含的對象按類型添加到mappedInterceptors和adaptedInterceptors. initInterceptors(); }
AbstractHandlerMapping通過initApplicationContext()方法進行自動初始化,它的創(chuàng)建其實就是初始化上面三個interceptor,其一般是由父類執(zhí)行ApplicationContextAware#setApplicationContext()方法間接調(diào)用,主要是獲取springmvc上下文中的攔截器集合MappedInterceptor。
- getHandler() 獲取處理鏈對象
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { //getHandlerInternal(request)方法為抽象方法,供子類實現(xiàn) //獲取到的handler對象一般為bean/HandlerMethod Object handler = getHandlerInternal(request); //上述找不到則使用默認的處理類,沒有設定則返回null,則會返回前臺404錯誤 if (handler == null) { handler = getDefaultHandler(); } if (handler == null) { return null; } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } //創(chuàng)建處理鏈對象 HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); //針對cros跨域請求的處理,此處就不分析了 if (CorsUtils.isCorsRequest(request)) { CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request); CorsConfiguration handlerConfig = getCorsConfiguration(handler, request); CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig); executionChain = getCorsHandlerExecutionChain(request, executionChain, config); } return executionChain; } //針對HandlerMethod的獲取 protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { //獲取訪問的路徑,一般類似于request.getServletPath()返回不含contextPath的訪問路徑 String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); //獲取讀鎖 this.mappingRegistry.acquireReadLock(); try { //獲取HandlerMethod作為handler對象,這里涉及到路徑匹配的優(yōu)先級 //優(yōu)先級:精確匹配>最長路徑匹配>擴展名匹配 HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); //HandlerMethod內(nèi)部含有bean對象,其實指的是對應的Controller return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { //釋放讀鎖 this.mappingRegistry.releaseReadLock(); } } //針對beanName的獲取 protected Object getHandlerInternal(HttpServletRequest request) throws Exception { String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); //從handlerMap查找路徑對應的beanName Object handler = lookupHandler(lookupPath, request); if (handler == null) { // We need to care for the default handler directly, since we need to // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well. Object rawHandler = null; if ("/".equals(lookupPath)) { rawHandler = getRootHandler(); } if (rawHandler == null) { rawHandler = getDefaultHandler(); } if (rawHandler != null) { // Bean name or resolved handler? if (rawHandler instanceof String) { String handlerName = (String) rawHandler; rawHandler = getApplicationContext().getBean(handlerName); } validateHandler(rawHandler, request); handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null); } } return handler; }
HandlerMapping是通過getHandler方法來獲取處理器Handler和攔截器Interceptors的,AbstractHandlerMapping實現(xiàn)獲取handler。
在獲取handler對象時是由其子類實現(xiàn)的,分別為AbstractHandlerMethodMapping,AbstractUrlHandlerMapping
- AbstractHandlerMethodMapping當bean被注入到容器后會執(zhí)行一系列的初始化過程,用于注解@Controller,@RequestMapping來定義controller。
//容器啟動時會運行此方法,完成handlerMethod的注冊操作 @Override public void afterPropertiesSet() { initHandlerMethods(); }
protected void initHandlerMethods() { //獲取上下文中所有bean的name,不包含父容器 for (String beanName : getCandidateBeanNames()) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { processCandidateBean(beanName); } } //日志記錄HandlerMethods的總數(shù)量 handlerMethodsInitialized(getHandlerMethods()); }
protected void processCandidateBean(String beanName) { Class<?> beanType = null; try { //根據(jù)name找出bean的類型 beanType = obtainApplicationContext().getType(beanName); } catch (Throwable ex) { // An unresolvable bean type, probably from a lazy bean - let's ignore it. if (logger.isTraceEnabled()) { logger.trace("Could not resolve type for bean '" + beanName + "'", ex); } } //處理Controller和RequestMapping if (beanType != null && isHandler(beanType)) { detectHandlerMethods(beanName); } }
//整個controller類的解析過程 protected void detectHandlerMethods(Object handler) { //根據(jù)name找出bean的類型 Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass()); if (handlerType != null) { //獲取真實的controller,如果是代理類獲取父類 Class<?> userType = ClassUtils.getUserClass(handlerType); //對真實的controller所有的方法進行解析和處理 key為方法對象,T為注解封裝后的對象RequestMappingInfo Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (MethodIntrospector.MetadataLookup<T>) method -> { try { // 調(diào)用子類RequestMappingHandlerMapping的getMappingForMethod方法進行處理, // 即根據(jù)RequestMapping注解信息創(chuàng)建匹配條件RequestMappingInfo對象 return getMappingForMethod(method, userType); } catch (Throwable ex) { throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, ex); } }); if (logger.isTraceEnabled()) { logger.trace(formatMappings(userType, methods)); } methods.forEach((method, mapping) -> { //找出controller中可外部調(diào)用的方法 Method invocableMethod = AopUtils.selectInvocableMethod(method, userType); //注冊處理方法 registerHandlerMethod(handler, invocableMethod, mapping); }); } } 后續(xù)主要是通過RequestMappingHandlerMapping 完成:
- 通過方法isHandler(beanType)判斷是否處理Controller和RequestMapping
- 調(diào)用子類RequestMappingHandlerMapping的getMappingForMethod方法進行處理,即根據(jù)RequestMapping注解信息創(chuàng)建匹配條件RequestMappingInfo對象getMappingForMethod(method, userType);
- 注冊處理方法registerHandlerMethod(handler, invocableMethod, mapping)
- AbstractUrlHandlerMapping是AbstractHandlerMapping的子類,實現(xiàn)針對beanName注冊為handler對象。
//注冊url和Bean的map,將具體的Handler注入到url對應的map中 protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException { Assert.notNull(urlPath, "URL path must not be null"); Assert.notNull(handler, "Handler object must not be null"); Object resolvedHandler = handler; // Eagerly resolve handler if referencing singleton via name. // 不是懶加載,默認為false,即不是,通過配置SimpleUrlHandlerMapping屬性lazyInitHandlers的值進行控制 // 如果不是懶加載并且handler為單例,即從上下文中查詢實例處理,此時resolvedHandler為handler實例對象; // 如果是懶加載或者handler不是單例,即resolvedHandler為handler邏輯名 if (!this.lazyInitHandlers && handler instanceof String) { String handlerName = (String) handler; ApplicationContext applicationContext = obtainApplicationContext(); // 如果handler是單例,通過bean的scope控制 if (applicationContext.isSingleton(handlerName)) { resolvedHandler = applicationContext.getBean(handlerName); } } Object mappedHandler = this.handlerMap.get(urlPath); if (mappedHandler != null) { if (mappedHandler != resolvedHandler) { throw new IllegalStateException( "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath + "]: There is already " + getHandlerDescription(mappedHandler) + " mapped."); } } else { if (urlPath.equals("/")) { if (logger.isTraceEnabled()) { logger.trace("Root mapping to " + getHandlerDescription(handler)); } //"/"-->設置為roothandler setRootHandler(resolvedHandler); } else if (urlPath.equals("/*")) { if (logger.isTraceEnabled()) { logger.trace("Default mapping to " + getHandlerDescription(handler)); } //對"/*"的匹配設置默認的handler setDefaultHandler(resolvedHandler); } else { //其余的路徑綁定關系則存入handlerMap this.handlerMap.put(urlPath, resolvedHandler); if (logger.isTraceEnabled()) { logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler)); } } } }
RequestMappingHandlerMapping
- 通過方法isHandler(beanType)判斷是否處理Controller和RequestMapping
@Override protected boolean isHandler(Class<?> beanType) { //獲取@Controller和@RequestMapping return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); } public static boolean hasAnnotation(AnnotatedElement element, Class<? extends Annotation> annotationType) { // Shortcut: directly present on the element, with no merging needed? if (AnnotationFilter.PLAIN.matches(annotationType) || AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) { return element.isAnnotationPresent(annotationType); } // Exhaustive retrieval of merged annotations... return findAnnotations(element).isPresent(annotationType); }
- requestMapping封裝成RequestMappingInfo對象
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { RequestMappingInfo info = createRequestMappingInfo(method); if (info != null) { //解析方法所在類上的requestMapping RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType); if (typeInfo != null) { info = typeInfo.combine(info);//合并類和方法上的路徑,比如Controller類上有@RequestMapping("/demo"),方法的@RequestMapping("/demo1"),結(jié)果為"/demo/demo1" } String prefix = getPathPrefix(handlerType);//合并前綴 if (prefix != null) { info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info); } } return info; } private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) { //找到方法上的RequestMapping注解 RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class); //獲取自定義的類型條件(自定義的RequestMapping注解) RequestCondition<?> condition = (element instanceof Class ? getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element)); return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null); }
- 注冊處理方法registerHandlerMethod(handler, invocableMethod, mapping)
public void register(T mapping, Object handler, Method method) { // Assert that the handler method is not a suspending one. if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) { throw new IllegalStateException("Unsupported suspending handler method detected: " + method); } this.readWriteLock.writeLock().lock(); try { //beanName和method封裝成HandlerMethod對象 HandlerMethod handlerMethod = createHandlerMethod(handler, method); //驗證RequestMappingInfo是否有對應不同的method,有則拋出異常 validateMethodMapping(handlerMethod, mapping); //RequestMappingInfo和handlerMethod綁定 this.mappingLookup.put(mapping, handlerMethod); List<String> directUrls = getDirectUrls(mapping);//可以配置多個url for (String url : directUrls) { //url和RequestMappingInfo綁定 可以根據(jù)url找到RequestMappingInfo,再找到handlerMethod this.urlLookup.add(url, mapping); } String name = null; if (getNamingStrategy() != null) { name = getNamingStrategy().getName(handlerMethod, mapping); addMappingName(name, handlerMethod);//方法名和Method綁定 } CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping); if (corsConfig != null) { this.corsLookup.put(handlerMethod, corsConfig); } this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name)); } finally { this.readWriteLock.writeLock().unlock(); } }
BeanNameUrlHandlerMapping
public class HelloController implements Controller { public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { ModelAndView mav = new ModelAndView("index"); mav.addObject("message", "默認的映射處理器示例"); return mav; }
<bean id="resourceView" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/"></property> <property name="suffix" value=".jsp"></property> </bean> <bean id="/name.do" class="com.zy.mvc.controller.HelloController"></bean>
//向上繼承自ApplicationObjectSupport實現(xiàn)ApplicationContextAware接口 public abstract class ApplicationObjectSupport implements ApplicationContextAware { protected final Log logger = LogFactory.getLog(this.getClass()); @Nullable private ApplicationContext applicationContext; ... public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException { if (context == null && !this.isContextRequired()) { this.applicationContext = null; this.messageSourceAccessor = null; } else if (this.applicationContext == null) { if (!this.requiredContextClass().isInstance(context)) { throw new ApplicationContextException("Invalid application context: needs to be of type [" + this.requiredContextClass().getName() + "]"); } this.applicationContext = context; this.messageSourceAccessor = new MessageSourceAccessor(context); this.initApplicationContext(context);//模板方法,調(diào)子類 } else if (this.applicationContext != context) { throw new ApplicationContextException("Cannot reinitialize with different application context: current one is [" + this.applicationContext + "], passed-in one is [" + context + "]"); } } ... } //AbstractDetectingUrlHandlerMapping @Override public void initApplicationContext() throws ApplicationContextException { super.initApplicationContext();//加載攔截器 // 處理url和bean name,具體注冊調(diào)用父類AbstractUrlHandlerMapping類完成 detectHandlers(); } //建立當前ApplicationContext中controller和url的對應關系 protected void detectHandlers() throws BeansException { // 獲取應用上下文 ApplicationContext applicationContext = obtainApplicationContext(); //獲取ApplicationContext中的所有bean的name(也是id,即@Controller的屬性值) String[] beanNames = (this.detectHandlersInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext,Object.class) : applicationContext.getBeanNamesForType(Object.class)); //遍歷所有beanName for (String beanName : beanNames) { // 通過模板方法模式調(diào)用BeanNameUrlHandlerMapping子類處理 String[] urls = determineUrlsForHandler(beanName);//判斷是否以/開始 if (!ObjectUtils.isEmpty(urls)) { //調(diào)用父類AbstractUrlHandlerMapping將url與handler存入map registerHandler(urls, beanName); } } } protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException { Assert.notNull(urlPath, "URL path must not be null"); Assert.notNull(handler, "Handler object must not be null"); Object resolvedHandler = handler; // Eagerly resolve handler if referencing singleton via name. if (!this.lazyInitHandlers && handler instanceof String) { String handlerName = (String) handler; ApplicationContext applicationContext = obtainApplicationContext(); if (applicationContext.isSingleton(handlerName)) { resolvedHandler = applicationContext.getBean(handlerName); } } //已存在且指向不同的Handler拋異常 Object mappedHandler = this.handlerMap.get(urlPath); if (mappedHandler != null) { if (mappedHandler != resolvedHandler) { throw new IllegalStateException( "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath + "]: There is already " + getHandlerDescription(mappedHandler) + " mapped."); } } else { if (urlPath.equals("/")) { if (logger.isTraceEnabled()) { logger.trace("Root mapping to " + getHandlerDescription(handler)); } setRootHandler(resolvedHandler);//設置根處理器,即請求/的時候 } else if (urlPath.equals("/*")) { if (logger.isTraceEnabled()) { logger.trace("Default mapping to " + getHandlerDescription(handler)); } setDefaultHandler(resolvedHandler);//默認處理器 } else { this.handlerMap.put(urlPath, resolvedHandler);//注冊進map if (logger.isTraceEnabled()) { logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler)); } } } }
處理器bean的id/name為一個url請求路徑,前面有"/"; 如果多個url映射同一個處理器bean,那么就需要定義多個bean,導致容器創(chuàng)建多個處理器實例,占用內(nèi)存空間; 處理器bean定義與url請求耦合在一起。
SimpleUrlHandlerMapping
間接實現(xiàn)了org.springframework.web.servlet.HandlerMapping接口,直接實現(xiàn)該接口的是org.springframework.web.servlet.handler.AbstractHandlerMapping抽象類,映射Url與請求handler bean。支持映射bean實例和映射bean名稱
public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping { // 存儲url和bean映射 private final Map<String, Object> urlMap = new LinkedHashMap<>(); // 注入property的name為mappings映射 public void setMappings(Properties mappings) { CollectionUtils.mergePropertiesIntoMap(mappings, this.urlMap); } // 注入property的name為urlMap映射 public void setUrlMap(Map<String, ?> urlMap) { this.urlMap.putAll(urlMap); } public Map<String, ?> getUrlMap() { return this.urlMap; } // 實例化本類實例入口 @Override public void initApplicationContext() throws BeansException { // 調(diào)用父類AbstractHandlerMapping的initApplicationContext方法,只要完成攔截器的注冊 super.initApplicationContext(); // 處理url和bean name,具體注冊調(diào)用父類完成 registerHandlers(this.urlMap); } // 注冊映射關系,及將property中的值解析到map對象中,key為url,value為bean id或name protected void registerHandlers(Map<String, Object> urlMap) throws BeansException { if (urlMap.isEmpty()) { logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping"); } else { urlMap.forEach((url, handler) -> { // 增加以"/"開頭 if (!url.startsWith("/")) { url = "/" + url; } // 去除handler bean名稱的空格 if (handler instanceof String) { handler = ((String) handler).trim(); } // 調(diào)用父類AbstractUrlHandlerMapping完成映射 registerHandler(url, handler); }); } } }
- SimpleUrlHandlerMapping類主要接收用戶設定的url與handler的映射關系,其實際的工作都是交由其父類來完成的
- AbstractHandlerMapping 在創(chuàng)建初始化SimpleUrlHandlerMapping類時,調(diào)用其父類的initApplicationContext()方法,該方法完成攔截器的初始化
- AbstractUrlHandlerMapping
- 在創(chuàng)建初始化SimpleUrlHandlerMapping類時,調(diào)用AbstractUrlHandlerMapping類的registerHandler(urlPath,handler)方法
總結(jié)
到此這篇關于springmvc組件中的HandlerMapping解析的文章就介紹到這了,更多相關springmvc組件HandlerMapping內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java實現(xiàn)BP神經(jīng)網(wǎng)絡MNIST手寫數(shù)字識別的示例詳解
這篇文章主要為大家詳細介紹了Java實現(xiàn)BP神經(jīng)網(wǎng)絡MNIST手寫數(shù)字識別的相關方法,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起了解一下2023-01-01java編程實現(xiàn)求質(zhì)數(shù)與因式分解代碼分享
這篇文章主要介紹了Java編程實現(xiàn)求質(zhì)數(shù)與因式分解代碼分享,對二者的概念作了簡單介紹(多此一舉,哈哈),都是小學數(shù)學老師的任務,然后分享了求解質(zhì)數(shù)和因式分解的Java代碼,具有一定借鑒價值,需要的朋友可以參考下。2017-12-12簡單捋捋@RequestParam 和 @RequestBody的使用
這篇文章主要介紹了簡單捋捋@RequestParam 和 @RequestBody的使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-12-12java返回前端實體類json數(shù)據(jù)時忽略某個屬性方法
這篇文章主要給大家介紹了關于java返回前端實體類json數(shù)據(jù)時忽略某個屬性的相關資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下2023-08-08理解maven命令package、install、deploy的聯(lián)系與區(qū)別
這篇文章主要介紹了理解maven命令package、install、deploy的聯(lián)系與區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-07-07