In principle grasp @ModelAttribute level of use (use articles) [together] to learn Spring MVC

Each one

Everyone should think clearly this question: Are you a reward ancestral eat, or eat by God's reward

Foreword

The article depicts the @ModelAttributecore principle of this scenario is focused on the use, presentation @ModelAttributeused in different scenarios, and considerations (of course, some associated principles will be involved).

For Demo demo first to get clear once again that @ModelAttributerole.

@ModelAttributeRole of

Although you may have seen the core principles of articles , but still may lack some of the summary of the top concepts. Here I am with my understanding, summarize @ModelAttributethe role of this annotation, mainly divided into the following three aspects:

  1. Binding request parameters to the command object (the reference object) : When the controller is placed on the reference method for binding a plurality of parameters to the request command object , so that 简化binding process, and automatic exposure of model data when using the display view page;
  2. Exposure to form a reference object model data : general approach on the processor (non-functional approach, that is, there is no @RequestMappingmethod annotated) when on, is a form ready to show the form references data objects: where to choose when such registration static information cities. It performs the function in the processing method ( @RequestMappingpreviously annotated method), it is automatically added to the object model, a view used for page display;
  3. Exposure @RequestMappingmethod returns the value of the model data : on the function processing method when a return value, the method is exposure processing function return value model data, used for view display page.

The following scenarios for the use of these, are given Demouse case, for your reference to the actual use.


@ConstructorProperties explain

Because talked about in the papers principle, the model is automatically created when the object not only can use an empty constructor, you can also use java.beans.ConstructorPropertiesthis annotation, therefore it must first introduce a wave:

Official explanation: Notes on the constructor, display parameters of the constructor how the getter corresponding to the constructed object.

// @since 1.6
@Documented 
@Target(CONSTRUCTOR)  // 只能使用在构造器上
@Retention(RUNTIME)
public @interface ConstructorProperties {
    String[] value();
}

Examples are as follows:

@Getter
@Setter
public class Person {
    private String name;
    private Integer age;

    // 标注注解
    @ConstructorProperties({"name", "age"})
    public Person(String myName, Integer myAge) {
        this.name = myName;
        this.age = myAge;
    }
}

Here annotations on name, ageit means that corresponds to Personthat JavaBeanof getName()and getAge()methods.
It says: The first argument to the constructor may be used getName()to retrieve the second parameter can be getAge()retrieved, since the method parameter name / constructor at runtime is not met, the use of the annotation can achieve this effect.

What is the significance of this comment it? ? ?
In fact, tell the truth, now go xmlfull annotation-driven age it's not mean much. It is used most often as the scene before use xmlto configure Bean like this:

<bean id="person" class="com.fsx.bean.Person">
    <constructor-arg name="name" value="fsx"/>
    <constructor-arg name="age" value="18"/>
</bean>

So <constructor-arg>you do not need to follow the natural order parameter index (inflexible and error-prone with wood have) come, you can be in accordance with the corresponding attribute name, a lot of flexibility. Originally xml configuration basic need, but just @ModelAttributeparse it and this renewal of the newborn, the following specific examples will be given ~

> java.beansAlso provides an annotation java.beans.Transient(after 1.7 offers): Specifies that genus or field is not permanent. It is used to annotate entity class, or may be embedded mapped superclass properties or fields of the class . (May be marked on the properties and get methods)

Demo Show

Marked on the non-functional method

@Getter
@Setter
@ToString
public class Person {
    private String name;
    private Integer age;

    public Person() {
    }

    public Person(String myName, int myAge) {
        this.name = myName;
        this.age = myAge;
    }
}

@RestController
@RequestMapping
public class HelloController {

    @ModelAttribute("myPersonAttr")
    public Person personModelAttr() {
        return new Person("非功能方法", 50);
    }

    @GetMapping("/testModelAttr")
    public void testModelAttr(Person person, ModelMap modelMap) {
        //System.out.println(modelMap.get("person")); // 若上面注解没有指定value值,就是类名首字母小写
        System.out.println(modelMap.get("myPersonAttr"));
    }
}

Visit:/testModelAttr?name=wo&age=10 . Printout:

Person(name=wo, age=10)
Person(name=非功能方法, age=50)

You can see the parameters of Personthe object even without labels @ModelAttributealso be encapsulated into the normal values (and further into the ModelMapinside).

Because there is no annotation will create an empty construct Personobjects, and then use ServletRequestDataBinder.bind(ServletRequest request)to complete the data binding (of course, you can also check @Valid)

Note that the following details:
1, Personeven if there is no empty structure, means @ConstructorPropertiescan also be done automatically package

    // Person只有如下一个构造函数
    @ConstructorProperties({"name", "age"})
    public Person(String myName, int myAge) {
        this.name = myName;
        this.age = myAge;
    }

Print full above.

2, even if the above @ConstructorPropertiesis written in the name myName, the result is still a normal package. As long as there is no check bindingResult == nullwhen the execution will still ServletRequestDataBinder.bind(ServletRequest request)repackaging once. Unless add a @Validcheck, it will only use the @ConstructorPropertiespackage once, will not bind the secondary ~ ( since Spring think you have @Valid over, then do not go in Minato )

3, even if not marked on the top constructor @ConstructorPropertiesnotes, there is still no problem. The reason: BeanUtils.instantiateClass(ctor, args)most args are object creation [null,null]chant, also not being given thing (so to note: If you are the type int it is the basic parameter error friends ~ ~)

4, although @ModelAttributewrite do not write the same effect. But if this is written @ModelAttribute("myPersonAttr") Person person, it is designated as the same value above the value that is printed below:

Person(name=wo, age=10)
Person(name=wo, age=10)

As for the reason, they do not have to explain (refer to the principle of article).

== also need to know is: @ModelAttributelabeling only for the current controller in the present process. However, if you use @ControllerAdvicethe component, it is global. (Of course, can be specified basePackagesto limit its scope -) ==

In the method for labeling function (return value) on

Thus the form:

    @GetMapping("/testModelAttr")
    public @ModelAttribute Person testModelAttr(Person person, ModelMap modelMap) {
        ...
    }

To do this specific example, it is more simple: a method that returns a value into the model. ( Note that void, null return these values are not put to ~)

In the method of labeling the reference

The use should be our most used way, although the principle of complexity, but the user is still very simple, slightly.

And @RequestAttribute/ @SessionAttributeis used with

Referring to Bowen: master the use of @ RequestAttribute, @ SessionAttribute from the principle of the level of [school] with Spring MVC . Talia cooperation is very smooth to use, generally not a problem, no matter what idea

And @SessionAttributesused together

@ModelAttributeIt essentially: allows us to manipulate the model data before calling the target method . @SessionAttributesIt allows Modeldata (qualified) synchronized to the Session a convenient value between a plurality of transfer requests.
Let's feel through a use case:

@RestController
@RequestMapping
@SessionAttributes(names = {"name", "age"}, types = Person.class)
public class HelloController {

    @ModelAttribute
    public Person personModelAttr() {
        return new Person("非功能方法", 50);
    }

    @GetMapping("/testModelAttr")
    public void testModelAttr(HttpSession httpSession, ModelMap modelMap) {
        System.out.println(modelMap.get("person"));
        System.out.println(httpSession.getAttribute("person"));
    }
}

In order to see @SessionAttributesthe effect of, directly use my browser to visit here twice in a row (same session) to see results:

First visit Printing:

Person(name=非功能方法, age=50)
null

Second visit to print:

Person(name=非功能方法, age=50)
Person(name=非功能方法, age=50)

We can see the @ModelAttributecombination @SessionAttributeswill take effect. As for specific reasons, can aid in understanding the venue here: master @ModelAttribute use (core principle chapter) from the level of principle [together] to learn Spring MVC

Look at the following example variant of (important):

@RestController
@RequestMapping
@SessionAttributes(names = {"name", "age"}, types = Person.class)
public class HelloController {

    @GetMapping("/testModelAttr")
    public void testModelAttr(@ModelAttribute Person person, HttpSession httpSession, ModelMap modelMap) {
        System.out.println(modelMap.get("person"));
        System.out.println(httpSession.getAttribute("person"));
    }
}

Visit:/testModelAttr?name=wo&age=10 . Incorrect report:

 org.springframework.web.HttpSessionRequiredException: Expected session attribute 'person'
    at org.springframework.web.method.annotation.ModelFactory.initModel(ModelFactory.java:117)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:869)

Be sure to pay attention to this mistake: This is in front of me with special emphasis on the use of a misunderstanding, when you @SessionAttributesand @ModelAttributewhen used together, most likely to commit a mistake.

Cause of the error codes are as follows:

    public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod) throws Exception {
        Map<String, ?> sessionAttributes = this.sessionAttributesHandler.retrieveAttributes(request);
        container.mergeAttributes(sessionAttributes);
        invokeModelAttributeMethods(request, container);

        // 合并完sesson的属性,并且执行完成@ModelAttribute的方法后,会继续去检测
        // findSessionAttributeArguments:标注有@ModelAttribute的入参  并且isHandlerSessionAttribute()是SessionAttributts能够处理的类型的话
        // 那就必须给与赋值~~~~  注意是必须
        for (String name : findSessionAttributeArguments(handlerMethod)) {
            // 如果model里不存在这个属性(那就去sessionAttr里面找)
            // 这就是所谓的其实@ModelAttribute它是会深入到session里面去找的哦~~~不仅仅是request里
            if (!container.containsAttribute(name)) {
                Object value = this.sessionAttributesHandler.retrieveAttribute(request, name);
                
                // 倘若session里都没有找到,那就报错喽
                // 注意:它并不会自己创建出一个新对象出来,然后自己填值,这就是区别。
                // 至于Spring为什么这么设计 我觉得是值得思考一下子的
                if (value == null) {
                    throw new HttpSessionRequiredException("Expected session attribute '" + name + "'", name);
                }
                container.addAttribute(name, value);
            }
        }
    }

Note that this is initModel()the time on the error yo, not to resolveArgument()do. Spring intent of this design? ? ? I venture to guess: marked on the controller @SessionAttributesnotes, if you also use the parameter on @ModelAttribute, then you definitely want to get bound, if no procedural errors must be your problem, so you throw an exception, the display tells you to go to troubleshooting.

Modified as follows, together with the methods of the present controller:

    @ModelAttribute
    public Person personModelAttr() {
        return new Person("非功能方法", 50);
    }

(Please observe the following few visits as well as the corresponding print results)
Access:/testModelAttr

Person(name=非功能方法, age=50)
null

Then visit:/testModelAttr

Person(name=非功能方法, age=50)
Person(name=非功能方法, age=50)

access:/testModelAttr?name=wo&age=10

Person(name=wo, age=10)
Person(name=wo, age=10)

Note: At this point modeland sessioninside the values have changed, oh, into the parameter value on the date of the request link (and each request will use the value of the parameter).

access:/testModelAttr?age=11111

Person(name=wo, age=11111)
Person(name=wo, age=11111)

We can see that can be done locally modify the properties of

Access again: /testModelAttr(no request parameters, equivalent to only perform non-functional method)

Person(name=fsx, age=18)
Person(name=fsx, age=18)

This can be seen when modeland sessionwhere the value has not again been on the non-functional approach @ModelAttributechanged, and this is an important conclusion.
Its fundamental principle here:

    public void initModel(NativeWebRequest request, ModelAndViewContainer container, HandlerMethod handlerMethod) throws Exception {
        ...
        invokeModelAttributeMethods(request, container);
        ...
    }

    private void invokeModelAttributeMethods(NativeWebRequest request, ModelAndViewContainer container) throws Exception {
        while (!this.modelMethods.isEmpty()) {
            ...
            // 若model里已经存在此key 直接continue了
            if (container.containsAttribute(ann.name())) {
                ...
                continue;
            }
            // 执行方法
            Object returnValue = modelMethod.invokeForRequest(request, container);
            // 注意:这里只判断了不为void,因此即使你的returnValue=null也是会进来的
            if (!modelMethod.isVoid()){
                ...
                // 也是只有属性不存在 才会生效哦~~~~
                if (!container.containsAttribute(returnValueName)) {
                    container.addAttribute(returnValueName, returnValue);
                }
            }
        }
    }

So for the final @ModelAttributeand @SessionAttributeswhen used together is important to note concluded: has been added into the sessiondata useless in use SessionStatusbefore clearing off, @ModelAttributenon-functional approach marked the return value will not be updated again into the inner session

So @ModelAttributemeaning Siha ~ non-functional method of labeling a little initial value, of course, you can manually SessionStatusafter clear it will come into force

to sum up

Any technology will eventually fall into use, the paper mainly describes @ModelAttributeexamples of various use case, but also pointed out that it @SessionAttributespits used together.
@ModelAttributeThis annotation is still relatively more frequent use, and powerful, also recently talked about the most important notes, flowers and therefore more space, hoping to bring help small partners of the actual work to bring the United States Code of ~

Related Reading

In principle level to master the use HandlerMethod, InvocableHandlerMethod, ServletInvocableHandlerMethod with the [school] Spring MVC
master the use of @SessionAttributes from the principle of level [together] Spring MVC learn
from the master @ModelAttribute principle level of use (the core principles of article) [together] to learn Spring MVC

Knowledge Exchange

== The last: If you think this article helpful to you, may wish to point a praise chant. Of course, your circle of friends to share so that more small partners also are seeing 作者本人许可的~==

If interested in technology content can join the group wx exchange: Java高工、架构师3群.
If the group fails two-dimensional code, please add wx number: fsx641385712(or two-dimensional code is scanned beneath wx). And Note: "java入群"the word, will be invited into the group manually

If the article 格式混乱or图片裂开 click here ': description link - text link - text link

Guess you like

Origin www.cnblogs.com/fangshixiang/p/11361061.html