【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

相关推荐
IT_陈寒1 小时前
Vue的这个响应式陷阱,我debug了一整天才爬出来
前端·人工智能·后端
tq10861 小时前
数学:约束表征空间的最小闭包
笔记
兔子零10241 小时前
手把手教你在 Claude Code 中接入 DeepSeek-V4
后端
掌心向暖RPA自动化2 小时前
如何获取网页某个元素在屏幕可见部分的中心坐标影刀RPA懒加载坐标定位技巧
java·javascript·自动化·rpa·影刀rpa
日取其半万世不竭2 小时前
Minecraft Java版社区服务器搭建教程(Linux,适合新手)
java·linux·服务器
phenhorlin2 小时前
我做了个工具,让切换 Homebrew 镜像像切 npm 源一样简单
后端·shell
6952 小时前
两周浅学 RAG
后端
TeamDev2 小时前
JxBrowser 9.0.0 版本发布啦!
java·前端·混合应用·jxbrowser·浏览器控件·跨平台渲染·原声输入
freexyn3 小时前
Matlab自学笔记七十六:表达式的展开、因式分解、化简、合并同类项
笔记·算法·matlab
AI人工智能+电脑小能手3 小时前
【大白话说Java面试题】【Java基础篇】第24题:Java面向对象有哪些特征
java·开发语言·后端·面试