【Spring】分别基于XML、注解和配置类实现Spring的IOC(控制反转)

目录

1、理解loC是什么

2、基于XML实现Spring的IOC(这种方式已经不怎么使用了)

3、基于注解实现Spring的IOC

4、基于javaConfig实现Spring的IOC

5、总结

1、理解loC是什么

**lOC:**lnversion of Control 控制反转,简称就是 IOC

控制反转:控制(对象创建权利)被反转

控制反转通过依赖注入(DI)方式实现对象之间的松耦合关系。程序运行时,依赖对象由辅助程序动态生成并注入到被依赖对象中,动态绑定两者的使用关系。Spring IoC 容器就是这样的辅助程序,它负责对象的生成和依赖的注入,然后再交由我们使用。

依赖注入(DI)Dependency Injection,它是 spring 框架核心 ioc 的具体实现。其作用是去除Java类之间的依赖关系,实现松耦合,以便于开发测试。
假设我现在有两个java类,UserService 和 UserDao,然后 UserService 中有依赖 UserDao,这时候创建 UserService 有如下两种情况:

用IOC之前:由程序员控制创建对象

自己创建对象:UserService(new UserDao())---->UserDao;这种方式具有强耦合,再小的代码变更都会引起BUG的可能性

比如在UserService类中要用到UserDao类,就需要在UserService类中创建UserDao类的对象,这就会造成类之间的强耦合。写代码的耦合度越高,对后期的维护就越不利,比如说以后要把UserDao类给换掉的话,就必须来到UserService类中把UserDao类的对象代码给换掉,如果有几十处,就要改几十处,但是再小的代码变更都会引起BUG的可能性

用IOC之后:创建对象依赖Spring注入(DI)

Spring(new UserService(); new UserDao();) 由Spring来创建UserService类和UserDao类的对象,如果UserService中要用到UserDao就让Spring把UserDao注入进来

如果需要用到Spring管理的对象,需要依赖Spring注入(DI)
loC/DI优点: 解耦,管理好对象的创建和依赖,总的来说就是统一管理对象。某一个对象只会创建一次,可以节省内存的开支,这属于设计模式当中的一种,设计模式有23种 ,这种属于单例设计模式,Sprig的管理对象就用到了这个单例设计模式,但是我们自己也可以把他改成每一次用到都给他创建一次

没有引入Spring降低耦合度的方式:

这种方式就是,创建一个接口,通过把将来可能要换的类去实现这个接口,使用时都是用这个接口的实现类,当需要把这个实现类换掉的时候,就只需要再创建这个接口的实现类,然后把之前的那个实现类换成这个新创建的实现类就可以了

示例:

java 复制代码
//定义接口
public interface IUserDao {
    //执行查询用户
    void getUser();
}

//实现IUserDao接口的类,这个类在UserService中需要用到
public class UserDao implements IUserDao{
    @Override
    //执行查询用户
    public void getUser() {
        System.out.println("Holle Spring");
    }
}

//UserService中需要用到UserDao,但是要降低和UserDao之间的耦合度
public class UserService {
    //用IUserDao来接收UserDao的实现类
    //这样当以后要把UserDao换成其他实现了IUserDao的类
    //就只需要把实现类(也就是现在UserDao的位置)改成要换的那个IUserDao实现类就可以了
    IUserDao userDao = new UserDao();
    //比如要换成User2Dao,只需要改成:IUserDao userDao = new User2Dao();就可以了
    public void getUser() {
        userDao.getUser();
    }
}

这种方式确实能够降低耦合度,但还是需要修改代码,而通过Spring的loC/Dl就可以实现不需要修改代码来降低耦合度
**bean:**在 Spring 中,构成应用程序主干并由 Spring IoC 容器管理的对象称为 bean。bean 是由Spring IoC 容器实例化、组装和管理的对象。

2、基于XML实现Spring的IOC(这种方式已经不怎么使用了)

通过在 xml 文件中用 <bean class=""> 这个标签的方式来进行显式的声明要配置成bean的类,生成 applicationContext.xml 文件步骤,但是要在pom.xml文件中引入spring-context依赖才会出现生成 applicationContext.xml 文件这一选项

bean 标签: 在 Spring 的 XML 配置文件中配置一个 bean 标签 ,该标签最终会被加载为一个 BeanDefition对象(描述对象信息),这个标签里面就用来描述要配置成bean的那个类的信息,这个标签里面像 ref、name 这些属性如果感兴趣的话可以去查阅一下,因为这种方式不怎么用了,所以这里就演示一下大概的流程

java 复制代码
//pom.xml文件中需要引入的依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.1.2</version>
</dependency>


//applicationContext.xml 文件配置
//在我的项目中,我把applicationContext.xml文件命名为spring.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">

    <!--让Spring帮我们去new对象,对象之间的依赖也让spring帮我们进行组织-->
    <bean class="com.lt.service.UserService">
        <!--进行注入-->
        <property name="userDao" ref="userDao"></property>
    </bean>

    <bean class="com.lt.dao.UserDao" name="userDao"></bean>
    <bean class="com.lt.dao.User2Dao" name="user2Dao"></bean>
</beans>


//创建接口IUserDao
public interface IUserDao {
    //执行查询用户
    void getUser();
}


//接口IUserDao的实现类UserDao
public class UserDao implements IUserDao{
    @Override
    //执行查询用户
    public void getUser() {
        System.out.println("Holle Spring");
    }
}


//接口IUserDao的实现类User2Dao
public class User2Dao implements IUserDao{
    @Override
    //执行查询用户
    public void getUser() {
        System.out.println("Holle Spring 222");
    }
}


//创建接口IUserService
public interface IUserService {
    void getUser();
}


//接口IUserService的实现类UserService
//因为这种方式是基于get和set属性来进行注入的,所以要把UserService类的get和set方法声明一下
public class UserService implements IUserService{
    //耦合度过高 依赖spring注入(DI)
    IUserDao userDao;

    public IUserDao getUserDao() {
        return userDao;
    }

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

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

//测试类
public class Test01 {
    @Test
    public void test(){
        //UserService userService = new UserService();
        //如果是自己new一个UserService,Spring是不会为这个自己new的UserService注入userDao的
        //要依赖spring注入,就需要从spring容器中获取UserService
        ClassPathXmlApplicationContext ico = new ClassPathXmlApplicationContext("spring.xml");
        IUserService service = ico.getBean(IUserService.class);
        service.getUser();
    }
}

注入UserDao时的运行结果

注入User2Dao时的运行结果

3、基于注解实现Spring的IOC

通过 @component注解也可以将一个类声明为 Bean通过这种方式就不需要再在applicationContext.xml文件来进行复杂的添加每一个需要被配置成bean的类了

java 复制代码
//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">

<!--告诉spring注解所在的包在哪,在com.lt包下面的包-->
<context:component-scan base-package="com.lt"></context:component-scan>

</beans>



//IUserDao是一个接口
public interface IUserDao {
    //执行查询用户
    void getUser();
}



@Component  //标识当前类交给spring去new(实例化),交给spring进行管理,spring组件-bean
public class UserDao implements IUserDao{
    @Override
    //执行查询用户
    public void getUser() {
        System.out.println("Holle Spring");
    }
}



//@Component  //标识当前类交给spring去new(实例化),交给spring进行管理,spring组件-bean
public class User2Dao implements IUserDao{
    @Override
    //执行查询用户
    public void getUser() {
        System.out.println("Holle Spring 222");
    }
}


public interface IUserService {
    void getUser();
}


@Component
public class UserService implements IUserService{
    @Autowired  //让spring自动注入进来
    IUserDao userDao;

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


public class Test01 {
    @Test
    public void test(){
        //UserService userService = new UserService();
        //如果是自己new一个UserService,Spring是不会为这个自己new的UserService注入userDao的
        //要依赖spring注入,就需要从spring容器中获取UserService
        ClassPathXmlApplicationContext ico = new ClassPathXmlApplicationContext("spring.xml");
        IUserService service = ico.getBean(IUserService.class);
        service.getUser();
    }
}

想要注入哪一个类就在那一个类上添加@Component注解

运行结果

运行结果

4、基于javaConfig实现Spring的IOC

通过javaConfig(配置类)这种方式就不需要再在applicationContext.xml文件来配置要扫描的包是哪一个了,我们可以新建一个java类(javaConfig类)来代替这个xml文件,创建这个JavaConfig类,这里我把命名为SpringCopnfig,放在config文件下

ClassPathXmlApplicationContext 是基于xml配置的容器,这种方式不用xml文件了,所以用来获取对象的Spring容器也需要更改,这里需要用 AnnotationConfigApplicationContext 这个容器

然后就运行test02这个方法

**相关代码:**除了javaconfig(配置类)和测试方法的代码,其他代码没改动,和上面基于注解实现Spring的IOC中的代码一样

java 复制代码
/*这个配置类就用来代替xml*/
@Configuration // = xml的配置文件
@ComponentScan("com.lt") //这个注解告诉spring要扫描的包在哪里,等于在xml文件中配置的 <context:component-scan 标签
public class SpringConfig {

}


public class Test01 {
    
    @Test  //测试基于 XML 实现Spring的IOC的方法
    public void test(){
        //如果是自己new一个UserService,Spring是不会为这个自己new的UserService注入userDao的
        //要依赖spring注入,就需要从spring容器中获取UserService
        //(我这里把获得的容器命名为 ioc)
        ClassPathXmlApplicationContext ico = new ClassPathXmlApplicationContext("spring.xml");
        IUserService service = ico.getBean(IUserService.class);
        service.getUser();
    }

    @Test  //测试基于 javaconfig 实现Spring的IOC的方法
    public void test02(){
        //如果是自己new一个UserService,Spring是不会为这个自己new的UserService注入userDao的
        //要依赖spring注入,就需要从spring容器中获取UserService
        //从spring容器中获取UserService(我这里把获得的容器命名为 ioc)
        AnnotationConfigApplicationContext ico = new AnnotationConfigApplicationContext(SpringConfig.class);
        IUserService service = ico.getBean(IUserService.class);
        service.getUser();
    }
}

5、总结

**1.、spring1版本 纯xml文件的方式:**这种方式中,如果要把某个类交给Spring帮我们管理,帮我们去 new 这个对象,那就需要把这个类在 xml 文件中配置成相应的 bean,如果要配置的 bean 非常的多的话,那这个 xml 文件的信息就会非常的多,也不利于我们后期的一个维护,开发的效率也非常的低(因为单单去编写这个 xml 文件都要花费许多时间)
**2、spring2版本 xml文件 + @(注解)的方式:**在这种方式中,我们只需要在 xml 中配置一个扫描包,然后告诉他扫描的包路径,然后他就会通过这个包路径去扫描这个包下面所有的类,一旦发现类里面有个 @component 的注解,那他就会把当前这个类交给 spring 去管理,也就是把当前这个类配置成一个 bean (就是帮我们去 new 这个类的对象)。这中方式下就不需要像 纯xml 的方式那样去编写大量配置 bean 的信息了

**3、spring3版本 javaconfig(配置类) + @(注解)的方式:**这种方式中,只需要一个 javaconfig(配置类)就可以代替掉之前的 xml 配置文件

【java多线程】通过等待唤醒机制、局部变量、原子变量实现线程同步-CSDN博客https://blog.csdn.net/m0_65277261/article/details/137122461?spm=1001.2014.3001.5501

【java多线程】线程同步问题:用同步代码块、同步方法和重入锁实现线程同步-CSDN博客https://blog.csdn.net/m0_65277261/article/details/137022053?spm=1001.2014.3001.5501【Java多线程】多线程的三种实现方式和多线程常用方法-CSDN博客https://blog.csdn.net/m0_65277261/article/details/136961604?spm=1001.2014.3001.5501

相关推荐
_oP_i44 分钟前
Pinpoint 是一个开源的分布式追踪系统
java·分布式·开源
mmsx1 小时前
android sqlite 数据库简单封装示例(java)
android·java·数据库
武子康1 小时前
大数据-258 离线数仓 - Griffin架构 配置安装 Livy 架构设计 解压配置 Hadoop Hive
java·大数据·数据仓库·hive·hadoop·架构
豪宇刘2 小时前
MyBatis的面试题以及详细解答二
java·servlet·tomcat
秋恬意2 小时前
Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别
java·数据库·mybatis
刘大辉在路上3 小时前
突发!!!GitLab停止为中国大陆、港澳地区提供服务,60天内需迁移账号否则将被删除
git·后端·gitlab·版本管理·源代码管理
Aileen_0v03 小时前
【AI驱动的数据结构:包装类的艺术与科学】
linux·数据结构·人工智能·笔记·网络协议·tcp/ip·whisper
FF在路上3 小时前
Knife4j调试实体类传参扁平化模式修改:default-flat-param-object: true
java·开发语言
真的很上进3 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
众拾达人4 小时前
Android自动化测试实战 Java篇 主流工具 框架 脚本
android·java·开发语言