(待更!)1、Spring核心概念(bean、DI依赖注入)+IoC控制反转的相关实现和优劣对比

Bean(对象):

其实就是Spring框架对于java中“对象”这个概念的另一个说法。当然,不是代码中所有的对象都是bean,只有被IoC容器管理的才叫Bean。

DI(依赖注入):

Dependency Injection,称为依赖注入。最常见的是,一个对象A中某个属性是对象B,那么我们称对象A依赖对象B,创建对象A时需要注入对象B,这叫依赖注入。

(当然,依赖注入并不仅限于属性的注入,还可以包括方法参数的注入等)

IoC(控制反转)

背景引入

传统的对象的控制权是开发者拥有的。开发者需要自己创建(常说的new一个对象),自己调用,自己销毁。但是一旦程序代码量多了,体量变大,便会出现如下问题:

1、数量庞大:手动创建,手动调用的对象很多,代码实现很多。

2、位置不定:在代码中的任何一个地方都可能存在创建、调用对象的情况,后续更改维护困难。

3、回收麻烦:开发者需要自己销毁或回收对象,去管理它的生命周期。

4、手动注入:在多层依赖注入场景下,需要手动处理递归依赖关系。

所以Spring框架提供了这么一种机制,叫IoC,Inversion Of Control,控制反转。

即开发者不再自己创建、销毁、管理对象,而是它自己提供一个IoC容器,只要开发者在创建项目工程时导入相应的启动器或者依赖,这个容器便自动创建相关需要使用的对象。开发者要用时从容器中获取相关对象即可。

原始代码

这个机制的实现方式分为两种,基于XML实现和基于注解实现。

我们以一个传统的数据库配置对象为例,传统手动创建的结构如下,一个空的maven项目。

pom文件引入依赖坐标:

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
        </dependency>
    </dependencies>

DataConfig类代码如下:

package org.exmaple.ioc;

import lombok.Data;

@Data
//注意这个@Data只是帮我们自动创建setter和getter方法等,是lombok依赖的相关注解,和Ioc没有任何关系
public class DataConfig {
    private String url;
    private String drivername;
    private String username;
    private String password;
}

Main类代码如下:

package org.exmaple.ioc;

public class Main {
    public static void main(String[] args) {
        DataConfig dataConfig = new DataConfig();
        dataConfig.setUrl("localhost:3306");
        dataConfig.setDrivername("mysql");
        dataConfig.setUsername("root");
        dataConfig.setPassword("123456");
        System.out.println(dataConfig);
    }
}

运行Main类代码,我们可以发现这个对象的实例被创建且成功打印。

基于xml实现(代码1)

整体项目结构如下:

其中spring.xml的创建过程如下,选择Spring配置。

(注意这个选项是需要先完成下面pom.xml相关坐标导入之后才能看见的)

(一般都命名为applicationContext.xml。这里为了展示方便命名成spring.xml的hhh)

首先我们需要在pom文件中引入IoC需要的依赖坐标,因为IoC容器本身的实现也是底层的一段程序,那么它的执行肯定也需要依赖程序的上下文,所以需要提供spring上下文的相关依赖。

        <!--        引入spring上下文的依赖坐标。-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.1.3</version>
        </dependency>

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

    <bean class="org.exmaple.ioc.DataConfig" id="config">
        <property name="url" value="localhost:3306"></property>
        <property name="drivername" value="mysql"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>
</beans>

Main类代码如下:

package org.exmaple.ioc;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        System.out.println(context.getBean("config"));
    }
}

最后运行Main程序,我们可以发现这个对象的实例被创建且成功打印。

代码详解

观察spring.xml代码,我们发现,其实他就相当于用xml语言去实现new方法和set方法

1、我们的new对象,是用new+类名从而找到类,然后创建类的实例。譬如原始代码的new DataConfig()

而xml中是通过class="类的全路径"从而找到类,然后创建类的实例。譬如代码1的

class="org.exmaple.ioc.DataConfig"

2、而创建类的实例后,我们需要用一个变量去接收它,所以创建一个变量并给变量名。

譬如原始代码的DataConfig dataConfig

而xml中是通过id="变量名"来创建一个变量并给变量名,去接收类的实例。譬如代码1的

id="config"

3、之后我们需要给这个变量的相关属性赋值,譬如原始代码中的dataConfig.setUrl("xxx");

而xml中是通过<property name="a" value="b">,去给a属性赋值b的,譬如代码1的

<property name="url" value="localhost:3306"></property>

4、最后,我们手动创建对象的原始代码就可以直接调用对象了。

  但是xml中的如上步骤,只是相当于预定义了对象的相关属性。

  因为IoC容器需要加载我们的xml配置文件,才知道需要创建这么一个对象。也就是说如果没有接下来Main类中的相关代码,而只有xml中的代码,这个对象是不存在在IoC容器中的,甚至IoC容器本身都不存在,因为IoC容器本身也是ApplicationContext的实例。需要被实例化。

  于是我们必须先通过xml文件来实例化一个IoC容器。具体做法是,用ApplicationContext(IoC容器的上层接口)的new ClassPathXmlApplicationContext(“xml文件名”)方法,使得Spring框架根据XML文件创建并管理Spring容器(就是IoC容器)。也就是代码1中的

ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");

  随后我们通过他的getBean方法来获取具体的对象,也就是代码1中的

context.getBean("config")

优劣对比

如上,我们可以看到原来传统手动创建对象的劣势:

1、数量庞大:手动创建,手动调用的对象很多,代码实现很多。

2、位置不定:在代码中的任何一个地方都可能存在创建、调用对象的情况,后续更改维护困难。

3、回收麻烦:开发者需要自己销毁或回收对象,去管理它的生命周期。

4、手动注入:在多层依赖注入场景下,需要手动处理递归依赖关系。

其中因为将对象都放在了xml文件中,所以第二点有所改善。同时对象交给了IoC容器进行管理,所以第三点有所改善。

但第一点,因为还是要自己编写xml代码,所以依旧要实现很多代码。

同时第四点,代码1中没有相关展示,但是如果一个xml中的id为A的bean引入了另一个id为B的bean,我们还是要在那个A的bean中使用ref进行引用的,并且确保依赖的B先被创建完成,再将其注入到A中,也就是说还是需要手动处理递归依赖关系的。

为此产生了基于注解的IoC实现。

基于注解实现(代码2)

基于注解实现实际上可以细分为两种实现,第一种是用配置类实现,第二种是扫包+

基于配置类实现

用一个java类来替代xml文件,在这里是BeanConfiguration,整体项目结构如下:

BeanConfiguration类代码如下:

package org.exmaple.configuration;

import org.exmaple.ioc.DataConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class BeanConfiguration {
    @Bean
    public DataConfig dataConfig(){  //这里的dataConfig绑定的是context.getBean("dataConfig")中的dataConfig
        //如果这个方法改成了public DataConfig Config(){  那么后者也应该改成context.getBean("Config")
        DataConfig dataConfig = new DataConfig();
        dataConfig.setUrl("localhost:3306");
        dataConfig.setDrivername("mysql");
        dataConfig.setUsername("root");
        dataConfig.setPassword("123456");
        return dataConfig;
    }
}

Main代码如下:

package org.exmaple.ioc;

import org.exmaple.configuration.BeanConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfiguration.class);
        System.out.println(context.getBean("dataConfig"));//这个是通过类名
//        System.out.println(context.getBean(DataConfig.class));//也可以通过类来获取
    }
}

代码详解

可以看到BeanConfiguration类的代码和我们传统手动创建对象的代码长得很相似,甚至可以说是单纯做了两个步骤。

        第一,将手动创建对象的代码放到一个函数中,并用@Bean标记

        第二,在bean外面套了个配置类的壳,且配置类用@Configuration来标记。

第一步可以理解,因为代码量和传统手动创建对象是差不多的,而且还可以通过@Bean的标记让IoC容器来管理这个Bean,从而实现统一管理和自动装配。

但第二步该怎么理解呢?为什么要套这个配置类的壳呢?

首先是因为有了配置类,那么配置类里不仅可以定义多个bean,还可以定义bean之间的依赖关系。同时与其他注解配合使用,譬如与其他注解如@ComponentScan@PropertySource等配合使用,进一步配置应用程序的扫描、属性加载等功能。

所以遵循了这两步的写法才是规范化、工业化的写法。具体展开这两步的步骤就是:

1、定义Bean:使用@Bean注解,将一个方法声明为一个bean的定义方法,并将其返回值作为bean注册到Spring容器中。

2、声明配置类:@Configuration标识的类表示一个配置类,它通常用于替代传统的XML配置文件,里面含有Bean的相关定义和其他一些配置。

最后想说的是,黑马的Ioc和AOP这方面讲得是比较一般的,推荐另外看其他人的视频补全。我这里推荐【2022版】2小时学会Spring核心机制IoC和AOP_哔哩哔哩_bilibili

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鸡鸭扣

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值