点进来你就是我的人了
博主主页:戳一戳,欢迎大佬指点!
目录
一、前言:
二、储存Bean对象和使用Bean对象
1. 添加存储对象的注解 (这一步完成Spring容器已经有了Bean对象)
(1)类注解
为什么需要五大类注解
(2)方法注解
(3) 类注解和方法注解的区别
2. 对象装配/对象注入 (你的代码可以使用被注入的对象来完成各种任务)
(1) 属性注入 (日常开发中使用最多的一种注入方式)
(2) Setter方法注入
(3) 构造方法注入 ( Spring 官方从 4.x 之后推荐的注入方式)
(4) @Autowired与@Resource区别
(5) 注入异常问题
上篇博客,我们讲了一个spring core项目的大致流程:
创建项目——》将对象储存到Spring容器中 ——》从Spring容器中取出Bean对象
上篇博客的这种Spring对象的存储和读取方式虽然能够满足我们的需求,但是这也只是站在小项目的层面上来讲的。如果一个项目中的类多起来了,我们如果采用上篇博客这种面向xml配置配置Spring存储Bean对象,那得在spring的配置文件中添加多少bean标签,并且id还不能够重复!所以这种操作Spring存储和读取Bean对象的做法太过复杂.
接下来我们来看如何更简单的实现Spring中存储Bean对象的存取操作!!!
首先,Spring项目的创建——这个没有什么好说的!就按我们上篇博客的步骤来进行。
但注意:与上篇博客相比,spring的配置文件发生了改变——为了更简单的实现Bean对象的存入(把对象存到spring中)
更改后的spring配置文件(resources
目录下的spring-config.xml)
同时pom.xml中添加Spring的框架支持,xml配置如下:
org.springframework
spring-context
5.3.27
首先我们回忆下,我们执行储存Bean对象的方式。
之前我们存储 Bean 时,需要在 spring-config 中添加⼀⾏ bean 注册内容才⾏,如下图所示:
这种存入 Bean 的方式,并不是很好!
1、需要手动添加 bean 对象 到 配置文件中
2、如果 是配置文件中出现了问题,不好调试。
而现在我们不需要一个一个的在spring配置文件中添加我们要储存的Bean对象。我们直接:
你以为把扫描路径添加到配置文件中就行了吗?
不过你还需要再类中添加注解——再扫描的过程中,只有添加了注解的类才能成功存储到spring中
这就引起了我们注解的概念
@Controller
public class UserController {
public void sayHello(){
System.out.println("hello Controller");
}
}
- 使用注解注入Bean对象时,bean的 id 默认用
类名的小驼峰
的表示- 如果原类名的第一个字母和第二个字母都是大写,那么此时的bean
id为原类名
不变bean对象 为什么这样命名?让我们一起来翻看一下源码>> (五大类注解时的命名规则)
下面是添加注解后获取bean对象的示例: (其他四个注解与此用法相同)
public class Application {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//bean id为类名改写为小驼峰方式
UserController userController = context.getBean("userController",UserController.class);
userController.sayHello();
}
}
为什么需要五大类注解
既然这5大类注解的用法这么相似,那为啥还要分成5个不同的注解?统一弄成一个注解不好吗?
这就与每个省市都有自己限定名称的车牌号一样,不仅能够节约号码,同时让别人看到能够大致清楚这是哪里的车牌号。我们的类注解也是这样,在项目开发中,一个项目中可能有特别多的类,并且不同的类可能归属于不同的层级,我们这几种类注解就是让程序员看到之后,能够直接了解该类的用途层级。如下图所示:
更详细一点:
@Bean注解加到方法上,将方法的返回值添加到容器中
注意: @Bean注解必须结合五大类注解来使用,并且方法必须有返回值
@Component
public class Students {
@Bean
public Student s1(){
Student student = new Student();
student.setName("小诗诗");
student.setAge(18);
return student;
}
}
public class App1 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
Student student = context.getBean("s1",Student.class);
System.out.println(student);
}
}
@Bean命名规则:(使用方法注解配合类注解时的命名规则)
@Component
public class Students {
@Bean(name = "student1")
public Student s1(){
Student student = new Student();
student.setName("小诗诗");
student.setAge(18);
return student;
}
}
public class App1 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
Student student = context.getBean("student1",Student.class);
System.out.println(student);
}
}
@Bean注解在起名字的时候,也可以起多个名字:
@Bean(name = {"user1","user2","user3"})
public User getUser(){
User user = new User();
user.setUsername("张三");
user.setAge(10);
return user;
}
类注解(如 @Component
、@Service
、@Repository
或 @Controller
):这种方式告诉 Spring 框架,需要为这个类创建一个 bean 实例,并将其注册到 Spring 容器中。这个类的构造方法(如果有的话)会被自动调用,以创建新的实例。但是,你不能直接在注解中指定任何参数。Spring 将尝试使用容器中可用的 bean 作为参数,进行自动装配。如果需要更复杂的初始化过程,可以使用 @PostConstruct
注解的方法或者实现 InitializingBean
接口。
方法注解(@Bean
):这种方式更加灵活。它允许你在一个方法中定义 bean 的创建逻辑,可以包括任意的代码。这个方法可以接收参数,Spring 将尝试使用容器中可用的 bean 作为参数,进行自动装配。这个方法返回的对象将被注册到 Spring 容器中。这种方式特别适合于创建你无法修改的类的实例(例如,来自第三方库的类),或者当你需要在创建 bean 时执行一些自定义逻辑。
在Spring中,@Autowired
和@Resource
注解用于自动装配(自动注入)Bean。装配方式主要有属性注入、构造方法注入和Setter方法注入。
其中,@Autowired注解可以用于标注 属性注入、Setter注入以及构造方法注入;而@Resource只能用于标注 属性注入和Setter注入,不能用于标注构造方法注入!
这是最常见的注入方式。在类的字段上直接使用@Autowired
或@Resource
注解,Spring将自动查找并注入匹配的Bean。
@RestController
public class UserController {
// 属性对象
@Autowired
private UserService userService;
@RequestMapping("/add")
public UserInfo add(String username, String password) {
return userService.add(username, password);
}
}
优点:
缺点:
在类的setter方法上使用@Autowired
或@Resource
注解,Spring将自动查找并注入匹配的Bean。
@RestController
public class UserController {
// Setter 注入
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
@RequestMapping("/add")
public UserInfo add(String username, String password) {
return userService.add(username, password);
}
}
优点:
缺点:
原因:final修饰的变量必须直接赋值
在类的构造方法上使用@Autowired
或注解,Spring将自动查找并注入匹配的Bean。
如果当前类中只存在一个构造方法,那么@Autowired可以省略,多个构造方法时则不可以省略
@RestController
public class UserController {
// 构造方法注入
private UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
@RequestMapping("/add")
public UserInfo add(String username, String password) {
return userService.add(username, password);
}
}
构造方法注入相比于前两种注入方法,它可以注入不可变对象,并且它只会执行一次,也不存在像 Setter 注入那样,被注入的对象随时被修改的情况,它的优点有以下 4 个:
1. 可注入不可变对象
2. 注入对象不会被修改:
构造方法注入不会像 Setter 注入那样,构造方法在对象创建时只会执行一次,因此它不存在注入对象被随时(调用)修改的情况。
3. 注入对象会被完全初始化:
因为依赖对象是在构造方法中执行的,而构造方法是在对象创建之初执行的,因此被注入的对象在使用之前,会被完全初始化,这也是构造方法注入的优点之一。
4. 通用性更好
构造方法和属性注入不同,构造方法注入可适用于任何环境,无论是 IoC 框架还是非 IoC 框架,构造方法注入的代码都是通用的,所以它的通用性更好。
关于@Autowired
和@Resource
的具体区别,以下是一些主要的点:
@Autowired
是Spring框架的注解,它默认通过类型进行注入,然后是按照字段名称进行匹配(类型相同时)。如果Spring上下文中存在多个相同类型的Bean,你需要与@Qualifier
注解一起使用来指定Bean的名称。如果没有找到匹配的Bean,它将抛出异常。如果你希望允许为null的值,你可以设置其required
属性为false
。
@Resource
是Java EE的注解,它默认通过名称进行注入,然后是按照类型进行匹配(名称相同时)。它有两个重要的属性:name和type。如果你使用name属性,那么你使用的是按名称自动注入策略,如果你使用type属性,那么你使用的是按类型自动注入策略。如果既没有指定name也没有指定type,那么默认使用按名称的自动注入策略。如果没有找到匹配的Bean,那么会回退到按类型匹配,并自动注入如果找到匹配的Bean.
@Resource
的装配顺序是:如果指定了name和type,那么只有在Spring上下文中找到唯一匹配的Bean才会装配,如果没有找到,将抛出异常。如果指定了name,将装配上下文中匹配name(ID)的Bean,如果没有找到,将抛出异常。如果指定了type,当在上下文中找到唯一匹配的Bean类型时进行装配,如果没有找到或找到多个,将抛出异常。如果既没有指定name也没有指定type,装配将自动按名称进行,如果没有找到匹配,回退是按原始类型匹配,如果找到匹配,将自动装配。
当我们的ioc容器中存在了多个相同类型bean对象,再通过上方的注入方式来获取的话,程序就会报异常!
在spring容器中找bean有两种方式:
先创建一个Cat类,属性有name和color,并提供get,set,toString方法
往Spring容器中存入多个Cat对象 (方法注入)
@Controller
public class Cats {
@Bean
public Cat cat1(){
Cat cat = new Cat();
cat.setName("糯米");
cat.setColor("白色");
return cat;
}
@Bean
public Cat cat2(){
Cat cat = new Cat();
cat.setName("汤圆");
cat.setColor("黑色");
return cat;
}
}
在另一个类中获取Cat对象 (对象注入,你的代码可以使用被注入的对象来完成各种任务)
@Controller
public class CatController {
@Autowired
private Cat cat;
public void getCat(){
System.out.println(cat);
}
}
创建启动类
public class App3 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
CatController catController = context.getBean(CatController.class);
catController.getCat();
}
}
发现报错了,因为Spring中已经注入了两个Cat对象,所以此时不知道获取哪个对象
@Autowired注解默认它默认通过类型进行注入,但是在匹配过程中发现了两个相同的bean对象,然后按照字段名称进行匹配,发现找不到所以报错了!!!
三种解决方案
@Rsource设置name
方式来重命名注入对象@Autowired+@Qualifier
来筛选bean对象方案一:精确的描述bean的名称(将注入的名称写对)
@Controller
public class CatController {
@Autowired
private Cat cat1; //精确描述bean名称
public void getCat(){
System.out.println(cat1);
}
}
方案二: 使用@Rsource设置name方式来重命名注入对象
@Controller
public class CatController {
@Resource(name = "cat2")
private Cat cat;
public void getCat(){
System.out.println(cat);
}
}
方案三:使用@Autowired+@Qualifier来筛选bean对象
@Controller
public class CatController {
@Autowired
@Qualifier(value = "cat1")
private Cat cat;
public void getCat(){
System.out.println(cat);
}
}