Spring注解学习笔记

版权声明:原创内容,如需转载请联系作者。 https://blog.csdn.net/CrankZ/article/details/81984305

什么是注解

传统的Spring做法是使用.xml文件来对bean进行注入或者是配置aop、事物,这么做有两个缺点:

  1. 如果所有的内容都配置在.xml文件中,那么.xml文件将会十分庞大;如果按需求分开.xml文件,那么.xml文件又会非常多。总之这将导致配置文件的可读性与可维护性变得很低
  2. 在开发中在.java文件和.xml文件之间不断切换,是一件麻烦的事,同时这种思维上的不连贯也会降低开发的效率

为了解决这两个问题,Spring引入了注解,通过"@XXX"的方式,让注解与Java Bean紧密结合,既大大减少了配置文件的体积,又增加了Java Bean的可读性与内聚性。

注解组成

java annotation 的组成中,有3个非常重要的主干类。它们分别是:

  1. Annotation.java
  2. ElementType.java
  3. RetentionPolicy.java

Annotation.java

public interface Annotation {
    boolean equals(Object obj);
    int hashCode();
    String toString();
    Class<? extends Annotation> annotationType();
}
  • Annotation 是个接口
  • 有四个函数

ElementType.java

public enum ElementType {
    TYPE,               // 类、接口(包括注释类型)或枚举
    FIELD,              // 字段(包括枚举常量)
    METHOD,             // 方法
    PARAMETER,          // 参数
    CONSTRUCTOR,        // 构造方法
    LOCAL_VARIABLE,     // 局部变量
    ANNOTATION_TYPE,    // 注释类型
    PACKAGE             // 包
}

ElementType 是Enum枚举类型,它用来指定Annotation的类型。

     “每1个Annotation” 都与 “n个ElementType”关联。当Annotation与某个ElementType关联时,就意味着:Annotation有了某种用途。
     例如,若一个Annotation对象是METHOD类型,则该Annotation只能用来修饰方法。

RetentionPolicy.java

public enum RetentionPolicy {
    SOURCE,    // 只保留在源代码中,编译器编译时,直接丢弃这种注解,不记录在.class文件中
    CLASS,    // 编译器把注解记录在class文件中。当运行Java程序时,JVM中不可获取该注解信息,这是默认值
    RUNTIME    // 编译器把注解记录在class文件中。当运行Java程序时,JVM可获取该注解信息。程序可以通过反射获取该注解的信息
}

RetentionPolicy 是Enum枚举类型,它用来指定Annotation的策略。

通俗点说,就是不同RetentionPolicy类型的Annotation的作用域不同

     “每1个Annotation” 都与 “1个RetentionPolicy”关联。

  • SOURCE:只保留在源代码中,编译器编译时,直接丢弃这种注解,不记录在.class文件中。
  • CLASS:编译器把注解记录在class文件中。当运行Java程序时,JVM中不可获取该注解信息,这是默认值。
  • RUNTIME:编译器把注解记录在class文件中。当运行Java程序时,JVM可获取该注解信息。程序可以通过反射获取该注解的信息。

准备工作

使用注解是在 Spring2.5(2.0以后陆续加入) 以后新加的功能,所以至少要保证版本是 2.5+,并且引入了注解的包
然后需要在 Spring 的配置文件中告诉它你使用了注解,最简单的一种方式就是加入下面的一句:
<context:component-scan base-package="com.xxx" />
它的意思就是开启自动扫描,会自动扫描你设置的包路径下的所有类,如果有注解就进行解析
这就是所谓的用注解来构造 IoC 容器;base-package 是可以指定多个包的,用逗号分割

注解分类

注解可以按照很多种方式分类,这里我按照下面的来分类

1、声明bean的注解

  1. @Componentn
  2. @Repository
  3. @Service
  4. @Controller

2、注入bean的注解

  1. @Autowired
  2. @Qualifier
  3. @Resource

3、Spring MVC常见注解

  1. @Controller
  2. @RequestMapping
  3. @RequestParam
  4. @PathVariable
  5. @RequestBody
  6. @RespopnseBody
  7. @ResController

其他。。。

声明bean的注解

注解

作用范围

含义

@Component

 注解在类上,可以作用在任何层次。

泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。

是一个泛化的概念,仅仅表示一个组件 (Bean) ,将一个实体类,放入bean中。

@Repository

注解在类上

用于标注数据访问组件,即DAO组件。

@Service

注解在类上

用于标注业务层组件

@Controller

注解在类上

用于标注控制层组件(如struts中的action)

所有声明后的类,都用统一被Spring IoC容器管理。

@Component

源码

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
   String value() default "";
}

源码说明:

  1. @Target(ElementType.TYPE):可以用在类、接口(包括注释类型)或枚举上
  2. @Retention(RetentionPolicy.RUNTIME):编译器把注解记录在class文件中。当运行Java程序时,JVM可获取该注解信息。程序可以通过反射获取该注解的信息。
  3. @Documented:该注解能出现在Javadoc中

当一个类加上@Component注解后,

  1. 声明该类为通用的bean,并会被Spring IoC容器所管理
  2. 可以指定value,也就是bean的名字。不指定的话,默认为此类的首字母小写
  3. @Component 是所有受 Spring 管理组件的通用形式
  4. @Component 不推荐使用:因为太通用所以不推荐,实在不好归类的时候再用。

@Repository

源码

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Repository {
   @AliasFor(annotation = Component.class)
   String value() default "";
}

源码说明:

  1. @Target(ElementType.TYPE):可以用在类、接口(包括注释类型)或枚举上
  2. @Retention(RetentionPolicy.RUNTIME):编译器把注解记录在class文件中。当运行Java程序时,JVM可获取该注解信息。程序可以通过反射获取该注解的信息。
  3. @Documented:该注解能出现在Javadoc中
  4. @Component:说明该注解拥有@Component注解的所有属性,可理解为继承自@Component

当一个类加上@Controller注解后,

  1. 声明该类为数据访问层的bean,并会被Spring IoC容器所管理
  2. 可以指定value,也就是bean的名字。不指定的话,默认为此类的首字母小写

使用例子

@Repository
public class UserDaoImpl extends BaseDaoImpl<User> {
   //TODO
}

@Service

源码

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
   @AliasFor(annotation = Component.class)
   String value() default "";
}

源码说明

  1. @Target(ElementType.TYPE):可以用在类、接口(包括注释类型)或枚举上
  2. @Retention(RetentionPolicy.RUNTIME):编译器把注解记录在class文件中。当运行Java程序时,JVM可获取该注解信息。程序可以通过反射获取该注解的信息。
  3. @Documented:该注解能出现在Javadoc中
  4. @Component:说明该注解拥有@Component注解的所有属性,可理解为继承自@Component

当一个类加上@Service注解后,

  1. 声明该类为业务层的bean,并会被Spring IoC容器所管理
  2. 可以指定value,也就是bean的名字。不指定的话,默认为此类的首字母小写

使用例子:

@Service
public class UserService{
    //TODO
}

@Controller

源码

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
   @AliasFor(annotation = Component.class)
   String value() default "";
}

源码说明

  1. @Target(ElementType.TYPE):可以用在类、接口(包括注释类型)或枚举上
  2. @Retention(RetentionPolicy.RUNTIME):编译器把注解记录在class文件中。当运行Java程序时,JVM可获取该注解信息。程序可以通过反射获取该注解的信息。
  3. @Documented:该注解能出现在Javadoc中
  4. @Component:说明该注解拥有@Component注解的所有属性,可理解为继承自@Component

将一个类加上@Controller注解后,

  1. 声明该类为控制层的bean,并会被Spring IoC容器所管理
  2. 可以指定value,也就是bean的名字。不指定的话,默认为此类的首字母小写

使用例子

@Controller
public class CompanyController {
    //TODO
}

注入bean的注解

@Autowired

源码

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
   boolean required() default true;
}

源码说明

  1. @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE}):可以作用在CONSTRUCTOR、METHOD、PARAMETER、FIELD、ANNOTATION
  2. @Retention(RetentionPolicy.RUNTIME):编译器把注解记录在class文件中。当运行Java程序时,JVM可获取该注解信息。程序可以通过反射获取该注解的信息。
  3. @Documented:该注解能出现在Javadoc中
  4. @Component:说明该注解拥有@Component注解的所有属性,可理解为继承自@Component

补充说明

  1. @Autowired注解可用于为类的属性、构造器、方法进行注值。
  2. 默认情况下,其依赖的对象必须存在(bean可用),如果需要改变这种默认方式,可以设置其required属性为false。
  3. @Autowired注解默认按照类型装配,如果容器中包含多个同一类型的Bean,那么启动容器时会报找不到指定类型bean的异常,解决办法是结合@Qualified注解进行限定,指定注入的bean名称

@Qualifier

源码

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {
   String value() default "";
}

源码说明

  1. @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE}):可以作用在CONSTRUCTOR、METHOD、PARAMETER、FIELD、ANNOTATION
  2. @Retention(RetentionPolicy.RUNTIME):编译器把注解记录在class文件中。当运行Java程序时,JVM可获取该注解信息。程序可以通过反射获取该注解的信息。
  3. @Inherited:@Inherited指定注解具有继承性。如果某个类使用了@xxx注解(定义该注解时使用了@Inherited修饰)修饰,则其子类将自动被@xxx修饰。
  4. @Documented:该注解能出现在Javadoc中

补充说明

这个注解需要配合@Autowired使用,是用来指定注入 Bean 的名称 ;因为@Autowired是按照类型来匹配的,如果一个类型有两个实现,直接使用@Autowired就会报错。

也就是说如果容器中有一个以上匹配的 Bean,则可以通过 @Qualifier 注解限定 Bean 的名称,否则调用的时候会抛异常
比如:某个 bean 中引用了一个接口,实现这个接口的 bean 有多个,Spring 在注入的时候就不知道注入那一个了,这样要么删除其他的 bean 要么就是使用 @Qualifier 注解。

我们来举个例子

Car.java

public interface Car{
    public String carName();
}

两个实现类BMW和Benz:

BMW.java

@Service
public class BMW implements Car{
    public String carName(){
        return "BMW car";
    }
}

Benz.java

@Service
public class Benz implements Car{
    public String carName(){
        return "Benz car";
    }
}

写一个CarFactory,引用Car:

CarFactory.java

@Service
public class CarFactory{
    // 报错!!!
    @Autowired
    private Car car;
    public String toString(){
        return car.carName();
    }
}

不用说,一定是报错的,Car接口有两个实现类,Spring并不知道应当引用哪个实现类。这种情况通常有两个解决办法:

  1. 删除其中一个实现类,Spring会自动去base-package下寻找Car接口的实现类,发现Car接口只有一个实现类,便会直接引用这个实现类
  2. 实现类就是有多个该怎么办?此时可以使用@Qualifier注解

使用Qualifier()后的CarFactory.java

@Service
public class CarFactory{
    @Autowired
    @Qualifier("BMW")
    private Car car;
    public String toString(){
        return car.carName();
    }
}

@Resource

源码

// 并不是来自Spring,而是来自于Java
package javax.annotation;

@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource {
    String name() default "";
    String lookup() default "";
    Class<?> type() default java.lang.Object.class;
    enum AuthenticationType {
            CONTAINER,
            APPLICATION
    }
    AuthenticationType authenticationType() default AuthenticationType.CONTAINER;
    boolean shareable() default true;
    String mappedName() default "";
    String description() default "";
}

源码说明:

  1. @Target({TYPE, FIELD, METHOD}):可以作用在TYPE、FIELD、METHOD
  2. @Retention(RUNTIME):编译器把注解记录在class文件中。当运行Java程序时,JVM可获取该注解信息。程序可以通过反射获取该注解的信息。

补充说明

  1. 对于@Resource注解,它并不属于spring的注解,而是来自于JSR-250。
  2. @Resource默认为名称注入,但也可以指定 name或type 进行注入。

使用例子

public class Zoo {
    // 通过名称进行注入
    @Resource(name = "tiger")
    private Tiger tiger;

    // 通过类型进行注入
    @Resource(type = Monkey.class)
    private Monkey monkey;
}

@Resource与@Autowired区别

  1. @Autowired 默认按照 byType 方式进行 bean 匹配,@Resource 默认按照 byName 方式进行 bean 匹配
  2. @Autowired 是 Spring 的注解,@Resource 是 J2EE 的注解

Spring 属于第三方的,J2EE 是 Java 自己的东西

Spring MVC常见注解

@Controller

同上

@RequestMapping

源码

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {

// 有以下属性
String name() default "";
   @AliasFor("path")
   String[] value() default {};
   @AliasFor("value")
   String[] path() default {};
   RequestMethod[] method() default {};
   String[] params() default {};
   String[] headers() default {};
   String[] consumes() default {};
   String[] produces() default {};
}

源码说明:

  1. @Target({ElementType.METHOD, ElementType.TYPE}):可以作用在方法和类上
  2. @Retention(RetentionPolicy.RUNTIME):作用在运行时
  3. RequestMethod[]:以指定访问方式,如果不指定,默认既可以通过GET也可通过POST方式来访问

这个注解的属性有很多,这里只介绍常用的几种

使用例子

@Controller
// 可以作用在类上
@RequestMapping(value = "/aaa")
public class HappyController {
    // 可以作用在方法上
    // value可省略不写
    @RequestMapping("/bbb")
    public void sayHello() {
        //TODO
    }
    // 可指定访问方式,GET还是POST
    @RequestMapping(value = "/ccc", method = RequestMethod.GET)
    public void sayHaHa() {
        //TODO
    }
}

@RequestParam

源码

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
   @AliasFor("name")
   String value() default "";
   @AliasFor("value")
   String name() default "";
   boolean required() default true;
   String defaultValue() default ValueConstants.DEFAULT_NONE;
}

源码说明:

  1. @Target(ElementType.PARAMETER):只能作用在参数上
  2. @Retention(RetentionPolicy.RUNTIME):运行时
  3. boolean required() default true;:required默认为true,也就是说参数必填
  4. defaultValue:默认参数,也就是不填参数时,默认是什么

补充说明:

@RequestParam :将请求的参数绑定到方法中的参数上,有required参数,默认为true,也就是改参数必须要传。如果改参数可以传可不传,可以配置false。

使用例子

@RequestMapping("/happy")
public String sayHappy(
// 作用在参数上
// 参数名为name,必填
@RequestParam(value = "name", required = true) String name,
// age不是必填项,默认为20
@RequestParam(value = "age", required = false, defaultValue = "20") String age){
        //TODO
        }

@PathVariable

源码

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PathVariable {
   @AliasFor("name")
   String value() default "";
   @AliasFor("value")
   String name() default "";
   boolean required() default true;
}

源码说明:

  1. @Target(ElementType.PARAMETER):作用在参数上
  2. @Retention(RetentionPolicy.RUNTIME):运行时

补充说明:

  1. @PathVariable : 该注解用于方法修饰方法参数,会将修饰的方法参数变为可供使用的uri变量(可用于动态绑定)。
  2. @PathVariable中的参数可以是任意的简单类型,如int, long, Date等等。Spring会自动将其转换成合适的类型或者抛出 TypeMismatchException异常。当然,我们也可以注册支持额外的数据类型。
  3. @PathVariable支持使用正则表达式,这就决定了它的超强大属性,它能在路径模板中使用占位符,可以设定特定的前缀匹配,后缀匹配等自定义格式。

使用例子:

@RequestMapping(value="/happy/{dayid}",method=RequestMethod.GET)
// 绑定 {dayid} 到String dayid
public String findPet(@PathVariable String dayid, Model mode) {
    //TODO
}

@RequestBody

源码

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestBody {
   boolean required() default true;
}

源码说明:

  1. @Target(ElementType.PARAMETER):只能作用在参数上
  2. @Retention(RetentionPolicy.RUNTIME):运行时

补充说明

添加 @RequestBody 后 Spring 会根据请求中的 Content-Type 头信息来选择合适的转换器, 将请求数据转为 Java 对象

比如Content-Type是application/json, 那么就是 JSON -> Model

使用例子:

@RequestMapping(value = "/something", method = RequestMethod.PUT)
public void handle(@RequestBody String body, Writer writer) throws IOException {
    writer.write(body);
}

@ResponseBody

源码

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseBody {

}

源码说明:

  1. @Target({ElementType.TYPE, ElementType.METHOD}):可以作用在类和方法上
  2. @Retention(RetentionPolicy.RUNTIME):运行时

补充说明:

  1. 添加 @ResponseBody 后 Spring 会根据请求中的 Accept 头信息来选择合适的转换器, Java 对象转化为客户端可接受的表述形式,比如Accept头部信息包含“application/json”, 就是Model -> JSON
  2. @ResponseBody在输出JSON格式的数据时,会经常用到。

使用例子

@RequestMapping("/getJson")
@ResponseBody
public User jsonTest() {
    User user = new User ();
    user.setName("张三");
    user.setAge(20);
    return user;
}

结果

@RestController

  1. @RestController=@Controller+@ResponseBody
  2. 如果一个类有这个注解,那么就会在这个类下的每个方法都默认加上@ResponseBody

Java配置

除了xml和注解配置,Spring还提供了Java配置,什么叫java配置,即创建一个类来进行信息的注入,它和注解配置相似,不同的是它不是在bean的实现类中进行注解,而是新创建一个类进行配置: 这里涉及到了两个注解:

  • @Configuration
  • @Bean

User.java

public class User {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    //...
}

JavaConfig.java

//用来声明这个是由Java来配置
@Configuration
public class JavaConfig {
    @Bean
    public publisher createUser() {
        return new user("张三",20);
    }
}

JavaConfigTest.java

@Test
public void javaConfigTest() {
    ApplicationContext context =
            new AnnotationConfigApplicationContext(JavaConfig.class);
    User user = context.getBean("user", User.class);
    System.out.println(user.getName());
}

运行结果

参考:

http://www.voidcn.com/article/p-vnuuxhnq-bcq.html

http://blog.leanote.com/post/[email protected]/spring%E5%B8%B8%E7%94%A8%E6%B3%A8%E8%A7%A3%E6%B1%87%E6%80%BB

https://www.bilibili.com/video/av21450362

https://bfchengnuo.com/2017/09/04/Spring%E4%B8%AD%E7%9A%84%E6%B3%A8%E8%A7%A3%E4%BD%BF%E7%94%A8%E6%80%BB%E7%BB%93/

 

猜你喜欢

转载自blog.csdn.net/CrankZ/article/details/81984305