欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解SpringMVC組件之HandlerMapping(二)

 更新時(shí)間:2023年08月31日 11:02:10   作者:姠惢荇者  
這篇文章主要介紹了詳解SpringMVC組件之HandlerMapping(二),HandlerMapping組件是Spring?MVC核心組件,用來根據(jù)請(qǐng)求的request查找對(duì)應(yīng)的Handler,在Spring?MVC中,有各式各樣的Web請(qǐng)求,每個(gè)請(qǐng)求都需要一個(gè)對(duì)應(yīng)的Handler來處理,需要的朋友可以參考下

1、前言

在前面《詳解SpringMVC組件之HandlerMapping(一)》中,我們分析了HandlerMapping組件的整體邏輯及其AbstractUrlHandlerMapping系列的實(shí)現(xiàn)方式。

這一節(jié),我們將分析AbstractHandlerMethodMapping系列的實(shí)現(xiàn)方式。

2、AbstractHandlerMethodMapping體系

在AbstractHandlerMethodMapping體系中,只有三個(gè)類,分別是

  1. AbstractHandlerMethodMapping
  2. RequestMappingInfoHandlerMapping
  3. RequestMappingHandlerMapping

這三個(gè)類依次繼承于前面的類,而最前面的AbstractHandlerMethodMapping抽象類則繼承于AbstractHandlerMapping抽象類,并實(shí)現(xiàn)了InitializingBean接口,實(shí)現(xiàn)了InitializingBean接口后,就會(huì)在Bean實(shí)例化后調(diào)用afterPropertiesSet()方法。

在AbstractHandlerMethodMapping體系中,類的層級(jí)結(jié)構(gòu)比較簡單明確,但是Spring為了保證AbstractHandlerMethodMapping體系的靈活性,邏輯還是比較復(fù)雜的。

在分析AbstractHandlerMethodMapping類之前,我們先認(rèn)識(shí)一下其中的幾個(gè)核心的基礎(chǔ)類。

  • HandlerMethod
    • 一個(gè)基于方法的處理器,包括了該處理器對(duì)應(yīng)的方法和實(shí)例Bean,并提供了一些訪問方法參數(shù)、方法返回值、方法注解等方法。
  • RequestMappingInfo
    • 表示請(qǐng)求信息,并封裝了映射關(guān)系的匹配條件。使用@RequestMapping注解時(shí),配置的信息最后都設(shè)置到了RequestMappingInfo中,@RequestMapping注解的不同屬性,會(huì)映射到對(duì)應(yīng)的XXXRequestCondition上。
  • AbstractHandlerMethodMapping-內(nèi)部類Match
    • 封裝了HandlerMethod和泛型類T。泛型類T實(shí)際上表示了RequestMappingInfo。
  • 內(nèi)部類MappingRegistration
    • 記錄映射關(guān)系注冊(cè)時(shí)的信息。封裝了HandlerMethod、泛型類T、mappingName、directUrls屬性(保存url和RequestMappingInfo對(duì)應(yīng)關(guān)系,多個(gè)url可能對(duì)應(yīng)著同一個(gè)mappingInfo)
  • 內(nèi)部類MappingRegistry
    • 主要維護(hù)幾個(gè)Map,用來存儲(chǔ)映射的信息。下面詳細(xì)介紹該內(nèi)部類及其定義的映射關(guān)系

3、AbstractHandlerMethodMapping抽象類

在AbstractHandlerMethodMapping抽象類中定義了很多個(gè)內(nèi)部類,前面提到的Match、MappingRegistration、MappingRegistry均是在該類中,其中,又以MappingRegistry最重要,下面我們首先分析這個(gè)內(nèi)部類,因?yàn)槠渲猩婕暗降膸讉€(gè)Map屬性,是維護(hù)request與處理器Handler間關(guān)系的核心所在。

3.1、MappingRegistry內(nèi)部類

MappingRegistry內(nèi)部類主要維護(hù)了T(RequestMappingInfo)、mappingName、HandlerMethod、CorsConfiguration等對(duì)象間的映射關(guān)系,同時(shí)提供了一個(gè)讀寫鎖readWriteLock對(duì)象。

1、屬性

//維護(hù)mapping與MappingRegistration注冊(cè)信息的關(guān)系
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
//維護(hù)mapping與HandlerMethod的關(guān)系
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
//保存著URL與匹配條件(mapping)的對(duì)應(yīng)關(guān)系,key是那些不含通配符的URL,value對(duì)應(yīng)的是一個(gè)list類型的值。
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
//保存著name和HandlerMethod的對(duì)應(yīng)關(guān)系(一個(gè)name可以有多個(gè)HandlerMethod)
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
//保存HandlerMethod與跨域配置的關(guān)系
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
//讀寫鎖
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

2、register()方法、unregister()方法 在register()方法、unregister()方法中主要是用來注冊(cè)T(RequestMappingInfo)、mappingName、HandlerMethod、CorsConfiguration等對(duì)象間的映射關(guān)系。

其中,register()方法代碼如下:

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 {
		//創(chuàng)建一個(gè)HandlerMethod對(duì)象(構(gòu)造函數(shù)創(chuàng)建對(duì)象)
		HandlerMethod handlerMethod = createHandlerMethod(handler, method);
		//驗(yàn)證handlerMethod和mapping的對(duì)應(yīng)關(guān)系,如果已經(jīng)存在一個(gè)HandlerMethod對(duì)象且與handlerMethod不一樣,就會(huì)拋出異常“Ambiguous mapping. Cannot map”
		validateMethodMapping(handlerMethod, mapping);
		//維護(hù)mappingLookup對(duì)象,建立mapping和handlerMethod的映射關(guān)系
		this.mappingLookup.put(mapping, handlerMethod);
		//獲取匹配條件mapping,對(duì)應(yīng)的URL(那些不含通配符的URL),且URL是由子類實(shí)現(xiàn)getMappingPathPatterns()方法提供的,實(shí)際上還是由PatternsRequestCondition提供的,那個(gè)該directUrls 是什么時(shí)候初始化的呢?我們后續(xù)在分析。
		List<String> directUrls = getDirectUrls(mapping);
		for (String url : directUrls) {
			this.urlLookup.add(url, mapping);
		}
		String name = null;
		//獲取mappingName,并維護(hù)與handlerMethod的關(guān)系
		if (getNamingStrategy() != null) {
			//namingStrategy屬性對(duì)應(yīng)的是RequestMappingInfoHandlerMethodMappingNamingStrategy對(duì)象(是在子類RequestMappingInfoHandlerMapping構(gòu)造函數(shù)中進(jìn)行了設(shè)置),命名規(guī)則:類名中全部大小字符+“#”+方法名
			name = getNamingStrategy().getName(handlerMethod, mapping);
			addMappingName(name, handlerMethod);
		}
		//獲取跨域配置CorsConfiguration對(duì)象,維護(hù)與handlerMethod的關(guān)系。initCorsConfiguration()定了空方法,在RequestMappingHandlerMapping類中進(jìn)行了重寫。
		CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
		if (corsConfig != null) {
			this.corsLookup.put(handlerMethod, corsConfig);
		}
		//維護(hù)mapping與MappingRegistration的關(guān)系
		this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
	}
	finally {
		this.readWriteLock.writeLock().unlock();
	}
}

而unregister()方法主要是移除mapping匹配條件與其他對(duì)象的映射關(guān)系,這里不在貼出代碼了。

3.2、初始化

在前面我們提到了AbstractHandlerMethodMapping實(shí)現(xiàn)了InitializingBean接口,所以就會(huì)在Bean實(shí)例化后調(diào)用afterPropertiesSet()方法。

其實(shí),AbstractHandlerMethodMapping類的初始化工作也就是從這個(gè)地方開始的。

代碼如下:

@Override
public void afterPropertiesSet() {
	initHandlerMethods();
}
protected void initHandlerMethods() {
	//獲取候選的bean實(shí)例
	for (String beanName : getCandidateBeanNames()) {
		if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
			//遍歷后續(xù)的bean實(shí)例,并分別進(jìn)行處理
			processCandidateBean(beanName);
		}
	}
	//只是打印了日志,沒有實(shí)際的處理邏輯
	handlerMethodsInitialized(getHandlerMethods());
}

在afterPropertiesSet()方法中,調(diào)用了initHandlerMethods()方法,實(shí)際初始化邏輯就是在該方法中實(shí)現(xiàn)的。首先通過getCandidateBeanNames()方法獲取所有候選的Bean實(shí)例的name,代碼如下:

protected String[] getCandidateBeanNames() {
	//detectHandlerMethodsInAncestorContexts 表示是否從祖先容器中查找bean實(shí)例
	return (this.detectHandlerMethodsInAncestorContexts ?
			BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
			obtainApplicationContext().getBeanNamesForType(Object.class));
}

然后,遍歷通過getCandidateBeanNames()方法獲取的Bean實(shí)例,調(diào)用processCandidateBean()方法進(jìn)行處理,代碼如下:

protected void processCandidateBean(String beanName) {
	Class<?> beanType = null;
	try {
		//獲取對(duì)應(yīng)的Class類型
		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);
		}
	}
	//判斷beanType是不是處理器類型,通過isHandler()方法判斷,該方法在RequestMappingHandlerMapping類中實(shí)現(xiàn),根據(jù)是否有Controller或RequestMapping注解進(jìn)行判斷
	if (beanType != null && isHandler(beanType)) {
		//獲取指定Bean實(shí)例中對(duì)應(yīng)的處理方法,并通過mappingRegistry注冊(cè)到對(duì)應(yīng)的Map關(guān)系映射中
		detectHandlerMethods(beanName);
	}
}

在processCandidateBean()方法中,判斷beanName對(duì)應(yīng)的Bean實(shí)例是否是處理器類型(isHandler方法判斷),如果是的話,則調(diào)用detectHandlerMethods()方法,獲取該實(shí)例中對(duì)應(yīng)的處理方法,并通過mappingRegistry注冊(cè)到對(duì)應(yīng)的Map關(guān)系映射中,代碼如下:

protected void detectHandlerMethods(Object handler) {
	//獲取處理器對(duì)應(yīng)的Class
	Class<?> handlerType = (handler instanceof String ?
			obtainApplicationContext().getType((String) handler) : handler.getClass());
	if (handlerType != null) {
		//獲取用戶定義的本來的類型,大部分情況下就是類型本身,主要針對(duì)cglib做了額外的判斷,獲取cglib代理的父類;
		Class<?> userType = ClassUtils.getUserClass(handlerType);
		//查找給定類型userType中的指定方法,具體判斷條件由getMappingForMethod()方法來決定
		Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
				(MethodIntrospector.MetadataLookup<T>) method -> {
					try {
						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));
		}
		//把篩選出來的方法,通過registerHandlerMethod()注冊(cè)到映射關(guān)系中
		methods.forEach((method, mapping) -> {
			Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
			registerHandlerMethod(handler, invocableMethod, mapping);
		});
	}
}

自此,初始化工作就完成了,即注冊(cè)了mapping匹配條件與HandlerMethod的映射關(guān)系。

3.3、getHandlerInternal()方法

在前面分析了AbstractHandlerMethodMapping類的初始化方法,現(xiàn)在我們開始分析該類如何實(shí)現(xiàn)父類的getHandlerInternal()方法,即如何通過request獲取對(duì)應(yīng)的處理器HandlerMethod對(duì)象。

@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
	//獲取lookupPath
	String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
	//設(shè)置“org.springframework.web.servlet。HandlerMapping.lookupPath”參數(shù)
	request.setAttribute(LOOKUP_PATH, lookupPath);
	//獲取讀鎖
	this.mappingRegistry.acquireReadLock();
	try {
		//獲取request對(duì)應(yīng)的處理器
		HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
		//如果處理器對(duì)應(yīng)的是bean name,則調(diào)用createWithResolvedBean()方法,創(chuàng)建對(duì)應(yīng)的Bean實(shí)例
		return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
	}
	finally {
		this.mappingRegistry.releaseReadLock();
	}
}

在getHandlerInternal()方法中,我們知道lookupHandlerMethod()方法是真正實(shí)現(xiàn)查找處理器的方法,代碼如下:

@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
	List<Match> matches = new ArrayList<>();
	//從MappingRegistry.urlLookup屬性中,獲取lookupPath對(duì)應(yīng)的mapping集合
	List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
	if (directPathMatches != null) {
		//獲取匹配的mapping,添加到matches變種
		addMatchingMappings(directPathMatches, matches, request);
	}
	if (matches.isEmpty()) {
		// 如果沒有匹配lookupPath的實(shí)例,則遍歷所有的mapping,查找符合條件的mapping
		addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
	}
	if (!matches.isEmpty()) {//說明存在符合條件的mapping,可能是多個(gè)
		//獲取匹配條件的排序器,由抽象方法getMappingComparator()方法獲取,該方法由子類實(shí)現(xiàn)
		Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
		//排序
		matches.sort(comparator);
		Match bestMatch = matches.get(0);
		if (matches.size() > 1) {//不止一個(gè)匹配時(shí)
			if (logger.isTraceEnabled()) {
				logger.trace(matches.size() + " matching mappings: " + matches);
			}
			if (CorsUtils.isPreFlightRequest(request)) {//OPTIONS請(qǐng)求時(shí),直接處理
				return PREFLIGHT_AMBIGUOUS_MATCH;
			}
			Match secondBestMatch = matches.get(1);
			//如果存在多個(gè)處理器,則直接拋出異常
			if (comparator.compare(bestMatch, secondBestMatch) == 0) {
				Method m1 = bestMatch.handlerMethod.getMethod();
				Method m2 = secondBestMatch.handlerMethod.getMethod();
				String uri = request.getRequestURI();
				throw new IllegalStateException(
						"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
			}
		}
		//定義“.bestMatchingHandler”屬性
		request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
		//處理匹配的處理器,這里只是添加了一個(gè)".pathWithinHandlerMapping"屬性,具體實(shí)現(xiàn)在子類中進(jìn)行。
		handleMatch(bestMatch.mapping, lookupPath, request);
		return bestMatch.handlerMethod;
	}
	else {//不存在匹配的bean時(shí),調(diào)用handleNoMatch()方法,空方法,有子類進(jìn)行實(shí)現(xiàn)。
		return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
	}
}

4、RequestMappingInfoHandlerMapping抽象類

RequestMappingInfoHandlerMapping抽象類繼承自AbstractHandlerMethodMapping類,并明確了其中的泛型類為RequestMappingInfo。

在RequestMappingInfoHandlerMapping抽象類中做了以下幾件事:

1、定義了Options的默認(rèn)處置方法,其中涉及到了HTTP_OPTIONS_HANDLE_METHOD常量、內(nèi)部類HttpOptionsHandler和靜態(tài)代碼塊初始化常量。

2、構(gòu)造函數(shù),在構(gòu)造函數(shù)中設(shè)置了映射的命名策略,即RequestMappingInfoHandlerMethodMappingNamingStrategy實(shí)現(xiàn)的方式。

3、實(shí)現(xiàn)了父類中的幾個(gè)方法,如下所示:

//獲取RequestMappingInfo 對(duì)應(yīng)的URL集合
@Override
protected Set<String> getMappingPathPatterns(RequestMappingInfo info) {
	return info.getPatternsCondition().getPatterns();
}
//判斷當(dāng)前的RequestMappingInfo與request是否匹配,實(shí)際上是由RequestMappingInfo的getMatchingCondition()方法實(shí)現(xiàn)判斷,并返回一個(gè)新建的RequestMappingInfo 實(shí)例
@Override
protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
	return info.getMatchingCondition(request);
}
//獲取比較器
@Override
protected Comparator<RequestMappingInfo> getMappingComparator(final HttpServletRequest request) {
	return (info1, info2) -> info1.compareTo(info2, request);
}

4、重新父類的handleMatch()方法,在該方法中主要添加了處理變量(模板變量和矩陣變量(MatrixVariable))和媒體類型的邏輯。后續(xù)再詳細(xì)分析參數(shù)處理的過程。

5、重寫父類的handleNoMatch()方法,該方法中使用了內(nèi)部類PartialMatchHelper來判斷不匹配的原因,然后拋出指定的異常。不在貼出代碼。

5、RequestMappingHandlerMapping類

RequestMappingHandlerMapping類除了繼承了RequestMappingInfoHandlerMapping之外,還實(shí)現(xiàn)了MatchableHandlerMapping和EmbeddedValueResolverAware兩個(gè)接口。

其中,實(shí)現(xiàn)MatchableHandlerMapping接口的方法,暫時(shí)未使用;而實(shí)現(xiàn)了EmbeddedValueResolverAware接口,說明要支持解析String字符串。

定義的屬性:

//是否啟用后綴匹配
private boolean useSuffixPatternMatch = true;
//后綴模式匹配是否應(yīng)該只對(duì)顯式地在ContentNegotiationManager中注冊(cè)的路徑擴(kuò)展有效。
private boolean useRegisteredSuffixPatternMatch = false;
//尾部斜杠匹配
private boolean useTrailingSlashMatch = true;
//根據(jù)條件設(shè)置前綴path
private Map<String, Predicate<Class<?>>> pathPrefixes = new LinkedHashMap<>();
//多媒體類型判斷
private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
//字符串解析器,處理spring表達(dá)式
@Nullable
private StringValueResolver embeddedValueResolver;
//RequestMappingInfo構(gòu)建配置
private RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();

afterPropertiesSet()方法

在前面,我們知道afterPropertiesSet()方法是實(shí)現(xiàn)初始化的方法。在AbstractHandlerMethodMapping抽象類中,實(shí)現(xiàn)了handler類和方法的檢測和注冊(cè)。在RequestMappingHandlerMapping類中,又增加了RequestMappingInfo構(gòu)建配置的初始化,代碼如下:

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

isHandler()方法

在AbstractHandlerMethodMapping類中,進(jìn)行初始化的時(shí)候,在processCandidateBean()方法中使用了isHandler判斷當(dāng)前bean實(shí)例是否是處理器。實(shí)際判斷邏輯在這里實(shí)現(xiàn)的。

@Override
protected boolean isHandler(Class<?> beanType) {
	return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
			AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

getMappingForMethod方法

在AbstractHandlerMethodMapping類中,進(jìn)行初始化的時(shí)候,在detectHandlerMethods()方法中,調(diào)用該方法實(shí)現(xiàn)處理器方法的判斷。

@Override
@Nullable
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).build().combine(info);
		}
	}
	return info;
}
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
	RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
	RequestCondition<?> condition = (element instanceof Class ?
			getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
	return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
protected RequestMappingInfo createRequestMappingInfo(
		RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
	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();
}

其他方法 在registerMapping()、registerHandlerMethod()方法這兩個(gè)方法,除了調(diào)用父類的方法之外, 主要是設(shè)置了ConsumesRequestCondition的判斷條件。

后續(xù)還有配置跨域相關(guān)參數(shù),這里不在詳細(xì)分析了。

6、總結(jié)

在這篇文章中,我們只是分析了AbstractHandlerMethodMapping體系實(shí)現(xiàn)的HandlerMapping中這三個(gè)類的基本實(shí)現(xiàn),其中涉及到的RequestCondition及RequestMappingInfo(也是RequestCondition的子類)還有HandlerMethod等類的具體介紹,我們?cè)诤罄m(xù)過程中在逐漸的學(xué)習(xí)記錄。

到此這篇關(guān)于詳解SpringMVC組件之HandlerMapping(二)的文章就介紹到這了,更多相關(guān)SpringMVC的HandlerMapping內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java面向?qū)ο笤O(shè)計(jì)原則之單一職責(zé)與依賴倒置原則詳解

    java面向?qū)ο笤O(shè)計(jì)原則之單一職責(zé)與依賴倒置原則詳解

    這篇文章主要介紹了java面向?qū)ο笤O(shè)計(jì)原則之單一職責(zé)與依賴倒置原則的分析詳解,有需要的朋友可以借鑒參考下,希望可以有所幫助,祝大家多多進(jìn)步早日升職加薪
    2021-10-10
  • java application maven項(xiàng)目打自定義zip包實(shí)例(推薦)

    java application maven項(xiàng)目打自定義zip包實(shí)例(推薦)

    下面小編就為大家?guī)硪黄猨ava application maven項(xiàng)目打自定義zip包實(shí)例(推薦)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-05-05
  • Spring Bean的定義概念和使用

    Spring Bean的定義概念和使用

    這篇文章主要介紹了Spring Bean的定義概念和使用,Spring bean對(duì)象是構(gòu)成應(yīng)用程序的支柱,也是由Spring IoC容器管理的。bean是一個(gè)被實(shí)例化,組裝,并通過Spring IoC容器所管理的對(duì)象。這些bean是由用容器提供的配置元數(shù)據(jù)創(chuàng)建的
    2023-04-04
  • IDEA添加Java類注釋模版的方法

    IDEA添加Java類注釋模版的方法

    本篇文章主要介紹了IDEA添加Java類注釋模版的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-12-12
  • Mybatis之如何攔截慢SQL日志記錄

    Mybatis之如何攔截慢SQL日志記錄

    這篇文章主要介紹了Mybatis之如何攔截慢SQL日志記錄問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • SSH框架網(wǎng)上商城項(xiàng)目第17戰(zhàn)之購物車基本功能

    SSH框架網(wǎng)上商城項(xiàng)目第17戰(zhàn)之購物車基本功能

    這篇文章主要為大家詳細(xì)介紹了SSH框架網(wǎng)上商城項(xiàng)目第17戰(zhàn)之購物車基本功能的實(shí)現(xiàn)過程,感興趣的小伙伴們可以參考一下
    2016-06-06
  • 詳解Spring Boot工程集成全局唯一ID生成器 UidGenerator的操作步驟

    詳解Spring Boot工程集成全局唯一ID生成器 UidGenerator的操作步驟

    本文就在項(xiàng)目中來集成 UidGenerator這一工程來作為項(xiàng)目的全局唯一 ID生成器。接下來通過實(shí)例代碼給大家詳解詳解Spring Boot工程集成全局唯一ID生成器 UidGenerator的操作步驟,感興趣的朋友一起看看吧
    2018-10-10
  • Java實(shí)現(xiàn)基于UDP協(xié)議的網(wǎng)絡(luò)通信UDP編程

    Java實(shí)現(xiàn)基于UDP協(xié)議的網(wǎng)絡(luò)通信UDP編程

    在Java中使用UDP編程,仍然需要使用Socket,因?yàn)閼?yīng)用程序在使用UDP時(shí)必須指定網(wǎng)絡(luò)接口(IP地址)和端口號(hào)。注意:UDP端口和TCP端口雖然都使用0~65535,但他們是兩套獨(dú)立的端口,即一個(gè)應(yīng)用程序用TCP占用了端口1234,不影響另一個(gè)應(yīng)用程序用UDP占用端口1234
    2023-04-04
  • Java數(shù)據(jù)結(jié)構(gòu)之紅黑樹的實(shí)現(xiàn)方法和原理詳解

    Java數(shù)據(jù)結(jié)構(gòu)之紅黑樹的實(shí)現(xiàn)方法和原理詳解

    這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)之紅黑樹的實(shí)現(xiàn)方法和原理,紅黑樹是一種特殊的二叉查找樹,每個(gè)結(jié)點(diǎn)都要儲(chǔ)存位表示結(jié)點(diǎn)的顏色,或紅或黑,本文將通過示例為大家詳細(xì)講講紅黑樹的原理及實(shí)現(xiàn),感興趣的朋友可以了解一下
    2024-02-02
  • Spring Boot應(yīng)用事件監(jiān)聽示例詳解

    Spring Boot應(yīng)用事件監(jiān)聽示例詳解

    這篇文章主要給大家介紹了關(guān)于Spring Boot應(yīng)用事件監(jiān)聽的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-12-12

最新評(píng)論