上一章写的关于Spring的IOC容器,说了在IOC中配置一个组件就是配置一个Bean,IOC中包含了多个Bean对象,本章详细讲解关于Bean的原理知识

例子

工程结构图

spring配置文件定义set_java


MailService类,用于在用户登录和注册成功后发送邮件通知

public class MailService {
    private ZoneId zoneId = ZoneId.systemDefault();

    public void setZoneId(ZoneId zoneId) {
        this.zoneId = zoneId;
    }

    public String getTime() {
        return ZonedDateTime.now(this.zoneId).format(DateTimeFormatter.ISO_ZONED_DATE_TIME);
    }

    public void sendLoginMail(User user) {
        System.err.println(String.format("Hi, %s! You are logged in at %s", user.getName(), getTime()));
    }

    public void sendRegistrationMail(User user) {
        System.err.println(String.format("Welcome, %s!", user.getName()));

    }
}

UserService类,实现用户注册和登录。通过setMailService()注入了一个MailService

public class UserService {
    private MailService mailService;

    public void setMailService(MailService mailService) {
        this.mailService = mailService;
    }

    private List<User> users = new ArrayList<>(List.of( // users:
            new User(1, "bob@example.com", "password", "Bob"), // bob
            new User(2, "alice@example.com", "password", "Alice"), // alice
            new User(3, "tom@example.com", "password", "Tom"))); // tom

    public User login(String email, String password) {
        for (User user : users) {
            if (user.getEmail().equalsIgnoreCase(email) && user.getPassword().equals(password)) {
                mailService.sendLoginMail(user);
                return user;
            }
        }
        throw new RuntimeException("login failed.");
    }

    public User getUser(long id) {
        return this.users.stream().filter(user -> user.getId() == id).findFirst().orElseThrow();
    }

    public User register(String email, String password, String name) {
        users.forEach((user) -> {
            if (user.getEmail().equalsIgnoreCase(email)) {
                throw new RuntimeException("email exist.");
            }
        });
        User user = new User(users.stream().mapToLong(u -> u.getId()).max().getAsLong() + 1, email, password, name);
        users.add(user);
        mailService.sendRegistrationMail(user);
        return user;
    }
}

配置文件:application.xml配置文件,告诉Spring的IOC容器应该如何创建并组装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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置实例(id:“唯一标识”  class="需要被创建的目标对象全限定名") -->
    <bean id="userService" class="com.itranswarp.learnjava.service.UserService">
        <property name="mailService" ref="mailService" />
    </bean>

    <bean id="mailService" class="com.itranswarp.learnjava.service.MailService" />
</beans>

其实对于目前来说,我们所说的IOC就是将对象的创建权交给了Spring,由Spring代替我们去操作创建对象,不再由开发者进行操作,极大地降低了代码的耦合。
而由于Spring获取了对象的创建权和注入,Spring需要知道创建什么对象,并且注入到哪里,这时就需要通过配置文件来告诉Spring创建什么Bean,并且注入到哪里

配置文件

schema

配置文件中的顶级标签中包含了语义化标签的相关信息

xmlns:语义化标签所在的命名空间。
xmlns:xsi:XMLSchema-instance 标签遵循Schema标签标准。
xsi:schemaLocation:xsd文件位置,用以描述标签语义、属性、取值范围等。

application.xml配置文件中需要关注的是<beans …>的配置
1、每个<bean …>都有一个id标识,相当于Bean的唯一ID
2、通过注入了另一个Bean
3、Bean的顺序不重要,Spring根据依赖关系会自动正确初始化

在例子中的application.xml文件中的配置等同于下面的java代码

UserService userService = new UserService();
MailService mailService = new MailService();
userService.setMailService(mailService);

上述中注入的是引用类型的,如果注入的是基本类型,则是通过value注入

<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test" />
    <property name="username" value="root" />
    <property name="password" value="password" />
    <property name="maximumPoolSize" value="10" />
    <property name="autoCommit" value="true" />
</bean>

当我们写完配置文件后我们需要加载配置文件,从而让Spring容器为我们创建并装配好配置文件中指定的所有Bean

IOC容器的创建

有三种创建IOC容器的方式
1、ClassPathXmlApplicationContext:从项目的根目录下加载配置文件

ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");

2、FileSystemXmlApplicationContext:从磁盘中的加载配置文件
3、AnnotationConfigApplicationContext:当使用注解配置容器对象时使用此类进行注解读取,创建容器。

Spring容器就是ApplicationContext,它是一个接口,有很多实现类。
获得ApplicationContext的实例,就获得了IoC容器的引用。从ApplicationContext中我们可以根据Bean的ID获取Bean,但更多的时候我们根据Bean的类型获取Bean的引用

Spring还提供另一种IoC容器叫BeanFactory,使用方式和ApplicationContext类似

BeanFactory factory = new XmlBeanFactory(new ClassPathResource("application.xml"));
MailService mailService = factory.getBean(MailService.class);

BeanFactory和ApplicationContext的区别在于,BeanFactory的实现是按需创建
即第一次获取Bean时才创建这个Bean,ApplicationContext会一次性创建所有的Bean