Spring之IOC

1 IOC介绍

IOC(Inversion Of Control),即控制反转,是一种能够指导我们设计出松耦合、高可扩展应用程序的设计思想。

  • 反转的是什么
    正常情况下,我们会主动创建对象,然后再使用,对象的生命周期有我们通过编码控制。
    而IOC思想是将对象的创建及对对象之间关系的维护交由第三方容器实现,反转的正式这个方面的内容。
  • 实现方式
    DI(Dependency Injection),即依赖注入,是实现IOC的一种方式,指Spring创建对象的过程中,将对象依赖属性通过配置进行注入。
    IOC 就是一种控制反转的思想, 而 DI 是对IoC的一种具体实现。

2 Spring

Spring框架提供了IOC容器相关的实现方式,Spring通过IOC容器管理Bean及Bean之间的依赖关系,其实现方式包括如下两种:

  • BeanFactory
    这是 IoC 容器的基本实现,是 Spring 内部使用的接口。面向 Spring 本身,不提供给开发人员使用。
  • ApplicationContext
    BeanFactory 的子接口,提供了更多高级特性。面向 Spring 的使用者,几乎所有场合都使用 ApplicationContext 而不是底层的 BeanFactory。
类型名 说明
ClassPathXmlApplicationContext 通过读取类路径下的 XML 格式的配置文件创建 IOC 容器对象
FileSystemXmlApplicationContext 通过文件系统路径读取 XML 格式的配置文件创建 IOC 容器对象
  • Spring管理bean的两种方式:xml方式和注解

3 Spring基于xml管理bean

3.1 基础环境搭建

创建子模块giser-java-spring6-01,并引入配置文件beans.xml、log4j2.xml,添加如下依赖:

<dependencies>
	
    <dependency>
        <groupId>org.springframeworkgroupId>
        <artifactId>spring-contextartifactId>
        <version>6.1.1version>
    dependency>

    
    <dependency>
        <groupId>org.junit.jupitergroupId>
        <artifactId>junit-jupiter-apiartifactId>
        <version>5.10.1version>
    dependency>

    
    <dependency>
        <groupId>org.apache.logging.log4jgroupId>
        <artifactId>log4j-coreartifactId>
        <version>2.19.0version>
    dependency>
    <dependency>
        <groupId>org.apache.logging.log4jgroupId>
        <artifactId>log4j-slf4j2-implartifactId>
        <version>2.19.0version>
    dependency>
dependencies>

创建实体类:

public class HelloWorld {
    public HelloWorld(){
        System.out.println("constructor execute...");
    }
    public void sayHello(){
        System.out.println("hello world!");
    }
}

beans.xml配置


<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="helloWorld" class="com.giser.spring6.HelloWorld">bean>

beans>

log4j配置:


<configuration>
    <loggers>
        
        <root level="DEBUG">
            <appender-ref ref="spring6log"/>
            <appender-ref ref="RollingFile"/>
            <appender-ref ref="log"/>
        root>
    loggers>

    <appenders>
        
        <console name="spring6log" target="SYSTEM_OUT">
            
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/>
        console>

        
        <File name="log" fileName="d:/spring6_log/test.log" append="false">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
        File>

        
        <RollingFile name="RollingFile" fileName="d:/spring6_log/app.log"
                     filePattern="log/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
            <PatternLayout pattern="%d{yyyy-MM-dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>
            <SizeBasedTriggeringPolicy size="50MB"/>
            
            <DefaultRolloverStrategy max="20"/>
        RollingFile>
    appenders>
configuration>
3.2 获取bean的三种方式
public class HelloWorldTest {
    private Logger logger = LoggerFactory.getLogger(HelloWorldTest.class);
    @Test
    public void testSpringBean() {
        // 读取配置信息
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
    }
}
  • 根据bean标签的id获取
// 根据id获取bean
HelloWorld bean1 = (HelloWorld) applicationContext.getBean("helloWorld");
  • 根据bean的类型获取
// 根据bean的类型获取bean,此时要求此类型的bean只能存在一个,否则会抛异常
HelloWorld bean2 = applicationContext.getBean(HelloWorld.class);
  • 根据id和类型获取bean
HelloWorld bean3 = applicationContext.getBean("helloWorld", HelloWorld.class);
3.3 DI之setter注入

实体:

public class User {
    private String userName;
    private Integer userAge;
    // 无参构造函数
    // 全参构造函数
    // getter setter toString
}

beans-di.xml


<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.giser.spring6.entity.User">
        <property name="userName" value="张三"/>
        <property name="userAge" value="13" />
    bean>

beans>

测试

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-di.xml");
        User user = applicationContext.getBean("user", User.class);
3.4 DI之构造器注入
    
    <bean id="user1" class="com.giser.spring6.entity.User">
        <constructor-arg name="userName" value="张三" />
        <constructor-arg name="userAge" value="23" />
    bean>

测试

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-di.xml");
        User user1 = applicationContext.getBean("user1", User.class);
3.5 DI之特殊值注入

    
    
    <bean id="user2" class="com.giser.spring6.entity.User">
        <property name="userName" value="张三"/>
        <property name="userAge" value="13" />
    bean>

    
    <bean id="user3" class="com.giser.spring6.entity.User">
        <property name="userName">
            <null />
        property>
        <property name="userAge" value="13" />
    bean>

    
    <bean id="user4" class="com.giser.spring6.entity.User">
        <property name="userName" value="a < b">property>
        <property name="userAge" value="13" />
    bean>

    
    <bean id="user5" class="com.giser.spring6.entity.User">
        <property name="userName">
            <value>value>
        property>
        <property name="userAge" value="13" />
    bean>
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-di.xml");
User user2 = applicationContext.getBean("user2", User.class);
User user3 = applicationContext.getBean("user3", User.class);
User user4 = applicationContext.getBean("user4", User.class);
User user5 = applicationContext.getBean("user5", User.class);
logger.info("特殊值处理-字面量赋值:{}", user2);
logger.info("特殊值处理-null值:{}", user3);
logger.info("特殊值处理-xml实体:{}", user4);
logger.info("特殊值处理-CDATA节:{}", user5);
3.6 DI之对象类型赋值

包括三种方式:引入外部bean、使用内部bean、级联属性赋值。
实体:

public class Dept {
    private String deptName;
    // getter setter toString
}
public class Emp {

    private String empName;
    private Integer empAge;
    private Dept dept;
    // getter setter toString
}

xml文件


<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.giser.spring6.iocpojo.Dept">
        <property name="deptName" value="产品研发部" />
    bean>

    <bean id="emp1" class="com.giser.spring6.iocpojo.Emp">
        <property name="empName" value="小明" />
        <property name="empAge" value="32" />
        
        <property name="dept" ref="dept" />
    bean>

    
    <bean id="emp2" class="com.giser.spring6.iocpojo.Emp">
        <property name="empName" value="小明" />
        <property name="empAge" value="32" />
        
        <property name="dept" >
            
            <bean class="com.giser.spring6.iocpojo.Dept">
                <property name="deptName" value="董事会" />
            bean>
        property>
    bean>

    
    <bean id="dept3" class="com.giser.spring6.iocpojo.Dept">
        <property name="deptName" value="产品研发部" />
    bean>
    <bean id="emp3" class="com.giser.spring6.iocpojo.Emp">
        <property name="empName" value="小明" />
        <property name="empAge" value="32" />
        
        <property name="dept" ref="dept3" />
        <property name="dept.deptName" value="中央办公室" />
    bean>

beans>

测试:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-obj-di.xml");
Emp emp = applicationContext.getBean("emp3", Emp.class);
logger.info("对象类型属性赋值:{}", emp);
3.7 DI之数组类型属性赋值
public class Student {

    private String stuName;
    private String[] hobbyArr;

    // 无参构造函数
    // 全参构造函数
    // getter setter toString
}

<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="stu" class="com.giser.spring6.iocarray.Student">
        <property name="stuName" value="张三" />
        <property name="hobbyArr">
            <array>
                <value>吃饭value>
                <value>睡觉value>
                <value>敲代码value>
            array>
        property>
    bean>

beans>
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-arr-di.xml");
Student stu = applicationContext.getBean("stu", Student.class);
logger.info("数组类型属性赋值:{}", stu);
3.8 DI之List类型属性赋值
public class Cls {
    private List<Stu> stuList;
    private String clsName;

    // 无参构造函数
    // 全参构造函数
    // getter setter toString
}
public class Stu {
    private String stuName;
    private List<String> hobbyList;

    // 无参构造函数
    // 全参构造函数
    // getter setter toString
}

<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="stu" class="com.giser.spring6.ioclist.Stu">
        <property name="stuName" value="张三" />
        <property name="hobbyList">
            <list>
                <value>吃饭value>
                <value>睡觉value>
                <value>敲代码value>
            list>
        property>
    bean>

    <bean id="stu2" class="com.giser.spring6.ioclist.Stu">
        <property name="stuName" value="李四" />
        <property name="hobbyList">
            <list>
                <value>烧烤value>
                <value>炸鸡value>
                <value>胡辣汤value>
            list>
        property>
    bean>

    
    <bean id="cls" class="com.giser.spring6.ioclist.Cls">
        <property name="clsName" value="中一班" />
        <property name="stuList">
            <list>
                <ref bean="stu" />
                <ref bean="stu2" />
            list>
        property>
    bean>

beans>
private Logger logger = LoggerFactory.getLogger(SpringListDITest.class);

@Test
public void testSpecialVal(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-list-di.xml");
    Stu stu = applicationContext.getBean("stu", Stu.class);
    logger.info("List集合类型属性赋值:{}", stu);
}

@Test
public void testSpecialVal2(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-list-di.xml");
    Cls cls = applicationContext.getBean("cls", Cls.class);
    logger.info("List集合类型属性赋值:{}", cls);
}
3.9 DI之Map类型属性赋值
public class Family {

    private String familyName;
    private Map<String, Mem> memMap;

    // 无参构造函数
    // 全参构造函数
    // getter setter toString
}
public class Mem {
    private String memName;
    private Integer memAge;    
    // 无参构造函数
    // 全参构造函数
    // getter setter toString
}

<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="mem1" class="com.giser.spring6.iocmap.Mem">
        <property name="memAge" value="11" />
        <property name="memName" value="小李" />
    bean>

    <bean id="mem2" class="com.giser.spring6.iocmap.Mem">
        <property name="memAge" value="22" />
        <property name="memName" value="小张"/>
    bean>

    
    <bean id="family" class="com.giser.spring6.iocmap.Family">
        <property name="familyName" value="梦想家" />
        <property name="memMap">
            <map>
                <entry>
                    <key>
                        <value>1001value>
                    key>
                    <ref bean="mem1"/>
                entry>
                <entry>
                    <key>
                        <value>1002value>
                    key>
                    <ref bean="mem2"/>
                entry>
            map>
        property>
    bean>

    <bean id="family2" class="com.giser.spring6.iocmap.Family">
        <property name="familyName" value="梦想家" />
        <property name="memMap">
            <map>
                <entry value-ref="mem1">
                    <key>
                        <value>1001value>
                    key>
                entry>
                <entry value-ref="mem2">
                    <key>
                        <value>1002value>
                    key>
                entry>
            map>
        property>
    bean>

beans>
public class SpringMapDITest {

    private Logger logger = LoggerFactory.getLogger(SpringMapDITest.class);

    @Test
    public void testSpecialVal(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-map-di.xml");
        Family family = applicationContext.getBean("family", Family.class);
        logger.info("对象类型属性赋值:{}", family);
        Family family2 = applicationContext.getBean("family2", Family.class);
        logger.info("对象类型属性赋值:{}", family2);
    }

}
3.10 DI之引用集合类型注入
public class Family {

    private String familyName;
    private Map<String, Mem> memMap;

    private List<Mem> memList;
    // 无参构造函数
    // 全参构造函数
    // getter setter toString
public class Mem {
    private String memName;
    private Integer memAge;
    // 无参构造函数
    // 全参构造函数
    // getter setter toString

<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="family" class="com.giser.spring6.iocref.Family">
        <property name="familyName" value="一家人" />
        
        <property name="memMap" ref="memMap" />
        <property name="memList" ref="memList" />
    bean>

    <util:list id="memList">
        <ref bean="mem1" />
        <ref bean="mem2" />
        <ref bean="mem3" />
    util:list>

    <util:map id="memMap">
        <entry>
            <key>
                <value>1001value>
            key>
            <ref bean="mem1"/>
        entry>
        <entry>
            <key>
                <value>1002value>
            key>
            <ref bean="mem2"/>
        entry>
    util:map>

    <bean id="mem1" class="com.giser.spring6.iocref.Mem">
        <property name="memAge" value="11" />
        <property name="memName" value="小李" />
    bean>
    <bean id="mem2" class="com.giser.spring6.iocref.Mem">
        <property name="memAge" value="22" />
        <property name="memName" value="小张"/>
    bean>
    <bean id="mem3" class="com.giser.spring6.iocref.Mem">
        <property name="memAge" value="33" />
        <property name="memName" value="小王"/>
    bean>
beans>
3.11 DI之通过p标签为属性赋值

<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="family" class="com.giser.spring6.iocref.Family"
        p:familyName="一家人"
          p:memMap-ref="memMap"
          p:memList-ref="memList"
    >
    bean>

    <util:list id="memList">
        <ref bean="mem1" />
        <ref bean="mem2" />
        <ref bean="mem3" />
    util:list>

    <util:map id="memMap">
        <entry>
            <key>
                <value>1001value>
            key>
            <ref bean="mem1"/>
        entry>
        <entry>
            <key>
                <value>1002value>
            key>
            <ref bean="mem2"/>
        entry>
    util:map>

    <bean id="mem1" class="com.giser.spring6.iocref.Mem">
        <property name="memAge" value="11" />
        <property name="memName" value="小李" />
    bean>
    <bean id="mem2" class="com.giser.spring6.iocref.Mem">
        <property name="memAge" value="22" />
        <property name="memName" value="小张"/>
    bean>
    <bean id="mem3" class="com.giser.spring6.iocref.Mem">
        <property name="memAge" value="33" />
        <property name="memName" value="小王"/>
    bean>
beans>
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-p-di.xml");
Family family = applicationContext.getBean("family", Family.class);
logger.info("p标签属性赋值:{}", family);
3.12 DI之引入外部属性文件

jdbc.properties

jdbc.user=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/db_mybatis?serverTimezone=UTC
jdbc.driver=com.mysql.cj.jdbc.Driver

beans-jdbc.xml


<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:property-placeholder location="classpath:jdbc.properties" />

    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="${jdbc.url}"/>
        <property name="password" value="${jdbc.password}" />
        <property name="username" value="${jdbc.user}" />
        <property name="driverClassName" value="${jdbc.driver}" />
    bean>
beans>
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-ds.xml");
DruidDataSource druidDataSource = applicationContext.getBean(DruidDataSource.class);
logger.info("数据库对象:{}", druidDataSource);
3.13 bean的作用域

<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="apple" class="com.giser.spring6.scope.Apple" scope="prototype" />

beans>
public class SpringScopeTest {

    private final Logger logger = LoggerFactory.getLogger(SpringScopeTest.class);

    /**
     * scope=singleton时:
     * [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'apple'
     * [main] INFO spring6.SpringScopeTest - 单例对象:com.giser.spring6.scope.Apple@436390f4
     * [main] INFO spring6.SpringScopeTest - 单例对象:com.giser.spring6.scope.Apple@436390f4
     *
     * scope="prototype"时:
     * [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 1 bean definitions from class path resource [beans-scope.xml]
     * [main] INFO spring6.SpringScopeTest - 单例对象:com.giser.spring6.scope.Apple@733037
     * [main] INFO spring6.SpringScopeTest - 单例对象:com.giser.spring6.scope.Apple@7728643a
     *
     * 如果是在WebApplicationContext环境下还会有另外几个作用域(但不常用)
     * request:在一个请求域内有效
     * session:在一个会话范围内有效
     */
    @Test
    public void testScope(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-scope.xml");
        Apple apple = applicationContext.getBean(Apple.class);
        logger.info("单例对象:{}", apple);
        Apple apple2 = applicationContext.getBean(Apple.class);
        logger.info("单例对象:{}", apple2);
    }

}
3.14 bean的生命周期
package com.giser.spring6.lifecycle;

/**
 * @author giserDev
 * @description
 * @date 2023-12-31 21:22:01
 */
public class Phone {

    private String phoneName;

    public Phone() {
        System.out.println("Bean生命周期:[1 bean对象创建(调用无参构造方法)]");
    }

    public void initMethod(){
        System.out.println("Bean生命周期:[4 bean对象初始化(调用bean配置时init-method属性指定的初始化方法)] ");
    }

    public void destroyMethod(){
        System.out.println("Bean生命周期:[7 bean对象销毁(调用bean配置时destroy-method属性指定的销毁方法)] ");
    }

    public String getPhoneName() {
        return phoneName;
    }

    public void setPhoneName(String phoneName) {
        System.out.println("Bean生命周期:[2 为对象属性赋值(调用属性set方法)] ");
        this.phoneName = phoneName;
    }
}
package com.giser.spring6.lifecycle;

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

/**
 * @author giserDev
 * @description
 * @date 2023-12-31 21:26:40
 */
public class MyBeanProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Bean生命周期:[3 bean后置处理器(在bean初始化之前执行)] ");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Bean生命周期:[5 bean后置处理器(在bean初始化之后执行)] ");
        return bean;
    }

}

<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="phone" class="com.giser.spring6.lifecycle.Phone"
          init-method="initMethod"
          destroy-method="destroyMethod"
          scope="singleton" >
        <property name="phoneName" value="小米手机" />
    bean>

    
    <bean id="myBeanProcessor" class="com.giser.spring6.lifecycle.MyBeanProcessor" />

beans>
import com.giser.spring6.lifecycle.Phone;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author giserDev
 * @description
 * @date 2023-12-31 21:30:42
 */
public class SpringBeanLifeCycleTest {
    private Logger logger = LoggerFactory.getLogger(SpringBeanLifeCycleTest.class);

    /**
     * 在scope="singleton"情况下:
     * 2023-12-31 22:09:45 404 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@422c3c7a
     * 2023-12-31 22:09:45 631 [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 2 bean definitions from class path resource [beans-lifecycle.xml]
     * 2023-12-31 22:09:45 679 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'myBeanProcessor'
     * 2023-12-31 22:09:45 713 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'phone'
     * Bean生命周期:[1 bean对象创建(调用无参构造方法)]
     * Bean生命周期:[2 为对象属性赋值(调用属性set方法)]
     * Bean生命周期:[3 bean后置处理器(在bean初始化之前执行)]
     * Bean生命周期:[4 bean对象初始化(调用bean配置时init-method属性指定的初始化方法)]
     * Bean生命周期:[5 bean后置处理器(在bean初始化之后执行)]
     * 2023-12-31 22:09:45 787 [main] INFO spring6.SpringBeanLifeCycleTest - Bean生命周期:[6 bean对象准备就绪,可以使用了]
     * 2023-12-31 22:09:45 789 [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Closing org.springframework.context.support.ClassPathXmlApplicationContext@422c3c7a, started on Sun Dec 31 22:09:45 CST 2023
     * Bean生命周期:[7 bean对象销毁(调用bean配置时destroy-method属性指定的销毁方法)]
     * 2023-12-31 22:09:45 793 [main] DEBUG org.springframework.beans.factory.support.DisposableBeanAdapter - Custom destroy method 'destroyMethod' on bean with name 'phone' completed
     */
    @Test
    public void test01(){
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-lifecycle.xml");
        Phone phone = applicationContext.getBean(Phone.class);
        logger.info("Bean生命周期:[6 bean对象准备就绪,可以使用了]");
        applicationContext.close();
    }

}
3.15 FactoryBean
public class UserInfo {
}
public class UserFactoryBean implements FactoryBean<UserInfo> {

    @Override
    public UserInfo getObject() throws Exception {
        return new UserInfo();
    }

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

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

<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="userInfo" class="com.giser.spring6.factorybean.UserFactoryBean" />

beans>
public class SpringFactoryBeanTest {

    private Logger logger = LoggerFactory.getLogger(SpringFactoryBeanTest.class);

    /**
     * FactoryBean是Spring提供的一种整合第三方框架的常用机制。
     * 和普通的bean不同,配置一个FactoryBean类型的bean,
     *      在获取bean的时候得到的并不是class属性中配置的这个类的对象,
     *      而是getObject()方法的返回值。
     * 在整合Mybatis时,Spring就是通过FactoryBean机制来帮我们创建SqlSessionFactory对象的。
     *
     * [main] DEBUG org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@14b030a0
     * [main] DEBUG org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loaded 1 bean definitions from class path resource [beans-factorybean.xml]
     * [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userInfo'
     * [main] INFO spring6.SpringFactoryBeanTest - 通过FactoryBean获取的对象为com.giser.spring6.factorybean.UserInfo@255990cc
     */
    @Test
    public void testFactoryBean(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-factorybean.xml");
        UserInfo userInfo = applicationContext.getBean(UserInfo.class);
        logger.info("通过FactoryBean获取的对象为{}", userInfo);
    }

}
3.16 自动装配

UserDao

public interface UserDao {
    void sayHello();
}
public class UserDaoImpl implements UserDao {
    @Override
    public void sayHello() {
        System.out.println("userDao sayHello");
    }
}

UserService

public interface UserService {
    void sayHello();
}
public class UserServiceImpl implements UserService {

    private UserDao userDao;

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

    @Override
    public void sayHello() {
        userDao.sayHello();
    }
}

UserController

public class UserController {

    private UserService userService;

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

    public void sayHello(){
        userService.sayHello();
    }

}

自动装配


<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.giser.spring6.autowire.controller.UserController" autowire="byName" />
    <bean id="userService" class="com.giser.spring6.autowire.service.impl.UserServiceImpl" autowire="byName" />
    <bean id="userDao" class="com.giser.spring6.autowire.dao.impl.UserDaoImpl" autowire="byName" />

beans>

测试

import com.giser.spring6.autowire.controller.UserController;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author giserDev
 * @description
 * @date 2023-12-31 22:51:15
 */
public class SpringAutowiredTest {

    @Test
    public void testAutowired(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-autowire.xml");
        UserController userController = applicationContext.getBean(UserController.class);
        userController.sayHello();
    }

}

4 Spring基于注解管理bean

从 Java 5 开始,Java 增加了对注解(Annotation)的支持,它是代码中的一种特殊标记,可以在编译、类加载和运行时被读取,执行相应的处理。Spring 从 2.5 版本开始提供了对注解技术的全面支持,我们可以使用注解来实现自动装配,简化 Spring 的 XML 配置。

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

  1. 引入依赖
  2. 开启组件扫描
  3. 使用注解定义 Bean
  4. 依赖注入
4.1 基础环境搭建

创建子模块giser-java-spring6-ioc-annotation,并引入配置文件beans.xml、log4j2.xml,添加如下依赖:

<dependencies>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-contextartifactId>
        dependency>

        <dependency>
            <groupId>org.junit.jupitergroupId>
            <artifactId>junit-jupiter-apiartifactId>
            <scope>testscope>
        dependency>

        
        <dependency>
            <groupId>org.apache.logging.log4jgroupId>
            <artifactId>log4j-coreartifactId>
        dependency>
        <dependency>
            <groupId>org.apache.logging.log4jgroupId>
            <artifactId>log4j-slf4j2-implartifactId>
        dependency>

        
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
        dependency>

        
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druidartifactId>
        dependency>

        
        <dependency>
            <groupId>jakarta.annotationgroupId>
            <artifactId>jakarta.annotation-apiartifactId>
            <version>2.1.1version>
        dependency>
    dependencies>
4.2 开启组件扫描

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


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

    
    
    <context:component-scan base-package="com.giser.spring6.anno" />
beans>

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

有三种情况:

  • 扫描包下所有组件
    
    
    <context:component-scan base-package="com.giser.spring6.anno" />
  • 可指定排除的组件
    
    <context:component-scan base-package="com.giser.spring6.anno" >
        
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    context:component-scan>
  • 仅扫描指定组件
    
    
    <context:component-scan base-package="com.giser.spring6.anno" use-default-filters="false">
        
        
        
        
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        
    context:component-scan>
4.3 使用注解定义bean
4.3.1 @Autowired

此注解可以用在构造方法上、方法上、形参上、属性上、注解上。

  • 单独使用@Autowired注解,默认根据类型装配,即默认是byType
    @Autowired
    private UserService userService;
  • 通过set方法注入
    private UserService userService;

    /**
     * 通过set方法注入
     * @param userService
     */
    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
  • 通过构造方法注入
    private UserService userService;

    /**
     * 根据构造方法注入
     * Autowiring by type from bean name 'userController' via constructor to bean named 'userServiceImpl'
     * @param userService
     */
    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }
  • 形参注入
    private UserService userService;

    /**
     * 形参上注入
     * Autowiring by type from bean name 'userController' via constructor to bean named 'userServiceImpl'
     * @param userService
     */
    public UserController(@Autowired UserService userService) {
        this.userService = userService;
    }
  • Autowired之只有一个构造函数时,无需注解也可以注入
    private UserService userService;

    /**
     * 只有一个构造函数时,无需注解也可以注入
     * Autowiring by type from bean name 'userController' via constructor to bean named 'userServiceImpl'
     * @param userService
     */
    public UserController(UserService userService) {
        this.userService = userService;
    }
  • Autowired之使用@Qualifier指定bean名称(多实现情况)
    /**
     * @Autowired默认是根据类型注入的,如果需要根据名称注入,需添加@Qualifier注解
     *
     */
    @Autowired
    @Qualifier("userDaoImpl")
    private UserDao userDao;
4.3.2 @Resource

使用@Resource注解需要引入以下坐标:


<dependency>
    <groupId>jakarta.annotationgroupId>
    <artifactId>jakarta.annotation-apiartifactId>
dependency>
    /**
     * 根据名称注入
     * 默认byName注入,没有指定name时把属性名当做name,根据name找不到时,才会byType注入。
     * byType注入时,某种类型的Bean只能有一个.
     * 当@Resource注解使用时没有指定name的时候,还是根据name进行查找,这个name是属性名
     */
    @Resource(name = "myUserService")
    private UserService userService;
4.4 Spring全注解开发

创建配置类

@Configuration
@ComponentScan(basePackages = "com.giser.spring6.anno")
public class SpringConfig {
}
@Test
public void testFullAnno(){
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
    MyUserController myUserController = applicationContext.getBean("myUserController", MyUserController.class);
    myUserController.exec();
}

你可能感兴趣的:(#,Spring,spring,java,后端)