对于注解的一些理解
- 使用注解必须在Core Controller(DispatcherController)的注册文件dispatcher-servlet.xml中添加扫描设置<context:component-scan base-package = “” />
- 注解会被xml中的配置覆盖掉
- 使用注解方式的Controller没有任何限定,不再需要实现Controller接口,也不用继承什么类。其中的请求处理方法也没有限定。
- 在使用注解的请求处理方法中,任何需要的信息对象都可以在方法参数中直接申明,然后通过注解实现绑定。特别地,部分信息对象无需注解直接实现了绑定。
- 需要传递给视图view的信息都将通过model对象(包括Model、ModelMap、ModelAndview等)来传递,直接将信息写入方法参数中申明的model对象即可。此外,也可以利用HttpServletRequst和HttpServletResponse对象直接传递信息。
- 处理方法对应的视图可以通过view对象(包括View、ModelAndView等)来指定,也可以通过String类型的方法返回值来指定。对于视图逻辑名和真实路径的映射可以在xml中配置
- 在默认情况下,String类型的处理方法返回值指示了逻辑视图。当方法整体附加了@ModelAttribute且方法返回值为实体类时,被返回的实体类对象自动加入Modelmap中。
XML中的扫描配置
- 扫描路径为com.tan.*
<context:component-scan base-package="com.tan" >
- 扫描路径仅包括com.tan下的controller、service、dao三个包,其余都排除
<context:component-scan base-package="com.tan" >
<context:include-filter type="regex" expression=".controller.*"/>
<context:include-filter type="regex" expression=".service.*"/>
<context:include-filter type="regex" expression=".dao.*"/>
</context:component-scan>
- 扫描路径仅排除com.tan下的model包,其余都包括
- 与上述方式include等价
<context:component-scan base-package="com.tan" >
<context:exclude-filter type="regex" expression=".model.*"/>
</context:component-scan>
@RequestMapping 标记的处理器方法支持的方法参数和返回类型
此类方法的参数和返回值均有特殊含义
-
支持的方法参数类型
(1 )HttpServlet 对象,主要包括HttpServletRequest 、HttpServletResponse 和HttpSession 对象。 这些参数Spring 在调用处理器方法的时候会自动给它们赋值,所以当在处理器方法中需要使用到这些对象的时候,可以直接在方法上给定一个方法参数的申明,然后在方法体里面直接用就可以了。但是有一点需要注意的是在使用HttpSession 对象的时候,如果此时HttpSession 对象还没有建立起来的话就会有问题。
(2 )Spring 自己的WebRequest 对象。 使用该对象可以访问到存放在HttpServletRequest 和HttpSession 中的属性值。
(3 )InputStream 、OutputStream 、Reader 和Writer 。 InputStream 和Reader 是针对HttpServletRequest 而言的,可以从里面取数据;OutputStream 和Writer 是针对HttpServletResponse 而言的,可以往里面写数据。
(4 )使用@PathVariable 、@RequestParam 、@CookieValue 和@RequestHeader 标记的参数。
(5 )使用@ModelAttribute 标记的参数。
(6 )java.util.Map 、Spring 封装的Model 和ModelMap 。 这些都可以用来封装模型数据,用来给视图做展示。
(7 )实体类。 可以用来接收上传的参数。
(8 )Spring 封装的MultipartFile 。 用来接收上传文件的。
(9 )Spring 封装的Errors 和BindingResult 对象。 这两个对象参数必须紧接在需要验证的实体对象参数之后,它里面包含了实体对象的验证结果。
-
支持的返回类型
(1 )一个包含模型和视图的ModelAndView 对象。
(2 )一个模型对象,这主要包括Spring 封装好的Model 和ModelMap ,以及java.util.Map ,当没有视图返回的时候视图名称将由RequestToViewNameTranslator 来决定。
(3 )一个View 对象。这个时候如果在渲染视图的过程中模型的话就可以给处理器方法定义一个模型参数,然后在方法体里面往模型中添加值。
(4 )一个String 字符串。这往往代表的是一个视图名称。这个时候如果需要在渲染视图的过程中需要模型的话就可以给处理器方法一个模型参数,然后在方法体里面往模型中添加值就可以了。
(5 )返回值是void 。这种情况一般是我们直接把返回结果写到HttpServletResponse 中了,如果没有写的话,那么Spring 将会利用RequestToViewNameTranslator 来返回一个对应的视图名称。如果视图中需要模型的话,处理方法与返回字符串的情况相同。
(6 )如果处理器方法被注解@ResponseBody 标记的话,那么处理器方法的任何返回类型都会通过HttpMessageConverters 转换之后写到HttpServletResponse 中,而不会像上面的那些情况一样当做视图或者模型来处理。
(7 )除以上几种情况之外的其他任何返回类型都会被当做模型中的一个属性来处理,而返回的视图还是由RequestToViewNameTranslator 来决定,添加到模型中的属性名称可以在该方法上用@ModelAttribute(“attributeName”) 来定义,否则将使用返回类型的类名称的首字母小写形式来表示。使用@ModelAttribute 标记的方法会在@RequestMapping 标记的方法执行之前执行。
常用注解
@Controller,@Service,@repository,@Component
- @Component指示对一个类的Spring IoC注册,表明这个类成为了Bean,已经被添加到Spring容器中了(将被Spring容器实例化)
- @Controller, @Service,@Repository是@Component的细化,这三个注解比@Component带有更多的语义,它们分别对应了控制层、服务层、持久层的类。
- 仅@Controller有特殊含义,其余注解本质上没有差别,只不过是方便MVC分层的指示性注解。
- @Controller注解表明该类是一个Controller,省去了该类对Controller接口的implement。在被@Controller注解的类中,通过对方法进行**@RequestMapping注解来指明Servlet处理方法**
- 默认Bean ID就是类名,但首字母小写。如果类名以连续几个大写字母开头,首字母不小写。据我分析,这样做是因为注册后Spring容器会创建该Bean的一个实例对象,对象的命名规则就是小写字母开头。在对bean的依赖注入时,注入的对象是以BeanId做标识的。综上,这种默认命名很有道理。
@Resource,@Autowired,@Value
- @Resource 默认按照ByName进行bean匹配
- @Autowired默认按照ByType进行bean匹配,当使用@Qualifier注解时,是通过名称(ByName)匹配
- @Resource(import javax.annotation.Resource;)是J2EE的注解,@Value和@Autowired(import org.springframework.beans.factory.annotation.Autowired;)是Spring的注解
- @Value用于对值的注入
- @Resource,@Autowired都用于对类属性的注入,都可用于字段、constructor、setter。两者如果都写在字段上,那么就不需要再写setter方法
- 对于接口单属性,如果只有一个实现类,默认情况下将注入这个类。如果有多个实现类,需要使用@Qualifier(“bean_id”)注解显式指定。如果没有指定,Spring容器将根据变量名匹配一个实现类,如果找不到匹配,抛出异常。
- 对于接口集合属性,Spring将注入所有实现类的实例。
@PathVariable
- 用于将请求URL中的模板变量映射到功能处理方法的参数上,即取出uri模板中的变量作为参数。
@Controller
public class TestController {
@RequestMapping(value="/user/{userId}/roles/{roleId}",method = RequestMethod.GET)
public String getLogin(@PathVariable("userId") String userId,
@PathVariable("roleId") String roleId){
System.out.println("User Id : " + userId);
System.out.println("Role Id : " + roleId);
return "hello";
}
@RequestMapping(value="/product/{productId}",method = RequestMethod.GET)
public String getProduct(@PathVariable("productId") String productId){
System.out.println("Product Id : " + productId);
return "hello";
}
@RequestMapping(value="/javabeat/{regexp1:[a-z-]+}",
method = RequestMethod.GET)
public String getRegExp(@PathVariable("regexp1") String regexp1){
System.out.println("URI Part 1 : " + regexp1);
return "hello";
}
}
@RequestParam,@RequestAttribute
- @RequsetParam对应于request.getParameter(Sting key):String,而@RequestAttribute对应于requesst.getAttribute(String key):Object
- getParameter()获取的是客户端设置的数,getAttribute()获取的是服务器设置的数据。
- getParameter()永远返回字符串,getAttribute()返回值是任意类型
- 服务器端不能通过setParameter(key,value)来添加参数,因为没有这个函数.所以如果需要在服务器端进行跳转,并需要想下个页面发送新的参数时,则没法实现。但是Attribute可以,可以通过setAttribute(),将值放入到request对象,然后在其他页面使用getAttribute获取对应的值,这样就达到一次请求可以在多个页面共享一些对象信息
- parameter返回值是字符串,意味着不能传递其他的对象,如Map,List,但是attribute则可以存放任意类型的Java对象
- 当不添加@RequestPara和@RequeAttribute注解时,参数按名(ByName)实现自动绑定
@ModelAttribute
在Spring mvc中,注解@ModelAttribute是一个非常常用的注解,其功能主要在两方面:
- 运用在参数上,会将客户端传递过来的参数按名称注入到指定对象中,并且会将这个对象自动加入ModelMap中,便于View层使用;
- 运用在方法上,会在每一个@RequestMapping标注的方法前执行,如果有返回值,则自动将该返回值加入到ModelMap中
<form action="add-stu" method="post">
id: <input type="id" name="id"> <br>
name: <input type="name" name="name"> <br>
age: <input type="age" name="age"> <br>
<input type="submit" value="SUBMIT"> <br>
</from>
//MyController.java
@RequestMapping(value = "/add-stu", method = RequestMethod.POST)
public String addStudent(@ModelAttribute( ) Student student, ModelMap modelMap){
modelMap.addAttribute("name", student.getName());
modelMap.addAttribute("id",student.getId());
modelMap.addAttribute("age",student.getAge());
return "result";
//也可以在jsp中直接解析student
//${student.name}
//${student.id}
//${student.age}
}
public class Student {
private int id;
private String name;
private int age;
//...
//setter and getter
}
@SessionAttributes,@SessionAttribute
- 默认情况下SpringMVC将模型中的数据存储到request域中。当一个请求结束后,数据就失效了。如果要跨页面使用。那么需要使用到session。而@SessionAttributes注解就可以使得模型中的数据存储一份到session域中。
- @SessionAttributes注解仅用于类,其参数指明了将哪些模型数据保存在Session中。可以指定模型数据的在map中键的名称(names和values等价)或者实体类型(type),二者是可以同时指定。
- 下面的注解指明:任何value为book/description的数据或者实体类型为Doublie.class的数据在存入model对象时都会被同时存入HttpSession
@SessionAttributes(value={"book","description"},types={Double.class})
- 要想在jsp中访问存入session的数据,则因通过sessionScope对象来访问user
<body>
<h2>${sessionScope.user}</h2>
<h2>${sessionScope.user.username}</h2>
</body>
- 要想在Controller的handle方法中访问存入session的数据,则应通过@SessionAttribute或@RequestAttribute注解的方法参数来进行。但令人不解的是,无法通过HTTPSession参数来访问session中的值。
@RequestMapping("/session-test")
public void showSession(@RequestAttribute User userRa, @SessionAttribute User user, HttpServletResponse response) throws IOException {
PrintWriter writer = response.getWriter();
writer.println("SessionAttribute-" + user.getUsername());
writer.println("RequestAttribute-" + userRa.getUsername());
}
- 如果需要清除通过@SessionAttribute添加至 session 中的数据,则需要在controller 的 handler method中添加 SessionStatus参数,在方法体中调用SessionStatus#setComplete。需要注意的是,此时清除的只是该Controller通过@SessionAttribute添加至session的数据
@RequestMapping("/user-info")
public String showInfo(SessionStatus sessionStatus){
sessionStatus.setComplete();
return "user";
}
综合感受
- Controller是一个请求-++处理+±反馈的中转站,其中请求是从其他地方发来的,处理是要在Controller里面完成的,(视觉)反馈是前端页面完成的。
- 为了实现中转这一目的,对于任何一个请求,Controller都需要完成两项工作:提供处理结果,指明视觉反馈页面。
- 提供处理结果是通过将数据放入Model/ModelMap/ModelAndView中。
- 指明视觉反馈页面是通过将页面名称在String返回值/View/ModelAndView中指明。
参考资料
- https://www.cnblogs.com/straybirds/p/9147182.html (注解辨析)
- https://www.qikegu.com/docs/1815 (Spring注解)
- https://www.cnblogs.com/leskang/p/5445698.html (SpringMVC常用注解)
- https://www.cnblogs.com/canger/p/10301275.html (@SessionAttributes注解分析)
- https://blog.csdn.net/csyy140225/article/details/82454611 (@SessionAttribute注解分析)