searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Spring框架核心——基于IDEA实现IoC(一)

2022-11-29 00:46:40
36
0

0.引言

大家可能不知道什么是IoC,但只要你接触过Java,多多少少都会接触到Spring框架,比如用于web开发的Spring MVC、持久层开发的Spring Data、微服务开发Spring Cloud,以及经典的“轮子的轮子,框架的框架”——Spring Boot。我们把这一大坨东西就叫做Spring全家桶。

今天给大家介绍的,就是spring全家桶中相当基础的概念:IoC

本文我们简单的介绍下概念以及实现方法,后面有机会来分享高阶应用以及源码分析

1 什么是IoC?为什么要用IoC?

IoC,即控制反转——是Spring全家桶各个功能模块的基础

每个功能模块都依赖于对象而发挥作用,IoC的本质就是帮助创建对象

↑↑↑ 这句话听起来真的迷惑,创建对象还要帮助?那我不是要多少就new多少?

能有这个疑惑的读者,可能跟我一样年轻,没咋改过那种耦合度很高的代码。尤其是现在利用spring开发的绝大部分大型工程,耦合度都很高。

举个简单的例子:在业务层,我定义了一个类,比如说是用来计算某个几何图形的属性,里面可能有计算面积啊、计算周长啊这些方法。同时肯定会有一个实例化的图形类对象,我们就叫他diagram。那你写出来可能就是下面这样:

private Diagram diagram = new Diagram( );

这个业务层的类比如定义为DiagramService0,那定义Diagram这个类的数据层就被DiagramService0调用了。一些大型的工程里,我们极有可能写了Diagram这个类,运用到很多地方,可能不仅仅是DiagramService0。

也许换个使用场景,比如我又需要算图形有几个内角,但是这个需求又只在某个场景用得上。那我是不是没必要专门把这个功能集成到DiagramService0,更好的办法显然是写一个继承类,比如说是DiagramService1。

如果有一天,Diagram这个类要升级要迭代(就比如最简单的改名,改成DiagramNew),我们就需要去维护DiagramService0、DiagramService1。这只是一个简单的例子,事实上你需要维护的东西在工程的方方面面,非常的不方便。

那怎么解决呢?

——是不是我们可以考虑,不由自己主动去new对象,交给外部来创建对象。不管我的类怎么迭代,它只要按照我写的这个类去对应的地方创建对象就可以了

这就是控制反转的核心,我们把对象创建的权利,从我们的程序转移到外部,从而解决我们在开发中修改一处代码,往往要连带着修改很多关联代码的问题。这个外部,就是我们的IoC容器(对,IoC的本质就是一个容器)

学术一点的说:IOC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在IOC容器中统称为Bean

2 如何实现IoC

其实上实现IoC的办法并不少,比较常用的就有:1.基于注解 2.基于xml文件 3.扫包+注解

这里我们只介绍第三种方法,因为它最简单,不再需要xml与配置类,直接在目标类创建。
1.我们先在IDEA建立一个SpringBoot项目(本质上也是maven项目),这样就不用特意引入一些spring的依赖了,这个过程很简单就不展示了
2.先创建一个domian包,放一个User类用来做测试,随便定义了uid/uname/password/addrs四个参数
注意:这里是用了lombok所以没写get set方法,这个东西侵入性有点强,有些项目是不让用的
package wy.springboot01.domain;

import lombok.Data;
import org.springframework.stereotype.Component;

import java.util.ArrayList;

/**
 * springboot项目启动的时候,自动将application.yml内容加载到实体对象中
 */

@Data
//将实体类交给spring管理,自动扫描
@Component
public class User {
    private Integer uid;
    private String uname;
    private String password;
    private ArrayList<String> addrs;
}
3 .创建一个包用来放IoC的代码,这里就叫IoC好了,引入刚刚的User类,就新建一个IoCTest类,让main方法生成一个User对象好了
package wy.springboot01.IoC;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import wy.springboot01.domain.User;

public class IoCTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("wy.springboot01.domain");
        //context.getBean(User.class)利用IoC生成对象
        System.out.println(context.getBean(User.class));
    }
}
4.好了,我们直接运行这个IoCTest的main方法,会输出什么呢?是如下的结果:
User(uid=null, uname=null, password=null, addrs==null)
很显然context.getBean(User.class)成功的创建了对象并sout了出来!有了它我们就不用担心User类的迭代会影响到其他代码了

3 对IoC创建对象的参数进行赋值

当然,如果你将这个IoC创建的User对象保存下来,就可以用set对这些参数进行赋值,但是我们往往喜欢给他们来上一个默认值,怎么做呢?

方法一:在参数上加@Value注解

这种方法非常简单,如下:

@Data
//将实体类交给spring管理,自动扫描
@Component
public class User {
    @Value("132456")
    private Integer uid;
    @Value("wy")
    private String uname;
    @Value("asd")
    private String password;
    @Value("Beijing, Sichuan, Nanchang")
    private List<String> addrs;

    public User() {
    }

    public User(Integer uid, String uname, String password, ArrayList<String> addrs) {
        this.uid = uid;
        this.uname = uname;
        this.password = password;
        this.addrs = addrs;
    }
}

方法二:导入一个配置类

通常我们还是推荐这个方法,如下更改代码:

package wy.springboot01.domain;

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.ArrayList;

/**
 * springboot项目启动的时候,自动将application.yml内容加载到实体对象中
 */

@Data
//将实体类交给spring管理,自动扫描
@Component
//加载配置内容,设定配置前缀,注意:prefix参数不支持小驼峰原则,必须全部小写
@ConfigurationProperties(prefix = "user")
public class User {
    private Integer uid;
    private String uname;
    private String password;
    private ArrayList<String> addrs;

    public User() {
    }

    public User(Integer uid, String uname, String password, ArrayList<String> addrs) {
        this.uid = uid;
        this.uname = uname;
        this.password = password;
        this.addrs = addrs;
    }
}

显然我们通过@ConfigurationProperties这个注解去加载了配置类,我们只需要在与java这个路径平行的resources路径下新增一个yml文件当做配置即可,比如我们就叫做application.yml(通常这个路径会有一个默认的application.properties,但我习惯了用yml做配置这里就不写application.properties做配置的方法了,其实也很简单感兴趣的可以自行去搜索一下),我们这么来配置它(一定要注意冒号后面的空格)

#配置属性并加载到User实体中
user:
  uid: 1546879
  uname: wuyu
  password: wuyu1999
  addrs:
    - Beijing
    - Sichuan
    - Nanchang

可以看到我们的前缀叫做user,所以我们在User中使用@ConfigurationProperties注解时,参数要填 prefix="user"。这么一来,这些默认值就会在运行的时候被加载进去,此时如果你再次运行IoCTest,就会输出有上述值的对象(需要注意的是,很多时候IoC创建对象这种方式有可能不生效,还是建议使用@Value)

还有一种特殊情况:如果一个类的参数包含了另一个类的对象,这个时候我们就要用到依赖注入@Autowired:

@Data
//将实体类交给spring管理,自动扫描
@Component
public class SuperUser {
    @Value(“777”)
    private String info1;
    @Value(“666”)
    private String info2;
    @Autowired
    private User user;
}

 当时我试到这一步的时候产生了一个疑问,有的时候我们为了好区分或者说为了好给读代码的人信息,可能我这个User类在注入Bean的时候(即@Component这一步)我会加一些参数,打个比方我会写成@Component("ctyunUser"),那这个User类注入到bean里面的名称就叫ctyunUser。

这个时候我的SuperUser的user参数还找不找得到User这个类呢?大家可以自己去试一下,实际上是找的到的,因为@Autowired是默认使用参数的类型去找,在这个例子里他是默认去找Bean中有没有User这个类,实际上是有的只不过它的别名叫做ctyunUser。

如果我们要用名称去找,就需要在@Autowired下面再加一个@Qualifier(“ctyunUser”),这样他就会根据名称去找,如果这个时候User的@Component的参数没有写或写的不是ctyunUser,创建对象就会报错

这一块大家可以自己去试一试,还是挺有意思的

 


以上就是IoC非常初级的一个用法,下一篇我们会介绍一些进阶的用法和更多有趣的案例

0条评论
0 / 1000
才开始学技术的小白
23文章数
2粉丝数
才开始学技术的小白
23 文章 | 2 粉丝
原创

Spring框架核心——基于IDEA实现IoC(一)

2022-11-29 00:46:40
36
0

0.引言

大家可能不知道什么是IoC,但只要你接触过Java,多多少少都会接触到Spring框架,比如用于web开发的Spring MVC、持久层开发的Spring Data、微服务开发Spring Cloud,以及经典的“轮子的轮子,框架的框架”——Spring Boot。我们把这一大坨东西就叫做Spring全家桶。

今天给大家介绍的,就是spring全家桶中相当基础的概念:IoC

本文我们简单的介绍下概念以及实现方法,后面有机会来分享高阶应用以及源码分析

1 什么是IoC?为什么要用IoC?

IoC,即控制反转——是Spring全家桶各个功能模块的基础

每个功能模块都依赖于对象而发挥作用,IoC的本质就是帮助创建对象

↑↑↑ 这句话听起来真的迷惑,创建对象还要帮助?那我不是要多少就new多少?

能有这个疑惑的读者,可能跟我一样年轻,没咋改过那种耦合度很高的代码。尤其是现在利用spring开发的绝大部分大型工程,耦合度都很高。

举个简单的例子:在业务层,我定义了一个类,比如说是用来计算某个几何图形的属性,里面可能有计算面积啊、计算周长啊这些方法。同时肯定会有一个实例化的图形类对象,我们就叫他diagram。那你写出来可能就是下面这样:

private Diagram diagram = new Diagram( );

这个业务层的类比如定义为DiagramService0,那定义Diagram这个类的数据层就被DiagramService0调用了。一些大型的工程里,我们极有可能写了Diagram这个类,运用到很多地方,可能不仅仅是DiagramService0。

也许换个使用场景,比如我又需要算图形有几个内角,但是这个需求又只在某个场景用得上。那我是不是没必要专门把这个功能集成到DiagramService0,更好的办法显然是写一个继承类,比如说是DiagramService1。

如果有一天,Diagram这个类要升级要迭代(就比如最简单的改名,改成DiagramNew),我们就需要去维护DiagramService0、DiagramService1。这只是一个简单的例子,事实上你需要维护的东西在工程的方方面面,非常的不方便。

那怎么解决呢?

——是不是我们可以考虑,不由自己主动去new对象,交给外部来创建对象。不管我的类怎么迭代,它只要按照我写的这个类去对应的地方创建对象就可以了

这就是控制反转的核心,我们把对象创建的权利,从我们的程序转移到外部,从而解决我们在开发中修改一处代码,往往要连带着修改很多关联代码的问题。这个外部,就是我们的IoC容器(对,IoC的本质就是一个容器)

学术一点的说:IOC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在IOC容器中统称为Bean

2 如何实现IoC

其实上实现IoC的办法并不少,比较常用的就有:1.基于注解 2.基于xml文件 3.扫包+注解

这里我们只介绍第三种方法,因为它最简单,不再需要xml与配置类,直接在目标类创建。
1.我们先在IDEA建立一个SpringBoot项目(本质上也是maven项目),这样就不用特意引入一些spring的依赖了,这个过程很简单就不展示了
2.先创建一个domian包,放一个User类用来做测试,随便定义了uid/uname/password/addrs四个参数
注意:这里是用了lombok所以没写get set方法,这个东西侵入性有点强,有些项目是不让用的
package wy.springboot01.domain;

import lombok.Data;
import org.springframework.stereotype.Component;

import java.util.ArrayList;

/**
 * springboot项目启动的时候,自动将application.yml内容加载到实体对象中
 */

@Data
//将实体类交给spring管理,自动扫描
@Component
public class User {
    private Integer uid;
    private String uname;
    private String password;
    private ArrayList<String> addrs;
}
3 .创建一个包用来放IoC的代码,这里就叫IoC好了,引入刚刚的User类,就新建一个IoCTest类,让main方法生成一个User对象好了
package wy.springboot01.IoC;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import wy.springboot01.domain.User;

public class IoCTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("wy.springboot01.domain");
        //context.getBean(User.class)利用IoC生成对象
        System.out.println(context.getBean(User.class));
    }
}
4.好了,我们直接运行这个IoCTest的main方法,会输出什么呢?是如下的结果:
User(uid=null, uname=null, password=null, addrs==null)
很显然context.getBean(User.class)成功的创建了对象并sout了出来!有了它我们就不用担心User类的迭代会影响到其他代码了

3 对IoC创建对象的参数进行赋值

当然,如果你将这个IoC创建的User对象保存下来,就可以用set对这些参数进行赋值,但是我们往往喜欢给他们来上一个默认值,怎么做呢?

方法一:在参数上加@Value注解

这种方法非常简单,如下:

@Data
//将实体类交给spring管理,自动扫描
@Component
public class User {
    @Value("132456")
    private Integer uid;
    @Value("wy")
    private String uname;
    @Value("asd")
    private String password;
    @Value("Beijing, Sichuan, Nanchang")
    private List<String> addrs;

    public User() {
    }

    public User(Integer uid, String uname, String password, ArrayList<String> addrs) {
        this.uid = uid;
        this.uname = uname;
        this.password = password;
        this.addrs = addrs;
    }
}

方法二:导入一个配置类

通常我们还是推荐这个方法,如下更改代码:

package wy.springboot01.domain;

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.ArrayList;

/**
 * springboot项目启动的时候,自动将application.yml内容加载到实体对象中
 */

@Data
//将实体类交给spring管理,自动扫描
@Component
//加载配置内容,设定配置前缀,注意:prefix参数不支持小驼峰原则,必须全部小写
@ConfigurationProperties(prefix = "user")
public class User {
    private Integer uid;
    private String uname;
    private String password;
    private ArrayList<String> addrs;

    public User() {
    }

    public User(Integer uid, String uname, String password, ArrayList<String> addrs) {
        this.uid = uid;
        this.uname = uname;
        this.password = password;
        this.addrs = addrs;
    }
}

显然我们通过@ConfigurationProperties这个注解去加载了配置类,我们只需要在与java这个路径平行的resources路径下新增一个yml文件当做配置即可,比如我们就叫做application.yml(通常这个路径会有一个默认的application.properties,但我习惯了用yml做配置这里就不写application.properties做配置的方法了,其实也很简单感兴趣的可以自行去搜索一下),我们这么来配置它(一定要注意冒号后面的空格)

#配置属性并加载到User实体中
user:
  uid: 1546879
  uname: wuyu
  password: wuyu1999
  addrs:
    - Beijing
    - Sichuan
    - Nanchang

可以看到我们的前缀叫做user,所以我们在User中使用@ConfigurationProperties注解时,参数要填 prefix="user"。这么一来,这些默认值就会在运行的时候被加载进去,此时如果你再次运行IoCTest,就会输出有上述值的对象(需要注意的是,很多时候IoC创建对象这种方式有可能不生效,还是建议使用@Value)

还有一种特殊情况:如果一个类的参数包含了另一个类的对象,这个时候我们就要用到依赖注入@Autowired:

@Data
//将实体类交给spring管理,自动扫描
@Component
public class SuperUser {
    @Value(“777”)
    private String info1;
    @Value(“666”)
    private String info2;
    @Autowired
    private User user;
}

 当时我试到这一步的时候产生了一个疑问,有的时候我们为了好区分或者说为了好给读代码的人信息,可能我这个User类在注入Bean的时候(即@Component这一步)我会加一些参数,打个比方我会写成@Component("ctyunUser"),那这个User类注入到bean里面的名称就叫ctyunUser。

这个时候我的SuperUser的user参数还找不找得到User这个类呢?大家可以自己去试一下,实际上是找的到的,因为@Autowired是默认使用参数的类型去找,在这个例子里他是默认去找Bean中有没有User这个类,实际上是有的只不过它的别名叫做ctyunUser。

如果我们要用名称去找,就需要在@Autowired下面再加一个@Qualifier(“ctyunUser”),这样他就会根据名称去找,如果这个时候User的@Component的参数没有写或写的不是ctyunUser,创建对象就会报错

这一块大家可以自己去试一试,还是挺有意思的

 


以上就是IoC非常初级的一个用法,下一篇我们会介绍一些进阶的用法和更多有趣的案例

文章来自个人专栏
Java开发者绕不开的技术
12 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
1
0