当前位置: 代码网 > it编程>编程语言>Java > Spring IOC 控制反转实战教程

Spring IOC 控制反转实战教程

2026年08月05日 Java 我要评论
一、含义把自己创建资源、向环境索取资源变成环境将资源准备好,我们享受资源注入。二、基于xml管理 bean1、准备创建 maven 工程,pom.xml 添加依赖。<dependencies&g

一、含义

        把自己创建资源、向环境索取资源变成环境将资源准备好,我们享受资源注入。

二、基于 xml 管理 bean

1、准备

        创建 maven 工程,pom.xml 添加依赖。

<dependencies>
    <dependency>
        <groupid>org.springframework</groupid>
        <artifactid>spring-context</artifactid>
        <version>5.3.1</version>
    </dependency>
</dependencies>

       com.example.pojo 包下,创建实体类 teacher、clazz、student。

public class teacher { //alt+insert,一键生成构造器、get、set、tostring 方法...
    private integer tid;
    private string tname;
    public teacher() {
    }
    public teacher(integer tid, string tname) {
        this.tid = tid;
        this.tname = tname;
    }
    public void settid(integer tid) {
        this.tid = tid;
    }
    public void settname(string tname) {
        this.tname = tname;
    }
    public integer gettid() {
        return tid;
    }
    public string gettname() {
        return tname;
    }
    @override
    public string tostring() {
        return "teacher{" +
                "tid=" + tid +
                ", tname='" + tname + '\'' +
                '}';
    }
}
public class clazz {
    private integer cid;
    private string cname;
    private list<student> students;
    public clazz() {
    }
    public clazz(integer cid, string cname) {
        this.cid = cid;
        this.cname = cname;
    }
    public integer getcid() {
        return cid;
    }
    public string getcname() {
        return cname;
    }
    public list<student> getstudents() {
        return students;
    }
    public void setcid(integer cid) {
        this.cid = cid;
    }
    public void setcname(string cname) {
        this.cname = cname;
    }
    public void setstudents(list<student> students) {
        this.students = students;
    }
    @override
    public string tostring() {
        return "clazz{" +
                "cid=" + cid +
                ", cname='" + cname + '\'' +
                ", students=" + students +
                '}';
    }
}
public class student {
    private integer sid;
    private string sname;
    private integer age;
    private string gender;
    private clazz clazz;
    private string[] hobby;
    private map<string,teacher> teachermap;
    public student() {
    }
    public student(integer sid, string sname, integer age, string gender) {
        this.sid = sid;
        this.sname = sname;
        this.age = age;
        this.gender = gender;
    }
    public integer getsid() {
        return sid;
    }
    public string getsname() {
        return sname;
    }
    public integer getage() {
        return age;
    }
    public string getgender() {
        return gender;
    }
    public clazz getclazz() {
        return clazz;
    }
    public string[] gethobby() {
        return hobby;
    }
    public map<string, teacher> getteachermap() {
        return teachermap;
    }
    public void setsid(integer sid) {
        this.sid = sid;
    }
    public void setsname(string sname) {
        this.sname = sname;
    }
    public void setage(integer age) {
        this.age = age;
    }
    public void setgender(string gender) {
        this.gender = gender;
    }
    public void setclazz(clazz clazz) {
        this.clazz = clazz;
    }
    public void sethobby(string[] hobby) {
        this.hobby = hobby;
    }
    public void setteachermap(map<string, teacher> teachermap) {
        this.teachermap = teachermap;
    }
    @override
    public string tostring() {
        return "student{" +
                "sid=" + sid +
                ", sname='" + sname + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                ", clazz=" + clazz +
                ", hobby=" + arrays.tostring(hobby) +
                ", teachermap=" + teachermap +
                '}';
    }
}

2、配置 bean

        右击目录 resources,依次选择 new -> xml configuration file -> spring config,创建 spring 配置文件 applicationcontext.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"
       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
       http://www.springframework.org/schema/util
       https://www.springframework.org/schema/util/spring-util.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">
    <!--
        bean 标签:用于配置 bean,将对象交给 ioc 容器管理,其中
        id 用于设置 bean 的唯一标识;class 用于设置 bean 对应类型的全类名。
    -->
    <bean id="student" class="com.example.pojo.student">
        <!--
            property 标签:用于给对象的属性赋值,其中 name 用于设置属性名;value 用于设置属性值;
            ref 用于设置要引用的 ioc 容器中 bean 的 id,从而将 bean 对应对象赋值给当前属性。
            此时,spring 使用 bean 对应类型的全类名,通过反射调用无参构造创建对象,再通过 setxxx() 为对象的属性赋值。
        -->
        <property name="sid" value="1001"></property>
        <property name="sname" value="张三"></property>
        <!-- 为属性赋 null 值 -->
        <property name="age">
            <null />
        </property>
        <!-- 为属性赋含有 >、<等符号 的值,使用 cdata 节将内容原样解析 -->
        <property name="gender">
            <value><![cdata[<男>]]></value>
        </property>
        <!-- 通过引用外部已声明的 bean,为属性赋 类类型 的值 -->
        <!--<property name="clazz" ref="clazz"></property>-->
        <!-- 通过内部 bean,为属性赋 类类型 的值 -->
        <property name="clazz">
            <!-- 内部 bean 仅用于给属性赋值,不能在外部通过 ioc 容器获取,因此可以省略 id 属性 -->
            <bean id="clazzinner" class="com.example.pojo.clazz">
                <property name="cid" value="3002"></property>
                <property name="cname" value="飞船班"></property>
            </bean>
        </property>
        <!-- 为属性赋 数组类型 的值 -->
        <property name="hobby">
            <array>
                <value>抽烟</value>
                <value>喝酒</value>
                <value>烫头</value>
            </array>
        </property>
        <!-- 通过引用外部已声明的集合类型的 bean,为属性赋 map 集合类型 的值 -->
        <!--<property name="teachermap" ref="teachermap"></property>-->
        <!-- 通过 map 标签,为属性赋 map 集合类型 的值 -->
        <property name="teachermap">
            <map>
                <entry key="2001" value-ref="teacherone"></entry>
                <entry key="2002" value-ref="teachertwo"></entry>
            </map>
        </property>
    </bean>
    <bean id="clazz" class="com.example.pojo.clazz">
        <property name="cid" value="3001"></property>
        <property name="cname" value="火箭班"></property>
        <!-- 通过引用外部已声明的集合类型的 bean,为属性赋 list 集合类型 的值 -->
        <!--<property name="students" ref="studentlist"></property>-->
        <!-- 通过 list 标签,为属性赋 list 集合类型 的值 -->
        <property name="students">
            <list>
                <ref bean="student"></ref>
            </list>
        </property>
    </bean>
    <!-- 通过 util 约束,配置集合类型的 bean -->
    <util:list id="studentlist">
        <ref bean="student"></ref>
    </util:list>
    <!-- 通过 util 约束,配置集合类型的 bean -->
    <util:map id="teachermap">
        <entry key="2001" value-ref="teacherone"></entry>
        <entry key="2002" value-ref="teachertwo"></entry>
    </util:map>
    <bean id="teacherone" class="com.example.pojo.teacher">
        <!--
            constructor-arg 标签:用于指定有参构造,其中 name 用于设置参数名;value 用于设置参数值。
            此时,spring 使用 bean 对应类型的全类名,通过反射调用与 constructor-arg 标签参数匹配的有参构造创建对象。
        -->
        <constructor-arg name="tid" value="2001"></constructor-arg>
        <constructor-arg name="tname" value="李白"></constructor-arg>
    </bean>
    <bean id="teachertwo" class="com.example.pojo.teacher">
        <property name="tid" value="2002"></property>
        <property name="tname" value="陶渊明"></property>
    </bean>
</beans>

要点精炼:基于 xml 配置 bean,依赖注入之 setter 注入、依赖注入之构造器注入,为属性赋字面量类型的值、null 值、含有特殊符号的值、类类型的值、数组类型的值、list 集合类型的值、map 集合类型的值。

3、获取 bean

        根据 id、类型、id & 类型,从 ioc 容器中获取 bean 对象。

//通过读取类路径下 xml 格式的配置文件,创建 ioc 容器对象
applicationcontext ioc = new classpathxmlapplicationcontext("applicationcontext.xml");
//(1)根据 id,从 ioc 容器中获取 bean 对象
student studentone = (student) ioc.getbean("student");
/*
    (2)根据类型,从 ioc 容器中获取 bean 对象
    注意:要求容器中有且仅有一个类型匹配的 bean,否则报错 nosuchbeandefinitionexception/nouniquebeandefinitionexception;
         满足 bean 唯一性的前提下,通过 bean 的类型、bean 所继承的类的类型、bean 所实现的接口的类型均可获取 bean 对象。
*/
student studenttwo = ioc.getbean(student.class);
//(3)根据类型 & id,从 ioc 容器中获取 bean 对象
teacher teacher = ioc.getbean("teacherone", teacher.class);

4、spring 管理数据源

        pom.xml 添加依赖。

<!-- mysql驱动 -->
<dependency>
    <groupid>mysql</groupid>
    <artifactid>mysql-connector-java</artifactid>
    <version>8.0.16</version>
</dependency>
<!-- 数据源 -->
<dependency>
    <groupid>com.alibaba</groupid>
    <artifactid>druid</artifactid>
    <version>1.0.31</version>
</dependency>

        右击目录 resources,依次选择 new -> resource bundle,创建属性配置文件 jdbc.properties。

jdbc.driver=com.mysql.jdbc.driver
jdbc.url=jdbc:mysql://localhost:3306/mydb?useunicode=true&characterencoding=utf-8&servertimezone=gmt%2b8
jdbc.username=root
jdbc.password=root
jdbc.initialsize=10
jdbc.maxactive=20
jdbc.maxwait=5000

        spring 配置文件 applicationcontext.xml,配置数据源。

<!-- 配置 alibaba druid 数据库连接池 -->
<bean id="datasource" class="com.alibaba.druid.pool.druiddatasource">
	<property name="driverclassname" value="${jdbc.driver}"></property>
	<property name="url" value="${jdbc.url}"></property>
	<property name="username" value="${jdbc.username}"></property>
	<property name="password" value="${jdbc.password}"></property>
	<!-- 数据库连接池,初始化连接数,默认为 0 -->
	<property name="initialsize" value="${jdbc.initialsize}"></property>
	<!-- 数据库连接池,最大连接数,默认为 8 -->
	<property name="maxactive" value="${jdbc.maxactive}"></property>
	<!-- 从数据库连接池获取连接的最大等待时间,默认为 -1(无限等待) -->
	<property name="maxwait" value="${jdbc.maxwait}"></property>
</bean>
<!-- 引入外部属性文件 -->
<context:property-placeholder location="jdbc.properties"></context:property-placeholder>

        根据类型,从 ioc 容器中获取数据源。

//通过读取类路径下 xml 格式的配置文件,创建 ioc 容器对象
applicationcontext ioc = new classpathxmlapplicationcontext("applicationcontext.xml");
//根据类型,从 ioc 容器中获取 druid 数据库连接池对象
druiddatasource datasource = ioc.getbean(druiddatasource.class);
//从数据库连接池,获取数据库连接对象
system.out.println(datasource.getconnection());

5、自动装配

        ioc 容器按照既定规则,匹配某个 bean,为指定 bean 依赖的类类型或接口类型属性赋值的过程。

        通过 bean 标签的属性 autowire 设置自动装配的规则,值 no、default 表不装配,属性使用默认值;bytype 表根据 setter 参数类型匹配 bean,为属性赋值,无匹配则不装配,多个匹配则抛 nouniquebeandefinitionexception;byname 表截取 setter 名称作为 bean 的 id 匹配 bean,为属性赋值,无匹配则不装配。

        值得注意的是,基于 xml 配置 bean,设置 autowire 为 bytype / byname,spring 会先调用无参构造创建对象,再 set 注入,为对象类类型或接口类型的属性赋值;字符串、数字等字面量类型的属性,跳过不赋值,需手动 property 赋值。

三、基于注解管理 bean

1、准备

        创建 maven 工程,pom.xml 添加依赖。

<dependencies>
    <dependency>
        <groupid>org.springframework</groupid>
        <artifactid>spring-context</artifactid>
        <version>5.3.1</version>
    </dependency>
</dependencies>

2、标记

        com.example.controller 包下,创建类 usercontroller,添加注解 @controller 将其标识为控制层组件。

@controller
public class usercontroller {
    @autowired
    private userservice userservice;
    //添加用户信息
    public void saveuser(){
        userservice.saveuser();
    }
}

        com.example.service 包下,创建接口 userservice;com.example.service.impl 包下,创建实现类 userserviceimpl,添加注解 @service 将其标识为业务层组件。

public interface userservice {
    //保存用户信息
    void saveuser();
}
@service
public class userserviceimpl implements userservice {
    @autowired
    private userdao userdao;
    @override
    public void saveuser() {
        userdao.saveuser();
    }
}

        com.example.dao 包下,创建接口 userdao;com.example.dao.impl 包下,创建实现类 userdaoimpl,添加注解 @repository 将其标识为持久层组件。

public interface userdao {
    //保存用户信息
    void saveuser();
}
@repository
public class userdaoimpl implements userdao {
    @override
    public void saveuser() {
        system.out.println("保存成功!");
    }
}

        此外,注解 @component 用于将类标识为普通组件,@controller、@service、@repository 都是其派生注解,基本功能一致,仅语义分层区别,方便开发者区分代码层次;注解只是做标记,为了让 spring 知道开发者在哪些地方标记了什么注解,还需要配置组件扫描。

3、扫描

        右击目录 resources,依次选择 new -> xml configuration file -> spring config,创建 spring 配置文件 applicationcontext.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.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 扫描组件 -->
    <context:component-scan base-package="com.example">
        <!--
            不扫描谁
            type 用于设置排除依据,值 annotation 表根据注解排除,此时 expression 用于设置要排除的注解的全类名;
                               值 assignable 表根据类型排除,此时 expression 为要排除的类型的全类名;
                               ssm 整合时,springmvc 只扫描控制层,spring 扫描其他层。
        -->
        <!--<context:exclude-filter type="annotation" expression="org.springframework.stereotype.controller"/>-->
        <!--<context:exclude-filter type="assignable" expression="com.example.controller.usercontroller"/>-->
        <!--
            只扫描谁,此时需要在 context:component-scan 标签中设置属性 use-default-filters 的值为 false
            use-default-filters 属性默认值为 true,表所设置包下的所有类都需要扫描,此时可以使用排除扫描
            use-default-filters 属性设置值为 false,表所设置包下的所有类都不需要扫描,此时可以使用包含扫描
        -->
        <!--<context:include-filter type="annotation" expression="org.springframework.stereotype.controller"/>-->
        <!--<context:include-filter type="assignable" expression="com.example.controller.usercontroller"/>-->
    </context:component-scan>
</beans>

        通过 注解 & 扫描 配置的 bean,id 默认为类的小驼峰(如:usercontroller),可通过标识类的注解的 value 属性值设置 bean 的自定义 id(如:@controller("controller"));此时,spring 通过反射调用默认无参构造创建对象。

4、获取 bean

        根据 id、类型、id & 类型,从 ioc 容器中获取 bean 对象。

//通过读取类路径下 xml 格式的配置文件,创建 ioc 容器对象
applicationcontext ioc = new classpathxmlapplicationcontext("applicationcontext.xml");
//(1)根据 id,从 ioc 容器中获取 bean 对象
usercontroller usercontroller = (usercontroller) ioc.getbean("usercontroller");
//(2)根据类型,从 ioc 容器中获取 bean 对象
userservice userservice = ioc.getbean(userservice.class);
//(3)根据类型 & id,从 ioc 容器中获取 bean 对象
userdao userdao = ioc.getbean("userdaoimpl", userdao.class);

5、自动装配

        成员变量添加 @autowired 注解,即可完成自动装配。

        spring 默认根据成员变量的类型,在 ioc 容器中匹配某个 bean,为成员变量赋值。若有多个类型匹配的 bean,则将成员变量的变量名作为 bean 的 id,在 ioc 容器中匹配某个 bean,为成员变量赋值;若有多个类型匹配的 bean,但这些 bean 的 id 均与要赋值成员变量的变量名不一致,则抛 nouniquebeandefinitionexception,此时,可以在要赋值成员变量的变量名上添加注解 @qualifier,通过该注解 value 属性值指定某个 bean 的 id,为成员变量赋值。若没有任何一个类型匹配的 bean,则抛 nosuchbeandefinitionexception,因为 @autowired 注解中属性 required 默认值为 true,要求必须完成自动装配,可以将属性 required 设置为 false,此时能装配则装配,无法装配则使用成员变量默认值。

        值得注意的是,基于注解配置 bean,成员变量添加 @autowired 注解,spring 会先调用默认无参构造创建对象,再利用反射直接给成员变量赋值,不属于 setter 注入、构造器注入;而 @autowired 注解也可标记在 set 方法、构造器上,此处不做探讨。

四、其他知识

1、bean 的作用域

        规定 spring ioc 容器,何时创建对象、创建多少个对象,以及对象的生效范围。基于 xml 管理 bean,通过 bean 标签的属性 scope 设置 bean 的作用域,值 singleton 表单例(默认)、prototype 表多例(每次获取均为新对象);基于注解管理 bean,通过注解 @scope 设置 bean 的作用域,值 configurablebeanfactory.scope_singleton 表单例(默认)、configurablebeanfactory.scope_prototype 表多例(每次获取均为新对象)。

2、bean 的生命周期

        bean 为单例,获取 ioc 容器时,创建对象、依赖注入、执行后置处理器的 postprocessbeforeinitialization() 方法、执行初始化方法、执行后置处理器的 postprocessafterinitialization() 方法;ioc 容器关闭时,执行销毁方法,代码中没有地方持有这个 bean 的引用后,等待 jvm 发生 gc 回收对象。

        bean 为多例,获取 bean 时,创建对象、依赖注入、执行后置处理器的 postprocessbeforeinitialization() 方法、执行初始化方法、执行后置处理器的 postprocessafterinitialization() 方法;不会执行销毁方法,代码中没有地方持有这个 bean 的引用后,等待 jvm 发生 gc 回收对象。

         基于 xml 管理 bean,通过 bean 标签的属性 init-method 设置 bean 的初始化方法,destroy-method 设置 bean 的销毁方法;基于注解管理 bean,通过注解 @postconstruct 设置 bean 的初始化方法,@predestroy 设置 bean 的销毁方法。

3、bean 的后置处理器

        用于在 bean 生命周期初始化前后,添加额外操作。创建类 mybeanpostprocessor 实现 beanpostprocessor 接口,重写 postprocessbeforeinitialization()、postprocessafterinitialization() 方法;

public class mybeanpostprocessor implements beanpostprocessor { //ctrl+o,选择需要实现的方法
    //在 bean 生命周期初始化之前执行,参数 bean 为当前 ioc 容器管理的 bean、beanname 为 bean 的 id
    @override
    public object postprocessbeforeinitialization(object bean, string beanname) throws beansexception {
        return bean;
    }
    //在 bean 生命周期初始化之后执行,参数 bean 为当前 ioc 容器管理的 bean、beanname 为 bean 的 id
    @override
    public object postprocessafterinitialization(object bean, string beanname) throws beansexception {
        return bean;
    }
}

        spring 配置文件 applicationcontext.xml,配置后置处理器。此处以 xml 方式进行配置,也可使用注解方式配置 bean。

<bean class="com.example.process.mybeanpostprocessor"></bean>

        值得注意的是,bean 的后置处理器不是单独针对某一个 bean 生效,而是针对 ioc 容器中所有 bean 都会执行。

4、factorybean

        spring 提供的工厂 bean 接口,用于封装复杂对象的创建逻辑。com.example.pojo 包下,创建实体类 user。

public class user {
    private integer id;
    private string username;
    private string password;
    private integer age;
    public user() {
    }
    public user(integer id, string username, string password, integer age) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.age = age;
    }
    public integer getid() {
        return id;
    }
    public string getusername() {
        return username;
    }
    public string getpassword() {
        return password;
    }
    public integer getage() {
        return age;
    }
    public void setid(integer id) {
        this.id = id;
    }
    public void setusername(string username) {
        this.username = username;
    }
    public void setpassword(string password) {
        this.password = password;
    }
    public void setage(integer age) {
        this.age = age;
    }
    @override
    public string tostring() {
        return "user{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", age=" + age +
                '}';
    }
}

        创建类 userfactorybean 实现 factorybean 接口,泛型配置为最终交给 ioc 容器管理的对象类型,重写 getobject()、getobjecttype()、issingleton() 方法;

public class userfactorybean implements factorybean<user> { //指定泛型为最终交给 ioc 容器管理的对象类型
    @override
    public user getobject() throws exception {
        //在这里手写复杂创建逻辑!
        return new user(); //返回最终交给 ioc 容器管理的对象
    }
    @override
    public class<?> getobjecttype() {
        return user.class; //返回最终交给 ioc 容器管理的对象的类型
    }
    @override
    public boolean issingleton() {
        return true; //设置最终交给 ioc 容器管理的对象是否单例,true表单例(默认)、false表多例
    }
}

        spring 配置文件 applicationcontext.xml,配置 userfactorybean。此处以 xml 方式进行配置,也可使用注解方式配置 bean。

<bean id="userbean" class="com.example.factory.userfactorybean"></bean>

        根据 id,从 ioc 容器中获取 bean 对象。

//通过读取类路径下 xml 格式的配置文件,创建 ioc 容器对象
applicationcontext ioc = new classpathxmlapplicationcontext("applicationcontext.xml");
//根据 id,从 ioc 容器中获取 bean 对象
user user = (user) ioc.getbean("userbean");
//加 & 符号,从 ioc 容器中获取工厂本身 userfactorybean 对象
userfactorybean userfactorybean = (userfactorybean) ioc.getbean("&userbean");

        值得注意的是,beanfactory 是 ioc 容器最顶层根接口,是 bean 的工厂,负责创建、获取、管理所有 bean 对象;而 factorybean 是 spring 提供的工厂 bean 接口,用于封装复杂对象的创建逻辑,spring 先实例化 factorybean 工厂,再调用 getobject() 获取目标对象放入容器,对外提供使用,工厂本身也可通过 id 加 & 前缀,从 ioc 容器中获取。

到此这篇关于spring ioc 控制反转实战教程的文章就介绍到这了,更多相关spring ioc 控制反转内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com