上图中就是五大类注解对应的层,通过源码可以看到其他四个注解都基于@Conponent
Spring既然是一个包含众多工具方法的IoC容器,它是一个控制反转的容器,所以就需要将Bean对象存入到容器中,需要用的时候从容器中获取Bean对象,下面我们来介绍下存入Bean对象。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:content="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
// 这句代码中, id 就是给 这个要注入的对象取个名字, class就是 要存入的 Bean 的类型是哪一个
<bean id = "user" class="com.java.demo.User">bean>
beans>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:content="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<content:component-scan base-package="com.java.demo">content:component-scan>
beans>
配置了 Bean 的扫描路径,只有当前目录下的类才会被扫描是否加了类注解,如果加了类注解后,就会将 所有加了类注解的类 存入到 IoC 容器中。
@Component
public class User {
public void say(){
System.out.println("Hello User...");
}
}
类注解存Bean需要注意几点
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:content="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<content:component-scan base-package="com.java.demo">content:component-scan>
beans>
public class Student {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
@Component
public class User {
@Bean
public Student student(){
Student student = new Student();
student.setName("张三");
student.setAge(18);
return student;
}
public void say(){
System.out.println("Hello User...");
}
}
在上述代码中,我们可以通过 在方法上 加上 @Bean 注解 将该方法存入到 IoC 容器中,并且可以直接获取到该对象。
在使用 @Bean 注解的时候,需要注意几点
- @Bean 注解必须配合 五大类注解一块使用
- @Bean 注解的默认命名 = 方法名称
- 如果使用 @Bean(“xxxx”)方式,那么名称就是 xxxx, 并且 @Bean({“xxx”,“xxxxx”,“xxxxxx”}),里面可以类似和数组一样多个名称
- 如果@Bean重命名后,那么默认的方法名就没用,获取Bean的时候就不能写默认方法名
- 如果多个 Bean 使用相同的名称,则不会报错,只会存储第一个 Bean对象,之后相同名称的不会存入,会自动忽略
依赖查找(依赖Bean的名称),有两种方式,一种是 ApplicationContext, 一种是 BeanFactory。这两种都是容器管理对象,都可以获取到 Bean对象
public class App {
public static void main(String[] args) {
// 1. 获取 Spring 上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
// 2。获取 Bean 对象
Student student = (Student) context.getBean("student");
System.out.println(student);
}
}
public static void main(String[] args) {
// 1. 得到 spring 上下文对象
BeanFactory context = new XmlBeanFactory(new ClassPathResource("spring-config.xml"));
// 2. 获取 Bean 对象
//Student student = (Student)context.getBean("user");
Student student = (Student)context.getBean("student");
System.out.println(student.toString());
}
ApplicationContext | BeanFactory |
---|---|
ApplicationContext是BeanFactory的子类,其拥有更多功能(国际化支持、资源访问支持、事件传播) | BeanFactory是ApplicationContext的父类 |
ApplicationContext加载Bean:一次性加载所有Bean对象 | BeanFactory加载Bean:懒加载,按需加载(使用一个加载一个) |
在 spring 中实现依赖注入的常见方式有3种:
@Controller
public class UserController {
/**
* 属性注入
*/
@Autowired
private UserService userService;
// 此处 main方法里面,必须使用依赖查找的方式,先获取到UserController
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
UserController userController = (UserController) context.getBean("userController");
userController.userService.say();
}
}
属性注入:
优点: 实现简单、使用简单,只需要在属性上加一个注解@Autowired,就不需要自己new一个对象,直接获得注入的对象。
缺点:
@Controller
public class UserController2 {
private UserService userService;
/**
* setter注入
*/
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
UserController2 userController2 = (UserController2) context.getBean("userController2");
userController2.userService.say();
}
}
优点:似乎没什么优点,而且比属性注入更加麻烦,要说唯一可能有用的就是,完全符合单一职责设计原则,每个 setter 只针对一个对象。Setter注入 也适用于非IoC容器
缺点:
@Controller
public class UserController3 {
private final UserService userService;
/**
* 构造方法注入
*/
// @Autowired 此处如果构造方法参数只有一个的时候,该注解可有可无
public UserController3(UserService userService) {
this.userService = userService;
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
UserController3 userController3 = (UserController3) context.getBean("userController3");
userController3.userService.say();
}
}
优点:
注入对象不会被更改,因为是在构造方法注入的,构造方法在对象创建的时候只会创建一次
注入的对象会被完全初始化
通用性会更好,无论在 IoC容器 还是 非 IoC容器中都能用
缺点:
Spring官方推荐的是构造方法的注入,可以注入不可变对象,通用性更好;如果想要注入可变对象,那么可以使用 Setter 注入。
上述代码,可以看到报错,出现了多个同一类型的多个Bean报错,需要1个,找到了2个。如何解决呢
出身不同:@Resource来自于JDK,@Resource来自于Spring
兼容性问题:@Autowired在社区版 IDEA可能会误报;