阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

装配Bean

32次阅读
没有评论

共计 4917 个字符,预计需要花费 13 分钟才能阅读完成。

我们前面讨论了为什么要使用 Spring 的 IoC 容器,因为让容器来为我们创建并装配 Bean 能获得很大的好处,那么到底如何使用 IoC 容器?装配好的 Bean 又如何使用?

我们来看一个具体的用户注册登录的例子。整个工程的结构如下:

spring-ioc-appcontext
├── pom.xml
└── src
    └── main
        ├── java
        │   └── com
        │       └── itranswarp
        │           └── learnjava
        │               ├── Main.java
        │               └── service
        │                   ├── MailService.java
        │                   ├── User.java
        │                   └── UserService.java
        └── resources
            └── application.xml

首先,我们用 Maven 创建工程并引入 spring-context 依赖:

  • org.springframework:spring-context:6.0.0

我们先编写一个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,实现用户注册和登录:

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, "[email protected]", "password", "Bob"), // bob
            new User(2, "[email protected]", "password", "Alice"), // alice
            new User(3, "[email protected]", "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;
    }
}

注意到 UserService 通过 setMailService() 注入了一个MailService

然后,我们需要编写一个特定的 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">

    <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>

注意观察上述配置文件,其中与 XML Schema 相关的部分格式是固定的,我们只关注两个 <bean ...> 的配置:

  • 每个 <bean ...> 都有一个 id 标识,相当于 Bean 的唯一 ID;
  • userServiceBean 中,通过<property name="..." ref="..." /> 注入了另一个 Bean;
  • Bean 的顺序不重要,Spring 根据依赖关系会自动正确初始化。

把上述 XML 配置文件用 Java 代码写出来,就像这样:

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

只不过 Spring 容器是通过读取 XML 文件后使用反射完成的。

如果注入的不是 Bean,而是 booleanintString 这样的数据类型,则通过 value 注入,例如,创建一个HikariDataSource

<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 的 IoC 容器实例,然后加载配置文件,让 Spring 容器为我们创建并装配好配置文件中指定的所有 Bean,这只需要一行代码:

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

接下来,我们就可以从 Spring 容器中“取出”装配好的 Bean 然后使用它:

// 获取 Bean:
UserService userService = context.getBean(UserService.class);
// 正常调用:
User user = userService.login("[email protected]", "password");

完整的 main() 方法如下:

public class Main {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
        UserService userService = context.getBean(UserService.class);
        User user = userService.login("[email protected]", "password");
        System.out.println(user.getName());
    }
}

ApplicationContext

我们从创建 Spring 容器的代码:

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

可以看到,Spring 容器就是ApplicationContext,它是一个接口,有很多实现类,这里我们选择ClassPathXmlApplicationContext,表示它会自动从 classpath 中查找指定的 XML 配置文件。

获得了 ApplicationContext 的实例,就获得了 IoC 容器的引用。从 ApplicationContext 中我们可以根据 Bean 的 ID 获取 Bean,但更多的时候我们根据 Bean 的类型获取 Bean 的引用:

UserService userService = context.getBean(UserService.class);

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

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

BeanFactoryApplicationContext 的区别在于,BeanFactory的实现是按需创建,即第一次获取 Bean 时才创建这个 Bean,而 ApplicationContext 会一次性创建所有的 Bean。实际上,ApplicationContext接口是从 BeanFactory 接口继承而来的,并且,ApplicationContext提供了一些额外的功能,包括国际化支持、事件和通知机制等。通常情况下,我们总是使用ApplicationContext,很少会考虑使用BeanFactory

练习

在上述示例的基础上,继续给 UserService 注入DataSource,并把注册和登录功能通过数据库实现。

下载练习

小结

Spring 的 IoC 容器接口是ApplicationContext,并提供了多种实现类;

通过 XML 配置文件创建 IoC 容器时,使用ClassPathXmlApplicationContext

持有 IoC 容器后,通过 getBean() 方法获取 Bean 的引用。

正文完
星哥说事-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2024-08-05发表,共计4917字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中