跳转至

控制反转#

约 1700 个字 53 行代码 3 张图片 预计阅读时间 36 分钟

不使用Spring存在的问题#

  • 使用new创建对象时,用到了具体的 实现类。当业务需求反复变化,实现类变更为其它类。需要反复修改创建对象处的代码。不满足开闭原则
  • 使用new创建对象。依赖了具体实现类,不符合依赖倒置原则

Spring概述#

Spring框架是 Java 平台的一个开源的全栈(full-stack)应用程序框架和控制反转容器实现,一般被直接称为 Spring。该框架的一些核心功能理论上可用于任何 Java 应用,但 Spring 还为基于Java企业版平台构建的 Web 应用提供了大量的拓展支持。Spring 没有直接实现任何的编程模型,但它已经在 Java 社区中广为流行,基本上完全代替了企业级JavaBeans(EJB)模型

Spring框架以 Apache License 2.0 开源许可协议的形式发布,该框架最初由 Rod Johnson 以及 Juergen Hoeller 等人开发。

版本说明#

Warning

Spring Framework 6.0开始,Spring需要Java 17+

Spring版本 Jakarta EE版本 JDK版本
5.3.x Java EE 7-8 (javax namespace) 8-21
6.0.x 9-10 17-21
6.1.x 9-10 17-23
6.2.x 9-11 17-25(预计)

ICO入门案例#

  • 引入依赖:
XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.luguosong</groupId>
    <artifactId>spring-hello</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.1.8</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.20.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j2-impl</artifactId>
            <version>2.20.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>6.1.8</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>6.1.8</version>
        </dependency>
        <dependency>
            <groupId>jakarta.annotation</groupId>
            <artifactId>jakarta.annotation-api</artifactId>
            <version>3.0.0</version>
        </dependency>

    </dependencies>

</project>

spring-context依赖内部会引用其它核心模块:

spring-context依赖内部引入其它核心模块
  • 创建User对象:
User.java
package com.luguosong.ioc;

import java.util.Arrays;
import java.util.Map;

/**
 * @author luguosong
 */
public class User {
    private String name;
    private Integer age;
    private String[] hobby;
    private Map<String, String> additionalInfo;

    public User() {

    }

    public User(Integer age, String name) {
        this.age = age;
        this.name = name;
    }

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

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

    public void setHobby(String[] hobby) {
        this.hobby = hobby;
    }

    public void setAdditionalInfo(Map<String, String> additionalInfo) {
        this.additionalInfo = additionalInfo;
    }

    @Override
    public String toString() {
        return "User{" +
                "additionalInfo=" + additionalInfo +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", hobby=" + Arrays.toString(hobby) +
                '}';
    }
}
  • 编写Spring配置文件:
spring_config_hello.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--id:唯一标识-->
    <!--class:类的全限定类名-->
    <bean id="userBean" class="com.luguosong.ioc.User"/>
</beans>
  • 编写测试类:
SpringHello.java
package com.luguosong.ioc.hello;

import com.luguosong.ioc.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author luguosong
 */
public class SpringHello {
    public static void main(String[] args) {
        /*
         * 启动Spring容器
         * 解析xml文件
         * 实例化所有Bean对象,放入Spring容器中
         * */
        ApplicationContext context = new ClassPathXmlApplicationContext("spring_config_hello.xml");

        /*
         * 根据bean的id获取对应bean的实例
         * */
        User userBean = context.getBean("userBean", User.class);

        System.out.println(userBean); //User{age=null, name='null'}
    }
}

IOC原理分析#

初始化阶段:Spring读取配置文件中的bean标签,根据其中的class属性(类的全限定类名),默认通过反射 调用类的无参构造创建对象,并将对象实例跟id属性绑定封装成Map集合。

使用阶段:Spring根据id名称可以获取到对应的对象实例

IOC原理图

配置日志#

引入依赖
<dependencies>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.20.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-slf4j2-impl</artifactId>
        <version>2.20.0</version>
    </dependency>
</dependencies>
log4j2.xml:日志配置文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- 配置appender -->
    <Appenders>
        <!-- 配置consoleAppender -->
        <Console name="consoleAppender" target="SYSTEM_OUT">
            <PatternLayout pattern="[%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n"/>
        </Console>
    </Appenders>

    <!-- 配置logger -->
    <Loggers>
        <!-- 配置rootlogger -->
        <Root level="debug">
            <AppenderRef ref="consoleAppender"/>
        </Root>
    </Loggers>
</configuration>

bean的实例化方式#

构造方法#

直接配置文件中配置类的全路径,Spring会调用构造方法创建对象。

XML
<!--Spring会根据class类路径,通过构造方法创建对象-->
<bean id="user" class="com.luguosong.ioc.User"/>

简单工厂#

编写简单工厂:

Java
package com.luguosong.ioc.create_bean;

import com.luguosong.ioc.User;

/**
 * 通过简单工厂创建bean
 *
 * @author luguosong
 */
public class SimpleFactory {
    //定义静态方法用于创建对象
    public static User getUser() {
        return new User(12,"李四");
    }
}

编写配置文件,配置简单工厂类

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--通过简单工厂过去Bean对象-->
    <bean id="user" class="com.luguosong.ioc.create_bean.SimpleFactory" factory-method="getUser"/>
</beans>

通过简单工厂创建对象:

Java
package com.luguosong.ioc.create_bean;

import com.luguosong.ioc.User;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author luguosong
 */
public class SimpleFactoryTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ioc_create_simple_factory.xml");
        User user = context.getBean("user", User.class);
        System.out.println(user);
    }
}

工厂方法#

实现FactoryBean<T>接口的对象本身就是用于创建单个对象的工厂(工厂方法模式)。如果一个bean实现了这个接口,它将作为一个工厂来暴露对象,而不是直接作为一个bean实例暴露自身。

Warning

实现此接口的bean不能用作普通bean。一个FactoryBean以bean的风格定义,但对于bean引用暴露的对象(通过getObject())总是它创建的对象。

容器只负责管理FactoryBean实例的生命周期,而不负责FactoryBean创建的对象的生命周期。因此,暴露的bean对象上的销毁方法(如java.io.Closeable.close())不会自动调用。相反,FactoryBean应该实现DisposableBean接口并将任何此类关闭调用委托给底层对象。

通过配置文件实现#

编写工厂对象:

Java
package com.luguosong.ioc.create_bean;

import com.luguosong.ioc.User;

/**
 * @author luguosong
 */
public class FactoryMethod2 {
    public User getUser() {
        return new User(12, "lsg");
    }
}

编写配置文件:

ioc_create_factory_method2.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--工厂方法对象-->
    <bean id="userFactory" class="com.luguosong.ioc.create_bean.FactoryMethod2"/>

    <!--通过工厂方法创建对象-->
    <bean id="user" factory-bean="userFactory" factory-method="getUser"/>
</beans>

测试:

Java
package com.luguosong.ioc.create_bean;

import com.luguosong.ioc.User;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author luguosong
 */
public class FactoryMethod2Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ioc_create_factory_method2.xml");
        //通过工厂方法创建对象
        User user = context.getBean("user", User.class);
        System.out.println(user);
    }
}

直接继承FactoryBean接口#

编写工厂类,继承FactoryBean<T>接口:

Java
package com.luguosong.ioc.create_bean;

import com.luguosong.ioc.User;
import org.springframework.beans.factory.FactoryBean;

/**
 * @author luguosong
 */
public class FactoryMethod1 implements FactoryBean<User> {
    @Override
    public User getObject() throws Exception {
        return new User(12, "lsg");
    }

    @Override
    public Class<?> getObjectType() {
        return User.class;
    }

    @Override
    public boolean isSingleton() {
        return FactoryBean.super.isSingleton();
    }
}

编写配置文件:

ioc_create_factory_method1.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="userFactory" class="com.luguosong.ioc.create_bean.FactoryMethod1"/>
</beans>

测试:

Java
package com.luguosong.ioc.create_bean;

import com.luguosong.ioc.User;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author luguosong
 */
public class FactoryMethod1Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ioc_create_factory_method1.xml");
        //通过工厂方法创建对象
        User user = context.getBean("userFactory", User.class);
        System.out.println(user);
    }
}

依赖注入#

set注入#

<property>标签通过set方法进行依赖注入。

可以使用p命名空间简化set注入

设置配置文件:

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

    <bean id="user" class="com.luguosong.ioc.User">
        <!--注入基本数据类型-->
        <property name="name" value="张三"/>
        <property name="age" value="18"/>
        <!--注入数组-->
        <property name="hobby">
            <array>
                <value>篮球</value>
                <value>足球</value>
            </array>
        </property>
        <!--注入Map对象-->
        <property name="additionalInfo">
            <map>
                <entry key="性别" value="男"/>
                <entry key="职业" value="IT"/>
            </map>
        </property>
    </bean>

    <bean id="userService" class="com.luguosong.ioc.UserService">
        <!--注入对象-->
        <property name="user" ref="user"/>
    </bean>

    <!--使用p命名空间进行注入-->
    <bean id="user2" class="com.luguosong.ioc.User" p:name="李四" p:age="20"/>
</beans>
Java
package com.luguosong.ioc.dependency_injection;

import com.luguosong.ioc.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * 使用set方法进行依赖注入
 *
 * @author luguosong
 */
public class IocSetterTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("ioc_set.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.test();
    }
}

Property标签的name属性

name属性对应set方法方法名去除set,并且首字母小写。

构造方法注入#

<constructor-arg>标签通过构造方法进行依赖注入。

  • 默认通过参数的类型推断指定参数(❗这种方式构造函数中的参数类型不能重复)
  • 通过index属性指定构造函数参数位置设置参数
  • 通过name属性指定构造函数参数名称设置参数

可以使用c命名空间简化构造方法注入

设置配置文件:

ioc_constructor.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:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="user" class="com.luguosong.ioc.User">
        <!--指定构造函数第一个参数-->
        <constructor-arg index="0" value="12"/>
        <!--指定构造函数第二个参数-->
        <constructor-arg index="1" value="李四"/>
    </bean>


    <bean id="userService" class="com.luguosong.ioc.UserService">
        <!--注入对象-->
        <constructor-arg index="0" ref="user"/>
    </bean>

    <bean id="userService2" class="com.luguosong.ioc.UserService">
        <!--使用参数名称name指定参数-->
        <constructor-arg name="user" ref="user"/>
    </bean>

    <bean id="userService3" class="com.luguosong.ioc.UserService">
        <!--根据类型推断指定参数-->
        <constructor-arg ref="user"/>
    </bean>

    <!--基于c命名空间注入-->
    <bean id="user2" class="com.luguosong.ioc.User" c:_0="12" c:_1="李四"/>
</beans>

测试:

Java
package com.luguosong.ioc.dependency_injection;

import com.luguosong.ioc.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * 使用构造方法进行依赖注入
 *
 * @author luguosong
 */
public class IocConstructorTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("ioc_constructor.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.test();
    }
}

util命名空间#

util命名空间针对与集合的复用场景。

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

    <!--util命名空间可以被复用-->
    <util:map id="utilMap">
        <entry key="性别" value="男"/>
        <entry key="职业" value="IT"/>
    </util:map>

    <bean id="user1" class="com.luguosong.ioc.User">
        <property name="additionalInfo" ref="utilMap"/>
    </bean>

    <bean id="user2" class="com.luguosong.ioc.User">
        <property name="additionalInfo" ref="utilMap"/>
    </bean>
</beans>

自动注入#

依赖可以根据名字类型自动注入。

基于的还是set方法

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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="user" class="com.luguosong.ioc.User">
        <property name="name" value="张三"/>
    </bean>

    <!--根据set注入的 方法名 去找对应 id 的bean标签-->
    <bean id="userService1" class="com.luguosong.ioc.UserService" autowire="byName"/>

    <!--根据类型去寻找对应class的bean-->
    <!--❗根据类型匹配,同一种类型只能有一个bean,否则会报错-->
    <bean id="userService2" class="com.luguosong.ioc.UserService" autowire="byType"/>

</beans>

加载外部配置文件#

jdbc.properties
url=jdbc:mysql://localhost:3306/test
user=root
password=12345678
driver=com.mysql.jdbc.Driver
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 http://www.springframework.org/schema/context/spring-context.xsd">

    <!--引入配置文件-->
    <context:property-placeholder location="jdbc.properties"/>

    <!--从配置文件中取值-->
    <bean id="jdbc" class="com.luguosong.ioc.dependency_injection.Jdbc">
        <property name="url" value="${url}"/>
        <property name="username" value="${user}"/>
        <property name="password" value="${password}"/>
        <property name="driver" value="${driver}"/>
    </bean>
</beans>

bean作用域#

  • 单例(singleton):将单个bean定义范围限定为每个Spring IoC容器的单个对象实例。
  • 原型(prototype):将单个bean定义范围限定为任意数量的对象实例。

仅在支持web的Spring ApplicationContext上下文中有效:

  • 请求(request):将单个bean定义范围限定为单个HTTP请求的生命周期。也就是说,每个HTTP请求都有自己的bean实例,该实例是基于单个bean定义创建的。
  • 会话(session):将单个bean定义范围限定为HTTP会话的生命周期。
  • 应用程序(application):将单个bean定义范围限定为ServletContext的生命周期。
  • WebSocket(websocket):将单个bean定义范围限定为WebSocket的生命周期。
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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--singleton:默认值,表示整个Spring容器中只有一个Bean实例,无论多少次请求,都会返回同一个实例。-->
    <bean id="user1" class="com.luguosong.ioc.User" scope="singleton"/>

    <!--prototype:每次请求都会创建一个新的Bean实例。-->
    <bean id="user2" class="com.luguosong.ioc.User" scope="prototype"/>

    <!--request:每个HTTP请求都会创建一个新的Bean实例。-->
    <!--适用于Web应用,需要包含spring-webmvc依赖。-->
    <bean id="user3" class="com.luguosong.ioc.User" scope="request"/>

    <!--session:每个HTTP会话都会创建一个新的Bean实例。-->
    <!--适用于Web应用,需要包含spring-webmvc依赖。-->
    <bean id="user4" class="com.luguosong.ioc.User" scope="session"/>
</beans>

bean生命周期#

  1. 实例化Bean
  2. 调用setter方法属性赋值
  3. 实现Aware相关接口,执行对应方法:BeanNameAware接口、BeanClassLoaderAware接口、BeanFactoryAware接口
  4. 初始化Bean之前执行:自定义处理器,实现BeanPostProcessor接口,重写postProcessBeforeInitialization方法
  5. 实现InitializingBean接口执行对应方法
  6. 初始化Bean:配置文件bean配置init-method属性
  7. 初始化Bean之后执行:自定义处理器,实现BeanPostProcessor接口,重写postProcessAfterInitialization方法
  8. 使用Bean
  9. 实现DisposableBean接口,执行对应方法
  10. 销毁Bean:配置文件bean配置destroy-method属性

Warning

Spring容器只对作用域scope="singleton"的bean进行完整的生命周期管理。

Java
package com.luguosong.ioc.life_cycle;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;

/**
 * @author luguosong
 */
public class Dog implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware , InitializingBean,DisposableBean {

    private String name;

    public Dog() {
        System.out.println("实例化Bean");
    }

    public void setName(String name) {
        System.out.println("调用setter方法属性赋值");
        this.name = name;
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("实现BeanNameAware接口,setBeanName方法执行");
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        System.out.println("实现BeanClassLoaderAware接口,setBeanClassLoader方法执行");
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("实现BeanFactoryAware接口,setBeanFactory方法执行");
    }

    public void initBean() {
        System.out.println("初始化Bean");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("实现InitializingBean接口,afterPropertiesSet方法执行");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("实现DisposableBean接口,destroy方法执行");
    }

    public void destroyBean() {
        System.out.println("销毁Bean");
    }

}

配置文件,指定init-methoddestroy-method:

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

    <bean class="com.luguosong.ioc.life_cycle.MyBeanPostProcessor"/>

    <!--需要手动指定初始化方法和销毁方法,对应方法才会生效-->
    <bean id="dog" class="com.luguosong.ioc.life_cycle.Dog" init-method="initBean" destroy-method="destroyBean">
        <property name="name" value="wangcai"/>
    </bean>
</beans>

配置处理器,其方法在初始化前后执行:

Java
package com.luguosong.ioc.life_cycle;

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

/**
 * @author luguosong
 */
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化之前执行");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化之后执行");
        return bean;
    }
}

测试:

Java
package com.luguosong.ioc.life_cycle;

import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author luguosong
 */
public class LifeCycleTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ioc_life_cycle.xml");
        context.getBean("dog", Dog.class);
        context.close();
    }
}

将自己new的对象添加到Bean#

Java
package com.luguosong.ioc.other;

import com.luguosong.ioc.User;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;

/**
 * @author luguosong
 */
public class MyObjectToApplication {
    public static void main(String[] args) {
        User user = new User();
        //打印地址
        System.out.println(Integer.toHexString(System.identityHashCode(user))); //5b480cf9

        //将对象添加到容器中
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        factory.registerSingleton("user",user);

        //从容器中获取Bean
        User user1 = factory.getBean("user",User.class);
        System.out.println(Integer.toHexString(System.identityHashCode(user1))); //5b480cf9
    }
}

注解开发#

注解开发依赖于aop包,context包中包含aop了

入门示例#

通过注解的方式引入依赖:

Java
package com.luguosong.ioc.annotation.hello;

import org.springframework.stereotype.Component;

/**
 * 如果不指定value,则默认使用类名首字母小写作为id名
 * @author luguosong
 */
@Component("user")
public class User {
    private String name;

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

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}

Note

如果不设置@Component注解的value属性,Spring会默认使用类名首字母小写作为bean的id

配置文件配置包扫描:

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

    <!--Spring扫描指定包中的类-->
    <context:component-scan base-package="com.luguosong.ioc.annotation.hello"/>
</beans>

测试:

Java
package com.luguosong.ioc.annotation.hello;

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

/**
 * @author luguosong
 */
public class Test {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("ioc_annotation_hello.xml");
        User user = context.getBean("user", User.class);
        System.out.println(user);
    }
}

@Component别名#

一下别名功能与@Component相同,只是为了区分不同使用场景:

  • @Controller:表示层bean
  • @Service:业务逻辑层bean
  • @Repository:持久化层bean

依赖注入#

使用注解方式就行依赖注入可以分为以下几种方式:

  • @Value:注入基本数据类型
  • @Autowired:根据类型注入(接口只能有一个实现类)
  • @Autowired+@Qualifier:根据bean名称注入
  • @Resource:根据bean名称注入

Warning

如果@Resource根据名称没有找到对应的类,则会根据类型注入。

此时也会要求接口只有唯一的实现类。

要使用@Resource注解需要依赖以下包:

XML
<dependency>
    <groupId>jakarta.annotation</groupId>
    <artifactId>jakarta.annotation-api</artifactId>
    <version>3.0.0</version>
</dependency>
Java
package com.luguosong.ioc.annotation.dependency_injection;

import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * @author luguosong
 */
@Component
public class User {

    //注入基本数据类型
    @Value("张三")
    private String name;

    /*
    * 根据类型注入
    *
    * ⭐接口只能有一个实现类
    * */
    @Autowired
    private Hobby hobby;

    /*
    * 根据名字注入(也就是bean的id)
    *
    * 这种情况允许接口有多个实现类
    * */
    @Autowired
    @Qualifier("bike")
    private Vehicle vehicle1;

    /*
    * 根据名字注入
    * */
    @Resource(name = "car")
    private Vehicle vehicle2;

    /*
    * @Resource省略name属性
    * 会根据属性名寻找对应名字的实现类
    * */
    @Resource
    private Vehicle car;

    public void play(){
        hobby.play();
    }

    public void driver(){
        vehicle1.drive();
        vehicle2.drive();
        car.drive();
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}

注解配置文件#

可以使用@Configuration注解替代xml方式的配置文件。做到全注解无xml开发。

Java
package com.luguosong.ioc.annotation.config;

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

/**
 * 通过@ComponentScan注解配置包扫描
 *
 * @author luguosong
 */
@Configuration
@ComponentScan({"com.luguosong.ioc.annotation.config"})
public class SpringConfig {
}

编写测试类,将ClassPathXmlApplicationContext更改为AnnotationConfigApplicationContext,从配置类读取配置:

Java
package com.luguosong.ioc.annotation.config;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @author luguosong
 */
public class Test {
    public static void main(String[] args) {
        // 根据配置类创建Spring容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        User user = context.getBean("user", User.class);
        System.out.println(user);
    }
}

评论