Spring IoC

本文详细介绍了Spring框架中的IoC(控制反转)和DI(依赖注入)概念,包括基于XML、注解和JavaConfig的配置方式,以及对象的实例化、依赖关系管理和测试整合。

IOC控制反转:是一种设计理念,由代理人来创建和管理对象,消费者通过代理人来获取对象。IOC的目的是降低对象之间直接耦合。

加入IOC容器将对象统一管理,让对象关联变为弱耦合。
DI依赖注入,完成在程序运行过程中对象的创建与绑定。
DI在Java中利用反射技术实现对象注入。

Sping的含义:狭义Spring是指Spring框架,广义的Spring是指Spring生态体系。
Spring框架的核心是IOC容器和AOP面向切面编程。
Spring IOC负责创建和管理系统对象,并在此基础上扩展功能
基于配置实现应用程序的可维护性和可扩展性
在这里插入图片描述
三种配置方式:

  1. 基于XML配置Bean
  2. 基于注解配置Bean
  3. 基于Java代码配置Bean

一、基于XML配置Bean

实例化Bean的三种方式:

  1. 基于构造方法对象实例化(默认、带参)
  2. 基于静态工厂实例化
  3. 基于工厂实例方法实例化

基于构造方法对象实例化

 <!--利用默认构造方法实例化Bean-->
    <bean id="apple1" class="com.io.spring.ioc.entity.Apple">
    </bean>
<!--利用带参构造方法实例化Bean-->
    <bean id="apple2" class="com.io.spring.ioc.entity.Apple">
        <!--通过参数名实例化对象-->
        <constructor-arg name="title" value="红富士"/>
        <constructor-arg name="color" value="红色"/>
        <constructor-arg name="origin" value="欧洲"/>
    </bean>
    
    <bean id="apple3" class="com.io.spring.ioc.entity.Apple">
        <!--通过索引实例化对象-->
        <constructor-arg index="0" value="红富士"/>
        <constructor-arg index="1" value="红色"/>
        <constructor-arg index="2" value="欧洲"/>
    </bean>

基于静态工厂实例化

/**
 * 静态工厂通过静态方法创建对象,隐藏创建对象的细节
 */
public class AppleStaticFactory {
    public static Apple createSweetApple(){
        Apple apple = new Apple();
        apple.setTitle("红富士");
        apple.setOrigin("欧洲");
        apple.setColor("红色");
        return apple;
    }
}
<!--    利用静态工厂获取对象-->
    <bean id="apple4" class="com.io.spring.ioc.factory.AppleStaticFactory"
            factory-method="createSweetApple"/>

基于工厂实例方法实例化

/**
 * 工厂方法创建对象是指IOC容器对工厂类进行实例化并调用对应对实例方法创建对象的过程
 */
public class AppleFactoryInstance {
    public Apple createSweetApple(){
        Apple apple = new Apple();
        apple.setTitle("红富士");
        apple.setOrigin("欧洲");
        apple.setColor("红色");
        return apple;
    }
}
<!--    利用工厂实例方法获取对象-->
    <bean id="factoryInstance" class="com.io.spring.ioc.factory.AppleFactoryInstance"/>
    <bean id="apple5" factory-bean="factoryInstance" factory-method="createSweetApple"/>

Bean的id和name属性:
都是设置对象在IOC容器中唯一标识,两者在同一个配置文件中都不允许重复,多个配置文件中允许出现重复且新对象覆盖旧对象。
id要求更为严格,一次只能定义一个对象标识,name一次允许定义多个对象标识

<bean name="apple1,apple7" class="com.io.spring.ioc.entity.Apple"/>

在没有id和name的bean默认使用类名全称作为bean标识

路径表达式用法:
在这里插入图片描述

对象依赖注入

依赖注入是指运行时将容器内对象利用反射赋值给其他对象
对象注入的方法:

  1. 基于setter方法注入对象
<!--IOC容器自动利用反射机制运行时调用setXXX方法为属性赋值-->
<bean id="sweetApple" class="com.io.spring.ioc.entity.Apple">
        <!--利用value设置静态值-->
        <property name="title" value="红富士"></property>
        <property name="origin" value="欧洲"></property>
        <property name="color" value="红色"></property>
</bean>
<bean id="andy" class="com.io.spring.ioc.entity.Child">
       <!--利用ref注入依赖对象-->
       <property name="name" value="安迪"></property>
       <property name="apple" ref="rdApple"></property>
</bean>
  1. 基于构造方法注入对象
<bean id="andy" class="com.io.spring.ioc.entity.Child">
		<!--利用ref注入依赖对象-->
        <constructor-arg name="name" value="安迪"/>
        <constructor-arg name="apple" ref="sourApple"/>
</bean>

注入集合对象:

public class Company {
    private List<String> rooms;
    private Map<String,Computer> computers;
    private Properties info;
    ...
<bean id="company" class="com.spring.ioc.entity.Company">
        <property name="rooms">
        //生成ArrayList对象
            <list>
                <value>2001-总裁办</value>
                <value>2003-总经理办公室</value>
                <value>2010-研发部会议室</value>
            </list>
        </property>
        <property name="computers">
        //生成LinkedHashMap对象
            //<map>
            //    <entry key="dev-88172" value-ref="c1"/>
            //</map>
            <map>
            	<entry key="dev-88172">
					<bean class="com.spring.ioc.entity.Computer">
        				<constructor-arg name="brand" value="联想"/>
        				<constructor-arg name="type" value="台式机"/>
        				<constructor-arg name="sn" value="8389283012"/>
        				<constructor-arg name="price" value="3085"/>
    				</bean>
				</entry>
            </map>
        </property>
        <property name="info">
        //Properties类似于map,但是key和value都必须是String类型
            <props>
                <prop key="phone">010-12345678</prop>
                <prop key="address">XXXXXXXXX</prop>
                <prop key="website">http://www.xxx.com</prop>
            </props>
        </property>
</bean>

需要去重时使用set


public class Company {
    private Set<String> rooms;
    ...
<bean id="company" class="com.spring.ioc.entity.Company">
        <property name="rooms">
        //生成LinkedHashSet对象
            <set>
                <value>2001-总裁办</value>
                <value>2003-总经理办公室</value>
                <value>2010-研发部会议室</value>
                 <value>2010-研发部会议室</value>
            </set>
        </property>
    </bean>

运行

public class SpringApplication {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        Company company = context.getBean("company", Company.class);
        System.out.println(company);
        System.out.println(company.getInfo().getProperty("address"));
    }
}

查看容器内对象

//获取容器内所有beanId数组
        String[] beanNames = context.getBeanDefinitionNames();
        for(String beanName:beanNames){
            System.out.println(beanName);
            System.out.println(context.getBean(beanName).getClass().getName());
            System.out.println(context.getBean(beanName));
        }
        //通过类全称+标识获取匿名bean
        System.out.println(context.getBean("com.spring.ioc.entity.Computer#1", Computer.class));
        System.out.println(context.getBean("com.spring.ioc.entity.Computer#0", Computer.class));
      

bean scope属性:决定对象何时被创建与作用范围
设置bean scope属性将影响容器内对象的数量
默认情况下bean会在IoC容器创建后自动实例化,全局唯一

scope="prototype" //允许存在多个实例
在这里插入图片描述
在这里插入图片描述

bean的生命周期:
在这里插入图片描述

创建对象-设置属性-执行init()方法-执行业务方法-执行destroy()方法释放对象相关资源

//执行销毁IOC容器的方法
((ClassPathXmlApplicationContext)context).registerShutdownHook();

二、基于注解配置IoC容器

基于注解配置IoC容器

优势:
摆脱繁琐的XML形式的bean与依赖注入配置
基于“声明式”的原则,更适合轻量级的现代企业应用
让代码可读性变好,研究人员拥有更好的开发体验

三类注解:

  • 组件类型注解-声明当前类的功能和职责
  • 自动装配注解-根据属性特征自动注入对象
  • 元数据注解-更细化的辅助IoC容器管理对象的注解

四类组件类型注解:
在这里插入图片描述

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

    <!--在IoC容器初始化时自动扫描四种组件类型注解完成实例化
        @Repository
        @Service
        @Controller
        @Component
    -->
    <context:component-scan base-package="spring.ioc"/>

</beans>

两类自动装配注解:
在这里插入图片描述

	@Autowired
    //Spring IoC容器会自动通过反射技术将属性private修饰符自动改为public,直接进行赋值
    //不再执行set方法
    private UserDao udao;

	//@Autowired
    //如果装配注解放在set方法上,则自动按类型/名称对set方法参数进行注解
    //所以一般无set方法
    //public void setUdao(UserDao udao) {

@Autowired默认按照类型装配,默认情况下要求依赖对象必须存在。

使用@Autowired进行属性注入时,如果该类型有多个实现类,可以在一个实现类上使用@Primary,表示优先使用该类。

 /**
     * 1.@Resource设置name属性,则按照name在IoC容器中将bean注入
     * 2.@Resource未设置name属性
     * 2.1 以属性名作为bean name在IoC容器中匹配bean,如有匹配到就注入
     * 2.2 按属性名未匹配,则按类型进行匹配,同@Autowired,需要加入@Primary解决类型冲突
     * 使用建议:在使用@resource对象时推荐设置name,或保证属性名与bean name一致
     */
    //@Resource(name="userOracleDao")
    //private IUserDao udao;
    @Resource
    private IUserDao udao;

    public void joinDepartment(){
        System.out.println(udao);
    }

@Resource有两个属性:name和type,Spring将@Resource注解的name属性解析为bean的名称,type属性解析为bean的类型,默认按照名称进行装配,名称可以通过name属性进行指定。

@Resource和@Autowired的区别:

  • 都可以用来装配bean,都可以写在字段或setter方法上。
  • @Resource默认按照名称装配,如果没有指定name属性,注解写在字段上,默认时取字段名作为名称查找,如果注解写在setter方法上默认是取属性的名称进行装配,当找不到名称匹配的bean时才按照类型进行匹配,如果name属性一旦指定,只会按照名称装配。
  • @Resource的装配顺序:(1)如果同时指定了name和type,则寻找唯一匹配的bean进行装配;(2)如果指定了name,则查找名称(id)匹配的bean进行装配;(3)如果指定了type,查找类型匹配的唯一bean进行装配,找不到或者找到多个都抛出异常;(4)如果没指定name或type,自动按照byName方式装配,没匹配到按照原始类型匹配
  • @Autowired允许null值,@Autowired(requied=false)
  • @Autowired使用名称装配要结合@Qualifier,@Autowired @Qualifier(“test”),解决@Service重名问题。

元数据注解:
在这里插入图片描述

@Primary
@Scope("prototype") //设置单例/多例,和XML中bean scope完全相同
public class UserService {
	@Value("${metadata}") //读取config.properties的metadata属性值
    private String metaData;
	@PostConstruct //XML中bean init-method完全相同
    public void init(){
        System.out.println("初始化UserService对象,metaData="+metaData);
    }
}

@Value使用
1)创建配置文件config.properties,设置属性名=属性值
2)在applicationContext.xml中通过placeholder加载配置文件
<context:property-placeholder location="classpath:config.properties"/>
3)在代码中使用@Value("${属性名}")来给属性设置静态值

三、基于Java Config配置IoC容器

优势:完全摆脱XML的束缚,使用独立Java类管理对象与依赖。
注解配置相对分散,利用Java Config可对配置集中管理。
可以在编译时进行依赖检查,不容易出错。

Java Config核心注解:
在这里插入图片描述

@Configuration //说明当前类是一个配置类,用于替代applicationContext.xml
@ComponentScan(basePackages = "spring.ioc")
public class Config {
    @Bean //Java Config利用方法创建对象,将方法返回对象放进容器,beanId=方法名
    @Primary
    public UserDao userDao(){
        UserDao userDao = new UserDao();
        System.out.println("已创建"+userDao);
        return userDao;
    }
    @Bean
    //通过参数进行依赖注入
    //先按name尝试注入,name不存在时按类型注入
    public UserService userService(UserDao userDao, EmployeeDao employeeDao){
        UserService userService = new UserService();
        System.out.println("已创建"+userService);
        userService.setUserDao(userDao);
        System.out.println("调用setUserDao:"+userDao);
        System.out.println("调用employeeDao:"+employeeDao);
        return userService;
    }
    @Bean
    @Scope("prototype")
    public UserController userController(UserService userService){
       UserController userController = new UserController();
       System.out.println("已创建"+userController);
       userController.setUserService(userService);
        System.out.println("调用setUserService:"+userService);
       return userController;
    }
}

Spring Test测试模块

Spring Test对JUnit单元测试框架有良好的整合。
通过Spring Test可以在JUnit单元测试时自动初始化IoC容器。

Spring与JUnit4整合过程:

  1. Maven工程依赖spring-test和junit
  2. 利用@RunWith和@ContextConfiguration描述测试用例类
  3. 测试用例类从容器获取对象完成测试用例执行
@RunWith(SpringJUnit4ClassRunner.class) //将Junit4的执行权交给Spring Test,在测试用例执行前自动初始化IoC容器
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})//在IoC容器初始化过程中,通知要加载那个配置文件
public class SpringTestor {
    @Resource
    private UserService userService;
    @Test
    public void testUserService(){
        userService.createUser();
    }
}
### Spring IOC 原理及实现 #### 1. 控制反转(IOC)的概念 控制反转(Inversion of Control,简称 IOC)是一种设计思想,旨在将对象的创建和管理从应用程序代码中分离出来。通过 IOC,开发者无需手动管理对象的生命周期和依赖关系,而是由框架负责这些任务。这种思想使得开发者可以专注于业务逻辑的实现,而不需要关心对象之间的交互细节[^1]。 #### 2. 依赖注入(DI) 依赖注入(Dependency Injection,简称 DI)是实现 IOC 的一种方式。DI 的核心在于通过外部配置或注解的方式,将对象的依赖关系注入到目标对象中,而不是由对象自己负责依赖的创建和管理。依赖注入有三种主要形式:构造器注入、Setter 方法注入和字段注入[^4]。 #### 3. Spring IOC 容器的核心接口 Spring 框架提供了两种主要的 IOC 容器接口: - **BeanFactory**:这是 Spring 框架的基础接口,面向 Spring 内部使用。它负责配置、创建和管理 Bean 的生命周期。 - **ApplicationContext**:这是一个更高级的容器接口,面向开发者使用。它扩展了 BeanFactory 的功能,提供了更多的企业级特性,如国际化支持、事件传播等。在实际开发中,通常使用 ApplicationContext 而非 BeanFactory[^2]。 #### 4. Spring IOC 的实现方式 Spring IOC 的实现主要包括以下几个方面: ##### (1) 元数据解析 Spring 使用 XML 文件、Java 注解或 Java 配置类来定义 Bean 的元数据。例如,通过 `<bean>` 标签在 XML 文件中定义 Bean,或者使用 `@Component`、`@Service` 等注解在 Java 类上声明 Bean[^5]。 示例代码(XML 配置): ```xml <bean id="userDao" class="com.mayuanfei.springioc.dao.impl.UserDaoImpl" /> ``` 示例代码(注解配置): ```java @Component public class UserDaoImpl implements UserDao { // 实现方法 } ``` ##### (2) 依赖注入 Spring 通过反射机制解析 Bean 的依赖关系,并将其注入到目标对象中。以下是一个简单的依赖注入示例: 示例代码(字段注入): ```java @Service public class UserService { @Autowired private UserDao userDao; // 自动注入 UserDao } ``` ##### (3) 生命周期管理 Spring 容器会管理 Bean 的整个生命周期,包括初始化、运行时和销毁阶段。开发者可以通过实现特定的接口(如 `InitializingBean` 和 `DisposableBean`)或使用注解(如 `@PostConstruct` 和 `@PreDestroy`)来定制 Bean 的生命周期行为。 示例代码(生命周期管理): ```java @Component public class MyBean { @PostConstruct public void init() { System.out.println("Bean 初始化"); } @PreDestroy public void destroy() { System.out.println("Bean 销毁"); } } ``` ##### (4) 扩展机制 Spring 提供了丰富的扩展点,允许开发者自定义容器的行为。例如,通过实现 `BeanFactoryPostProcessor` 或 `BeanPostProcessor` 接口,可以在 Bean 初始化前后插入自定义逻辑[^4]。 #### 5. Spring IOC 的优势 Spring IOC 的核心优势包括: - **解耦**:对象之间不再直接依赖具体实现,而是通过接口或抽象类进行交互。 - **灵活性**:通过配置文件或注解动态管理 Bean,便于修改和扩展。 - **可维护性**:集中处理对象的创建和依赖关系,降低了代码复杂度。 ### 总结 Spring IOC 是一种通过容器管理对象生命周期和依赖关系的设计模式。其核心原理包括元数据解析、依赖注入、生命周期管理和扩展机制。通过这些机制,Spring 实现了对象控制权的反转,从而提高了系统的解耦性、灵活性和可维护性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值