SpringMVC 원본 해석의 HandlerMapping - AbstractDetectingUrlHandlerMapping 시리즈 초기화

AbstractDetectingUrlHandlerMapping은 스캔을 통해 Handler를 등록하고 요청을 받을 때 AbstractUrlHandlerMapping의 getHandlerInternal에서 배포합니다.
모두 다섯 개의 자류, 하나의 추상류가 있다.
Simple UrlHandler Mapping과 유사하게 init Application Context를 덮어쓰고 detectHandlers를 호출하여 초기화합니다.
detectHandlers는 BeanFactoryUtils를 통해 응용된 Object를 스캔한 다음determineUrlsForHandler를 미리 남겨 하위 클래스에 Handler에 따라 대응하는 URL을 생성합니다.
등록하여 사용하는 registerHandler는 여전히 AbstractUrlHandlerMapping에서 제공합니다.

// AbstractDetectingUrlHandlerMapping
/**
* Calls the {@link #detectHandlers()} method in addition to the
* superclass's initialization.
*/
@Override
public void initApplicationContext() throws ApplicationContextException {
super.initApplicationContext();
detectHandlers();
}
여기와 같이 AbstractHandlerMapping을 호출하는 init ApplicationContext 초기화 차단기입니다.
주인공 등장, detectHandlers, 스캔Handlers

// AbstractDetectingUrlHandlerMapping
/**
* Register all handlers found in the current ApplicationContext.
* <p>The actual URL determination for a handler is up to the concrete
* {@link #determineUrlsForHandler(String)} implementation. A bean for
* which no such URLs could be determined is simply not considered a handler.
* @throws org.springframework.beans.BeansException if the handler couldn't be registered
* @see #determineUrlsForHandler(String)
*/
protected void detectHandlers() throws BeansException {
if (logger.isDebugEnabled()) {
logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
}
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
// Take any bean name that we can determine URLs for.
for (String beanName : beanNames) {
String[] urls = determineUrlsForHandler(beanName);
if (!ObjectUtils.isEmpty(urls)) {
// URL paths found: Let's consider it a handler.
registerHandler(urls, beanName);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
}
}
}
}
여기에 미리 설정된 템플릿 방법은 다음과 같이 정의됩니다.

/**
* Determine the URLs for the given handler bean.
* @param beanName the name of the candidate bean
* @return the URLs determined for the bean,
* or {@code null} or an empty array if none
*/
protected abstract String[] determineUrlsForHandler(String beanName); 

 BeanNameUrlHandlerMapping AbstractControllerUrlHandlerMapping .

BeanNameUrlHandlerMapping , determineUrlsForHandler.

 alias beanName .

// BeanNameUrlHandlerMapping
/**
* Checks name and aliases of the given bean for URLs, starting with "/".
*/
@Override
protected String[] determineUrlsForHandler(String beanName) {
List<String> urls = new ArrayList<String>();
if (beanName.startsWith("/")) {
urls.add(beanName);
}
String[] aliases = getApplicationContext().getAliases(beanName);
for (String alias : aliases) {
if (alias.startsWith("/")) {
urls.add(alias);
}
}
return StringUtils.toStringArray(urls);
}
Abstract Controller UrlHandler Mapping의 실현을 다시 한 번 보도록 하겠습니다.
isEligibleForMapping은 controller가 제외되었는지 판단합니다(패키지 패키지 배제 또는 클래스 클래스 배제).
buildUrlsForHandler는 하위 클래스에서 구체적인 URL 생성 규칙을 실현한다
isControllerType에서 Controller의 하위 클래스 여부를 판단합니다.
buildUrlsForHandler가 하위 클래스 생산 URL에 미리 남겨 놓은 템플릿 방법입니다.

// AbstractControllerUrlHandlerMapping
/**
* This implementation delegates to {@link #buildUrlsForHandler},
* provided that {@link #isEligibleForMapping} returns {@code true}.
*/
@Override
protected String[] determineUrlsForHandler(String beanName) {
Class beanClass = getApplicationContext().getType(beanName);
if (isEligibleForMapping(beanName, beanClass)) {
return buildUrlsForHandler(beanName, beanClass);
}
else {
return null;
}
} 
// AbstractControllerUrlHandlerMapping
/** controller ( package class ).
* Determine whether the specified controller is excluded from this mapping.
* @param beanName the name of the controller bean
* @param beanClass the concrete class of the controller bean
* @return whether the specified class is excluded
* @see #setExcludedPackages
* @see #setExcludedClasses
*/
protected boolean isEligibleForMapping(String beanName, Class beanClass) {
if (beanClass == null) {
if (logger.isDebugEnabled()) {
logger.debug("Excluding controller bean '" + beanName + "' from class name mapping " +
"because its bean type could not be determined");
}
return false;
}
if (this.excludedClasses.contains(beanClass)) {
if (logger.isDebugEnabled()) {
logger.debug("Excluding controller bean '" + beanName + "' from class name mapping " +
"because its bean class is explicitly excluded: " + beanClass.getName());
}
return false;
}
String beanClassName = beanClass.getName();
for (String packageName : this.excludedPackages) {
if (beanClassName.startsWith(packageName)) {
if (logger.isDebugEnabled()) {
logger.debug("Excluding controller bean '" + beanName + "' from class name mapping " +
"because its bean class is defined in an excluded package: " + beanClass.getName());
}
return false;
}
}
return isControllerType(beanClass);
} 
// AbstractControllerUrlHandlerMapping
/**
* Determine whether the given bean class indicates a controller type
* that is supported by this mapping strategy.
* @param beanClass the class to introspect
*/
protected boolean isControllerType(Class beanClass) {
return this.predicate.isControllerType(beanClass);
} 
// ControllerTypePredicate

 2 api, Controller MultiActionController .

/**
* Internal helper class that identifies controller types.
*
* @author Juergen Hoeller
* @since ..
*/
class ControllerTypePredicate {
public boolean isControllerType(Class beanClass) {
return Controller.class.isAssignableFrom(beanClass);
}
public boolean isMultiActionControllerType(Class beanClass) {
return MultiActionController.class.isAssignableFrom(beanClass);
}
}
URL을 생성하는 템플릿을 미리 남기는 방법

// AbstractControllerUrlHandlerMapping
/**
* Abstract template method to be implemented by subclasses.
* @param beanName the name of the bean
* @param beanClass the type of the bean
* @return the URLs determined for the bean
*/
protected abstract String[] buildUrlsForHandler(String beanName, Class beanClass); 
Abstract Controller UrlHandler Mapping의 두 가지 실현 Controller BeanName UrlHandler Mapping과 Controller ClassName UrlHandler Mapping을 살펴보겠습니다.
사실 이 두 가지는 간단합니다. 하나는 beanName에 따라 URL을 생산하고, 하나는className에 따라 URL을 생산합니다.

// ControllerBeanNameUrlHandlerMapping
@Override
protected String[] buildUrlsForHandler(String beanName, Class beanClass) {
List<String> urls = new ArrayList<String>();
urls.add(generatePathMapping(beanName));
String[] aliases = getApplicationContext().getAliases(beanName);//  
for (String alias : aliases) {
urls.add(generatePathMapping(alias));
}
return StringUtils.toStringArray(urls);
} 
// ControllerBeanNameUrlHandlerMapping
/** path , /
* Prepends a '/' if required and appends the URL suffix to the name.
*/
protected String generatePathMapping(String beanName) {
String name = (beanName.startsWith("/") ? beanName : "/" + beanName);
StringBuilder path = new StringBuilder();
if (!name.startsWith(this.urlPrefix)) {
path.append(this.urlPrefix);
}
path.append(name);
if (!name.endsWith(this.urlSuffix)) {
path.append(this.urlSuffix);
}
return path.toString();
} 
// ControllerClassNameUrlHandlerMapping
generate PathMappings에 직접 의뢰하여 실현

@Override
protected String[] buildUrlsForHandler(String beanName, Class beanClass) {
return generatePathMappings(beanClass);
} 
// ControllerClassNameUrlHandlerMapping
buildPathPrefix를 통해 path 접두사 가져오기
ClassUtils를 통해 BookController와 같은 className을 가져오고 cglib 에이전트를 사용하는 문제를 함께 해결합니다.
대소문자가 민감한지 여부에 따라className (기본caseSensitive =false;)
isMultiActionController Type은 Controller가 MultiActionController의 하위 클래스인지 판단합니다. 바로 controller가 여러 handler를 포함하는지 여부입니다.

/**
* Generate the actual URL paths for the given controller class.
* <p>Subclasses may choose to customize the paths that are generated
* by overriding this method.
* @param beanClass the controller bean class to generate a mapping for
* @return the URL path mappings for the given controller
*/
protected String[] generatePathMappings(Class beanClass) {
StringBuilder pathMapping = buildPathPrefix(beanClass);
String className = ClassUtils.getShortName(beanClass);
String path = (className.endsWith(CONTROLLER_SUFFIX) ?
className.substring(, className.lastIndexOf(CONTROLLER_SUFFIX)) : className);
if (path.length() > ) {
if (this.caseSensitive) {
pathMapping.append(path.substring(, ).toLowerCase()).append(path.substring());
}
else {
pathMapping.append(path.toLowerCase());
}
}
if (isMultiActionControllerType(beanClass)) {
return new String[] {pathMapping.toString(), pathMapping.toString() + "/*"};
}
else {
return new String[] {pathMapping.toString() + "*"};
}
} 
// ControllerClassNameUrlHandlerMapping
/**
* Build a path prefix for the given controller bean class.
* @param beanClass the controller bean class to generate a mapping for
* @return the path prefix, potentially including subpackage names as path elements
*/
private StringBuilder buildPathPrefix(Class beanClass) {
StringBuilder pathMapping = new StringBuilder();
if (this.pathPrefix != null) {
pathMapping.append(this.pathPrefix);
pathMapping.append("/");
}
else {
pathMapping.append("/");
}
if (this.basePackage != null) {
String packageName = ClassUtils.getPackageName(beanClass);
if (packageName.startsWith(this.basePackage)) {
String subPackage = packageName.substring(this.basePackage.length()).replace('.', '/');
pathMapping.append(this.caseSensitive ? subPackage : subPackage.toLowerCase());
pathMapping.append("/");
}
}
return pathMapping;
} 
// AbstractControllerUrlHandlerMapping
predicate.isMultiActionControllerType 구현 위의 ControllerTypePredicate 보기

/**
* Determine whether the given bean class indicates a controller type
* that dispatches to multiple action methods.
* @param beanClass the class to introspect
*/
protected boolean isMultiActionControllerType(Class beanClass) {
return this.predicate.isMultiActionControllerType(beanClass);
}
위에서 설명한 내용은 Spring MVC 원본 해석의 Handler Mapping - Abstract Detecting Url Handler Mapping 시리즈 초기화에 관한 지식입니다. 도움이 되시기 바랍니다!

좋은 웹페이지 즐겨찾기