Spring更简单的存储和读取Bean

简介: 在上一篇文章【Spring的创建与使用】中,我们已经了解了Spring中bean对象的基本的创建和使用方法,这篇文章通过注解的方法,使得存储和读取对象更加简单。

1.存储Bean


简单的将Bean存储到容器,有以下几种方法:


a)使用5大类注解实现

b)通过@Bean方法注解


下标我们一起来看具体的实现方式。


1.1 前置工作


在使用更简单的方法存储和读取bean对象之前,先完成前置工作——在Spring配置文件中设置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=""></content:component-scan>
</beans>


微信图片_20230111195134.png


1.2 使用5大类注解实现存储


1.2.1 @Controller(控制器存储)


使⽤ @Controller 存储 bean 的代码如下所示:


@Controller
public class UserController {
    public void sayHi() {
        System.out.println("你好,Contorller");
    }
}


我们先使⽤之前读取对象的⽅式来读取上⾯的 UserController 对象,如下代码所示:


public class App {
    public static void main(String[] args) {
        //1.得到Spring上下文
        ApplicationContext context=
                new ClassPathXmlApplicationContext("spring-config.xml");
        //2.得到bean
        UserController userController=context.getBean("userController",UserController.class);
        //3.使用bean
        userController.sayHi();
    }
}


此处我们的beanName依旧是类名的小驼峰命名法,在后边我们会具体讲述Spring使用五大类注解生成beanName的问题。


1.2.2 @Service(服务存储)


使⽤ @Service 存储 bean 的代码如下所示:


@Service
public class UserService {
    public void sayHi() {
        System.out.println("你好,Service");
    }
}


读取bean的代码:


public class App {
    public static void main(String[] args) {
        //1.得到Spring上下文
        ApplicationContext context=
                new ClassPathXmlApplicationContext("spring-config.xml");
        //2.得到bean
        UserService userService=context.getBean("userService",UserService.class);
        //3.使用bean
        userService.sayHi();
    }
}


1.2.3 @Repository(仓库存储)


使⽤ @Repository 存储 bean 的代码如下所示:


@Repository
public class UserRepository {
    public void sayHi() {
        System.out.println("你好,Repository");
    }
}


读取 bean 的代码:

public class App {
    public static void main(String[] args) {
        //1.得到Spring上下文
        ApplicationContext context=
                new ClassPathXmlApplicationContext("spring-config.xml");
        //2.得到bean
        UserRepository userRepository=context.getBean("userRepository",UserRepository.class);
        //3.使用bean
        userRepository.sayHi();
    }
}


1.2.4 @Component(组件存储)


使⽤ @Component 存储 bean 的代码如下所示:


@Component
public class UserComponent {
    public void sayHi() {
        System.out.println("你好,Component");
    }
}


读取 bean 的代码:


public class App {
    public static void main(String[] args) {
        //1.得到Spring上下文
        ApplicationContext context=
                new ClassPathXmlApplicationContext("spring-config.xml");
        //2.得到bean
        UserComponent userComponent=context.getBean("userComponent",UserComponent.class);
        //3.使用bean
        userComponent.sayHi();
    }
}


1.2.5 @Configuration(配置存储)


使⽤ @Configuration 存储 bean 的代码如下所示:


@Configuration
public class UserConfiguration {
    public void sayHi() {
        System.out.println("你好,Configuration");
    }
}


读取 bean 的代码:


public class App {
    public static void main(String[] args) {
        //1.得到Spring上下文
        ApplicationContext context=
                new ClassPathXmlApplicationContext("spring-config.xml");
        //2.得到bean
        UserConfiguration userConfiguration=context.getBean("userConfiguration",UserConfiguration.class);
        //3.使用bean
        userConfiguration.sayHi();
    }
}


1.3 为什么需要这么多的注解?


既然上述的五大类注解都能够完成将Bean对象存储到Spring中的功能,那为什么需要这么多的注解呢?


这是因为不同的注解有不同的用途,在程序猿看到类注解后,就能直接了解当前类的用途,增加了代码的可读性。


程序的工程分层,调用流程如下:


微信图片_20230111195126.png

1.4 五大类注解之间的关系


查看 @Controller / @Service / @Repository / @Configuration 等注解的源码发现:


微信图片_20230111195123.png

这些注解里都有一个注解@Component,说明它们本身就是属于@Component的“子类”。


1.5 Spring使用五大类注解生成beanName


在1.2所有的获取bean对象的方法中,使用到的beanName都是小驼峰命名,但如果出现以下bean,还能通过小驼峰获取到吗?


@Controller
public class APIController {
    public void sayHi() {
        System.out.println("你好,APIController");
    }
}


微信图片_20230111195120.png

在我们去还以小驼峰命名法去获取bean对象时,发现报错,说明并不是所有的beanName都是以小驼峰命名法来命名的。


我们来查看beanName的命名规则源码:

(关于注解beanName的生成器)


微信图片_20230111195116.png

我们来看bean对象的命名规则的方法:


微信图片_20230111195113.png

它使⽤的是 JDK Introspector 中的 decapitalize ⽅法,源码如下:


public static String decapitalize(String name) {
  if (name == null || name.length() == 0) {
    return name;
  }
  // 如果第⼀个字⺟和第⼆个字⺟都为⼤写的情况,是把 bean 的⾸字⺟也⼤写存储了
  if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
   Character.isUpperCase(name.charAt(0))){
    return name;
  }
  // 否则就将⾸字⺟⼩写
  char chars[] = name.toCharArray();
  chars[0] = Character.toLowerCase(chars[0]);
  return new String(chars);
}


如果第⼀个字母和第⼆个字母都为⼤写的情况,是把 bean 的⾸字母也⼤写存储了,否则就将⾸字母⼩写


所以对于上⾯报错的代码,我们只要改为以下代码就可以正常运⾏了:


微信图片_20230111195110.png

1.6 通过@Bean方法注解实现存储


类注解是添加到某个类上的,而方法注解是放到某个方法上的,但仅依靠方法注解是仍然获取不到bean的,还需要搭配类注解:


1.6.1 方法注解要搭配类注解使用


先完成类代码:


public class User {
    private int id;
    private String name;
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}


@Component
public class UserBeans {
    @Bean
    public User getUser() {
        User user=new User();
        user.setId(1);
        user.setName("张三");
        return user;
    }
}


使用User类:


微信图片_20230111195105.png

我们发现这样也可以获得User对象,但是beanName很别扭(也可以只通过bean type来获取该类,但如果出现多次存储的情况就会报错),因为是方法名,而不是类名的小驼峰,所以我们可以通过设置name属性给Bean对象,进行重命名操作,下边看具体演示:


1.6.2 重命名Bean


可以通过设置name属性给Bean对象进行重命名操作,如下代码所示:


@Component
public class UserBeans {
    @Bean(name="u1")
    public User getUser() {
        User user=new User();
        user.setId(1);
        user.setName("张三");
        return user;
    }
}


此时我们使用u1就可以获取到User对象了,如下代码所示:


微信图片_20230111195101.png


2.获取Bean


获取bean对象也叫做对象装配,是把对象取出来放到某个类中,有时候也叫对象注入。


对象装配(对象注入)的实现方法有以下三种:


属性注入

构造方法注入

Setter注入

下边我们按照实际开发中的模式,将Service类注入到Controller类中。


2.1 属性注入


属性注入是使用@Autowired实现的,将Service类注入到Controller类中。


Service类的实现代码如下:


@Service
public class UserService {
    public void sayHi() {
        System.out.println("你好,Service");
    }
}


Controller类的实现代码如下:


@Controller
public class UserController2 {
    //属性注入
    @Autowired
    private UserService userService;
    public void sayHi() {
        userService.sayHi();
    }
}


获取 Controller 中的 sayHi⽅法:


微信图片_20230111195056.png


2.2 构造方法注入


构造方法注入是在类的构造方法上,使用@Autowired实现注入,如下代码所示:


@Controller
public class UserController3 {
    private UserService userService;
    //构造方法注入
    @Autowired
    public UserController3(UserService userService) {
        this.userService = userService;
    }
    public void sayHi() {
        userService.sayHi();
    }
}


注意:

如果只有一个构造方法,那么@Autowired注释可以省略,但如果类中有多个构造方法,就必须加上注解来明确指定到底使用哪个构造方法,否则会报错。


2.3 Setter注入


Setter注入和属性的Setter方法实现类似,只不过在设置set方法的时候需要加上@Autowired注解,如下代码所示:


@Controller
public class UserController4 {
    private UserService userService;
    //Setter注入
    @Autowired
    public void setUserService(UserService userService) {
        this.userService=userService;
    }
    public void sayHi() {
        userService.sayHi();
    }
}


2.4 三种注入方式的区别


属性注入:写法简单,但是通用性不好,它只能运行在IoC容器下,如果是非IoC容器就会出现问题。

Setter注入:该注入方式是早期Spring版本推荐的方式,但Setter注入的通用性没有构造方法注入的通用性好。

构造方法注入:通用性更好,它能确保在使用注入对象之前,此注入对象一定初始化过了。当构造方法参数较多时,开发者就要检查自己所写的代码是否符合单一设计原则的规范了,此注入方式也是Spring后期版本中推荐的注入方式。


2.5 另一种注入注解@Resourse


该注入注解与@Autowired功能几乎相同,但也有差别,下边看两者的具体差别:


1、出身不同:@Resourse来自于JDK,而@Autowired是Spring框架提供的。

2、用法不同:@Autowired支持属性注入、构造方法注入和Setter注入,而@Resourse比前者少一个,它不支持构造方法注入。

3、支持的参数不同:@Resourse支持更多的参数设置,比如name、type设置,而@Autowired只支持required参数设置。


2.6 同一个类型多个@Bean报错


当出现以下多个Bean,返回同一对象类型时程序会报错,如下代码所示:


@Component
public class UserBeans {
    @Bean(name="u1")
    public User getUser1() {
        User user=new User();
        user.setId(1);
        user.setName("张三");
        return user;
    }
    @Bean(name="u2")
    public User getUser2() {
        User user=new User();
        user.setId(2);
        user.setName("李四");
        return user;
    }
}


在另一个类中获取User对象,如下代码所示:


@Controller
public class UserController {
    @Resource
    private User user;
    public void sayHi() {
        System.out.println("user->"+user);
    }
}


运行结果如下:


微信图片_20230111195050.png

报错的原因是非唯一的Bean对象。


同一个类型多个Bean的报错处理有以下三种方式:


1、精确地描述bean的名称(将注入的名称写对)


微信图片_20230111195046.png

2、使用@Resourse设置name的方式来重命名注入对象


微信图片_20230111195043.png

3、使用@Autowired+@Qualifier来筛选bean对象


微信图片_20230111195036.png


3.总结


1、将对象存储到Spring中:


使用类注解:@Controller、@Service、@Repository、@Configuration、@Component【了解它们之间的关系与区别】

使用方法注解:@Bean【注意搭配类注解一起使用】

2、Bean的命名规则:

首字母和第二个字母都是大写,那么直接使用原Bean名来获取Bean,否则就让首字母小写(小驼峰)来获取Bean。


3、从Spring中获取对象:


属性注入

Setter注入

构造函数注入(官方推荐)

4、注入的关键字:


@Autowired

@Resourse

5、 @Autowired和 @Resourse的区别:


出身不同

@Resourse不支持构造方法注入

@Resourse支持更多的参数

6、解决同一类型多个Bean的报错:


使用更精确的bean名称

使用 @Resourse(name="")

使用@Autowired @Qulifier("value =")

相关文章
|
1天前
|
缓存 Java Spring
实战指南:四种调整 Spring Bean 初始化顺序的方案
本文探讨了如何调整 Spring Boot 中 Bean 的初始化顺序,以满足业务需求。文章通过四种方案进行了详细分析: 1. **方案一 (@Order)**:通过 `@Order` 注解设置 Bean 的初始化顺序,但发现 `@PostConstruct` 会影响顺序。 2. **方案二 (SmartInitializingSingleton)**:在所有单例 Bean 初始化后执行额外的初始化工作,但无法精确控制特定 Bean 的顺序。 3. **方案三 (@DependsOn)**:通过 `@DependsOn` 注解指定 Bean 之间的依赖关系,成功实现顺序控制,但耦合性较高。
实战指南:四种调整 Spring Bean 初始化顺序的方案
|
29天前
|
XML Java 数据格式
Spring从入门到入土(bean的一些子标签及注解的使用)
本文详细介绍了Spring框架中Bean的创建和使用,包括使用XML配置文件中的标签和注解来创建和管理Bean,以及如何通过构造器、Setter方法和属性注入来配置Bean。
59 9
Spring从入门到入土(bean的一些子标签及注解的使用)
|
2月前
|
缓存 安全 Java
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
从底层源码入手,通过代码示例,追踪AnnotationConfigApplicationContext加载配置类、启动Spring容器的整个流程,并对IOC、BeanDefinition、PostProcesser等相关概念进行解释
169 24
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
|
20天前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
2月前
|
XML Java 数据格式
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
Spring 第二节内容补充 关于Bean配置的更多内容和细节 万字详解!
193 18
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
|
2月前
|
XML Java 数据格式
spring复习02,xml配置管理bean
详细讲解了Spring框架中基于XML配置文件管理bean的各种方式,包括获取bean、依赖注入、特殊值处理、属性赋值、集合类型处理、p命名空间、bean作用域及生命周期和自动装配。
spring复习02,xml配置管理bean
|
29天前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细解析Spring Bean的生命周期及其核心概念,并深入源码分析。Spring Bean是Spring框架的核心,由容器管理其生命周期。从实例化到销毁,共经历十个阶段,包括属性赋值、接口回调、初始化及销毁等。通过剖析`BeanFactory`、`ApplicationContext`等关键接口与类,帮助你深入了解Spring Bean的管理机制。希望本文能助你更好地掌握Spring Bean生命周期。
62 1
|
1月前
|
Java Spring
获取spring工厂中bean对象的两种方式
获取spring工厂中bean对象的两种方式
27 1
|
1月前
|
Java 开发者 Spring
Spring bean的生命周期详解!
本文详细介绍了Spring框架中的核心概念——Spring Bean的生命周期,包括实例化、属性赋值、接口回调、初始化、使用及销毁等10个阶段,并深入剖析了相关源码,如`BeanFactory`、`DefaultListableBeanFactory`和`BeanPostProcessor`等关键类与接口。通过理解这些核心组件,读者可以更好地掌握Spring Bean的管理和控制机制。
75 1
|
2月前
|
XML Java 数据格式
spring复习03,注解配置管理bean
Spring框架中使用注解配置管理bean的方法,包括常用注解的标识组件、扫描组件、基于注解的自动装配以及使用注解后的注意事项,并提供了一个基于注解自动装配的完整示例。
spring复习03,注解配置管理bean