前言
在 Spring 的第二篇中主要讲解了 Spring Core 模块的使用 IOC 容器创建对象的问题, Spring Core 模块主要是解决对象的创建和对象之间的依赖关系, 因此本博文主要讲解如何使用 IOC 容器来解决对象之间的依赖关系!
回顾以前对象依赖
我们来看一下我们以前关于对象依赖, 是怎么的历程
直接 new 对象
在最开始, 我们是直接 new 对象给 serice 的 userDao 属性赋值...
- class UserService{UserDao userDao = new UserDao();
- }
写 DaoFactory, 用字符串来维护依赖关系
后来, 我们发现 service 层紧紧耦合了 dao 层我们就写了 DaoFactory, 在 service 层只要通过字符串就能够创建对应的 dao 层的对象了
- DaoFactory
- public class DaoFactory {
- private static final DaoFactory factory = new DaoFactory();
- private DaoFactory(){}
- public static DaoFactory getInstance(){
- return factory;
- }
- public <T> T createDao(String className,Class<T> clazz){
- try{
- T t = (T) Class.forName(className).newInstance();
- return t;
- }catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- }
- serivce
- private CategoryDao categoryDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.CategoryDAOImpl", CategoryDao.class);
- private BookDao bookDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.BookDaoImpl", BookDao.class);
- private UserDao userDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.UserDaoImpl", UserDao.class);
- private OrderDao orderDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.OrderDaoImpl", OrderDao.class);
DaoFactory 读取配置文件
再后来, 我们发现要修改 Dao 的实现类, 还是得修改 service 层的源代码呀.. 于是我们就在 DaoFactory 中读取关于 daoImpl 的配置文件, 根据配置文件来创建对象, 这样一来, 创建的是哪个 daoImpl 对 service 层就是透明的
- DaoFactory
- ```java
- public class DaoFactory {
- private UserDao userdao = null;
- private DaoFactory(){
- try{
- InputStream in = DaoFactory.class.getClassLoader().getResourceAsStream("dao.properties");
- Properties prop = new Properties();
- prop.load(in);
- String daoClassName = prop.getProperty("userdao");
- userdao = (UserDao)Class.forName(daoClassName).newInstance();
- }catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- private static final DaoFactory instance = new DaoFactory();
- public static DaoFactory getInstance(){
- return instance;
- }
- public UserDao createUserDao(){
- return userdao;
- }
- }
- ```
- service
- UserDao dao = DaoFactory.getInstance().createUserDao();
Spring 依赖注入
通过上面的历程, 我们可以清晰地发现: 对象之间的依赖关系, 其实就是给对象上的属性赋值! 因为对象上有其他对象的变量, 因此存在了依赖...
Spring 提供了好几种的方式来给属性赋值
1) 通过构造函数
2) 通过 set 方法给属性注入值
3) p 名称空间
4) 自动装配 (了解)
5) 注解
搭建测试环境
UserService 中使用 userDao 变量来维护与 Dao 层之间的依赖关系
UserAction 中使用 userService 变量来维护与 Service 层之间的依赖关系
- userDao
- public class UserDao {
- public void save() {
- System.out.println("DB: 保存用户");
- }
- }
- userService
- public class UserService {
- private UserDao userDao;
- public void save() {
- userDao.save();
- }
- }
- userAnction
- public class UserAction {
- private UserService userService;
- public String execute() {
- userService.save();
- return null;
- }
- }
构造函数给属性赋值
其实我们在讲解创建带参数的构造函数的时候已经讲过了... 我们还是来回顾一下呗..
我们测试 service 和 dao 的依赖关系就好了.... 在 serice 中加入一个构造函数, 参数就是 userDao
- public UserService(UserDao userDao) {
- this.userDao = userDao;
- // 看看有没有拿到 userDao
- System.out.println(userDao);
- }
applicationContext.xml 配置文件
- <!-- 创建 userDao 对象 -->
- <bean id="userDao" class="UserDao"/>
- <!-- 创建 userService 对象 -->
- <bean id="userService" class="UserService">
- <!-- 要想在 userService 层中能够引用到 userDao, 就必须先创建 userDao 对象 -->
- <constructor-arg index="0" name="userDao" type="UserDao" ref="userDao"></constructor-arg>
- </bean>
测试: 可以成功获取到 userDao 对象
- // 创建容器对象
- ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
- // 得到 service 对象
- UserService userService = (UserService) ac.getBean("userService");
通过 set 方法给属性注入值
我们这里也是测试 service 和 dao 层的依赖关系就好了... 在 service 层通过 set 方法来把 userDao 注入到 UserService 中
为 UserService 添加 set 方法
- public class UserService {
- private UserDao userDao;
- public void setUserDao(UserDao userDao) {
- this.userDao = userDao;
- // 看看有没有拿到 userDao
- System.out.println(userDao);
- }
- public void save() {
- userDao.save();
- }
- }
applicationContext.xml 配置文件: 通过 property 节点来给属性赋值
引用类型使用 ref 属性
基本类型使用 value 属性
- <!-- 创建 userDao 对象 -->
- <bean id="userDao" class="UserDao"/>
- <!-- 创建 userService 对象 -->
- <bean id="userService" class="UserService">
- <property name="userDao" ref="userDao"/>
- </bean>
测试:
内部 Bean
我们刚才是先创建 userDao 对象, 再由 userService 对 userDao 对象进行引用... 我们还有另一种思维: 先创建 userService, 发现 userService 需要 userDao 的属性, 再创建 userDao... 我们来看看这种思维方式是怎么配置的:
applicationContext.xml 配置文件: property 节点内置 bean 节点
- <!--
- 1. 创建 userService, 看到有 userDao 这个属性
- 2. 而 userDao 这个属性又是一个对象
- 3. 在 property 属性下又内置了一个 bean
- 4. 创建 userDao
- -->
- <bean id="userService" class="UserService">
- <property name="userDao">
- <bean id="userDao" class="UserDao"/>
- </property>
- </bean>
测试
我们发现这种思维方式和服务器访问的执行顺序是一样的, 但是如果 userDao 要多次被其他 service 使用的话, 就要多次配置了...
p 名称空间注入属性值
p 名称控件这种方式其实就是 set 方法的一种优化, 优化了配置而已...p 名称空间这个内容需要在 Spring3 版本以上才能使用... 我们来看看:
applicationContext.xml 配置文件: 使用 p 名称空间
- <bean id="userDao" class="UserDao"/>
- <!-- 不用写 property 节点了, 直接使用 p 名称空间 -->
- <bean id="userService" class="UserService" p:userDao-ref="userDao"/>
测试
自动装配
Spring 还提供了自动装配的功能, 能够非常简化我们的配置
自动装载默认是不打开的, 自动装配常用的可分为两种:
根据名字来装配
根据类型类装配
XML 配置根据名字
applicationContext.xml 配置文件: 使用自动装配, 根据名字
- <bean id="userDao" class="UserDao"/>
- <!--
- 1. 通过名字来自动装配
- 2. 发现 userService 中有个叫 userDao 的属性
- 3. 看看 IOC 容器中没有叫 userDao 的对象
- 4. 如果有, 就装配进去
- -->
- <bean id="userService" class="UserService" autowire="byName"/>
测试
XML 配置根据类型
applicationContext.xml 配置文件: 使用自动装配, 根据类型
值得注意的是: 如果使用了根据类型来自动装配, 那么在 IOC 容器中只能有一个这样的类型, 否则就会报错!
- <bean id="userDao" class="UserDao"/>
- <!--
- 1. 通过名字来自动装配
- 2. 发现 userService 中有个叫 userDao 的属性
- 3. 看看 IOC 容器 UserDao 类型的对象
- 4. 如果有, 就装配进去
- -->
- <bean id="userService" class="UserService" autowire="byType"/>
测试:
我们这只是测试两个对象之间的依赖关系, 如果我们有很多对象, 我们也可以使用默认自动分配
使用注解来实现自动装配
@Autowired 注解来实现自动装配:
可以在构造器上修饰
也可以在 setter 方法上修饰
** 来自 java 的 @Inject 的和 @AutoWired 有相同的功能 **
如果没有匹配到 bean, 又为了避免异常的出现, 我们可以使用 required 属性上设置为 false 谨慎对待
测试代码
- @Component
- public class UserService {
- private UserDao userDao ;
- @Autowired
- public void setUserDao(UserDao userDao) {
- this.userDao = userDao;
- }
- }
顺利拿到 userDao 的引用
使用 JavaConfig 配置类实现对象依赖
在
第一种 (测试不出来)
- import org.springframework.context.annotation.Bean;
- @org.springframework.context.annotation.Configuration
- public class Configuration {
- @Bean()
- public UserDao userDao() {
- return new UserDao();
- }
- @Bean
- public UserService userService() {
- // 直接调用 @bean 的方法
- return new UserService(userDao());
- }
- }
第二种 (测试不出来)
- import org.springframework.context.annotation.Bean;
- @org.springframework.context.annotation.Configuration
- public class Configuration {
- @Bean()
- public UserDao userDao() {
- return new UserDao();
- }
- @Bean
- public UserService userService(UserDao userDao) {
- // 通过构造函数依赖注入
- return new UserService(userDao);
- }
- }
如果我直接通过构造器传入的话, 那么报错了
- import org.springframework.beans.factory.annotation.Autowire;
- import org.springframework.context.annotation.Bean;
- @org.springframework.context.annotation.Configuration
- public class Configuration {
- @Bean()
- public UserDao userDao() {
- return new UserDao();
- }
- @Bean(autowire = Autowire.BY_TYPE)
- public UserService userService(UserDao userDao) {
- return new UserService(userDao);
- }
- }
我测试中只有通过这种方法才能拿到 userDao 的引用
- public class Configuration {
- @Bean()
- public UserDao userDao() {
- return new UserDao();
- }
- @Bean(autowire = Autowire.BY_TYPE)
- public UserService userService() {
- return new UserService(userDao());
- }
- }
当然了, 最简单直观的方法还有一种: 在 UserService 中加入 setUser() 方法, 那么只要 set 进去就行了..
- UserService
- public class UserService {
- private UserDao userDao ;
- public UserService() {
- }
- public UserService(UserDao userDao) {
- }
- public void setUserDao(UserDao userDao) {
- this.userDao = userDao;
- }
- }
- Config
- import org.springframework.context.annotation.Bean;
- @org.springframework.context.annotation.Configuration
- public class Config1 {
- @Bean(name = "userDao")
- public UserDao userDao() {
- return new UserDao();
- }
- @Bean(name="userService")
- public UserService userService() {
- UserService userService = new UserService();
- userService.setUserDao(userDao());
- return userService;
- }
- }
最后
来源: https://www.cnblogs.com/Java3y/p/8566706.html