springMVCソース分析--Model Factory
9699 ワード
Model FactoryはModelを維持するためのもので、具体的には二つの機能が含まれています.
(1)Modelを初期化する
(2)プロセッサが実行後、Modelの対応するパラメータをSession Attributesに更新する
1、初期化Modelは@ModelAttributeと@Session Attributeに対する注釈の実行であり、実行する操作はinitModelに@Session Attributeの注釈を取得するパラメータの値と@ModelAttributeに注釈された関数を含む.
(1)Modelを初期化する
(2)プロセッサが実行後、Modelの対応するパラメータをSession Attributesに更新する
1、初期化Modelは@ModelAttributeと@Session Attributeに対する注釈の実行であり、実行する操作はinitModelに@Session Attributeの注釈を取得するパラメータの値と@ModelAttributeに注釈された関数を含む.
public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod handlerMethod)
throws Exception {
// @SessionAttribute , mavContainer
Map sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
mavContainer.mergeAttributes(sessionAttributes);
// @ModelAttribute Model
invokeModelAttributeMethods(request, mavContainer);
// @ModelAttribute @SessionAttribute
for (String name : findSessionAttributeArguments(handlerMethod)) {
if (!mavContainer.containsAttribute(name)) {
Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
if (value == null) {
throw new HttpSessionRequiredException("Expected session attribute '" + name + "'");
}
mavContainer.addAttribute(name, value);
}
}
}
2、Modelを更新する操作は、udateModelにおいて、まず@Session Attributeのコメント値を更新し、Modelの値を更新します.// Model, @SessionAttribute value, Model
public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer) throws Exception {
ModelMap defaultModel = mavContainer.getDefaultModel();
if (mavContainer.getSessionStatus().isComplete()){
this.sessionAttributesHandler.cleanupAttributes(request);
}
else {
this.sessionAttributesHandler.storeAttributes(request, defaultModel);
}
if (!mavContainer.isRequestHandled() && mavContainer.getModel() == defaultModel) {
updateBindingResult(request, defaultModel);
}
}
Model Factoryの完全なソースコードは以下の通りです.public final class ModelFactory {
private static final Log logger = LogFactory.getLog(ModelFactory.class);
private final List modelMethods = new ArrayList();
private final WebDataBinderFactory dataBinderFactory;
private final SessionAttributesHandler sessionAttributesHandler;
public ModelFactory(List invocableMethods, WebDataBinderFactory dataBinderFactory,
SessionAttributesHandler sessionAttributesHandler) {
if (invocableMethods != null) {
for (InvocableHandlerMethod method : invocableMethods) {
this.modelMethods.add(new ModelMethod(method));
}
}
this.dataBinderFactory = dataBinderFactory;
this.sessionAttributesHandler = sessionAttributesHandler;
}
public void initModel(NativeWebRequest request, ModelAndViewContainer mavContainer, HandlerMethod handlerMethod)
throws Exception {
// @SessionAttribute , mavContainer
Map sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
mavContainer.mergeAttributes(sessionAttributes);
// @ModelAttribute Model
invokeModelAttributeMethods(request, mavContainer);
// @ModelAttribute @SessionAttribute
for (String name : findSessionAttributeArguments(handlerMethod)) {
if (!mavContainer.containsAttribute(name)) {
Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
if (value == null) {
throw new HttpSessionRequiredException("Expected session attribute '" + name + "'");
}
mavContainer.addAttribute(name, value);
}
}
}
private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer mavContainer)
throws Exception {
while (!this.modelMethods.isEmpty()) {
// @ModelAttribute
InvocableHandlerMethod attrMethod = getNextModelMethod(mavContainer).getHandlerMethod();
// @ModelAttribute value
String modelName = attrMethod.getMethodAnnotation(ModelAttribute.class).value();
// mavContainer
if (mavContainer.containsAttribute(modelName)) {
continue;
}
// @ModelAttribute
Object returnValue = attrMethod.invokeForRequest(request, mavContainer);
if (!attrMethod.isVoid()){
// getNameForReturnValue
String returnValueName = getNameForReturnValue(returnValue, attrMethod.getReturnType());
if (!mavContainer.containsAttribute(returnValueName)) {
mavContainer.addAttribute(returnValueName, returnValue);
}
}
}
}
// ModelMethod
private ModelMethod getNextModelMethod(ModelAndViewContainer mavContainer) {
for (ModelMethod modelMethod : this.modelMethods) {
if (modelMethod.checkDependencies(mavContainer)) {
if (logger.isTraceEnabled()) {
logger.trace("Selected @ModelAttribute method " + modelMethod);
}
this.modelMethods.remove(modelMethod);
return modelMethod;
}
}
ModelMethod modelMethod = this.modelMethods.get(0);
if (logger.isTraceEnabled()) {
logger.trace("Selected @ModelAttribute method (not present: " +
modelMethod.getUnresolvedDependencies(mavContainer)+ ") " + modelMethod);
}
this.modelMethods.remove(modelMethod);
return modelMethod;
}
// @SessionAttribute @ModelAttribute
private List findSessionAttributeArguments(HandlerMethod handlerMethod) {
List result = new ArrayList();
for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
String name = getNameForParameter(parameter);
// @SessionAttribute
if (this.sessionAttributesHandler.isHandlerSessionAttribute(name, parameter.getParameterType())) {
result.add(name);
}
}
}
return result;
}
//
public static String getNameForParameter(MethodParameter parameter) {
ModelAttribute annot = parameter.getParameterAnnotation(ModelAttribute.class);
String attrName = (annot != null) ? annot.value() : null;
return StringUtils.hasText(attrName) ? attrName : Conventions.getVariableNameForParameter(parameter);
}
// @ModelAttribute
public static String getNameForReturnValue(Object returnValue, MethodParameter returnType) {
ModelAttribute annotation = returnType.getMethodAnnotation(ModelAttribute.class);
if (annotation != null && StringUtils.hasText(annotation.value())) {
return annotation.value();
}
else {
Method method = returnType.getMethod();
Class> resolvedType = GenericTypeResolver.resolveReturnType(method, returnType.getContainingClass());
return Conventions.getVariableNameForReturnType(method, resolvedType, returnValue);
}
}
// Model, @SessionAttribute value, Model
public void updateModel(NativeWebRequest request, ModelAndViewContainer mavContainer) throws Exception {
ModelMap defaultModel = mavContainer.getDefaultModel();
if (mavContainer.getSessionStatus().isComplete()){
this.sessionAttributesHandler.cleanupAttributes(request);
}
else {
this.sessionAttributesHandler.storeAttributes(request, defaultModel);
}
if (!mavContainer.isRequestHandled() && mavContainer.getModel() == defaultModel) {
updateBindingResult(request, defaultModel);
}
}
//
private void updateBindingResult(NativeWebRequest request, ModelMap model) throws Exception {
List keyNames = new ArrayList(model.keySet());
for (String name : keyNames) {
Object value = model.get(name);
if (isBindingCandidate(name, value)) {
String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + name;
if (!model.containsAttribute(bindingResultKey)) {
WebDataBinder dataBinder = dataBinderFactory.createBinder(request, value, name);
model.put(bindingResultKey, dataBinder.getBindingResult());
}
}
}
}
//
private boolean isBindingCandidate(String attributeName, Object value) {
if (attributeName.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
return false;
}
Class> attrType = (value != null) ? value.getClass() : null;
if (this.sessionAttributesHandler.isHandlerSessionAttribute(attributeName, attrType)) {
return true;
}
return (value != null && !value.getClass().isArray() && !(value instanceof Collection) &&
!(value instanceof Map) && !BeanUtils.isSimpleValueType(value.getClass()));
}
private static class ModelMethod {
private final InvocableHandlerMethod handlerMethod;
private final Set dependencies = new HashSet();
private ModelMethod(InvocableHandlerMethod handlerMethod) {
this.handlerMethod = handlerMethod;
for (MethodParameter parameter : handlerMethod.getMethodParameters()) {
if (parameter.hasParameterAnnotation(ModelAttribute.class)) {
this.dependencies.add(getNameForParameter(parameter));
}
}
}
public InvocableHandlerMethod getHandlerMethod() {
return this.handlerMethod;
}
public boolean checkDependencies(ModelAndViewContainer mavContainer) {
for (String name : this.dependencies) {
if (!mavContainer.containsAttribute(name)) {
return false;
}
}
return true;
}
public List getUnresolvedDependencies(ModelAndViewContainer mavContainer) {
List result = new ArrayList(this.dependencies.size());
for (String name : this.dependencies) {
if (!mavContainer.containsAttribute(name)) {
result.add(name);
}
}
return result;
}
@Override
public String toString() {
return this.handlerMethod.getMethod().toGenericString();
}
}
}