Spring【依赖注入】就是这么简单

简介: 前言在Spring的第二篇中主要讲解了Spring Core模块的使用IOC容器创建对象的问题,Spring Core模块主要是解决对象的创建和对象之间的依赖关系,因此本博文主要讲解如何使用IOC容器来解决对象之间的依赖关系!回顾以前对象依赖我们来看一下我们以前关于对象依赖,是怎么的历程直接new对象在最开始,我们是直接new对象给serice的userDao属性赋值.

前言

在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注解来实现自动装配:

如果没有匹配到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;
    }

}

这里写图片描述


最后

扩展阅读:

如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:Java3y

更多的文章可往: 文章的目录导航
目录
相关文章
|
26天前
|
Java Spring
在使用Spring的`@Value`注解注入属性值时,有一些特殊字符需要注意
【10月更文挑战第9天】在使用Spring的`@Value`注解注入属性值时,需注意一些特殊字符的正确处理方法,包括空格、引号、反斜杠、新行、制表符、逗号、大括号、$、百分号及其他特殊字符。通过适当包裹或转义,确保这些字符能被正确解析和注入。
|
1月前
|
Java 测试技术 程序员
为什么Spring不推荐@Autowired用于字段注入?
作为Java程序员,Spring框架在日常开发中使用频繁,其依赖注入机制带来了极大的便利。然而,尽管@Autowired注解简化了依赖注入,Spring官方却不推荐在字段上使用它。本文将探讨字段注入的现状及其存在的问题,如难以进行单元测试、违反单一职责原则及易引发NPE等,并介绍为何Spring推荐构造器注入,包括增强代码可读性和维护性、方便单元测试以及避免NPE等问题。通过示例代码展示如何将字段注入重构为构造器注入,提高代码质量。
|
4月前
|
XML Java 测试技术
Spring Boot中的依赖注入和控制反转
Spring Boot中的依赖注入和控制反转
|
1月前
|
缓存 Java Spring
源码解读:Spring如何解决构造器注入的循环依赖?
本文详细探讨了Spring框架中的循环依赖问题,包括构造器注入和字段注入两种情况,并重点分析了构造器注入循环依赖的解决方案。文章通过具体示例展示了循环依赖的错误信息及常见场景,提出了三种解决方法:重构代码、使用字段依赖注入以及使用`@Lazy`注解。其中,`@Lazy`注解通过延迟初始化和动态代理机制有效解决了循环依赖问题。作者建议优先使用`@Lazy`注解,并提供了详细的源码解析和调试截图,帮助读者深入理解其实现机制。
29 1
|
3月前
|
XML Java 数据格式
Spring5入门到实战------4、IOC容器-Bean管理XML方式、集合的注入(二)
这篇文章是Spring5框架的实战教程,主题是IOC容器中Bean的集合属性注入,通过XML配置方式。文章详细讲解了如何在Spring中注入数组、List、Map和Set类型的集合属性,并提供了相应的XML配置示例和Java类定义。此外,还介绍了如何在集合中注入对象类型值,以及如何使用Spring的util命名空间来实现集合的复用。最后,通过测试代码和结果展示了注入效果。
Spring5入门到实战------4、IOC容器-Bean管理XML方式、集合的注入(二)
|
3月前
|
Java Spring 容器
彻底改变你的编程人生!揭秘 Spring 框架依赖注入的神奇魔力,让你的代码瞬间焕然一新!
【8月更文挑战第31天】本文介绍 Spring 框架中的依赖注入(DI),一种降低代码耦合度的设计模式。通过 Spring 的 DI 容器,开发者可专注业务逻辑而非依赖管理。文中详细解释了 DI 的基本概念及其实现方式,如构造器注入、字段注入与 setter 方法注入,并提供示例说明如何在实际项目中应用这些技术。通过 Spring 的 @Configuration 和 @Bean 注解,可轻松定义与管理应用中的组件及其依赖关系,实现更简洁、易维护的代码结构。
51 0
|
3月前
|
设计模式 自然语言处理 Java
简单了解下Spring中的各种Aware接口实现依赖注入
在Spring框架中,Aware接口是一组用于提供特定资源或环境信息的回调接口。这些接口被设计用来允许Bean获取对Spring容器或其他相关资源的引用,并在需要时进行适当的处理。
36 2
|
3月前
|
缓存 Java 数据库连接
Spring Boot 资源文件属性配置,紧跟技术热点,为你的应用注入灵动活力!
【8月更文挑战第29天】在Spring Boot开发中,资源文件属性配置至关重要,它让开发者能灵活定制应用行为而不改动代码,极大提升了可维护性和扩展性。Spring Boot支持多种配置文件类型,如`application.properties`和`application.yml`,分别位于项目的resources目录下。`.properties`文件采用键值对形式,而`yml`文件则具有更清晰的层次结构,适合复杂配置。此外,Spring Boot还支持占位符引用和其他外部来源的属性值,便于不同环境下覆盖默认配置。通过合理配置,应用能快速适应各种环境与需求变化。
43 0
|
3月前
|
安全 Java 开发者
开发者必看!@Resource与private final的较量,Spring Boot注入技巧大揭秘,你不可不知的细节!
【8月更文挑战第29天】Spring Boot作为热门Java框架,其依赖注入机制备受关注。本文通过对比@Resource(JSR-250规范)和@Autowired(Spring特有),并结合private final声明的字段注入,详细探讨了两者的区别与应用场景。通过示例代码展示了@Resource按名称注入及@Autowired按类型注入的特点,并分析了它们在注入时机、依赖性、线程安全性和单一职责原则方面的差异,帮助开发者根据具体需求选择最合适的注入策略。
135 0
|
3月前
|
自然语言处理 Java 开发者
简单了解下Spring中的各种Aware接口实现依赖注入
【8月更文挑战第21天】在Spring框架中,Aware接口系列是一种特殊的机制,它允许Bean在初始化过程中获取到Spring容器或容器中的特定资源,从而实现了更加灵活和强大的依赖注入方式。本文将围绕Spring中的各种Aware接口,详细探讨它们如何帮助开发者在工作和学习中更好地实现依赖注入。
107 0