Spring IOC的详解

一、前言

  学习spring boot框架,了解到@Bean、@Componet等注解。通过注解的使用拓展到Spring IOC内容,在Spring中Spring IOC、Spring AOP都是至关重要的模块,所以本章就具体介绍Spring IOC,从Spring IOC的通俗的定义->具体的实践->内在的原理->思考总结完成整个学习内容。

二、定义

  Spring IOC是Spring框架的重要技术,其中IOC是控制反转(技术思想)的意思,核心就是将new对象的操作交由IOC容器去帮助我们完成,包括创建实例化对象并管理它,我们需要使用哪个对象,去IOC容器获取即可。控制指的是对象创建(实例化、管理)的权力,反转指的是控制权交给了外部环境(IOC容器)。解决的问题是面向对象编程中对象之间的耦合问题,目标是设计成高内聚低耦合的系统。

  上述图形化的方式解释了IOC设计思想,借助于第三方(IOC)实现具有依赖关系的对象之间的解耦,使用的A、B、C、D四个对象不在相互交互,没有耦合关系(在代码中对象间使用new创建关联对象)。对象之间的传动全部依靠IOC容器,全部对象的控制权上交给IOC容器,所以IOC容器成了整个系统的关键核心,它起到一个“粘合剂”的作用,把系统中所有对象粘合在一起发挥作用。IOC本质是大容器、大工程,主要作用是创建和管理对象的依赖关系,削减计算机程序的耦合,提供程序的可扩展和维护性。

三、实践

  第一步、在IDEA创建一个spring boot项目,使用Maven工程管理项目的包依赖关系,引入相关的springframework包、XML读取的dom4j包、单元测试包,pom.xml如下代码所示。

复制代码
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--引入dom4J-->
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>

    </dependencies>
复制代码

  第二步、在项目中新增各个层级目录文件夹或文件,比如控制层Controller、服务层Service、持久化层Dao、领域层Domain、ORM层Mapper等文件夹,创建UserController、IUserService、UserServiceImpl、IUserDao、UserDaoImpl、User、UserMapper。

复制代码
package com.test.springioc.controller;

import org.springframework.stereotype.Controller;

/**
 * 控制器
 */
@Controller
public class UserController {
}
复制代码
复制代码
package com.test.springioc.domain;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 领域对象
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String name;
    private Integer sex;
}
复制代码
复制代码
package com.test.springioc.service;

import com.test.springioc.domain.User;

/**
 * 服务层接口
 */
public interface IUserService {
    int Save(User user);
}


package com.test.springioc.service.impl;

import com.test.springioc.domain.User;
import com.test.springioc.service.IUserService;
import org.springframework.stereotype.Service;

/**
 * 服务层实例
 */
@Service
public class UserServiceImpl implements IUserService {
    @Override
    public int Save(User user) {
        System.out.println("UserService save method running...... ");
        return 0;
    }
}
复制代码
复制代码
package com.test.springioc.dao;

import com.test.springioc.domain.User;

/**
 * 持久化层接口
 */
public interface IUserDao {
    int save(User user);
}

package com.test.springioc.dao.impl;

import com.test.springioc.dao.IUserDao;
import com.test.springioc.domain.User;
import org.springframework.stereotype.Repository;

/**
 * 持久化层实例
 */
@Repository
public class UserDaoImpl implements IUserDao {
    @Override
    public int save(User user) {
        System.out.println("userDao save method running...... ");
        return 0;
    }
}
复制代码
复制代码
package com.test.springioc.mapper;

import com.test.springioc.domain.User;

/**
 * ORM层mybits-plus
 */
public interface IUserMapper {
    int save(User user);
}
复制代码

   第三步、新增Spring配置文件,可以通过XML或者注解的方式来定义Bean实例,本文通过XML的方式来获取容器中的Bean,文件all.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">
    <!--1、使用无参构造函数构建Bean-->
    <!--配置userDaoImpl-->
    <bean id="userDao" class="com.test.springioc.dao.impl.UserDaoImpl"></bean>
    <!--配置userServiceImpl-->
    <bean id="userService" class="com.test.springioc.service.impl.UserServiceImpl"></bean>

    <!--2、使用静态方法构建Bean-->
    <bean id="userServiceStaticFactory" class="com.test.springioc.factory.MyBeanFactory" factory-method="getUserService"></bean>

    <!--3、使用实例方法构建Bean-->
    <bean id="instanceFactory" class="com.test.springioc.factory.InstanceFactory"></bean>
    <bean id="userServiceMethodFactory" factory-bean="instanceFactory" factory-method="getUserService"></bean>
</beans>
复制代码

  在Bean标签中通过scope属性定义Bean的作用范围及生命周期,包括①singleton:单例模式,在容器中仅有一个实例对象,IOC创建的对象默认都是单例的;②prototype:原型模型(多例模式),每次getBean都会创建一个新的对象;③request:(web环境)每个request请求维护一个实例session(web环境)每个;④session:会话维护一个实例。单例模式:单例模式bean对象生命周期与容器相同;多例模式:每次使用对象都重新创建一个新对象,Spring框架只负责创建,销毁由JAVA垃圾回收器负责。

  在Bean标签中常用的属性包括①id属性:bean对象的唯一标识;class属性:类全限定名;②name属性:类名称,可以重复;③factory-bean属性:用于指定创建当前bean对象的工厂方法,如配合factory-bean属性使用, 则class属性失效。如配合class属性使用,则用法必须是static的;④scope:指定bean对象的作用范围,默认是singleton;⑤init-method属性:指定bean对象的初始化方法,此方法会在bean对象装配后调用,该方法必须是一个无参方法;⑥destory-method属性:用于指定bean对象的销毁⽅法,此⽅法会在bean对象销毁前执行。它只 能为scope是singleton时起作用。

  第三步、在测试单元中测试获取配置的实例,主要通过三种方式实例化Bean,①使用无参的构造函数,在默认情况下,通过反射调用无参构造函数来创建对象,如果类中没有无参构造函数,则创建对象失败。

复制代码
    @Test
    public void TestBean()
    {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("all.xml");
        User user = User.builder().id(1).name("test").sex(1).build();

        IUserDao userDao = (IUserDao) applicationContext.getBean("userDao");
        userDao.save(user);

        IUserService userService = (IUserService) applicationContext.getBean("userService");
        userService.Save(user);

        IUserMapper userMapper = (IUserMapper) applicationContext.getBean("userMapper");
        userMapper.save(user);
    }
复制代码

  ②使用静态方法构建Bean。

复制代码
package com.test.springioc.factory;

import com.test.springioc.service.IUserService;
import com.test.springioc.service.impl.UserServiceImpl;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Bean创建工厂
 */
public class MyBeanFactory {
    /**
     * 使用静态方法创建Bean
     * @return
     */
    public static IUserService getUserService(){
        return new UserServiceImpl();
    }
}
复制代码
@Test
    public void TestFactoryStaticMethodBean()
    {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("all.xml");
        User user = User.builder().id(1).name("test").sex(1).build();
        IUserService userService = (IUserService) applicationContext.getBean("userServiceStaticFactory");
        userService.Save(user);
    }

  ③使用实例方法构建Bean。

复制代码
package com.test.springioc.factory;

import com.test.springioc.service.IUserService;
import com.test.springioc.service.impl.UserServiceImpl;

/**
 * 模拟一个工厂类(该类可能是存在于jar包中的,我们无法通过修改源码的方式来提供默认构造函数)
 */
public class InstanceFactory {
    /**
     * 实例方法构建Bean
     * @return
     */
    public IUserService getUserService() {
        return new UserServiceImpl();
    }
}
复制代码
  @Test
    public void TestFactoryInstaceMethodBean()
    {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("all.xml");
        User user = User.builder().id(1).name("test").sex(1).build();
        IUserService userService = (IUserService) applicationContext.getBean("userServiceMethodFactory");
        userService.Save(user);
    }

  第四步、自定义一个简易的IOC容器,基于Spring IOC的构建Bean的原理,通过Bean的Id获取实例对象。①声明一个Bean的test.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">
    <!--配置userDaoImpl-->
    <bean id="userDao" class="com.test.springioc.dao.impl.UserDaoImpl"></bean>
</beans>

  ②定义一个静态工厂方法,设置对象容器,遍历XML文件Bean声明,创建相关的对象实例,使用Id,对象的方式Map存储,提供静态方法获取对象实例,工厂类代码如下所示。

复制代码
package com.test.springioc.factory;

import com.test.springioc.service.IUserService;
import com.test.springioc.service.impl.UserServiceImpl;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Bean创建工厂
 */
public class MyBeanFactory {
    private static Map<String,Object> beanMap =new HashMap<>();
    static {
        try {
            SAXReader saxreader = new SAXReader();
            String xml = "src/main/resources/test.xml";
            Document doc = saxreader.read(xml);

            Element rootElement = doc.getRootElement();
            List<Element> BeanList = rootElement.elements("bean");
            for (Element element :BeanList){
                String id1 = element.attributeValue("id");
                String class1=element.attributeValue("class");
                Class clz = Class.forName(class1);
                Object object = clz.newInstance();
                beanMap.put(id1,object);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public static Object getBean(String id){
        Object o = beanMap.get(id);
        return o;
    }

    /**
     * 使用静态方法创建Bean
     * @return
     */
    public static IUserService getUserService(){
        return new UserServiceImpl();
    }
}
复制代码

  ③在单元测试中获取对象实例。

复制代码
/**
     * 自定义容器、构建Bean
     */
    @Test
    public void testMyFactory(){
        //1:创建工厂对象
        MyBeanFactory factory =new MyBeanFactory();
        //2:从容器当中根据id获得对象
        IUserDao userDao =  (IUserDao)factory.getBean("userDao");
        System.out.println(userDao);
        User user = User.builder().id(1).name("test").sex(1).build();
        userDao.save(user);
    }
复制代码

四、总结

  通过上述定义、实践学习了Spring IOC,使用IOC技术在面向对象编程中实现对象间解耦,提供代码可扩展、可维护行。在实际开发中使用XML的方式被注解所代替,减少XML的配置化工作。基于IOC容器的控制反转技术在不同的高级语言都提供开源或自带的框架,比如JAVA的Spring IOC、.NET的Autofac、.NET Core自带的依赖注入等。

posted @   tuqunfu  阅读(125)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示