• 认真地记录技术中遇到的坑!
  • 能摸鱼真是太好啦!嘿嘿嘿!

SpringMVC依赖注入

Java 悠悠 7年前 (2017-10-19) 4009次浏览 0个评论

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 用于指定构造函数的参数,可选的属性如下:

  1. type:构造函数参数类型
  2. value:初始值,用于基本类型如 int ,String等
  3. index:显式地指定参数索引,从0开始
  4. 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中用得最多的应该就是使用注解的依赖注入了,常用的有:

  1. @Controller:声明一个控制器,系统表示层
  2. @Service:声明一个服务(对dao的封装),系统业务逻辑层
  3. @Repository:声明一个dao层(实现dao的访问,如Mybatis的mapper),系统数据访问层
  4. @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的转配规则如下:

  1. 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
  2. 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
  3. 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
  4. 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配

另外还有一些注解用于控制自动加载的行为:

  1. @Scope注解:标注一个Bean的作用范围
  2. @PostConstruct注解:标注一个方法在Spring初始化Bean之后执行
  3. @PreDestroy注解:标注一个方法在Spring销毁Bean之前执行

3. 其他一些常用Spring注解说明

3.1 @Controller

标记一个类为控制层处理由DispatcherServlet 分发的请求。

3.2 @RequestMapping

配合 @Controller 一起使用,标记特定的地址映射,可用于类或者方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。属性如下:

  1. value:指定请求的实际地址
  2. method:指定请求的method类型, GET、POST、PUT、DELETE等
  3. consumes:指定处理请求的提交内容类型(Content-Type),例如application/json, text/html
  4. produces:指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回
  5. params:指定request中必须包含某些参数值是,才让该方法处理
  6. 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";
    }
}

转载请注明出处 SpringMVC依赖注入
喜欢 (2)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址