🏀🏀🏀来都来了,不妨点个关注!
🎧🎧🎧博客主页:欢迎各位大佬!
文章目录
前言
在上一篇Spring的基础概念和使用中,我们讲解了如何存储Bean对象和获取Bean对象,大致流程就是在配置好的扫描路径文件.xml文件中添加一行Bean注册才行,如下:
但设想一下如果我有很多的对象需要存储到Spring容器中,那我是不是得添加多个Bean注册,这样写起来是不是就太麻烦了,每次我需要存储一个Bean就需要添加一个到扫描路径文件中,今天我们就讲解一种更简单的存储和获取Bean对象的方法——通过注解存储和获取Bean。
前置工作
在我们使用注解存储和获取Bean对象之前,我们需要配置一下扫描路径,具体配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<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>
注意事项:
1. 基于注解存储Bean对象
1.1 什么是注解
在Spring框架中,注解(Annotation)是一种元数据的形式,它允许开发者将额外的信息添加到代码中,而无需使用传统的XML配置文件。Spring框架中的注解大大简化了配置,使得开发者能够更直接地在Java代码中定义Spring的组件、依赖注入、切面、事务管理等。
而我们存储Bean对象就有两种方式,一种是通过五大类注解,一种是通过方法注解,如下:
类注解:
- @Controller【控制器】校验参数的合法性(安检系统)
- @Service【服务】业务组装(客服中心)
- @Repository【数据持久层】实际业务处理(实际办理的业务)
- @Configuration【配置层】配置
- @Component【组件】工具类层(基础的工具)
方法注解:@Bean
对于以上五个类注解,其他四个都是@Component的子类,五个类注解的功能是一样的,命名不同是为了作区分,下面是Java项目开发时的规范,就是以不同的注解作区分:
1.2 通过类注解存储Bean对象
- 通过@Controller存储
package com.java.demo.controller;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
public void sayHi() {
System.out.println("我是UserController");
}
}
- 通过@Service存储
package com.java.demo.service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
public void sayHi() {
System.out.println("我是UserService");
}
}
- 通过@Repository
package com.java.demo.dao;
import org.springframework.stereotype.Repository;
@Repository
public class UserRepository {
public void sayHi() {
System.out.println("我是UserRepository");
}
}
- 通过@Configuration
package com.java.demo.configuration;
import org.springframework.context.annotation.Configuration;
@Configuration
public class UserConfiguration {
public void sayHi() {
System.out.println("我是UserConfiguration");
}
}
- 通过@Component
package com.java.demo.component;
import org.springframework.stereotype.Component;
@Component
public class UserComponent {
public void sayHi() {
System.out.println(" 我是UserComponent");
}
}
1.2.1 读取Bean对象
以读取UserController为例,默认情况下我们获取Bean对象的名称为类名首字母小写,即userController,如下:
import com.java.demo.controller.UserController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
//1.创建上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//2.获取Bean对象
UserController userController = context.getBean("userController",UserController.class);
//3.使用Bean对象
userController.sayHi();
}
}
1.2.2 读取Bean对象时命名规则的问题(默认命名)
通过上面UserController例子,我们知道当我们的Bean对象使用大驼峰命名时,我们可以通过首字母小写的方式进行读取Bean对象,
但当我们使用下面这个例子将IService存储到Spring容器中时,通过首字母小写的格式进行读取Bean对象的时候就会发生报错:
package com.java.demo.service;
import org.springframework.stereotype.Service;
@Service
public class IService {
public void sayHi() {
System.out.println("我是IService");
}
}
而上述例子我们需要直接使用类名读取Bean对象,读取如下:
通过上面两个例子,我们就可以知道Bean的命名规则:当第一个字母大写,第二个字母小写的情况,即大驼峰命名时,我们通过第一个字母小写读取Bean对象,当第一个字母和第二个字母都大写的情况,我们通过原类名读取Bean对象。
这个我们可以通过Bean注解的源码进行解释:
在idea中双击shift:
最终找到以下方法:
1.3 通过方法注解存储Bean对象
值得注意的是,当我们使用方法注解@Bean存储对象时需要搭配着五大类注解使用,如下:
ArticleInfo定义如下:
package com.java.demo;
public class ArticleInfo {
String id;
String content;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public String toString() {
return "ArticleInfo{" +
"id='" + id + '\'' +
", content='" + content + '\'' +
'}';
}
}
Articles定义如下:
package com.java.demo;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class Articles {
@Bean //将当前方法的返回值存储到IoC容器中
public ArticleInfo articleInfo() {
ArticleInfo articleInfo = new ArticleInfo();
articleInfo.setId("1");
articleInfo.setContent("斗破苍穹");
return articleInfo;
}
}
这里的读取Bean对象和上面一样这里就不赘述了。
获取Bean对象时的注意事项:@Bean的默认命名=方法名。
重命名Bean对象的几种方式:
@Bean("art1")
@Bean(name = "art2")
@Bean(value = "art3")
同时Bean支持指定多个名称
@Bean(value = {"art4","art5"})
@Bean(name = {"art4","art5"})
2. 获取Bean对象(对象装配)
获取 bean 对象也叫做对象装配,是把对象取出来放到某个类中,有时候也叫对象注⼊。
对象装配(对象注⼊)的实现⽅法以下 3 种:
- 属性注⼊
- 构造⽅法注⼊
- Setter 注入
在我们项目开发中,如我们上面提到的项目开发规范,需要在Controller层中调用Service层中的类,此时就需要用到我们上述提到的对象装配, 比如现在我们需要在UserController中调用UserSerivice类中的sayHi()方法。
2.1 属性注入
属性注⼊是使⽤ @Autowired 实现的,下面我们以将UserService注入到UserController中并调用其sayHi()方法。
UserController定义如下:
package com.java.demo.controller;
import com.java.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
@Autowired
private UserService userService;
public void sayHi() {
userService.sayHi();
System.out.println("我是UserController");
}
}
package com.java.demo.service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
public void sayHi() {
System.out.println("我是UserService");
}
}
属性注入的优缺点:
优点:使用简单
缺点:
- 无法注入final修饰的变量
- 通用性问题:只适用于IoC容器
- 更容易违背单一设计原则,因为使用起来比较简单
2.1.1 同一类型多个@Bean报错
当出现以下多个 Bean,返回同⼀对象类型时程序可能会报错,如下代码所示:
在UserService中我们注入了两个User对象到Spring容器中,然后在UserController中打印User的信息,代码实现如下:
UserService定义如下:
package com.java.demo.service;
import com.java.demo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Bean
public User user1() {
User user1 = new User();
user1.setName("张三");
return user1;
}
@Bean
public User user2() {
User user2 = new User();
user2.setName("张三");
return user2;
}
}
UserController定义如下:
package com.java.demo.controller;
import com.java.demo.User;
import com.java.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
@Autowired
private User user;
@Autowired
private UserService userService;
public void sayHi() {
System.out.println(user);
}
}
报错如下:
解决方案如下:
1. 将属性的名字和Bean的名字对应上
2. @Autowired和@Qualifier配合使用
2.2 Setter方法注入
Setter方法注入即在setter方法上加上@Autowired注解,下面我们用代码进行演示:
UserController定义如下:
package com.java.demo.controller;
import com.java.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
private UserService userService;
//setter方法注入
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public void sayHi() {
userService.sayHi();
}
}
UserService定义和属性注入时一样,这里就不赘述了,
Setter方法注入优缺点:
优点:通常Setter只set一个属性,因此更符合单一设计原则
缺点:
- 无法修饰final变量
- setter注入的方法可以被修改(这是因为setter本身就是一个方法,因此可以在多个地方被调用和修改)
2.3 构造方法注入(Spring4.x之后推荐的注入方法)
构造方法注入即在类的构造方法中注入,需要注意的是,当类中只有这一个构造方法的时候, @Autowired是可以省略的。
package com.java.demo.controller;
import com.java.demo.User;
import com.java.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
private UserService userService;
//构造方法注入
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
public void sayHi() {
userService.sayHi();
}
}
构造方法注入优缺点:
优点:
- 可以修饰final变量(这是因为构造方法有两种初始化方式,一种是直接赋值,一种则是构造方法初始化)
- 注入的对象不会被修改,因为构造方法只会被加载一次
- 构造方法注入可以保证对象完全初始化
- 构造方法注入通用性更好
缺点:
- 写法比属性注入复杂
- 使用构造方法注入,无法解决循环依赖的问题
2.4 @Autowired和@Resouce 的区别
- 出身不同: @Autowired 是 Spring 提供的注解,@Resource 是 JDK 提供的注解。
- 依赖查找顺序不同:** @Autowired 是先根据类型(byType)查找,如果存在多个 Bean 再根据名称(byName)进行查找 @Resource 是先根据名称查找(byName),如果(根据名称)查找不到,再根据类型(byType)进行查找.
- 支持的参数不同: @Autowired 和 @Resource 在使用时都可以设置参数,比如给 @Resource 注解设置 name 和 type 参数,实现代码如下:
@Resource(name = "userinfo", type = UserInfo.class)
private UserInfo user;
但二者支持的参数以及参数的个数完全不同,其中 @Autowired 只支持设置一个 required 的参数,而 @Resource 支持 7 个参数。
- 依赖注入的支持不同: @Autowired 支持在构造函数、方法、字段和参数上使用。@Resource 主要用于字段和方法上的注入,不支持在构造函数或参数上使用。
今天的分享到这里结束了,感谢支持!