前言:Spring 中想要更简单的存储和读取对象的核⼼是使⽤注解,接下来学习Spring相关注解,来存储于读取Bean对象
一、存储Bean对象
1.前置工作:配置扫描路径(重要)
想要将对象成功的存储到Spring中,我们需要配置一下存储对象的扫描包路径,只有被配置包下的所有类,添加了注解才能被正确地识别并保存到Spring中。
在 spring-config.xml 添加如下配置:
<?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>
标红的部分尤为重要,必须配置好扫描包下的类对象,否则不能存储到Spring中。
2. 添加注解存储Bean对象
想要将对象存储在 Spring 中,有两种注解类型可以实现:
1. 类注解:
@Controller、@Service、@Repository、@Component、@Configuration。
2. ⽅法注解:
@Bean
2.1 @Controller(控制器存储)
@Controller//将对象存储到Spring中
public class UserController {
public void say(String name){
System.out.println("This is "+name);
}
}
此时需要先使用读取对象的方式读取上面的UserController对象,如下面代码所示
public class Application {
public static void main(String[] args) {
//1.得到Spring上下文
ApplicationContext context=
new ClassPathXmlApplicationContext("spring-config.xml");
//2.得到Bean
UserController userController=(UserController) context.getBean("userController");
//3.调用bean方法
userController.say("Controller");
}
}
运行结果:
2.2 @Service(服务存储)
使用@Service存储Bean的代码如下所示:
@Service
public class UserService {
private UserRepository userRepository;
public void say(String name){
System.out.println("This is "+name);
}
}
public class App {
public static void main(String[] args) {
//1.得到spring上下文
ApplicationContext context=
new ClassPathXmlApplicationContext("spring-config.xml");
//2.得到bean
UserService userService=(UserService)context.getBean("userService");
//3.调用方法:
userService.say("userService");
}
}
2.3 @Repository (仓库存储)
使用Repository存储bean代码如下所示:
@Repository
public class UserRepository{
public void say(String name){
System.out.println("This is "+name);
}
}
读取bean的代码:
public class App {
public static void main(String[] args) {
//1.得到spring上下文
ApplicationContext context=
new ClassPathXmlApplicationContext("spring-config.xml");
//2.得到bean
UserRepository userRepository =(UserRepository)context.getBean("userRepository");
//3.调用方法:
userRepository.say("userRepository");
}
}
2.4 @Component(组件存储)
使⽤ @Component 存储 bean 的代码如下所示:
代码是差不多跟上面一样的,我们只要知道,类注解在代码中的位置,以及使用方法即可。
2.5 @Configuration(配置存储)
代码跟上面差不多
3.为什么要这么多类注解?
1.提供语义化注解:不同的注解,更好表示Bean的作用用途,提高代码可读性
2.实现组件化开发:通过不同的注解,可以将应用程序按照业务逻辑功能进行组件化开发,实现灵活应用程序架构。
3.实现依赖注入:通过使用不同的注解,可以实现依赖注入,将Bean的依赖关系自动注入到Bean中,实现更加松散耦合的应用程序。
4.实现AOP功能:通过使用不同的注解,可以实现AOP功能,实现横切伙计与业务逻辑的分离,提高代码可维护性与可拓展性。
它们虽然功能相同,但是为了便于区分代码所属的功能区,
比如:@Controller:表示的是业务逻辑层;
@Servie:服务层;
@Repository:持久层;
@Configuration:配置层
举个例子就是每个省的车牌号,车牌号功能相同,但是不同的车牌号方便区分车的归属地。
它们之间的关系,如上图,查看它们的源码,发现这些注解里面都有一个注解@Component
说明它们本身属于@Component的“子类”。
二、 Bean的命名规则
通常使用标准的大驼峰命名,读取的时候首字母小写就可以获取到bean,如下图所示
错误做法:如果⾸字⺟和第⼆个字⺟都是⼤写时,就不能正常读取到 bean 了,如下图所示:
根据bean源代码的命名规则:如果出现如果⾸字⺟和第⼆个字⺟都是⼤写时,就不能正常读取到 bean 的情况,需要把bean的首字母也大写存储才可以。如下图所示
2.1 方法注解 @Bean
类注解是添加到某个类上的,⽽⽅法注解是放到某个⽅法上的,如下代码:
@Component
public class Users {
@Bean
public User user1(){
User user=new User();
user.setId(1);
user.setName("Java");
return user;
}
}
public class App {
public static void main(String[] args) {
//1.得到spring上下文
ApplicationContext context=
new ClassPathXmlApplicationContext("spring-config.xml");
//2.得到bean
User user=(User)context.getBean("U1");
//3.调用bean方法
System.out.println(user);
}
}
注意:在 Spring 框架的设计中,⽅法注解 @Bean 要配合类注解才能将对象正常的存储到 Spring 容器中,也就是需要添加 "@Component"类注解
2.2 @Bean的重命名方法:
方式1:@Bean("aaa");
方式2:@Bean(name="aaa");
方式3:@Bean(value="aaa");
重命名拓展:@Bean支持指定多个名称
@Bean(value={"ccc","aaa");
注意事项:
1.当@在使用方法名获取Bean对象时,方法名应该与被注解的方法的名称相同,否则会抛出NoSuchBeanDefinitionException异常。
2.如果多个Bean使用相同的名称,程序执行后续不会报错,但是第一个Bean之后的对象不会存储到容器中,因为只有在第一次创建Bean的时候会将对称和Bean名称关联起来,后续再有相同名称Bean存储时,容器会自动忽略。
如果它们返回同一个对象类型的话,程序会报错,详情请看第三部分。
三、获取Bean对象(对象装配)
获取 bean 对象也叫做对象装配,是把对象取出来放到某个类中,也叫对象注⼊。
对象装配(对象注⼊)的实现⽅法以下 3 种:
1. 属性注⼊
2. 构造⽅法注⼊
3. Setter 注⼊
3.1属性注入:
核心实现:
//注入方法1:属性注入
@Autowired
private UserService userService;
属性注⼊是使⽤ @Autowired 实现的,将 Service 类注⼊到 Controller 类中,代码如下
Service类的实现代码如下:
@Service
public class UserService {
/*
* 根据ID获取用户数据*/
public User getUser(Integer id) {
//伪代码,不需要连接数据库
User user = new User();
user.setId(id);
user.setName("This is id : " + id);
return user;
}
}
Controller类的实现代码如下:
@Controller
public class UserController {
//注入方法1:属性注入
@Autowired
private UserService userService;
public User getUser(Integer id){
return userService.getUser(id);
}
}
获取Controller的getUser方法:
public class UserControllerTest {
public static void main(String[] args) {
// 1.得到Spring上下文
ApplicationContext context=
new ClassPathXmlApplicationContext("spring-config.xml");
UserController userController=context.getBean(UserController.class);
System.out.println(userController.getUser(1).toString());
}
}
最终结果:
3.2 构造方法注入
构造方法注入是在类的构造方法中实现注入,如下代码所示:
public class UserController2 {
//注入方法2:构造方法注入
private UserService userService;
@Autowired
public UserController2(UserService userService){
this.userService=userService;
}
public User getUser(Integer id){
return userService.getUser(id);
}
}
注意:如果只有一个构造方法,@Autowired可以省略
如果有多个构造方法,需要添加上@Autowired来明确指明到底使用哪个构造方法,否则会报错
3.3 Setter注入
Setter注入与属性Setter方法实现类似,不同就是:在设置set方法时需要加上@Autowired注解,如下代码所示:
public class UserController2 {
//注入方法2:构造方法注入
private UserService userService;
@Autowired
public UserController2(UserService userService){
this.userService=userService;
}
public User getUser(Integer id){
return userService.getUser(id);
}
}
注意事项:
如果一个类中的set方法没有使用@Autowired注解,
那么Spring将不会自动注入Bean,并且调用该set方法时,传入的参数值为null。
如果希望使用依赖注入的方式为属性赋值,应该在set方法上添加@Autowired注解或者在XML配置文件中进行配置。
四、三种注入优缺点分析:
属性注⼊:
优点是简洁,使⽤⽅便,适用于大部分场景;
缺点是无法保证Bean完整性,只能⽤于 IoC 容器,如果是⾮ IoC 容器不可⽤,并且只有在使⽤的时候才会出现 NPE(空指针异常)。 无法注入final修饰的变量。
构造⽅法注⼊:
是 Spring 推荐的注⼊⽅式。
优点:
1.通⽤性,在使⽤之前⼀定能把保证注⼊的类不为空。
2.可以注入一个final修饰的变量
3.注入的对象不会被修改,因为构造方法只会加载一次
4.构造方法注入可以保证注入对象完全初始化
5.构造方法注入通用性更好
6.保证了Bean完整性,进行非空校验
缺点:
1.写法比属性注入复杂。
2.使用构造方法注入,无法解决循环依赖的问题。
3.如果有多个注⼊会显得⽐较臃肿,但出现这种情况你应该考虑⼀下当前类是否符合程序的单⼀职责的设计模式了。
Setter ⽅式:
是 Spring 前期版本推荐的注⼊⽅式,但通⽤性不如构造⽅法,所有 Spring 现版本已经推荐使⽤构造⽅法注⼊的⽅式来进⾏类注⼊了。
优点:
1.通常Setter只Set一个属性,所以Setter注入更符合单一设计原则
2.灵活控制Bean属性,进行非空校验
缺点:
1.setter注入的对象可以被修改,setter本来就是一个方法,一个方法就可能被多次调用和修改。
2.需要在Bean类定义Setter方法,增加代码量。
三种方式的总结:
总的来说,属性注入是最简单、最常用的注入方式,适用于大部分场景;构造方法注入可以保证Bean的完整性,适用于需要强制校验的场景;Setter方式注入可以灵活控制Bean的属性,适用于需要动态修改Bean属性的场景。在实际应用中,应根据具体的场景和需求来选择合适的注入方式。
五、@Resource:另⼀种注⼊关键字
在进行类注入时,除了可以使用@Autowired关键字之外,还可以使用@Resource进行注入,如下代码:
@Controller
public class UserController {
//注入
@Resource
private UserService userService;
public User getUser(Integer id){
return userService.getUser(id);
}
}
@Resource和@Autowired的区别:
1.出身不同:@Autowired来自Spring,@Resource来自JDK注解
2.支持参数不同:相比@Autowired来说,@Resource支持更多的参数设置,例如name设置,根据名称获取Bean。
3.使用上的区别:@Autowired支持构造方法注入,@Resource不支持。
4.idea兼容性支持不同:使用@Autowired在idea专业版可能会误报,@Resource不存在误报。
六、同一类型多个@Bean报错的问题
当出现以下多个Bean,返回同一对象类型时,程序会报错,如下图代码所示:
@Component
public class Users {
@Bean
public User user1(){
User user=new User();
user.setId(1);
user.setName("User1");
return user;
}
@Bean
public User user2(){
User user=new User();
user.setId(2);
user.setName("User2");
return user;
}
}
@Controller
public class UserController4 {
//注入
@Resource
private User user;
public User getUser(){
return user;
}
}
public class UserControllerTest {
public static void main(String[] args) {
// 1.得到Spring上下文
ApplicationContext context=
new ClassPathXmlApplicationContext("spring-config.xml");
UserController4 userController=context.getBean(UserController4.class);
System.out.println(userController.getUser().toString());
}
}
结果:
报错的原因是:非唯一的Bean对象
处理方式:
解决同⼀个类型,多个 bean 的解决⽅案有以下两个:
1.使⽤ @Resource(name="user1") 定义。
@Controller
public class UserController4 {
//注入
@Resource(name="user1")
private User user;
public User getUser(){
return user;
}
}
2.使⽤ @Qualifier 注解定义名称
public class UserController5 {
//注入
@Autowired
@Qualifier(value="user2")
private User user;
public User getUser(){
return user;
}
}
七、总结
1.将对象存储到 Spring 中:
a. 使⽤类注解:@Controller、@Service、@Repository、@Configuration、@Component【它们之间的关系】
b. 使⽤⽅法注解:@Bean【注意事项:必须配合类注解⼀起使⽤】
2. Bean 的命名规则:
⾸字⺟和第⼆个字⺟都⾮⼤写,⾸字⺟⼩写来获取 Bean,如果⾸字⺟和第⼆个
字⺟都是⼤写,那么直接使⽤原 Bean 名来获取 Bean。
3. 从 Spring 中获取对象:
a. 属性注⼊
b. Setter 注⼊
c. 构造函数注⼊(推荐)
4. 注⼊的关键字有:
a. @Autowired
b. @Resource
5. @Autowired 和 @Resource 区别:
出身不同; 使⽤时设置参数不同 @Resource ⽀持更多的参数,⽐如 name。
6. 解决同⼀类型多个 Bean 的报错:
a. 使⽤ @Resource(name="")
b. 使⽤ @Qualifier("")