1. SpringMVC简单介绍
对于SpringMVC,我也只是简单的了解,这儿进行一些简单的总结。更加详细的内容可以参考官方文档:Spring Framework Reference
SpringMVC是一个非常强大的IOC依赖注入容器,通过xml配置文件来管理类和类之间的依赖关系,对于程序员来说,我们只要通过注解或者配置等方式来指定类之间的依赖关系,由SpringMVC来对类的实例进行管理,我们只需要使用就可以了,而不用关注依赖类的实例化过程。真正地使得接口和实现分离开来。
2. SpringMVC依赖注入
2.1 构造器注入
基于构造函数注入有SpringMVC容器调用一个含有多个参数的构造函数完成,这儿每个参数表示一个依赖。这里的 MovieFinder 是一个POJO类(不含业务逻辑的java简单对象)。
package com.example; public class SimpleMovieLister { // 该类依赖于MovieFinder private MovieFinder movieFinder; // 告诉SpringMVC在构造器中注入 MovieFinder 实例 public SimpleMovieLister(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // 下面可以使用 movieFinder 实例进行相关逻辑 }
相应的 Bean 实体配置如下:
<bean id="SimpleMovieLister" class="com.example.SimpleMovieLister"> <property name="MovieFinder" ref="MovieFinder"></property> </bean>
使用这种方式配置Bean时,可选的子节点 constructor-arg 用于指定构造函数的参数,可选的属性如下:
- type:构造函数参数类型
- value:初始值,用于基本类型如 int ,String等
- index:显式地指定参数索引,从0开始
- name:参数别名
<bean id="exampleBean1" class="examples.ExampleBean"> <constructor-arg type="int" value="7500000"/> <constructor-arg type="java.lang.String" value="42"/> </bean> <bean id="exampleBean2" class="examples.ExampleBean"> <constructor-arg index="0" value="7500000"/> <constructor-arg index="1" value="42"/> </bean> <bean id="exampleBean3" class="examples.ExampleBean"> <constructor-arg name="years" value="7500000"/> <constructor-arg name="ultimateAnswer" value="42"/> </bean>
2.2 set 注入
基于 set 的依赖注入在容器调用无参构造函数或者无参静态工厂方法实例化Bean之后调用Bean的set方法。
public class SimpleMovieLister { // the SimpleMovieLister has a dependency on the MovieFinder private MovieFinder movieFinder; // a setter method so that the Spring container can inject a MovieFinder public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; } // business logic that actually uses the injected MovieFinder is omitted... }
看下面的示例:
public class ExampleBean { private AnotherBean beanOne; private YetAnotherBean beanTwo; private int i; public void setBeanOne(AnotherBean beanOne) { this.beanOne = beanOne; } public void setBeanTwo(YetAnotherBean beanTwo) { this.beanTwo = beanTwo; } public void setIntegerProperty(int i) { this.i = i; } }
Bean配置如下:
<bean id="exampleBean" class="examples.ExampleBean"> <!-- setter injection using the nested ref element --> <property name="beanOne"> <ref bean="anotherExampleBean"/> </property> <!-- setter injection using the neater ref attribute --> <property name="beanTwo" ref="yetAnotherBean"/> <property name="integerProperty" value="1"/> </bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
2.3 使用注解依赖注入
在SpringMVC中用得最多的应该就是使用注解的依赖注入了,常用的有:
- @Controller:声明一个控制器,系统表示层
- @Service:声明一个服务(对dao的封装),系统业务逻辑层
- @Repository:声明一个dao层(实现dao的访问,如Mybatis的mapper),系统数据访问层
- @Component:把普通POJO类实例化到Spring容器中,相当于声明一个Bean
以上的注解要能注入到Spring容器中,还需要在配置文件中配置扫描路径,以告诉Spring这些注入的类的位置。
比如下面的一个 Mybatis 中 dao 层的Mapper:
package com.configservice.dao; import com.configservice.model.GameBean; import org.springframework.stereotype.Repository; @Repository public interface GameMapper { GameBean selectGameByGameId(int gameId); GameBean selectGameByGameName(String gameName); }
还需要配置该dao的扫描路径,在Spring配置中添加以下配置:
<!-- 配置的bean所在包的位置 --> <context:component-scan base-package="com.configservice.dao" />
其中base-package为需要扫描的包(含所有子包),component包含Controller,Repository,Service以及继承Component的自定义注解。
2.4 自动加载
对于已经注入的Bean实例,我们可以使用形如@Autowired的注解自动获取,而不需要手动new一个。
@Autowired注解
Spring2.5以后引入@Autowired注解,可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。
@Autowired默认按类型(byType)装配,默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false。
// 对成员变量使用 @Autowired private UserMapper userMapper; // 对集合使用,并定义required为false @Autowired(required=false) private List<User> userList; private MovieService movieService; // 对set方法使用 @Autowired public void setMovieService(MovieService movieService) { this.movieService = movieService; }
在默认情况下使用@Autowired注释进行自动注入时,Spring容器中匹配的候选Bean数目必须有且仅有一个。
当找不到一个匹配的Bean时,Spring容器将抛出BeanCreationException异常,并指出必须至少拥有一个匹配的Bean。
当不能确定Spring容器中一定拥有某个类的Bean时,可以在需要自动注入该类Bean的地方可以使用@Autowired(required=false),这等于告诉Spring:在找不到匹配Bean时也不报错。
@Qualifier注解
@Autowired是根据类型进行自动装配的。例如,如果当Spring上下文中存在不止一个UserDao类型的bean时,就会抛出BeanCreationException异常;如果Spring上下文中不存在UserDao类型的bean,也会抛出BeanCreationException异常。我们可以使用@Qualifier配合@Autowired来解决这些问题。
可能存在多个UserDao实例时,使用@Qualifier指定名称name,Spring会从UserDao中找出匹配名称的实例来加载。
@Autowired @Qualifier("userServiceImpl") private IUserService userService; // Spring会找到name为userServiceImpl和userDao的bean进行装配。
@Autowired 可以对成员变量、方法以及构造函数进行注释,而 @Qualifier 的标注对象是成员变量、方法入参、构造函数入参。@Qualifier(“XXX”) 中的 XX是 Bean 的名称,所以 @Autowired 和 @Qualifier 结合使用时,自动注入的策略就从 byType 转变成 byName 了。
@Resource注解
JSR-250标准注解,推荐使用它来代替Spring专有的@Autowired注解。@Resource的作用相当于@Autowired,不过@Autowired按byType自动注入,而@Resource默认按byName自动注入。
@Resource有两个重要的属性:name和type,name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则按照byName自动注入,使用type属性时则按照byType自动注入。如果既不指定name也不指定type属性,这时将通过反射机制按照byName自动注入。
// byName @Resource(name="userService") private UserService userService;
@Resource的转配规则如下:
- 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
- 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
- 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
- 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配
另外还有一些注解用于控制自动加载的行为:
- @Scope注解:标注一个Bean的作用范围
- @PostConstruct注解:标注一个方法在Spring初始化Bean之后执行
- @PreDestroy注解:标注一个方法在Spring销毁Bean之前执行
3. 其他一些常用Spring注解说明
3.1 @Controller
标记一个类为控制层处理由DispatcherServlet 分发的请求。
3.2 @RequestMapping
配合 @Controller 一起使用,标记特定的地址映射,可用于类或者方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。属性如下:
- value:指定请求的实际地址
- method:指定请求的method类型, GET、POST、PUT、DELETE等
- consumes:指定处理请求的提交内容类型(Content-Type),例如application/json, text/html
- produces:指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回
- params:指定request中必须包含某些参数值是,才让该方法处理
- headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求
3.3 @ModelAttribute和 @SessionAttributes
SpringMVC 支持使用 @ModelAttribute 和 @SessionAttributes 在不同的模型(model)和控制器之间共享数据。
@ModelAttribute 主要有两种使用方式,一种是标注在方法上,一种是标注在 Controller 方法参数上。
当用 @ModelAttribute 标记在方法上的时候,该方法将在处理器方法执行之前执行,然后把返回的对象放在模型属性中,属性名称可以用 @ModelAttribute(“attributeName”) 在标记方法的时候指定,若未指定,则使用返回类型的类名称(首字母小写)作为属性名称。
@SessionAttributes 注解用来绑定HttpSession中的attribute对象的值,便于在方法中的参数里使用。
有value、types两个属性,可以通过名字和类型指定要使用的attribute 对象。
用于标记一个类时,@SessionAttributes 定义了需要存放到 session 中的属性,而且这个模型中也有对应的属性,等处理器方法执行完成后, Spring 会把模型中对应的属性添加到 session 中,这样在下一次请求时就可以从 session 中访问到上一次保存的属性值。
3.4 @PathVariable 和 @RequestParam
两者都可以从url中获取到参数。@PathVariable 一般只用来获取 uri 参数模板中的参数,形如 @RequestMapping(value=”/page/{id}”) 中的 id 参数。这儿的 URI 模板参数还可以是正则表达式,如:@RequestMapping(value=”/page/{regexp1:[a-z-]+}”)。
而@RequestParam不仅可以用来获取 URL 请求中的查询参数,url 中 ? 后面的部分,还可以用来获取 post 请求里的参数。@RequestParam有三个参数:defaultValue 指定默认值,value 指定传入的参数,required 指定该参数是否必填。
如果使用了该注解,并且没有定义required选项,则提交没有包含该注解指定参数的请求是,会报400错误:Required String parameter is not present。
出现400错误:The request sent by the client was syntactically incorrect (),的原因是使用 @RequestParam等注解什么参数的时候,传入的参数对象和定义的不一致或者无法转换导致。
如:定义的Date对象的输入参数,但是传入空字符串,就会报这个错误。
另外:@RequestHeader 注解用在方法的参数上时,用于把Request请求中 header 部分的值绑定到方法的参数上。
@CookieValue 注解用于把Request请求中关于 cookie 的值绑定到方法的参数上。
3.5 @ResponseBody
该注解用于将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区。常用于返回的数据不是html标签的页面,而是其他某种格式的数据时(如json、xml等)。
3.6 以上注解示例如下
package com.configservice.controller; import com.configservice.service.UserInfoService; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpSession; @Controller // 标记当前类是一个控制器,处理url请求 @RequestMapping(value = "/user/") // 指定该控制器的前缀为 user/ @SessionAttributes(value = {"version", "count"}) public class UserController { @Resource // 标记 byType 自动加载 userInfoService private UserInfoService userInfoService; // 定义模型间共享属性 version @ModelAttribute("version") public String getVersion() { return "1.0.0"; } // 定义模型间共享属性 count @ModelAttribute("count") public int getTime() { return 10; } @RequestMapping(value = "list/{id}", // 处理路由: /user/list/{id} method = {RequestMethod.GET, RequestMethod.POST}, // 指定处理 GET 和 POST 方法 consumes = "application/json, text/html", // 指定处理头部中 Content-Type 为 application/json, text/html的请求 produces = "text/json;charset=UTF-8", // 指定返回类型,仅当request请求头中的(Accept)类型为 text/json;charset=UTF-8 时才返回 params = "from=uusama.com", // 指定处理请求参数有 from 并且值为 uusama.com 的请求 headers = "referer=http://uusama.com/" // 指定request的头部中包含:referer=http://uusama.com/ 才处理 ) @ResponseBody // 指定返回值直接写到body public String getList(@PathVariable("id") int id, // 指定模板参数 id @RequestParam(value = "from", // 指定参数名为 from defaultValue = "", // 指定请求参数 from 的默认值 required = true) String from, // 指定请求参数 from 为必须 @ModelAttribute(value = "version") String version, // 指定模型共享属性 version @RequestHeader("Accept-Encoding") String encoding, // 将请求头部恩Accept-Encoding的值绑定到encoding参数 @CookieValue("JSESSIONID") String cookie, // 将Cookie中的JSESSIONID值绑定到参数cookie HttpSession session // 请求的session ) { System.out.println("id: " + id + " from: " + from + " version:" + version + " encoding:" + encoding + " cookie:" + cookie + " session:" + session.getAttributeNames()); return "success"; } }