【Spring系列】自动注入(装配)

简介: 我们把 Spring 在 Bean 与 Bean 之间建立依赖关系的行为称为“装配”。Spring 的 IOC 容器虽然功能强大,但它本身不过只是一个空壳而已,它自己并不能独自完成装配工作。需要我们主动将 Bean 放进去,并告诉它 Bean 和 Bean 之间的依赖关系,它才能按照我们的要求完成装配工作。在前面的学习中,我们都是在 XML 配置中通过 和 中的 ref 属性,手动维护 Bean 与 Bean 之间的依赖关系的。对于只包含少量 Bean 的应用来说,这种方式已经足够满足我们的需求了。但随着应用的不断发展,容器中包含的 Bean 会越来越多,Bean 和 Bean 之

Spring 自动装配(基于xml配置文件)


Spring 的自动装配功能可以让 Spring 容器依据某种规则(自动装配的规则,有五种),为指定的 Bean 从应用的上下文(AppplicationContext 容器)中查找它所依赖的 Bean,并自动建立 Bean 之间的依赖关系。而这一过程是在完全不使用任何 和 元素 ref 属性的情况下进行的。


Spring 的自动装配功能能够有效地简化 Spring 应用的 XML 配置,因此在配置数量相当多时采用自动装配降低工作量。


Spring 框架式默认不支持自动装配的,要想使用自动装配,则需要对 Spring XML 配置文件中 元素的 autowire 属性进行设置


Spring 共提供了 5 中自动装配规则,它们分别与 autowire 属性的 5 个取值对应


a8ffc912765d484982fe3b58c50d862f (1).png


不使用自动装配(autowire=“no”)



autowire=“no” 表示不使用自动装配,此时我们必须通过 <bean> 元素的 <constructor-arg>和 <property> 元素的 ref 属性维护 Bean 的依赖关系

Dept部门类


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


Employee员工类


public class Employee {
    private Integer E_id;
    private String name;
    private Dept dept;
    public Integer getE_id() {
        return E_id;
    }
    public void setE_id(Integer e_id) {
        E_id = e_id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Dept getDept() {
        return dept;
    }
    public void setDept(Dept dept) {
        this.dept = dept;
    }
    @Override
    public String toString() {
        return "Employee{" +
                "E_id=" + E_id +
                ", name='" + name + '\'' +
                ", dept=" + dept +
                '}';
    }
}


autowire="no"


<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--管理DEPT-->
    <bean id="dept" class="com.liu.pojo.Dept">
        <property name="id" value="1"/>
        <property name="name" value="技术部"/>
    </bean>
    <!--管理员工-->
    <bean id="employee" class="com.liu.pojo.Employee" autowire="no">
        <property name="e_id" value="1"/>
        <property name="name" value="李白"/>
        <property name="dept" ref="dept"/>
    </bean>
</beans>


Test


public class AutoDITest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        Employee employee = context.getBean("employee", Employee.class);
        System.out.println(employee);
    }
}


结果


Employee{E_id=1, name='李白', dept=Dept{id=1, name='技术部'}}
1


按名称自动装配(autowire=“byName”)



autowire=“byName” 表示按属性名称自动装配,XML 文件中 Bean 的 id 或 name 必须与类中的属性名称相同。


<!--管理DEPT-->
<bean id="dept" class="com.liu.pojo.Dept">
    <property name="id" value="1"/>
    <property name="name" value="技术部"/>
</bean>
<!--管理员工-->
<bean id="employee" class="com.liu.pojo.Employee" autowire="byName">
    <property name="e_id" value="1"/>
    <property name="name" value="李白"/>
</bean>
Employee{E_id=1, name='李白', dept=Dept{id=1, name='技术部'}}


如果名字不一样,比如改下dept的id,则就会注入不成功,部门为null


<!--管理DEPT-->
<bean id="dept1" class="com.liu.pojo.Dept">
    <property name="id" value="1"/>
    <property name="name" value="技术部"/>
</bean>
<!--管理员工-->
<bean id="employee" class="com.liu.pojo.Employee" autowire="byName">
    <property name="e_id" value="1"/>
    <property name="name" value="李白"/>
</bean>
Employee{E_id=1, name='李白', dept=null}


按类型自动装配(autowire=“byType”)



autowire=“byType” 表示按类中对象属性数据类型进行自动装配。即使 XML 文件中 Bean 的 id 或 name 与类中的属性名不同,只要 Bean 的 class 属性值与类中的对象属性的类型相同,就可以完成自动装配。


<!--管理DEPT-->
<bean id="dept1" class="com.liu.pojo.Dept">
    <property name="id" value="1"/>
    <property name="name" value="技术部"/>
</bean>
<!--管理员工-->
<bean id="employee" class="com.liu.pojo.Employee" autowire="byType">
    <property name="e_id" value="1"/>
    <property name="name" value="李白"/>
</bean>


现在虽然id不一样了,但是是按照类型进行注入的,需要com.liu.pojo.Dept类型的,去spring工厂里找,咦,找到了,就把找到这个bean注入给需要的bean,如果没找到,就无法注入

Employee{E_id=1, name='李白', dept=Dept{id=1, name='技术部'}}


如果同时存在多个相同类型的 Bean,则注入失败,并且引发异常。


IDEA还是很智能的,直接提示了,并且这样运行也会报错,因为spring也蒙蔽了,他也不知道要注入哪个bean了,他解决不了这个问题,就报错了


ac64ce9c181b482ebab9514e07b5ddbc (1).png


构造函数自动装配(autowire=“constructor”)


autowire=“constructor” 表示按照 Java 类中构造函数进行自动装配。


<!--管理DEPT-->
<bean id="dept1" class="com.liu.pojo.Dept">
    <constructor-arg name="id" value="1"/>
    <constructor-arg name="name" value="技术部"/>
</bean>
<!--管理员工-->
<bean id="employee" class="com.liu.pojo.Employee" autowire="constructor">
    <constructor-arg index="0" value="1"/>
    <constructor-arg index="1" value="李白"/>
</bean>
Employee{E_id=1, name='李白', dept=Dept{id=1, name='技术部'}}


默认的自动装配模式(autowire=“default”)


<!--管理DEPT-->
<bean id="dept1" class="com.liu.pojo.Dept">
    <property name="id" value="1"/>
    <property name="name" value="技术部"/>
</bean>
<!--管理员工-->
<bean id="employee" class="com.liu.pojo.Employee" autowire="default">
    <property name="e_id" value="1"/>
    <property name="name" value="李白"/>
</bean>
Employee{E_id=1, name='李白', dept=Dept{id=1, name='技术部'}}


Spring自动装配 (基于注解)


从 Java 5 开始,Java 增加了对注解(Annotation)的支持,它是代码中的一种特殊标记,可以在编译、类加载和运行时被读取,执行相应的处理。开发人员可以通过注解在不改变原有代码和逻辑的情况下,在源代码中嵌入补充信息。


Spring 从 2.5 版本开始提供了对注解技术的全面支持,我们可以使用注解来实现自动装配,简化 Spring 的 XML 配置。


Spring 通过注解实现自动装配的步骤如下:


引入依赖

开启组件扫描

使用注解定义 Bean

依赖注入


1. 引入AOP依赖


使用注解的第一步,就是要在项目中引入以下 Jar 包。

org.springframework.core-5.3.13.jar

org.springframework.beans-5.3.13.jar

spring-context-5.3.13.jar

spring-expression-5.3.13.jar

commons.logging-1.2.jar

spring-aop-5.3.13.jar


maven项目直接导入以下依赖坐标即可


<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.11</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>4.3.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>4.3.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>4.3.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-expression</artifactId>
    <version>4.3.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>4.3.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>4.3.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>4.3.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>4.3.2.RELEASE</version>
</dependency>


2. 开启组件扫描


Spring 默认不使用注解装配 Bean,因此我们需要在 Spring 的 XML 配置中,通过 <context:component-scan> 元素开启 Spring Beans的自动扫描功能。开启此功能后,Spring 会自动从扫描指定的包(base-package 属性设置)及其子包下的所有类,如果类上使用了 @Component 注解,就将该类装配到容器中。


<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--xmlns:context="http://www.springframework.org/schema/context" 注意别导错了-->
    <!--开启组件扫描功能 扫描com.liu包下所有类是否添加了@Component注解-->
    <context:component-scan base-package="com.liu"/>
</beans>


注意:在使用 context:component-scan 元素开启自动扫描功能前,首先需要在 XML 配置的一级标签 中添加 context 相关的约束


3. 使用注解定义 Bean


Spring 提供了以下多个注解,这些注解可以直接标注在 Java 类上,将它们定义成 Spring Bean。


bfc4d1b70bca4ea79780d47d69f4fb2b.png


4. 基于注解方式实现依赖注入


我们可以通过以下注解将定义好 Bean 装配到其它的 Bean 中。


4a12b444caa44953bc433246926f0f8f.png


1.创建maven工程


2.导入相关依赖在pom.xm配置文件中


<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>4.3.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.3.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>4.3.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>4.3.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>4.3.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>4.3.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-expression</artifactId>
        <version>4.3.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>4.3.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>4.3.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>4.3.2.RELEASE</version>
    </dependency>
    <!--springmvc核心依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>4.3.2.RELEASE</version>
    </dependency>
    <!--servlet-api-->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
        <scope>provided</scope>
    </dependency>
    <!--jstl-->
    <dependency>
        <groupId>jstl</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
    <!--mybatis-->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.5</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>1.3.3</version>
    </dependency>
    <!--mysql-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
    <!--log4j-->
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.17.1</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.25</version>
    </dependency>
    <!--fastjson-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.62</version>
    </dependency>
    <!--druid-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.2.8</version>
    </dependency>
</dependencies>


3.在 com.liu.dao 包下,创建一个名为 UserDAO 的接口


85cf5fd12f104aa8aff9d78f477f7558.png

public interface UserDAO {
    void print();
}


4.在com.liu.dao.impl 包下,创建 UserDao 的实现类 UserDAOImpl


8dae13d1d3b94f01b89c07e175fe350d.png

/*注解 @Repository 将这个dao层类交给spring容器管理
* 尽管我们并没有在spring.xml中进行相应配置,这里的字符串
* value就相当于spring.xml配置bean时的id属性,底层反射代理*/
@Repository(value = "userDAO")
public class UserDAOImpl implements UserDAO {
    @Override
    public void print() {
        System.out.println("hello auto");
    }
}


5.在 com.liu.service 包下,创建一个名为 UserService 的接口


953d051c99ab40a0b95dc7f1e5712ae5.png

public interface UserService {
    void out();
}


6.在 com.liu.service.impl 包下,创建 UserService 的实现类 UserServiceImpl


/*这里@Service注解用于标识他是业务层类,然后
* 添加此注解,它就会被spring容器所管理,value
* 属性还是同理*/
@Service(value = "userService")
public class UserServiceImpl implements UserService {
    /*这里的@Resource注解是用于依赖注入的,以前我们就是通过配置
    * 现在直接在需要的地方,添加相应注解,就实现对他的注入,简化了
    * 配置文件的大小以及开发的繁琐,@Resource默认是按照类型进行注入的,
    * 你也可以指定它的name属性,让他按照name进行注入,或者指定name和
    * type这时,他会先按照name进行找,找不到在按照type进行查找,还找不到
    * 就会报错了*/
    @Resource
    private UserDAO userDAO;
    public UserDAO getUserDAO() {
        return userDAO;
    }
    public void setUserDAO(UserDAO userDAO) {
        this.userDAO = userDAO;
    }
    @Override
    public void out() {
        userDAO.print();
        System.out.println("我来了");
    }
}


7. 在 com.liu.controller 包下,创建一个名为 UserController 的类


ec6043a07c6548308e0bd8315480a911.png

什么时候给value呢,如果他被其他类需要,或者需要被main调用,就需要给value,就相当于我们在配置文件中给他了一个唯一标识id,然后工厂可以根据id拿到该bean,因为接下来要实例化它,所以给他添加了一个value属性


@Controller("userController")
public class UserController {
    @Resource
    private UserService userService;
    public UserService getUserService() {
        return userService;
    }
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
    public void doStr(){
        userService.out();
    }
}


8.创建 Spring 配置文件 Beans.xml,配置内容如下


注意:xml的相应依赖要导入全,不然会报错的,很好解决


<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                          http://www.springframework.org/schema/context
                          http://www.springframework.org/schema/context/spring-context.xsd">
    <!--xmlns:context="http://www.springframework.org/schema/context" 注意别导错了-->
    <!--开启组件扫描功能 扫描com.liu包下所有类是否添加了@Component注解-->
    <context:component-scan base-package="com.liu"></context:component-scan>
</beans>


9.测试


前端来一个请求,被springmvc拦截,交给了controller,然后controller被spring创建管理,controller的方法调用 service 层,service等去调用 dao 层,完成业务逻辑的处理,返回结果,然而spring为我们做了太多,对象的创建管理,虽然我们没有配置文件xml,但是我们通过注解的方式,注解传入了重要的信息,注解其实就是通过反射去创建对象,单例多例可以看设计模式,还有工厂模式,代理,动态代理,一下子全穿起来吧,是不是觉得豁然开朗,多学,重复写,这种领悟会越来越深刻,越来越清晰,就是在这种状态下进步的,ok


public class Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        UserController userController = context.getBean("userController", UserController.class);
        userController.doStr();
    }
}


输出

hello auto
我来了


相关文章
|
3月前
|
Java Spring
在使用Spring的`@Value`注解注入属性值时,有一些特殊字符需要注意
【10月更文挑战第9天】在使用Spring的`@Value`注解注入属性值时,需注意一些特殊字符的正确处理方法,包括空格、引号、反斜杠、新行、制表符、逗号、大括号、$、百分号及其他特殊字符。通过适当包裹或转义,确保这些字符能被正确解析和注入。
196 3
|
3月前
|
Java 测试技术 程序员
为什么Spring不推荐@Autowired用于字段注入?
作为Java程序员,Spring框架在日常开发中使用频繁,其依赖注入机制带来了极大的便利。然而,尽管@Autowired注解简化了依赖注入,Spring官方却不推荐在字段上使用它。本文将探讨字段注入的现状及其存在的问题,如难以进行单元测试、违反单一职责原则及易引发NPE等,并介绍为何Spring推荐构造器注入,包括增强代码可读性和维护性、方便单元测试以及避免NPE等问题。通过示例代码展示如何将字段注入重构为构造器注入,提高代码质量。
121 1
|
29天前
|
Java Spring
一键注入 Spring 成员变量,顺序编程
介绍了一款针对Spring框架开发的插件,旨在解决开发中频繁滚动查找成员变量注入位置的问题。通过一键操作(如Ctrl+1),该插件可自动在类顶部添加`@Autowired`注解及其成员变量声明,同时保持光标位置不变,有效提升开发效率和代码编写流畅度。适用于IntelliJ IDEA 2023及以上版本。
一键注入 Spring 成员变量,顺序编程
|
2月前
|
消息中间件 Java 数据库
解密Spring Boot:深入理解条件装配与条件注解
Spring Boot中的条件装配与条件注解提供了强大的工具,使得应用程序可以根据不同的条件动态装配Bean,从而实现灵活的配置和管理。通过合理使用这些条件注解,开发者可以根据实际需求动态调整应用的行为,提升代码的可维护性和可扩展性。希望本文能够帮助你深入理解Spring Boot中的条件装配与条件注解,在实际开发中更好地应用这些功能。
45 2
|
3月前
|
缓存 Java Spring
源码解读:Spring如何解决构造器注入的循环依赖?
本文详细探讨了Spring框架中的循环依赖问题,包括构造器注入和字段注入两种情况,并重点分析了构造器注入循环依赖的解决方案。文章通过具体示例展示了循环依赖的错误信息及常见场景,提出了三种解决方法:重构代码、使用字段依赖注入以及使用`@Lazy`注解。其中,`@Lazy`注解通过延迟初始化和动态代理机制有效解决了循环依赖问题。作者建议优先使用`@Lazy`注解,并提供了详细的源码解析和调试截图,帮助读者深入理解其实现机制。
78 1
|
5月前
|
XML Java 数据格式
Spring5入门到实战------4、IOC容器-Bean管理XML方式、集合的注入(二)
这篇文章是Spring5框架的实战教程,主题是IOC容器中Bean的集合属性注入,通过XML配置方式。文章详细讲解了如何在Spring中注入数组、List、Map和Set类型的集合属性,并提供了相应的XML配置示例和Java类定义。此外,还介绍了如何在集合中注入对象类型值,以及如何使用Spring的util命名空间来实现集合的复用。最后,通过测试代码和结果展示了注入效果。
Spring5入门到实战------4、IOC容器-Bean管理XML方式、集合的注入(二)
|
5月前
|
缓存 Java 数据库连接
Spring Boot 资源文件属性配置,紧跟技术热点,为你的应用注入灵动活力!
【8月更文挑战第29天】在Spring Boot开发中,资源文件属性配置至关重要,它让开发者能灵活定制应用行为而不改动代码,极大提升了可维护性和扩展性。Spring Boot支持多种配置文件类型,如`application.properties`和`application.yml`,分别位于项目的resources目录下。`.properties`文件采用键值对形式,而`yml`文件则具有更清晰的层次结构,适合复杂配置。此外,Spring Boot还支持占位符引用和其他外部来源的属性值,便于不同环境下覆盖默认配置。通过合理配置,应用能快速适应各种环境与需求变化。
57 0
|
5月前
|
安全 Java 开发者
开发者必看!@Resource与private final的较量,Spring Boot注入技巧大揭秘,你不可不知的细节!
【8月更文挑战第29天】Spring Boot作为热门Java框架,其依赖注入机制备受关注。本文通过对比@Resource(JSR-250规范)和@Autowired(Spring特有),并结合private final声明的字段注入,详细探讨了两者的区别与应用场景。通过示例代码展示了@Resource按名称注入及@Autowired按类型注入的特点,并分析了它们在注入时机、依赖性、线程安全性和单一职责原则方面的差异,帮助开发者根据具体需求选择最合适的注入策略。
211 0
|
6月前
|
Java Spring
spring注入的几种方式
spring注入的几种方式
33 0
|
7月前
|
Java Spring 容器
spring如何进行依赖注入,通过set方法把Dao注入到serves
spring如何进行依赖注入,通过set方法把Dao注入到serves