Spring WebMVC初始化Controller流程详解
Spring WebMVC初始化Controller流程
此篇文章开始之前先向大家介绍一个接口 InitializingBean
这个接口的作用如果了解spring生命周期的应该知道 ,这个接口的作用就是在bean初始化之后会执行的init方法
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
当然能实现这种方式的方法spring还介绍了关于注解的@PostConstruct 和xml 的 init-method = "" 的两种方式。但是springmvc使用的是接口的方式。
这里要介绍的初始化Controller是指填充完HandlerMapping map<String,Method>即代表初始化Controller流程
我们再提一点小知识。声明一个controller类有哪些方式。(现在应该没有人用第二/三种方式吧)
- 1.使用注解@Controller 和 请求路径@RequestMapping
- 2.实现 Controller 接口 并将该类交给spring容器管理beanName为请求路径
- 3.实现 HttpRequestHandler 接口并将该类交给spring容器管理beanName为请求路径
那么我们的map填充就从实现了InitializingBean 接口 的afterPropertiesSet这个方法开始。
源码中是这个类 AbstractHandlerMethodMapping(下面的代码有删减)
public void afterPropertiesSet() {
this.initHandlerMethods();
}
protected void initHandlerMethods() {
String[] var1 = this.getCandidateBeanNames();//1.获取容器初始化的所有beanName
int var2 = var1.length;
for(int var3 = 0; var3 < var2; ++var3) {
String beanName = var1[var3];
if (!beanName.startsWith("scopedTarget.")) {
this.processCandidateBean(beanName);//2.获取所有声明为Controller类的beanName
}
}
this.handlerMethodsInitialized(this.getHandlerMethods());
}
获取容器初始化的所有beanName(父子容器概念)
protected String[] getCandidateBeanNames() {
return this.detectHandlerMethodsInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.obtainApplicationContext(), Object.class) : this.obtainApplicationContext().getBeanNamesForType(Object.class);
}
获取所有声明为Controller类的beanName
protected void processCandidateBean(String beanName) {
Class beanType = null;
try {
beanType = this.obtainApplicationContext().getType(beanName);//获取bean的类型
} catch (Throwable var4) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Could not resolve type for bean '" + beanName + "'", var4);
}
}
if (beanType != null && this.isHandler(beanType)) {//获取所有声明为Controller类的beanName
this.detectHandlerMethods(beanName);//1.开始处理这种类型的beanName
}
}
开始处理这种类型的beanName
protected void detectHandlerMethods(Object handler) {
Class<?> handlerType = handler instanceof String ? this.obtainApplicationContext().getType((String)handler) : handler.getClass();
if (handlerType != null) {
Class<?> userType = ClassUtils.getUserClass(handlerType);
Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (method) -> {
try {
return this.getMappingForMethod(method, userType);//1.获取到类类型下所有的方法
} catch (Throwable var4) {
throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, var4);
}
});
if (this.logger.isTraceEnabled()) {
this.logger.trace(this.formatMappings(userType, methods));
}
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
this.registerHandlerMethod(handler, invocableMethod, mapping);//注册并填充map
});
}
}
获取到类类型下所有的方法和注册并填充map
第一个 MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap();
urlLookup.add(url, mapping);
eg:url = '/test/test.do'
mapping是一个RequestMappingInfo 对象 RequestMappingInfo.patternsCondition = T --> /test/test.do
第二个 Map<T, AbstractHandlerMethodMapping.MappingRegistration<T>> registry = new HashMap();
eg:key = 'url' value = 'method'
而我们的第二种和第三种的方式基本没有用了,因为会出现类爆炸,就像原始的servlet一样每一个方法都需要写一个类。
这两种方式是通过beanName为路径来实例化对象并执行通过该对象来执行里面的方法的。
源码中这两种map的填充方式是在bean的生命周期中通过实现beanFactory的applyBeanPostProcessorsBeforeInitialization方法来填充的。
@Controller 类中初始化问题
在Controller类中常常遇到有些参数需要初始化,甚至有些只允许初始化一次,而Controller类不像servelet类可以调用init()函数进行初始化,这里想到的办法是设置标记值,让初始化部分只调用一次。
第一种方法
设置isStart值。
private static Boolean isStart = false;
if(!isStart){
//进行初始化
isStart=true;
}
第二种方法
使用注释@PostConstruct,该注释的类会在类初始化时进行调用。
@PostConstruct
private void init(){
//进行初始化
}
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341