Spring MVC常用的注解及解释

常用的注解

  • @Controller
  • @RequestMapping
  • @RequestBody
  • @ResponseBody
  • @ModelAttribute
  • @RequestParam
  • @PathVariable
  • @ExceptionHandler
  • @ControllerAdvice
  • @Autowired
  • @Override
  • @Transactional
  • @Param
  • @Component
  • @Configuration
  • @Bean
  • @ComponentScan
  • @Service
  • @Repository
  • @Value

注解的具体解释

@Controller(注入服务)

@Component扩展,被@Controller注解的类表示Web层实现,从而见到该注解就想到Web层实现,使用方式和@Component相同;
在SpringMVC中只需要使用这个标记一个类是Controller,然后使用@RequestMapping和@RequestParam等一些注解用以定义URL请求和Controller方法之间的映射,这样的Controller就能被外界访问到。此外,Controller不会直接依赖于HttpServletRequest 和HttpServletResponse 等HttpServlet 对象,他们可以通过Controller的方法参数灵活的获取到。

@Controller
public class TestController {
    @RequestMapping ( "/showView" )
    public ModelAndView showView() {
       ModelAndView modelAndView = new ModelAndView();
       modelAndView.setViewName( "viewName" );
       modelAndView.addObject( " 需要放到 model 中的属性名称 " , " 对应的属性值,它是一个对象 " );
       return modelAndView;
    }
}

在上面的例子中,@Controller是标记在类TestController上面的, 所以类TestController就是一个SpringMVC Controller对象。分发处理器会扫描使用了该注解的类的方法,并检测该方法是否调用了@RequestMapping注解。@Controller只是定义了一个控制器类,而使用@RequestMapping注解的方法才是真正处理请求的处理器。然后使用@RequestMapping ( "/showView" )标记在Controller方法上,表示当请求/showView.do 的时候访问的是TestController 的showView 方法,该方法返回了一个包括Model 和View 的ModelAndView 对象。
单单使用@Controller 标记在一个类上还不能真正意义上的说它就是SpringMVC 的一个控制器类,因为这个时候Spring 还不认识它。那么要如何做Spring 才能认识它呢?这个时候就需要把这个控制器类交给Spring 来管理。

  • 第一种方式是在SpringMVC 的配置文件中定义MyController 的bean 对象。
<bean class="com.host.app.web.controller.TestController"/>
  • 第二种方式是在SpringMVC 的配置文件中告诉Spring 该到哪里去找标记为@Controller 的Controller 控制器。
< context:component-scan base-package = "com.host.app.web.controller" >
       < context:exclude-filter type = "annotation"
           expression = "org.springframework.stereotype.Service" />
</ context:component-scan >

注:上面 context:exclude-filter 标注的是不扫描 @Service 标注的类

@RequestMapping

使用 @RequestMapping 来映射 Request 请求与处理器,通过这个注解可以定义不同的处理器映射规则,即为控制器指定可以处理哪些URL请求。
用@RequestMapping 来映射URL 到控制器类,或者是到Controller 控制器的处理方法上。当@RequestMapping 标记在Controller 类上的时候,里面使用@RequestMapping 标记的方法的请求地址都是相对于类上的@RequestMapping 而言的;当Controller 类上没有标记@RequestMapping 注解时,方法上的@RequestMapping 都是绝对路径。这种绝对路径和相对路径所组合成的最终路径都是相对于根路径“/ ”而言的。
在上面那个例子中:
这个控制器里因为TestController 没有被@RequestMapping 标记,所以当需要访问到里面使用了@RequestMapping 标记的showView 方法时,就是使用的绝对路径/showView.do 请求就可以了。
如果改成这样:

@Controller
@RequestMapping ( "/test" )
public class TestController {
    @RequestMapping ( "/showView" )
    public ModelAndView showView() {
       ModelAndView modelAndView = new ModelAndView();
       modelAndView.setViewName( "viewName" );
       modelAndView.addObject( " 需要放到 model 中的属性名称 " , " 对应的属性值,它是一个对象 " );
       return modelAndView;
    }
}

这种情况下是在控制器上加了@RequestMapping 注解,所以当需要访问到里面使用了@RequestMapping 标记的方法showView() 的时候就需要使用showView 方法上@RequestMapping 相对于控制器TestController上@RequestMapping 的地址,即/test/showView.do
URL路径映射:@RequestMapping("/hello"),可以将多个url映射到同一个方法上。
窄化请求映射:
1、在class上面添加@RequestMapping(url)指定通用请求前缀,限制此类下的所有方法请求url必须以请求前缀开头,通过此方法来分类管理url;
2、在方法名上面再设置请求映射url,即添加@RequestMapping注解在方法名上。return “/WEB-INF/jsp/login.jsp” 调用这个方法,重定向的到指定的jsp页面或制定的@RequestMapping的请求路径;
3、 在浏览器上输入相应地址,完成访问。

@RequestBody

用于读取http请求的内容(字符串),通过springMVC提供的HttpMessageConverter接口将读取到的内容转换为json、xml等格式的数据,再转换为java对象绑定到Controller类方法的参数上。
简单点来说,就是把json格式的数据转换为java对象,就举个例子来说明:
编写一个jsp页面来向后台传递json格式的数据(切记是json格式的):

<script>
        jsonData();
        function jsonData()
        {
            $.ajax({
                url:"<%=path%>/user/jsonTest.do",
                contentType:'application/json;charset=utf-8',//设置json格式
                data:'{"username":"张三":"address":"福州"}',
                type:'post',
                success:function(data){
                    alert(data);
                },error:function(error){
                    alert(error);
                }
           })
        }
</script>

然后在后台接收一下:

@RequestMapping("/jsonTest.do")
public void jsonTest(@RequestBody User user) throws Exception
{
    System.out.println(user.toString());
}

这样的话,前台的两个json数据就会自动匹配到User这个对象的属性中了,当然属性名称要一样的。
查看一下结果:


运行结果

可以看到User这个对象中的username和address都已经自动赋值好了,这个就是json格式的数据转java对象了,可以省去我们在后台将json转成java对象。不过在使用的时候,要注意两边的名称要相同,前台的username要对应java对象中的username这样才能成功。否则得到如下:


结果
@ResponseBody

含义:@Responsebody 注解表示该方法的返回的结果直接写入 HTTP 响应正文(ResponseBody)中,一般在异步获取数据时使用,通常是在使用 @RequestMapping 后,返回值通常解析为跳转路径,加上 @Responsebody 后返回结果不会被解析为跳转路径,而是直接写入HTTP 响应正文中。
作用:用于将Controller中方法返回的对象通过适当的HttpMessageConverter转换为指定格式的数据,如:json、xml等,然后写入到response对象的body区,通过Response响应给客户端。需要注意的是,在使用此注解之后不会再走试图处理器,而是直接将数据写入到输入流中,他的效果等同于通过response对象输出指定格式的数据。
使用时机:返回的数据不是html标签的页面,而是其他某种格式的数据时(如json、xml等)使用;
举个例子:

  @RequestMapping("/login")
  @ResponseBody
  public User login(User user){
    return user;
  }

User字段是:userName pwd
那么在前台接收到的数据为:'{"userName":"xxx","pwd":"xxx"}'
效果等同于如下代码:

  @RequestMapping("/login")
  public void login(User user, HttpServletResponse response){
    response.getWriter.write(JSONObject.fromObject(user).toString());
  }
@ModelAttribute

在方法定义上使用该注解: SpringMVC在调用目标处理方法前, 会先逐个调用在方法级上标注了@ModelAttribute的方法;
在方法的入参前使用该注解:可以从隐含对象中获取隐含的模型数据中获取对象,再将请求参数 –绑定到对象中,再传入入参将方法入参对象添加到模型中。

@RequestParam

处理简单类型的绑定,用 @RequestParam 绑定 HttpServletRequest 请求参数到控制器方法参数,即在处理方法入参处使用该注解,可以把请求参数传递给请求方法。

@RequestMapping ( "requestParam" )
public String testRequestParam( @RequestParam(required=false) String name, @RequestParam ( "age" ) int age) {
       return "requestParam" ;
} 

在上面代码中利用@RequestParam 从HttpServletRequest 中绑定了参数name 到控制器方法参数name ,绑定了参数age 到控制器方法参数age 。当没有明确指定从request 中取哪个参数时,Spring 在代码是debug 编译的情况下会默认取跟方法参数同名的参数,如果不是debug 编译的就会报错。此外,当需要从request 中绑定的参数和方法的参数名不相同的时候,也需要在@RequestParam 中明确指出是要绑定哪个参数。在上面的代码中如果访问 /requestParam.do?name=hello&age=1 则Spring 将会把request请求参数name的值hello赋给对应的处理方法参数name ,把参数age 的值1 赋给对应的处理方法参数age 。
在@RequestParam 中除了指定绑定哪个参数的属性value之外,还有一个属性required,它表示所指定的参数是否必须在request 属性中存在,默认是true,表示必须存在,当不存在时就会报错。在上面代码中我们指定了参数name的required的属性为false ,而没有指定age 的required 属性,这时候如果我们访问/requestParam.do而没有传递参数的时候,系统就会抛出异常,因为age 参数是必须存在的,而我们没有指定。而如果我们访问 /requestParam.do?age=1 的时候就可以正常访问,因为我们传递了必须的参数age ,而参数name是非必须的,不传递也可以。
value:参数名,即入参的请求参数名字
如:value="id",表示将请求的参数区的名字为id的参数的值等待传入;
require:是否必需,默认是true,表示请求中一定要有相应的参数,否则会报400错误。且在每个参数定义前设置。
defaultValue:默认值,表示如果请求中没有同名参数时的默认值。
通过 require=true 限定参数id必须传递,如果不传递会报400错误;
可以使用defaultValue设置默认值,即使 require=true 也可以不传递id参数。

@PathVariable

绑定URL占位符到入参。

@ExceptionHandler

注解到方法上, 出现异常时会执行该方法。

@ControllerAdvice

使一个Controller成为全局的异常处理类, 类中用ExceptinHandler方法注解的方法可以处理所有Controller发生的异常。

@Autowired

它可以对类成员变量、方法以及构造函数进行标注,完成自动装配的工作。自动装配的意思就是让Spring从应用上下文中找到对应的bean的引用,并将它们注入到指定的bean。通过@Autowired注解可以完成自动装配。使用@Autowired 来消除代码Java代码里面的getter/setter与bean属性中的property。当然,getter看个人需求,如果私有属性需要对外提供的话,应当予以保留。
只要对应类型的bean有且只有一个,则会自动装配到该属性上。如果没有找到对应的bean,应用会抛出对应的异常,如果想避免抛出这个异常,则需要设置@Autowired(required=false)。不过,在应用程序设计中,应该谨慎设置这个属性,因为这会使得你必须面对NullPointerException的问题。
如果存在多个同一类型的bean,则Spring会抛出异常,表示装配有歧义,解决办法有两个:

  • 通过@Qualifier注解指定需要的bean的ID;
  • 通过@Resource注解指定注入特定ID的bean;
    @Autowired 和 @Service("")的配合使用:
@Controller
@RequestMapping("/test")
public class StudentController {
   @Autowired
   private StudentService studentService;
   @RequestMapping("getInfo")
   @ResponseBody
   public int  getInfo(Student student){
       return studentService.insertStu(student);
   }
}

在Controller中对私有变量用@Autowired标注,因为studentService这个变量是service层接口,所以要找到他的实现类StudentServiceImpl,并在实现类上添加@Service("")的注释。

@Service("StudentService")
public class StudentServiceImpl implements StudentService {
    @Autowired
    private StudentDao studentDao;
 
    public int insertStu(Student student){
        return studentDao.insertInfo(student);
    }
}

如果不添加@Service("")注释,会报如下错误。因为@Autowired 将寻找与之匹配的bean来创建(类名)bena,但因为删除接口实现类上@Service("")注解,找不到服务对象,@Autowired自然也就找不到实例bean了。


报错
@Override

@Override是伪代码,表示重写(当然不写也可以),不过也有好处:

  • 可以当注释用,方便阅读;
  • 编译器可以给你验证@Override下面的方法名是否是你父类中所有的,如果没有则报错。
    例如,你如果没写@Override,而你下面的方法名又写错了,这时你的编译器是可以编译通过的,因为编译器以为这个方法是你的子类中自己增加的方法。
    举例:在重写父类的onCreate时,在方法前面加上@Override 系统可以帮你检查方法的正确性。
    @Override
    public void onCreate(Bundle savedInstanceState)
    {…….}
    这种写法是正确的,如果你写成:
    @Override
    public void oncreate(Bundle savedInstanceState)
    {…….}
    编译器会报如下错误:
    The method oncreate(Bundle) of type HelloWorld must override or implement a supertype method,以确保你正确重写onCreate方法(因为oncreate应该为onCreate)。
    而如果你不加@Override,则编译器将不会检测出错误,而是会认为你为子类定义了一个新方法:oncreate
@Transactional

使用时机
对数据库的数据进行批量或连表操作时,为了保证数据的一致性和正确性,则需要添加事务管理机制进行管理;
当对数据库的数据操作失败时,事务管理可以很好保证所有的数据 回滚 到原来的数据,如果操作成功,则保证所有的需要更新的数据持久化。

回滚(Rollback)指的是程序或数据处理错误,将程序或数据恢复到上一次正确状态的行为。
回滚包括程序回滚和数据回滚等类型。
删除由一个或多个部分完成的事务执行的更新。为保证应用程序、数据库或系统错误后还原数据库的完整性,需要使用回滚。
回滚泛指程序更新失败, 返回上一次正确状态的行为。
回滚与恢复有本质的区别。
升级回滚:是指因升级中所发生的意外而自动回滚。

使用优点

  • 开发团队达成一致约定,明确标注事务方法的编程风格;
  • 保证事务方法的执行时间尽可能短,不要穿插其他网络操作,RPC/HTTP请求或者剥离到事务方法外部;
  • 不是所有的方法都需要事务,如果只有一条修改操作、只读操作不需要事务控制。
@Param

mybatis提供了一个使用注解来参入多个参数的方法,这种方法需要在接口的参数上添加@Param注解。

/**
     * 更新学生信息
     * @param student
     * @return
     */
 int updateInfo(@Param("student") Student student);

在这个updateInfo的方法中需要传入多个参数,那么在进行mybatis配置的时候,没有办法同事配置多个参数,所以需要@Param这个注解来绑定参数对象。student这个参数中包含了三个对象,用@Param来绑定参数并命名为"student"。并且在mapper.xml文件中调用时,对逐个参数在调用时,要加上 student. 的前缀。如下所示:

<update id="updateInfo">
        UPDATE test_student SET name=#{student.name},age=#{student.age} WHERE id=#{student.id}
    </update>

注意事项:在使用@Param来注解时,如果使用#{ } 或者 ${ } 的方式都可以,但如果不是用@Param注解时,则必须使用#{ }方式。

@Component

把普通pojo实例化到spring容器中,相当于配置文件中的 <bean id="" class=""/>
定义Spring管理Bean
在Java配置文件中有两个注解值得注意:
@Configuration 表示这个.java文件是一个配置文件;
@ComponentScan 表示开启Component扫描,Spring将会设置该目录以及子目录下所有被@Component注解修饰的类。

@Configuration

表示这个类是一个spring 配置类,一般这里面会定义Bean,会把这个类中bean加载到spring容器中。

@Configuration
@ComponentScan(basePackageClasses = {CDPlayer.class, DVDPlayer.class})
public class SoundSystemConfig {
}
@Value

@Value可以读取配置文件中的值,格式为@Value("${key}")

    @Value("${fullAPI}")
    public String FULL_API;

    @Value("${videoAPI}")
    public String VIDEO_API;

    @Value("${videoLibAPI}")
    public String VIDEO_LIB_API;
@Bean

在JavaConfig中的属性注入:

@Bean
public CDPlayer cdPlayer() {
    return new CDPlayer(sgtPeppers());
}

看起来是函数调用,实际上不是:由于sgtPeppers()方法被@Bean注解修饰,所以Spring会拦截这个函数调用,并返回之前已经创建好的bean——确保该SgtPeppers bean为单例。

@ComponentScan

@ComponentScan主要就是定义扫描的路径从中找出标识了需要装配的类自动装配到spring的bean容器中。且这个注解默认会装配标识了@Controller,@Service,@Repository,@Component注解的类到spring容器中。
如果不设置basePackage的话,默认会扫描包的所有类,所以最好还是写上basePackage,减少加载时间。默认扫描*/.class路径,比如这个注解在com.wuhulala下面,那么会扫描这个包下的所有类还有子包的所有类。比如com.wuhulala.service包的应用。
总结@ComponentScan的常用方式:

  • 自定扫描路径下边带有@Controller,@Service,@Repository,@Component注解加入spring容器
  • 通过includeFilters加入扫描路径下没有以上注解的类加入spring容器
  • 通过excludeFilters过滤出不用加入spring容器的类
  • 自定义增加了@Component注解的注解方式
@Service (注入dao)

用于标注服务层,主要用来进行业务的逻辑处理。@Component扩展,被@Service注解的POJO类表示Service层实现,从而见到该注解就想到Service层实现,使用方式和@Component相同;

@Repository (实现dao访问)

用于标注数据访问层,也可以说用于标注数据访问组件,即DAO组件。@Component扩展,被@Repository注解的POJO类表示DAO层实现,从而见到该注解就想到DAO层实现,使用方式和@Component相同;

@RequestParam和@RequestBody的区别

@RequestParam
A) 常用来处理简单类型的绑定,通过Request.getParameter() 获取的String可直接转换为简单类型的情况( 由String到 简单类型的转换操作由ConversionService配置的转换器来完成);因为使用request.getParameter()方式获取参数,所以可以处理get 方式中queryString的值,也可以处理post方式中 body data的值。
B)用来处理Content-Type: 为 application/x-www-form-urlencoded编码的内容,提交方式GET、POST。(不设置这个属性,好像这就是默认值)
C) 该注解有两个属性: value、required; value用来指定要传入值的id名称,required用来指示参数是否必须绑定。
在方法参数里面如是:

public WebResponse findReleventPolicyPage(@RequestParam("pageSize") Integer pageSize,
                                           @RequestParam("pageNum") Integer pageNum,
                                           @RequestParam("type") Integer type){}

@RequestBody
处理HttpEntity传递过来的数据,一般用来处理非Content-Type: application/x-www-form-urlencoded编码格式的数据。
GET请求中,因为没有HttpEntity,所以@RequestBody并不适用。
POST请求中,通过HttpEntity传递的参数,必须要在请求头中声明数据的类型Content-Type,SpringMVC通过使用HandlerAdapter 配置的HttpMessageConverters来解析HttpEntity中的数据,然后绑定到相应的bean上。
用于将Controller中方法返回的对象,通过适当的HttpMessageConverter转换为指定格式的数据,如:json、xml等,然后通过Response响应给客户端。
在方法参数里面如是:

@RequestMapping("/json_test")
// 响应json数据,把pojo对象转换成json数据并响应
@ResponseBody
public Items jsonTest (@RequestBody Items items){  // 接受json数据并转换成pojo对象
    return items;
}
总结

在GET请求中,不能使用@RequestBody。
在POST请求,可以使用@RequestBody和@RequestParam,但是如果使用@RequestBody,对于参数转化的配置必须统一。
举个例子,在SpringMVC配置了HttpMessageConverters处理栈中,指定json转化的格式,如Date转成‘yyyy-MM-dd’,则参数接收对象包含的字段如果是Date类型,就只能让客户端传递年月日的格式,不能传时分秒。因为不同的接口,它的参数可能对时间参数有不同的格式要求,所以这样做会让客户端调用同事对参数的格式有点困惑,所以说扩展性不高。
如果使用@RequestParam来接受参数,可以在接受参数的model中设置@DateFormat指定所需要接受时间参数的格式。
另外,使用@RequestBody接受的参数是不会被Servlet转化统一放在request对象的Param参数集中,@RequestParam是可以的。
综上所述,一般情况下,推荐使用@RequestParam注解来接受Http请求参数。

参考文献:https://blog.csdn.net/steriles_/article/month/2019/08

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343