Spring框架 —— 控制反转IOC

news2024/10/6 1:38:37

 前言

        在前一篇文章中荔枝已经初步了解了Spring并通过一个入门案例了解了Spring的基本操作,接下来荔枝就要梳理Spring中的最为重要的知识点之一——IoC控制反转,控制反转和属性注入均是基于Java中的反射机制来实现的。所以学习这个知识点之前必须要学习Java的反射机制。在这篇文章中荔枝会着重梳理两种获取bean和操作bean的方式,其中基于注解方式来操作bean更为常用。


文章目录

 前言

一、概述

1.1 依赖注入

1.2 IOC容器在Spring中的实现

二、基于XML管理bean

2.1 获取bean对象的三种方式

2.2 接口实现类的获取

2.3 注入

 2.3.1 Spring中基于setter注入

2.3.2 基于构造器进行注入

2.4 特殊值的注入

2.4.1 字面量赋值

2.4.2 null值注入

2.4.3 xml实体

2.4.4 CTADA节

2.5 特殊类型的注入

2.5.1 对象类型属性注入

2.5.2 数组类型属性注入

2.5.3 集合类型属性注入

2.5.4 P命名空间注入

2.5.5 引用外部文件注入

2.6 bean的作用域

2.7 bean的生命周期

2.7.1 具体的生命周期过程

2.7.2 bean的后置处理器 

2.8 基于xml自动装配

三、基于注解管理bean

3.1 通过注解创建bean对象 

3.2 @Autowired注入

3.3 @Resource注入

3.4 全注解开发

总结


一、概述

        loC是Inversion of Control的简写,译为“控制反转”,它不是一门技术,而是一种设计思想,是一个重要的面向对象编程法则,能够指导我们如何设计出松耦合、更优良的程序。Spring通过loC容器来管理所有Java对象的实例化和初始化,控制对象与对象之间的依赖关系。我们将由IoC容器管理的Java对象称为Spring Bean,它与使用关键字new创建的Java对象没有任何区别。loC容器是Spring框架中最重要的核心组件之一,它贯穿了Spring从诞生到成长的整个过程。 

容器放bean对象,使用的是map集合。

控制反转的具体实现方式:依赖注入DI

1.1 依赖注入

依赖注入指Spring创建对象的过程中,将对象依赖属性通过配置进行注入,依赖注入常见的实现方式包括两种:set注入和构造注入。

IOC就是一种控制反转的思想,而DI是对IoC容器对bean管理的一种具体实现。Bean管理指的是是:Bean对象的创建,以及Bean对象中属性的赋值(或者叫做Bean对象之间关系的维护)

1.2 IOC容器在Spring中的实现

Spring提供了IoC容器的两种实现方式:

  • BeanFactory接口:该接口是IoC容器的基本实现,是Spring内部使用的接口。一般我们用的都是它的子接口ApplicationContext。
  • ApplicationContext:BeanFactory的子接口,提供了更多高级特性。面向Spring的使用者,几乎所有场合都使用ApplicationContext而不是底层的BeanFactory。
类型名描述
ClassPathXmlApplicationContext通过读取类路径下的ML格式的配置文件创建IOC容器对象
FileSystemXmlApplicationContext通过文件系统路径读取ML格式的配置文件创建IOC容器对象
ConfigurableApplicationContextApplicationContext的子接口,包含一些扩展方法refresh()和close(),让ApplicationContext具有启动、关闭和刷新上下文的能力.
WebApplicationContext专门为Web应用准备,基于Web环境创建IOc容器对象,并将对象引入存入ServletContext域中。


二、基于XML管理bean

2.1 获取bean对象的三种方式

IoC中获取bean对象有三种方式:根据xml配置文件中的id来获取、根据类型来获取、根据ID和类型共同获取

在配置文件bean.xml中配置要创建的对象

<bean id="user" class="com.crj.spring6.iocxml.User"></bean>

三种获取bean对象的方式

package com.crj.spring6.iocxml;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class testUser {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        //获取bean
        根据ID获取bean
        User user1 = (User)context.getBean("user");

        根据类型来获取bean
        User user2 = context.getBean(User.class);

        根据ID和类型来获取
        User user3 = context.getBean("user",User.class);

        System.out.println("根据ID获取bean对象user1:"+user1);
        System.out.println("根据类型获取bean对象user2:"+user2);
        System.out.println("根据类型和ID获取bean对象user2:"+user3);
    }
}

需要注意的是:当根据类型获取bean时,要求IoC容器中指定类型的bean有且只能有一个。

2.2 接口实现类的获取

首先应该在bean.xml文件中配置

<!--一个接口实现类的获取过程-->
<bean id="userDaompl" class="com.crj.spring6.iocxml.bean.UserDaompl"></bean>

接口UserDao

package com.crj.spring6.iocxml.bean;
public interface UserDao {
    public void run();
}

接口实现类UserDaompl

package com.crj.spring6.iocxml.bean;

public class UserDaompl implements UserDao{
    @Override
    public void run() {
        System.out.println("UserDao is running!!!");
    }
}

测试类

package com.crj.spring6.iocxml.bean;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestUserDao {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        //根据类型获取接口对应的bean
        UserDao userDao = context.getBean(UserDao.class);
        System.out.println(userDao);
        userDao.run();
    }
}

这里context获取bean类型应该选择的是UserDao这个接口类,这样才是接口实现类的获取。 

2.3 注入

首先借助一个在原生类中的注入来明确注入的概念: 

package com.crj.spring6.iocxml.DI;

public class Book {
    private String name;
    private String author;

    //无参构造
    public Book(){
    }
    //有参构造
    public Book(String name,String author){
        this.name = name;
        this.author = author;
    }
    
    //生成set方法
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public static void main(String[] args) {
        //setter方法注入
        Book book = new Book();
        book.setAuthor("CRJ");
        book.setName("庄子的智慧");
        //基于构造器注入
        Book b = new Book("编程珠玑","CRJ");
    }
}

这里有一个小tips:IDEA中可以右键并在Generate中选择自动生成的类方法嘿嘿嘿~~~

package com.crj.spring6.iocxml.DI;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class testDI {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("di.xml");
        Book b = context.getBean("book", Book.class);
        System.out.println(b);
    }
}

 2.3.1 Spring中基于setter注入

 首先新建一个配置文件di.xml,直接在配置文件中使用setter方法将对象属性值进行注入。

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

    <!--set方法注入-->
    <bean id="book" class="com.crj.spring6.iocxml.DI.Book">
        <property name="name" value="图书名称"></property>
        <property name="author" value="作者"></property>
    </bean>
</beans>

测试类

package com.crj.spring6.iocxml.DI;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class testDI {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("di.xml");
        Book b = context.getBean("book", Book.class);
        System.out.println(b);
    }
}

2.3.2 基于构造器进行注入

基于构造器进行注入的步骤:

  • 创建类,定义属性,生成有参数构造方法
  • 在spring中进行配置
 <!--构造方法注入-->
    <bean id="bookCon" class="com.crj.spring6.iocxml.DI.Book">
        <constructor-arg value="图书名称2" name="name"></constructor-arg>
        <constructor-arg value="作者2" name="author"></constructor-arg>
    </bean>

2.4 特殊值的注入

2.4.1 字面量赋值

使用value属性给bean的属性赋值时,Spring会把value属性的值看做字面量 

<property name="author" value="crj"></property>

2.4.2 null值注入

<!--空值-->
<property name="name">
    <null />
</property>

2.4.3 xml实体

小于号在xml文件中用来定义标签的开始,不能随便使用,这时候就需要使用xml实体来代替。

<property name="others"value="&lt;"></property>
<!--
    其中:&gt;表示大于号、&lt表示小于号
-->

2.4.4 CTADA节

通过CTADA区来将特殊符号放在这个区里面。 

<property name="others">
    <value><![CDATA[a<b]]></value>
</property>

2.5 特殊类型的注入

2.5.1 对象类型属性注入

        首先创建两个类:公司类和员工类,并在员工类中建立与公司类的关系即定义一个Dept类型的属性。在这里注入采用setter方式进行注入,所以要味蕾的所有属性构建set方法。完成后再bean.xml文件中配置注入的内容。

公司类

package com.crj.spring6.iocxml.DITest;
//公司类
public class Dept {
    private String dName;
    public void info(){
        System.out.println("公司名称:"+dName);
    }
    public String getdName() {
        return dName;
    }
    public void setdName(String dName) {
        this.dName = dName;
    }
}

员工类

package com.crj.spring6.iocxml.DITest;
//员工类
public class Emp {
//    表示员工属于某个公司
    private Dept dept;

    private String name;
    private String age;

    public void work(){
        System.out.println("working>>>>>>");
        System.out.println("员工名称"+name+",年龄:"+age);
        dept.info();
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(String age) {
        this.age = age;
    }
}

引用外部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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--
        引入外部bean
        在emp的bean标签中使用proberty来引入dept的bean
    -->
    <bean id="dept" class="com.crj.spring6.iocxml.DITest.Dept">
        <property name="dName" value="A"></property>
    </bean>
    <bean id="emp" class="com.crj.spring6.iocxml.DITest.Emp">
        <property name="name" value="crj"></property>
        <property name="age" value="18"></property>
        <!--注入对象属性
            private Dept dept
        -->
        <property name="dept" ref="dept"></property>
    </bean>
    
</beans>

内部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 http://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <!--
        通过内部bean来注入
    -->
    <bean id="emp2" class="com.crj.spring6.iocxml.DITest.Emp">
        <property name="name" value="CRJ"></property>
        <property name="age" value="19"></property>
        <!--
            内部bean方式注入,某个属性类
        -->
        <property name="dept">
            <bean id="dept2" class="com.crj.spring6.iocxml.DITest.Dept">
                <property name="dName" value="B"></property>
            </bean>
        </property>
    </bean>
</beans>

级联属性赋值

<bean id="dept3" class="com.crj.spring6.iocxml.DITest.Dept">
    <property name="dName" value="C"></property>
</bean>
<bean id="emp3" class="com.crj.spring6.iocxml.DITest.Emp">
    <property name="name" value="C1"></property>
    <property name="age" value="20"></property>
    <property name="dept" ref="dept"></property>
<!--级联赋值-->
    <property name="dept.dName" value="D"></property>
</bean>

2.5.2 数组类型属性注入

 在员工类中添加数组类型属性

  private String[] loves;

对应在xml文件中需要加上配置才可以完成属性注入 

<!--数组类型属性-->
<property name="loves">
    <array>
        <value>eat</value>
        <value>sleep</value>
        <value>coding</value>
    </array>
</property>

2.5.3 集合类型属性注入

对于集合类型的属性注入,比如在上述的公司类中有一个包含员工信息的集合,因此可以在公司类中加入Emp类的类型集合并生成相应的set方法。

package com.crj.spring6.iocxml.DITest;
import java.util.List;

//公司类
public class Dept {
    private String dName;
    private List<Emp> empList;

    public List<Emp> getEmpList() {
        return empList;
    }
    public void setEmpList(List<Emp> empList) {
        this.empList = empList;
    }
    public void info(){
        System.out.println("公司名称:"+dName);
        for (Emp emp:empList){
            System.out.println(emp.getName());
        }
    }
    public String getdName() {
        return dName;
    }
    public void setdName(String dName) {
        this.dName = dName;
    }
}

List类型属性集合

这里因为我们在公司类中声明的集合属性是一个Emp类型的,因此我们需要在注入多个Emp类型的对象后,通过<ref>标签中的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 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="dept" class="com.crj.spring6.iocxml.DITest.Dept">
        <property name="dName" value="A"></property>
        <property name="empList">
            <list>
<!--                因为这里我们在集合中是引入一个对象,所以这里不再使用value标签-->
                <ref bean="empOne"></ref>
                <ref bean="empTwo"></ref>
            </list>
        </property>
    </bean>
    <bean id="empOne" class="com.crj.spring6.iocxml.DITest.Emp">
        <property name="name" value="crj"></property>
        <property name="age" value="18"></property>
        <!--注入对象属性
            private Dept dept
        -->
        <property name="dept" ref="dept"></property>
    </bean>

    <bean id="empTwo" class="com.crj.spring6.iocxml.DITest.Emp">
        <property name="name" value="crj"></property>
        <property name="age" value="18"></property>
        <!--注入对象属性
            private Dept dept
        -->
        <property name="dept" ref="dept"></property>
    </bean>
</beans>

Map类型的属性集合

<?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">
    <bean id="teacher" class="com.crj.spring6.iocxml.DIMap.Teacher">
        <property name="tid" value="1"></property>
        <property name="tName" value="CRJ"></property>
    </bean>
    <bean id="student" class="com.crj.spring6.iocxml.DIMap.Student">
        <property name="name" value="A"></property>
        <property name="id" value="S-1"></property>
        <property name="teachersMap">
            <map>
                <entry>
                    <key>
                        <value>001</value>
                    </key>
                    <ref bean="teacher"></ref>
                </entry>
            </map>
        </property>

    </bean>
</beans>

引入集合bean

这种方法是整合map和list集合bean注入的方式,使用util:map或util:bean来进行属性注入。但需要在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:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="teacherOne" class="com.crj.spring6.iocxml.DIMap.Teacher">
        <property name="tid" value="1"></property>
        <property name="tName" value="CRJ"></property>
    </bean>
    <bean id="teacherTwo" class="com.crj.spring6.iocxml.DIMap.Teacher">
        <property name="tid" value="2"></property>
        <property name="tName" value="CRJ"></property>
    </bean>
    <bean id="lession1" class="com.crj.spring6.iocxml.DIMap.Lession">
        <property name="lessionName" value="高数"></property>
    </bean>
    <bean id="lession2" class="com.crj.spring6.iocxml.DIMap.Lession">
        <property name="lessionName" value="大物"></property>
    </bean>

    <bean id="student" class="com.crj.spring6.iocxml.DIMap.Student">
        <property name="name" value="A"></property>
        <property name="id" value="S-1"></property>
        <!--注入list和map-->
        <property name="lession" ref="lessionList"></property>
        <property name="teachersMap" ref="teacherMap"></property>
    </bean>
    <util:list id="lessionList">
        <ref bean="lession1"></ref>
        <ref bean="lession2"></ref>
    </util:list>
    <util:map id="teacherMap">
        <entry>
            <key>
                <value>001</value>
            </key>
            <ref bean="teacherOne"></ref>
        </entry>
        <entry>
            <key>
                <value>002</value>
            </key>
            <ref bean="teacherTwo"></ref>
        </entry>
    </util:map>
</beans>

2.5.4 P命名空间注入

<?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:util="http://www.springframework.org/schema/util"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="studentP" class="com.crj.spring6.iocxml.DIMap.Student"
    p:id="5" p:name="crj" p:lession-ref="lessionList" p:teachersMap-ref="teacherMap">
        
    </bean>
    
    <bean id="teacherOne" class="com.crj.spring6.iocxml.DIMap.Teacher">
        <property name="tid" value="1"></property>
        <property name="tName" value="CRJ"></property>
    </bean>
    <bean id="teacherTwo" class="com.crj.spring6.iocxml.DIMap.Teacher">
        <property name="tid" value="2"></property>
        <property name="tName" value="CRJ"></property>
    </bean>
    <bean id="lession1" class="com.crj.spring6.iocxml.DIMap.Lession">
        <property name="lessionName" value="高数"></property>
    </bean>
    <bean id="lession2" class="com.crj.spring6.iocxml.DIMap.Lession">
        <property name="lessionName" value="大物"></property>
    </bean>

    <bean id="student" class="com.crj.spring6.iocxml.DIMap.Student">
        <property name="name" value="A"></property>
        <property name="id" value="S-1"></property>
        <!--注入list和map-->
        <property name="lession" ref="lessionList"></property>
        <property name="teachersMap" ref="teacherMap"></property>
    </bean>
    <util:list id="lessionList">
        <ref bean="lession1"></ref>
        <ref bean="lession2"></ref>
    </util:list>
    <util:map id="teacherMap">
        <entry>
            <key>
                <value>001</value>
            </key>
            <ref bean="teacherOne"></ref>
        </entry>
        <entry>
            <key>
                <value>002</value>
            </key>
            <ref bean="teacherTwo"></ref>
        </entry>
    </util:map>
</beans>

2.5.5 引用外部文件注入

在实际的开发中,我们一般都是采用通过数据库并引入外部文件依赖来实现属性注入的操作的,具体的步骤如下:

  1. 在整个项目的配置文件pom.xml中配置数据库环境依赖;
  2. 创建properties格式的外部属性文件,定义数据信息如:用户名、密码、地址等;
  3. 创建spring配置文件,引入context命名空间并引入属性文件完成注入  

配置数据库环境依赖

<dependencies>
    <!--引入数据库依赖-->
    <!--MySQL驱动-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.30</version>
    </dependency>
    <!--数据源-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.0.31</version>
    </dependency>
</dependencies>

配置外部属性文件

在resources文件夹中创建一个jdbc.properties的文件,之后在内部配置的bean-jdbc.xml文件中加上context依赖:

<?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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>

 那么在原生方法中,也就是不采用属性注入的方式来进行数据库外部文件的属性获取是通过一个DruidDataSource()这一个类来实现:

@Test
public void fun1(){
    DruidDataSource dataSource = new DruidDataSource();
//  //原生的调用数据库的方法
    dataSource.setUrl("jdbc:mysql://localhost:3306/spring?serverTimezone=UTC");
    dataSource.setUsername("root");
    dataSource.setPassword("root");
    dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
}

采用bean注入的方式我们也是需要在配置文件中配置号数据库的bean对象,通过${}来获取外部文件的属性值

<!--    引入外部属性文件-->
    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!--    完成数据库信息注入-->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="driverClassName" value="${jdbc.driver}"></property>
    </bean>

 定义一个测试类来实现:

@Test
    public void fun2(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean-jdbc.xml");
        DruidDataSource d = context.getBean(DruidDataSource.class);
        System.out.println(d.getUrl());

    }

2.6 bean的作用域

在spring中可以通过配置bean标签的scope属性来指定bean的作用域范围:

取值含义创建对象时间
singleton(默认)在IOC容器中,这个bean对象始终为单实例IOC容器初始化时
prototype这个bean在IOC容器中有多个实例获取bean时候

如果是在WebApplicationContext环境下还会有另外几个作用域:

取值含义
request在一个请求范围有效
session在一个会话范围有效

2.7 bean的生命周期

2.7.1 具体的生命周期过程

  • bean对象创建(调用无参构造器)
  • 给bean对象设置属性
  • bea的后置处理器(初始化之前)
  • bean对象初始化(需在配置bean时指定初始化方法)
  • bean的后置处理器(初始化之后)
  • bean对象就绪可以使用
  • bean对象销毁(在bean中配置指定销毁方法)
  • IOC容器关闭
<bean class="" id="" scope="singleton" init-method="初始化方法" destroy-method="销毁方法"></bean>

2.7.2 bean的后置处理器 

后置处理器需要配继承一个BeanPostProcessor 接口,并在相应的配置文件中需要使用bean配置好。

package com.crj.spring6.iocxml;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPost implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("bean的后置处理器,会在初始化之前执行");
        System.out.println("beanName:"+beanName+",bean:"+bean);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("bean的后置处理器,会在初始化之后执行");
        System.out.println("beanName:"+beanName+",bean:"+bean);
        return bean;
    }
}

2.8 基于xml自动装配

自动装配指的是:根据指定的策略,在OC容器中匹配某一个bean,自动为指定的bean中所依赖的类类型或接口类型属性赋值。

这里的自动装配指的是自动调用set方法来创建某个类类型的属性,比如有controller、service和dao三层并且每一层都有相应的接口和实现类:

Controller层 

package com.crj.spring6.iocxml.auto.controller;

import com.crj.spring6.iocxml.auto.service.UserService;

public class UserController {
    private UserService userService;

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public void addUser(){
        System.out.println("controller方法执行了");
        //调用Service中的方法
        userService.addUserService();
    }
}

Service层

package com.crj.spring6.iocxml.auto.service;

import com.crj.spring6.iocxml.auto.dao.UserDao;

public class UserServiceImpl implements UserService{
    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void addUserService() {
        System.out.println("UserService方法执行了");
        userDao.addUserDao();
    }
}

Dao层

package com.crj.spring6.iocxml.auto.dao;

public class UserDaoImpl implements UserDao{

    @Override
    public void addUserDao() {
        System.out.println("userDao方法执行");
    }
}

测试类

package com.crj.spring6.iocxml.auto;

import com.crj.spring6.iocxml.auto.controller.UserController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class testUser {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean-auto.xml");
        UserController userController = context.getBean("userController", UserController.class);
        userController.addUser();
    }
}

配置文件 

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

    <!--创建三个类的对象-->
    <bean id="userController" class="com.crj.spring6.iocxml.auto.controller.UserController" autowire="byType"></bean>
    <bean id="userService" class="com.crj.spring6.iocxml.auto.service.UserServiceImpl" autowire="byType"></bean>
    <bean id="userDao" class="com.crj.spring6.iocxml.auto.dao.UserDaoImpl" ></bean>

</beans>

<!--
    autowire还可以取byName属性值
    这里需要id值必须跟类中声明的属性名一致userService
-->

三、基于注解管理bean

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

3.1 通过注解创建bean对象 

Spring中通过注解实现自动装配步骤:

  • 引入依赖
  • 开启组件扫描
  • 使用注解定义bean
  • 依赖注入 

开启组件扫描 

        基本的依赖引入跟上面是一样滴,但在这里我们需要在bean.xml配置文件中来开启组件扫描功能,这是因为Spring默认是无法使用注解来装配bean的。开启此功能后,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/context http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--开启组件扫描-->
    <context:component-scan base-package="com.crj"></context:component-scan>
    <!--另外两种过滤情况-->
    <context:component-scan base-package="com.crj.spring6">
        <!--
            type有两种值:annotation和assignable
            annotation:根据注解排除,expression中设置要排除的注解的全类名
            assignable:更具类型排除,expression中设置要排除的类型的全类名
         
        -->
        <!--排除exclude-filter-->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <!--仅扫描include-filter-->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    
</beans>

四种注解

注解

说明

@Component该注解用于描述Spring中的Bean,它是一个泛化的概念,仅仅表示容器中的一个组件(Bean),并且可以作用在应用的任何层次,例如Service层、Dao层等。使用时只需将该注解标注在相应类上即可。
@Repository该注解用于将数据访问层(Dao层))的类标识为Spring中的Bean,其功能与@Component相同。
@Service该注解通常作用在业务层(Service层),用于将业务层的类标识为Spring中的Bean,其功能与@Component相同。
@Controller该注解通常作用在控制层(如SpringMVC的Controller),用于将控制层的类标识为Spring中的Bean,其功能与@Component相同。

其实四种注解的用法和功能是一样的,只不过我们一般为了区分比较常见地就把注解类型分开了。

package com.crj.spring6.bean;
import org.springframework.stereotype.Component;

@Component(value = "user") //等价于<bean id="user" class="...."> 这里的value值可以不设置,默认就是类名首字母小写
public class User {
}

3.2 @Autowired注入

Autowire注解默认是采用类型进行装配的,也就是默认是byType。该注解可以用在构造方法、普通方法、形参、属性和注解上。

属性注入

借助@Autowire注解来修饰类中声明的属性实现调用Service接口实现类。

package com.crj.spring6.bean.Controllor;

import com.crj.spring6.bean.UserService.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {

    //第一种方式:属性注入
    @Autowired
    private UserService userService;
    public void add(){
        System.out.println("controller>>>>>>>>>");
        userService.add();
    }

}

Service层

package com.crj.spring6.bean.UserService;

import com.crj.spring6.bean.Dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService{

    @Autowired
    private UserDao userDao;
    @Override
    public void add() {
        System.out.println("Service>>>>>>>>>");
        userDao.add();
    }
}

Set注入

    private UserService userService;
    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

构造方法注入

    private UserService userService;
    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

形参上注入

生成有参构造后再形参前加Autowire注入

    private UserService userService;
    public UserController(@Autowired UserService userService) {
        this.userService = userService;
    }

注意:只有一个有参构造的时候,无需注解也可以实现注入。

@Qualifier注解根据名称进行匹配,它可以和@Autowire联合进行匹配。这种情况会出现在接口有多个实现类的时候我们需要使用两个注解并根据名称进行注入。

@Autowired
@Qualifier(value = "userDaoImpl") //指定实现类
private UserDao userDao;

3.3 @Resource注入

        @Resource注解也能完成属性注入,它与@Autowire注解的区别在于@Resource是JDK中的一部分,属于标准注解,默认根据名称进行装配,未指定name时会使用属性名作为name,再找不到就会自动启用byType装配。需要注意的是,@Resource只能用在属性和setter方法上,并且在版本高于11的时候需要引入依赖。

引入依赖

<dependency>
    <groupId>jakarta.annotation</groupId>
    <artifactId>jakarta.annotation-api</artifactId>
    <version>2.1.1</version>
</dependency>

根据名称进行注入

@Resource(name "myUserService")
private UserService userservice;

3.4 全注解开发

所谓的全注解开发其实就是不再使用配置文件pom.xml了,而是采用一个配置类来取代配置文件。 

配置类 

package com.crj.spring6.bean.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration  //配置类
@ComponentScan("com.crj.spring6")  //开启组件扫描
public class SpringConfig {
}

测试类

 不再是new一个ClassPathXmlApplicationContext,而是实例化一个AnnotationConfigApplicationContext来注入配置类。

package com.crj.spring6.bean;

import com.crj.spring6.bean.config.SpringConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class testConfig {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        User user = context.getBean(User.class);
        System.out.println(user);
    }
}

总结

        以上就是IoC控制反转的主要知识点了,接下来荔枝也会梳理有关AOP面向切面编程的相关内容,希望能加快进度嘿嘿嘿,感觉自己学习梳理得太慢了哈哈,大家一起加油吧~~~

今朝已然成为过去,明日依然向往未来!我是小荔枝,在技术成长的路上与你相伴,码文不易,麻烦举起小爪爪点个赞吧哈哈哈~~~ 比心心♥~~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/817296.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【无标题】使用Debate Dynamics在知识图谱上进行推理(2020)7.31

使用Debate Dynamics在知识图谱上进行推理 摘要介绍背景与相关工作我们的方法 摘要 我们提出了一种新的基于 Debate Dynamics 的知识图谱自动推理方法。 其主要思想是将三重分类任务定义为两个强化学习主体之间的辩论游戏&#xff0c;这两个主体提取论点&#xff08;知识图中…

嵌入式基础知识-存储器

本篇介绍计算机存储硬件的一些基础知识&#xff0c;在嵌入式开发中&#xff0c;也同样适用。 1 计算机存储结构 存储器是计算机中的重要部件&#xff0c;理想的存储器应该是执行快&#xff0c;容量足&#xff0c;价格便宜等。但实际上&#xff0c;目前无法同时满足这些目标&a…

Vue2 第十节 内置指令和自定义指令

1.之前学过的指令 2. 内置指令 3. 自定义指令 一.之前学过的指令 指令名用法v-bind单项绑定解析表达式&#xff0c;可以简写为:xxxv-model双向绑定v-for遍历数组/对象/字符串v-on 绑定监听事件&#xff0c;可以简写为v-if条件渲染&#xff08;动态控制节点是否存在&#xf…

算法通关村第二关——反转链表白银挑战笔记

文章目录 1.链表指定区间翻转2.两两交换链表中的节点 1.链表指定区间翻转 LeetCode 92.反转链表 解法一&#xff1a;头插法。利用虚拟节点进行反转&#xff0c;因为头节点有可能发生变化&#xff0c;比如 left1 那么需要 dummyNode.next 记录头结点&#xff0c;使用虚拟头节点…

计算机网络(2) --- 网络套接字

计算机网络&#xff08;1&#xff09; --- 网络介绍_哈里沃克的博客-CSDN博客https://blog.csdn.net/m0_63488627/article/details/131967378?spm1001.2014.3001.5501 目录 1.端口号 2.TCP与UDP协议 1.TCP协议介绍 1.TCP协议 2.UDP协议 3.理解 2.网络字节序 发送逻辑…

学会这13个问题,轻松拿捏Java容器面试

java 容器都有哪些&#xff1f; 常用容器的图录&#xff1a; Collection 和 Collections 有什么区别&#xff1f; java.util.Collection 是一个集合接口&#xff08;集合类的一个顶级接口&#xff09;。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java …

29.利用fminbnd 求解 最大容积问题(matlab程序)

1.简述 用于求某个给定函数的最小值点。 使用方法是&#xff1a; xfminbnd(func,x1,x2) func是函数句柄&#xff0c;然后x1和x2就是函数的区间&#xff0c;得到的结果就是使func取最小值的x值 当然也可以使用[x,fv]fminbnd(func,x1,x2)的方式&#xff0c;这个时候fv就是函数…

项目实战 — 消息队列(2){数据库操作}

目录 一、SQLite &#x1f345; 1、添加依赖 &#x1f345; 2、修改配置文件后缀&#xff08;properties -> yaml&#xff09; &#x1f345; 3、编写配置文件 二、建立数据表 三、添加插入和删除方法 四、整合数据库操作&#xff08;DataBaseManger类&#xff09; &a…

螺旋矩阵(JS)

螺旋矩阵 题目 给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;[[1,2,3],[8,9,4],[7,6,5]]示例 2&#xff1a; 输入&#xff…

【自动化运维】编写LNMP分布式剧本

目录 一 playbook编写LNMP1.1环境设置1.2编写Nginx剧本1.3、编写Mysql剧本1.4准备PHP剧本 一 playbook编写LNMP 1.1环境设置 ip服务192.168.243.100ansible192.168.243.102nginx192.168.243.103PHP192.168.243.104mysql 1.2编写Nginx剧本 1.编写Nginx源 mkdir -p /etc/ans…

Linux之 centos、Ubuntu 安装常见程序

CentOS 安装 MySql 注意 需要有root权限 安装5.7版本 – 由于MySql并不在CentOS的官方仓库中&#xff0c;所以需要通过rmp命令&#xff1a; 导入MySQL仓库密钥 1、配置MySQL的yum仓库 配置yum仓库 更新密钥 rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022 安装…

【AHB-Lite总线】hreadyin和hreadyout的区别

AHB-Lite总线协议请查看 AHB-Lite总线协议 首先看下hreadyin和hreadyout在系统中的位置 从图中可以看到&#xff0c;每个AHB从设备都有一个HREADY输出信号&#xff08;hreadyout&#xff09;&#xff0c;它连接到多路复用器。该多路复用器的输出是AHB主机看到的全局hready信…

看完ChatGPT的两个比喻,更加确信为什么人人都应该去使用它

​ ChatGPT就像火 丹尼刘是悉尼大学教育创新临时学术总监。去年年底&#xff0c;在一次工作组会议上&#xff0c;他第一次向同事展示了ChatGPT。同事说出了让刘大吃一惊的话。 “他说&#xff0c;‘哇&#xff0c;这就像火一样&#xff0c;’”刘回忆道。 刘一开始觉得这个…

《JavaSE-第二十二章》之线程安全问题

前言 在你立足处深挖下去,就会有泉水涌出!别管蒙昧者们叫嚷:“下边永远是地狱!” 博客主页&#xff1a;KC老衲爱尼姑的博客主页 博主的github&#xff0c;平常所写代码皆在于此 共勉&#xff1a;talk is cheap, show me the code 作者是爪哇岛的新手&#xff0c;水平很有限&…

安卓相关内容

adb环境变量设置完之后&#xff0c;要重启一次“终端”窗口。 adb安装apk到手机 Android开发&#xff1a;用adb命令安装apk到手机-腾讯云开发者社区-腾讯云 This adb servers $ADB_VENDOR_KEYS is not set frida 出现问题&#xff1a;unable to start: address already in …

软件测试面试题——接口自动化测试怎么做?

面试过程中&#xff0c;也问了该问题&#xff0c;以下是自己的回答&#xff1a; 接口自动化测试&#xff0c;之前做过&#xff0c;第一个版本是用jmeter 做的&#xff0c;1 主要是将P0级别的功能接口梳理出来&#xff0c;根据业务流抓包获取相关接口&#xff0c;并在jmeter中跑…

ES开启身份认证

文章目录 X-Pack简介之前的安全方案ES开启认证ES服务升级https协议开启集群节点之间的证书认证 X-Pack简介 X-Pack是Elastic Stack扩展功能&#xff0c;提供安全性&#xff0c;警报&#xff0c;监视&#xff0c;报告&#xff0c;机器学习和许多其他功能。 X-Pack的发展演变&am…

聊聊拉长LLaMA的一些经验

Sequence Length是指LLM能够处理的文本的最大长度&#xff0c;越长&#xff0c;自然越有优势&#xff1a; 更强的记忆性。更多轮的历史对话被拼接到对话中&#xff0c;减少出现遗忘现象 长文本场景下体验更佳。比如文档问答、小说续写等 当今开源LLM中的当红炸子鸡——LLaMA…

开放麒麟1.0发布一个月后,到底怎么样?另一款操作系统引发热议

具有里程碑意义 7月5日&#xff0c;国产首个开源桌面操作系统“开放麒麟1.0”正式发布。 标志着我国拥有了操作系统组件自主选型、操作系统独立构建的能力&#xff0c;填补了我国在这一领域的空白。 举国欢庆&#xff0c;算的上是里程碑意义了&#xff01; 发布后用着如何&a…

Linux系统下U盘打不开: No application is registered as handling this file

简述 系统是之前就安装好使用的Ubuntu14.04&#xff0c;不过由于某些原因只安装到了机械硬盘中&#xff1b;最近新买了一块固态硬盘&#xff0c;所以打算把Ubuntu系统迁移到新的固态硬盘上&#xff1b; 当成功的迁移了系统之后发现其引导有点问题&#xff0c;导致多个系统启动不…