在探究注解前先了解一下注解,何为注解?注解本质上就是一个类,开发中我们可以使用注解 取代 xml配置文件。

注意:在使用注解开发时,请查看对应的框架是否需要开启注解支持,否则用了注解也无效!!!

IOC 容器相关

@Componen:

@Component注解

@component(value):用于创建对象的,作用于类.

value:指定 bean 的 id。如果不指定 value 属性,默认 bean 的 id 是当前类的类名。首字母小写。

@component是 spring 中的一个注解,它的作用就是实现 bean 的注入,代替了传统的 xml 方式注入 bean

1
2
3
4
5
6
//@Component组件 等价于:<bean id="user" class="com.ms.pojo.User"/>
//也说明这个类被Spring接管了,注册到了容器中
@Component("user") //("user")其实就是Bean id
public class User {
这里省略代码.....
}

在 web 开发中,会按照 MVC 三层架构分层!提供 3 个@Component注解衍生注解(功能一样)来取代,其实就是更加语义化

@controller 控制器(表现层注解)
用于标注控制层,相当于 MVC 层中的 web 层

1
2
3
@Controller("userCotroller")	//value相当于Bean中的id
public class UserController {
}

@service 服务(业务层注解)
用于标注服务层,主要用来进行业务的逻辑处理,相当于 MVC 层中的 service 层

1
2
3
4
@Service("userService")	//value相当于Bean中的id
public class UserService {
}

@repository(持久层注解)
用于标注数据访问层,也可以说用于标注数据访问组件,即 DAO 组件,相当于 MVC 层中的 dao 层

1
2
3
@Repository("userDao") 	//value相当于Bean中的id
public class UserDao {
}

总结:

@Component 用于把当前方法的返回值作为 bean 对象存入 spring 的 ioc 容器中
属性: value:用于指定 bean 的 id。当不写时,默认值是当前方法的名称
细节:当我们使用注解配置方法时,如果方法有参数,spring 框架会去容器中查找有没有可用的 bean 对象。
查找的方式和 Autowired 注解的作用是一样的

用于注入数据

@Autowired&@Qualifier&@Resource&@Value

@Autowired&@Qualifier&@Resource&@Value

这是官方的介绍:

This annotation may be used on a field or parameter as a qualifier for
candidate beans when autowiring. It may also be used to annotate other
custom annotations that can then in turn be used as qualifiers.

简单的理解就是:
(1)在使用@Autowire 自动注入的时候,加上@Qualifier(“test”)可以指定注入哪个对象;
(2)可以作为筛选的限定符,我们在做自定义注解时可以在其定义上增加@Qualifier,用来筛选需要的对象。这个理解看下面的代码吧,不好解释。

功能介绍

  1. 首先是对(1)的理解。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //我们定义了两个TestClass对象,分别是testClass1和testClass2
    //我们如果在另外一个对象中直接使用@Autowire去注入的话,spring肯定不知道使用哪个对象
    //会排除异常 required a single bean, but 2 were found
    @Configuration
    public class TestConfiguration {
    @Bean("testClass1")
    TestClass testClass1(){
    return new TestClass("TestClass1");
    }
    @Bean("testClass2")
    TestClass testClass2(){
    return new TestClass("TestClass2");
    }
    }

    下面是正常的引用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @RestController
    public class TestController {

    //此时这两个注解的连用就类似 @Resource(name="testClass1")
    @Autowired
    @Qualifier("testClass1")
    private TestClass testClass;

    public Object test(){
    return testClassList;
    }
    }

    @Autowired 和@Qualifier 这两个注解的连用在这个位置就类似 @Resource(name=“testClass1”)

  2. 对(2)的理解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    @Configuration
    public class TestConfiguration {
    //我们调整下在testClass1上增加@Qualifier注解
    @Qualifier
    @Bean("testClass1")
    TestClass testClass1(){
    return new TestClass("TestClass1");
    }

    @Bean("testClass2")
    TestClass testClass2(){
    return new TestClass("TestClass2");
    }
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @RestController
    public class TestController {
    //我们这里使用一个list去接收testClass的对象
    @Autowired
    List<TestClass> testClassList= Collections.emptyList();

    @GetMapping("/test")
    public Object test(){
    return testClassList;
    }
    }

    我们调用得到的结果是

    1
    2
    3
    4
    5
    6
    7
    8
    9
    [
    {
    "name": "TestClass1"
    },
    {
    "name": "TestClass2"
    }
    ]

    我们可以看到所有的 testclass 都获取到了。接下来我们修改下代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @RestController
    public class TestController {

    @Qualifier //我们在这增加注解
    @Autowired
    List<TestClass> testClassList= Collections.emptyList();

    @GetMapping("/test")
    public Object test(){
    return testClassList;
    }
    }

    和上面代码对比就是在接收参数上增加了@Qualifier 注解,这样看是有什么区别,我们调用下,结果如下:

    1
    2
    3
    4
    5
    [
    {
    "name": "TestClass1"
    }
    ]

    返回结果只剩下增加了@Qualifier 注解的 TestClass 对象,这样我们就可以理解官方说的标记筛选是什么意思了。
    另外,@Qualifier 注解是可以指定 value 的,这样我们可以通过 values 来分类筛选想要的对象了,这里不列举代码了~

@Resource 用法与@Autowired用法 用法相似,也是做依赖注的,从容器中自动获取 bean。但还是有一定的区别。

  • 在启动 spring 的时候,首先要启动容器;
  • 启动 spring 容器时,会默认寻找容器扫描范围内的可加载 bean,然后查找哪些 bean 上的属性和方法上有@Resource 注解;
  • 找到@Resource 注解后,判断@Resource 注解括号中的 name 属性是否为空,如果为空:看 spring 容器中的 bean 的 id 与@Resource 要注解的那个变量属性名是否相同,如相同,匹配成功;如果不相同,看 spring 容器中 bean 的 id 对应的类型是否与@Resource 要注解的那个变量属性对应的类型是否相等,若相等,匹配成功,若不相等,匹配失败。
  • 如果@Resource 注解括号中的 name 属性不为空,看 name 的属性值和容器中的 bean 的 id 名是否相等,如相等,则匹配成功;如不相等,则匹配失败。
    示例如下:

@Resource 注解注解的 name 属性不为空

首先创建 Person 类,并纳入容器中管理:

1
2
3
4
5
6
7
8
9
10
11
package com.lzj.springboot.resource;
import org.springframework.stereotype.Component;

/*纳入容器中后,bean的id名字为ps*/
@Component(value="ps")
public class Person {

public void say(){
System.out.println("------say()------");
}
}

然后创建 Man 类,类中的属性依赖 Person 类型的 bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.lzj.springboot.resource;
import javax.annotation.Resource;
import org.springframework.stereotype.Component;

@Component
public class Man {

/*从容器中取id名字为ps的bean,如果找不到该bean,spring启动过程中就会报错,表示把Man类型的bean注入到容器中不成功,因为person的属性依赖注入的时候就出错了,所以创建Man的bean的时候肯定不成功。*/
@Resource(name="ps")
private Person person;

/*依赖注入失败,因为Person类型注入到容器中的bean的id指定为ps,所以从容器中获取id为person的bean就会失败*/
// @Resource(name="person")
// private Person ps;

public void work(){
person.say();
System.out.println("------work()------");
}
}

启动类为:

1
2
3
4
5
6
7
8
9
10
11
@SpringBootApplication(scanBasePackages="com.lzj.springboot")
public class App {

public static void main(String[] args) {
SpringApplication app = new SpringApplication(App.class);
ConfigurableApplicationContext context = app.run(args);
/*从容器中获取Man类型的bean,如果Man类型的bean注入到容器成功,此时就能获取到;如果注入不成功,则获取不到。注入不成功,就会有可能是Man中的@Resource注解的依赖注入没有成功*/
context.getBean(Man.class).work();
context.close();
}
}

启动工程,输出如下:

1
2
------say()------
------work()------

@Resource 注解注解的 name 属性为空

1、@Resource 要注解的那个变量属性与容器中的 bean 的 id 的名字相等
启动类和 Person 的类与相面一样,下面直接修改 Man 类如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Component
public class Man {

/*@Resource注解的属性变量ps与容器中的bean的id名字ps相等,可以匹配*/
@Resource
private Person ps;

public void work(){
ps.say();
System.out.println("------work()------");
}
}

2、@Resource 要注解的那个变量属性与容器中的 bean 的 id 的名字不相等
启动类和 Person 的类与相面一样,下面直接修改 Man 类如下:

1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class Man {

/*@Resource注解的属性变量ps与容器中的bean的id名字ps不相等,然后通过bean的类型判断:person变量属性的类型为Person类,容器中的id为ps的bean的类型也为Person类型,因此此种情况下也可以匹配*/
@Resource
private Person person;

public void work(){
person.say();
System.out.println("------work()------");
}
}

配置类相关

@Configuration:

@Configuration注解

作用:用于指定当前类是一个 spring 配置类, 当创建容器时会从该类上加载注解。 获取容器时需要使用
AnnotationApplicationContext(有@Configuration 注解的类.class)
细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。

建议看一下这个

在使用@Bean 注解的时候必须配合@Configuration 来使用: 原因

未学习的注解:

1
2
3
4
5
6
7
8
9
10
11
12
@Import({User.class, ByteArrayUtil.class})
@Configuration(proxyBeanMethods = true) //告诉Spring这是一个配置类 === 配置文件
//@ConditionalOnBean(name = "tom") //条件注解 有某个组件才注入容器
@ConditionalOnMissingBean(name = "tom") //条件注解 没有某个组件才注入容器
@ImportResource("classpath:beans.xml")
//1. 开启Car配置绑定功能
@EnableConfigurationProperties({Car.class})
//2, 把这个Car这个组件自动注册到容器中
@Bean
@ConfigurationProperties(prefix = "mycar")
@RestController
@RequestMapping("/car")