百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

对Spring IOC还迷糊的?赶紧看过来吧

bigegpt 2024-09-11 01:12 3 浏览

满怀忧思,不如先干再说!持续高频更新技术文章,值得持续关注哦!


Spring框架是做Java开发必备的技能,我们常说的Spring框架其实指的是Spring Framework这个产品,Spring的生态非常多:

  • SpringMVC:通过MVC模式实现Web开发
  • SpringBoot:提供默认配置实现快速开发,微服务的基础
  • SpringCloud:提供微服务,分布式架构的解决方案
  • SpringData:提供数据层支持
  • SpringSecurity:提供权限控制,安全访问解决方案
  • ......

学习Spring生态第一个要接触的其实就是Spring Framework,Spring中文意思是春天,也被广大程序员戏称,我们的春天来啦,如今Spring已经发展到第 6 个版本,生态非常完善,稳定,活跃,是Java程序员不可或缺的技术!

一、概述


1.1、什么是Spring


Spring是一个一站式、开源、低侵入式、轻量级框架,它的核心理念是IoC (Inversion of Control,控制反转) 和 AOP(Aspect Oriented Programming,面向切面编程),可以适用于任何Java程序,方便Java开发,是每一个Java Coder的必备技能。


1.2、术语介绍


框架:是能完成一定功能半成品。框架帮助我们完成了一部分功能,比如:整体架构、资源管理、问题解决方案等。我们自己再完成业务,那这个项目就完成了。


一站式:Spring框架很“全面”且整合度高,提供IOC管理维护对象和AOP编写事务日志等模块,提供SpringMVC与用户交互,提供JDBCTemplate与数据库交互数据等技术。


开源:Sparing框架的源码对外开放,我们可以到git上下载源码,开源意味着全世界的Spring爱好者们可以参与到Spring框架的优化设计上,使该框架越来越好,同时知道源代码如何编写,对深入学习Spring框架也有很大好处,当然开源就也意味着免费。


低侵入式:侵入式代表使用该技术是否要求你的程序必须继承或者实现框架中的类或接口,在切换技术时就是一场灾难,而Spring是一个低侵入式的框架,不需要程序强制的实现或继承Spring中的接口和类。典型的Sturts框架侵入性很高,MyBatis-Plus其实也是有一定低侵入性!


轻量级:轻量级是相对于重量级而言的,轻量级一般就是非入侵性的、所依赖的东西非常少、资源占用非常少、部署简单等等,其实就是比较容易使用,而重量级正好相反


JavaBean:属性私有,有Getter和Setter方法,无参的构造方法,这个类就符合JvaBean规范,就是一个JavaBean,JavaBean一般用来存储对象数据。


容器:在日常生活中容器就是一种盛放东西的器具,从程序设计角度看就是装对象的的对象,我们可以放入和拿出对象,容器也管理对象生命周期,我们使用的Tomcat其实也是一个Web容器,用来存放web程序。Java中的集合、数组都可以称为容器,它们用来存放程序运行时的对象,每个容器功能不同而已。


1.3、Spring的优势


  • 低侵入 / 低耦合 (降低组件之间的耦合度,实现软件各层之间的解耦)
  • 声明式事务管理(基于切面和惯例)
  • 方便集成其他框架(如MyBatis、Hibernate等)
  • 降低 Java 开发难度
  • Spring 框架中包括了 J2EE 三层的每一层的解决方案(一站式)

小贴士:Spring是可以单独使用的哦!在做基础Java应用时也可以使用Spring


1.4、Spring能帮我们做什么


1、Spring 能帮我们根据配置文件创建及组装对象之间的依赖关系
2、Spring 面向切面编程能帮助我们无耦合的实现日志记录,性能统计,安全控制。
3、Spring非常简单的帮我们管理数据库事务
4、Spring提供了与第三方数据访问框架(如Hibernate、JPA)无缝集成,而且自己也提供了一套JDBC访问模板来方便数据库访问。
5、Spring 还提供与第三方Web(如Struts1/2、JSF)框架无缝集成,而且自己也提供了一套Spring MVC框架,来方便web层搭建。
6、Spring方便的与Java EE(如Java Mail、任务调度)整合,与更多技术整合(比如缓存框架、消息队列等)


1.5、结构

Spring大约有20个模块,由1300多个不同的文件构成。这些模块可以分为核心容器、AOP和设备支持、数据访问与集成、Web组件、通信报文和集成测试、集成兼容等类。Spring 5的模块结构如下图所示




spring核心模块

  • Spring-core:依赖注入IOC和DI的基本实现
  • Spring-beans:Bean工厂与Bean装配
  • Spring-context:定义基础的Spring上下文,也就是Spring容器
  • spring-context-support:对Spring IOC的扩展支持,以及IOC子容器
  • spring-context-indexer:Spring的类管理组件和Classpath扫描
  • spring-expression:Spring表达式语言

spring切面模块

  • spring-aop:面向切面编程的应用模块,整合Asm,CGLib,JDKProxy
  • spring-aspects:集成AspectJ,AOP应用框架
  • spring-instrument:动态Class Loading模块

spring数据访问与集成

  • spring-jdbc:spring提供的JDBC抽象框架的主要实现模块,用于简化Spring JDBC操作
  • spring-tx:Spring JDBC的事务支持模块
  • spring-orm:主要集成Hibernate、JPA、JDO
  • spring-oxm:将Java对象映射为XML数据,或者将XML数据解析为Java对象
  • spring-jms:Java Messaging Service能够发送和接收消息

spring web组件

  • spring-web:提供基础的Web支持,主要建立于核心容器之上,通过Servlet和Listeners初始化容器
  • spring-webmvc:实现SpringMVC的Web应用
  • spring-websocket:与前端的全双工通讯协议
  • spring-webflux:Spring5推出的一种新的非阻塞函数式Reactive Web框架,可以用来构建异步,非阻塞,事件驱动的web服务

spring通信报文

  • spring-messaging:spring4新加入的模块,为Spring集成基础的报文传送应用

spring集成测试

  • spring-test:主要为测试提供支持

spring集成兼容

  • spring-framework-bom:解决Spring不同版本,依赖版本不同的问题

二、IOC和DI


2.1、IoC:Inverse of Control(控制反转)


2.1.1、IOC概述


  • 读作“反转控制”,更好理解,不是什么技术,而是一种设计思想,就是将原本在程序中手动使用new关键字创建对象的控制权,交由Spring框架来管理。
  • 正控:若要使用某个对象,需要自己去负责对象的创建
  • 反控:若要使用某个对象,只需要从 Spring 容器中获取需要使用的对象,不关心对象的创建过程,也就是把创建对象的控制权反转给了Spring框架


2.1.2、创建名为spring的项目


使用IDEA通过Maven构建项目


2.1.3、pom依赖


<!--定义spring版本-->
    <properties>
        <spring-version>5.3.23</spring-version>
    </properties>

    <dependencies>
        <!--spring核心依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <!--beans负责spring的ioc-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring-version}</version>
        </dependency>
        <!--spring 的上下文,该模块主要是对beans的扩展-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring-version}</version>
        </dependency>
    </dependencies>


2.1.4、创建spring配置文件


在resources目录下创建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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--使用bena标签创建对象:创建Fruit的对象在spring中的标记为fruit。我们可以通过fruit找到该对象并使用
        id属性:可以随便写。spring创建对象存储在spring中容器中的名字,注意不是对象名
        class属性:创建的对象的全路径
    -->
    <bean id="fruit" class="com.stt.pojo.Fruit">
        <!--使用property标签赋值
        name属性:类中的属性名
        value属性:具体的数据
        -->
        <property name="name" value="banana"></property>
        <property name="color" value="yellow"></property>
        <property name="taste" value="sweet"></property>
    </bean>
</beans>


2.1.5、测试


在java目录下创建SpringTest类进行测试


package com.stt.test;

import com.stt.pojo.Fruit;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * ClassName: SpringTest
 */
public class SpringTest {
    public static void main(String[] args) {

        //读取applicationContext.xml配置文件,获取applicationContext对象
        ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        //通过applicationContext对象获取spring容器中的对象,我们来获取fruit对象
        Fruit fruit = (Fruit)applicationContext.getBean("fruit");
        //打印
        System.out.println(fruit);
    }
}


结果如下:



2.1.6、总结


1、传统的方式就是通过new关键字创建Fruit的对象来使用,对象的创建、管理维护、销毁的工作都需要我们做,不方便。


2、使用IOC,对象的创建、管理维护和销毁都交给IOC来管理,如果要使用对象就到IOC容器中获取即可。


3、类中一定要有无参的构造方法,IOC是通过无参的构造方法创建对象。


4、IOC创建的对象默认是单例的,可以在bean标签中使用scope属性修改作用范围。


  • singleton:单例的(默认值)
  • prototype:多例的
  • request:作用于web应用的请求范围
  • session:作用于web应用的会话范围
  • global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session


2.2、DI:Dependency Injection(依赖注入)


2.2.1、DI概述


指 Spring 创建对象的过程中,将对象依赖属性(简单值,集合,对象)通过配置设值给该对象,简单的说就是给类中的属性赋值,比如上边案例中的给Fruit赋名字、颜色、口味。


2.2.2、新建Person类


在上边代码的基础上继续开发,我们创建一个Person类,类中有一个Fruit对象,可以把它想象成是Service调用的Dao层。


package com.stt.pojo;

/**
 * ClassName: Person
 */
public class Person {
    private String name;//名字
    private Fruit fruit;//水果

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Fruit getFruit() {
        return fruit;
    }

    public void setFruit(Fruit fruit) {
        this.fruit = fruit;
    }
    //吃水果方法
    public void eat(){
        System.out.println(name+"吃一个"+fruit.getColor()+"的"+fruit.getName()+"觉得很"+fruit.getTaste());
    }
}


2.2.3、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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--使用bena标签创建对象:创建Fruit的对象在spring中的标记为fruit。我们可以通过fruit找到该对象并使用
        id属性:可以随便写。spring创建对象存储在spring中容器中的名字,注意不是对象名
        class属性:创建的对象的全路径
    -->
    <bean id="fruit" class="com.stt.pojo.Fruit">
        <!--使用property标签赋值
        name属性:类中的属性名
        value属性:具体的数据
        -->
        <property name="name" value="banana"></property>
        <property name="color" value="yellow"></property>
        <property name="taste" value="sweet"></property>
    </bean>
    <!--创建另外一个Fruit对象-->
    <bean id="fruit2" class="com.stt.pojo.Fruit">
        <property name="name" value="苹果"></property>
        <property name="color" value="绿"></property>
        <property name="taste" value="酸"></property>
    </bean>
    <!--创建Person对象-->
    <bean id="person" class="com.stt.pojo.Person">
        <!--使用ref属性引用IOC容器中的对象进行赋值-->
        <property name="fruit" ref="fruit2"></property>

        <property name="name" value="小明"></property>
    </bean>
</beans>


2.2.4、测试


package com.stt.test;

import com.stt.pojo.Fruit;
import com.stt.pojo.Person;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * ClassName: SpringTest
 */
public class SpringTest {
    public static void main(String[] args) {

        //读取applicationContext.xml配置文件,获取applicationContext对象
        ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        //通过applicationContext对象获取spring容器中的对象,我们来获取fruit对象
        //Fruit fruit = (Fruit)applicationContext.getBean("fruit");
        //打印
        //System.out.println(fruit);
        Person person = (Person) applicationContext.getBean("person");
        person.eat();
    }
}


结果:



2.2.5、总结


1、DI其实就是对属性进行赋值,可以是一个普通的变量(String),也可以是一个对象(Fruit)甚至是一个集合


2、注入值在bean标签中使用property标签,注入固定的值使用value属性,注入其他的IOC中的对象使用ref,填写在IOC中id属性值即可


3、类中一定要写set方法,因为注入值是使用set方法注入的


4、IOC可以单独存在使用,而DI需要依赖于IOC


2.3、IOC原理


这是我们本章节的重点,我们知道了IOC可以帮助我们创建对象,那么它到底是如何创建的对象,我们一起来学习一下


1、读取配置文件或者注解,看看Person依赖的是哪个Fruit,拿到类的全路径也就是包名.类名


2、通过Jva中的反射,基于类名调用getInstance()方法,该方法回去调用类中的无参构造进行实例化,所以类中必须有个一个无参构造


3、将对象实例,通过构造函数或者 setter,传递给 Person


我们也可以自己通过编码方式实现IOC,但是没有必要重复造轮子,但是需要知道轮子是如何创造出来的。可以通过阅读Spring源码具体了解Spring中的IOC实现的具体细节


三、注解方式实现IOC和DI


3.1、背景


我们上边的案例使用xml配置通过bean标签和property标签实现IOC和DI,如果我们有很多对象需要Spring帮助我们管理编写xml文件将非常繁琐,Spring2.X的推出伴随着JDK1.5的诞生支持了通过注解的方式实现IOC和DI,极大的简化了我们的开发


3.2、编码实战


3.2.1、applicationContext.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    <!--开启包扫描,可以扫描到com.stt包下的所有注解-->
    <context:component-scan base-package="com.stt"/>
</beans>


该配置文件需要添加context约束,使用context:component-scan开启包扫描,填写自己的包名,可以找到我们编写的注解。


3.2.2、Fruit类


package com.stt.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * ClassName: Fruit
 * 使用@Component注解创建对象,相当于bean标签,value属性相当于id属性
 * 意思是创建Fruit类的对象存储在IOC容器中,在IOC中名字为fruit
 * 使用Value注解来为属性注入固定的值
 */
@Component(value = "fruit")
public class Fruit {
    @Value("芒果")
    private String name;//水果名
    @Value("黄色")
    private String color;//颜色
    @Value("芒果味")
    private String taste;//口味

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public String getTaste() {
        return taste;
    }

    public void setTaste(String taste) {
        this.taste = taste;
    }

    @Override
    public String toString() {
        return name+"的味道是"+taste+"颜色是"+color;
    }
}


3.2.3、Person类


1、使用Resource注解注入Fruit对象


package com.stt.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * ClassName: Person
 */
@Component(value = "person")
public class Person {
    @Value("小明")
    private String name;//名字
    //使用Resource注解引用IOC容器中的对象给属性赋值
    @Resource(name = "fruit")
    private Fruit fruit;//水果

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Fruit getFruit() {
        return fruit;
    }

    public void setFruit(Fruit fruit) {
        this.fruit = fruit;
    }
    //吃水果方法
    public void eat(){
        System.out.println(name+"吃一个"+fruit.getColor()+"的"+fruit.getName()+"觉得很"+fruit.getTaste());
    }
}


2、使用Autowired注解注入Fruit对象


package com.stt.pojo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * ClassName: Person
 */
@Component(value = "person")
public class Person {
    @Value("小明")
    private String name;//名字
    //使用Resource注解引用IOC容器中的对象给属性赋值
    //@Resource(name = "fruit")
    @Autowired
    private Fruit fruit;//水果

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Fruit getFruit() {
        return fruit;
    }

    public void setFruit(Fruit fruit) {
        this.fruit = fruit;
    }
    //吃水果方法
    public void eat(){
        System.out.println(name+"吃一个"+fruit.getColor()+"的"+fruit.getName()+"觉得很"+fruit.getTaste());
    }
}


3.2.4、测试


package com.stt.test;

import com.stt.pojo.Person;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * ClassName: SpringTest
 */
public class SpringTest {
    public static void main(String[] args) {

        //读取applicationContext.xml配置文件,获取applicationContext对象
        ClassPathXmlApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        Person person =(Person) applicationContext.getBean("person");
        person.eat();
    }
}


结果:



3.3、注解详解


3.3.1、IOC注解


这些注解都是使用在类上,我们的示例代码使用的是@Component注解实现的IOC,其实一共有四个注解可以实现该功能,其余三个分别为@Service(使用在service层)、@Repository(使用在dao层)、 @Controller(使用在web层)。它们的功能都一样,可以填写一个value参数,该参数的值就是对象在IOC容器中的名字,当然该参数不是必须的,如果不写,在IOC中的名字默认为类名小写,比如:fruit、person


3.3.2、DI注解


这些注解都是使用在属性


1、@Value:注入一个固定的值


2、@Resource:该注解在javax.annotation包中,它属于Java,在使用时可以填写name参数,对应IOC容器中对象的名字,就是ByName进行引用注入,如果不写name参数则根据对象的类型,ByType(类的全路径)进行注入数据


3、@Autowired:该注解在org.springframework.beans.factory.annotation包中,它属于Spring,没有name属性,ByType从IOC容器中获取对象进行数据注入


3.3.3、作用域注解


使用SpringIOC管理对象,默认是单例的,使用xml文件我们可以在bean标签中使用scope属性设置对象的作用范围,如果使用的注解可以在类上添加@Scope注解自定义对象的作用范围,当然不写的话默认就是单例的,我们上边的案例就没有写。


3.4、总结


1、注解是Spring2.X推出的我们使用的Spring是5.3.23,Java是1.5支持注解,我这里的JDK是1.8


2、明显感觉出注解方式编写代码更加优雅方便,在工作中也是推荐使用注解方式


3、使用注解xml文件中就不需要编写bean标签,但是需要添加注解扫描<context:component-scan base-package="xxx.xx"/>


4、使用@Component、@Service、@Repository、 @Controller注解实现IOC


5、使用@Value、@Resource、@Autowired实现DI

不错记得三连哦,收藏等于学会,还不快快收藏!

相关推荐

得物可观测平台架构升级:基于GreptimeDB的全新监控体系实践

一、摘要在前端可观测分析场景中,需要实时观测并处理多地、多环境的运行情况,以保障Web应用和移动端的可用性与性能。传统方案往往依赖代理Agent→消息队列→流计算引擎→OLAP存储...

warm-flow新春版:网关直连和流程图重构

本期主要解决了网关直连和流程图重构,可以自此之后可支持各种复杂的网关混合、多网关直连使用。-新增Ruoyi-Vue-Plus优秀开源集成案例更新日志[feat]导入、导出和保存等新增json格式支持...

扣子空间体验报告

在数字化时代,智能工具的应用正不断拓展到我们工作和生活的各个角落。从任务规划到项目执行,再到任务管理,作者深入探讨了这款工具在不同场景下的表现和潜力。通过具体的应用实例,文章展示了扣子空间如何帮助用户...

spider-flow:开源的可视化方式定义爬虫方案

spider-flow简介spider-flow是一个爬虫平台,以可视化推拽方式定义爬取流程,无需代码即可实现一个爬虫服务。spider-flow特性支持css选择器、正则提取支持JSON/XML格式...

solon-flow 你好世界!

solon-flow是一个基础级的流处理引擎(可用于业务规则、决策处理、计算编排、流程审批等......)。提供有“开放式”驱动定制支持,像jdbc有mysql或pgsql等驱动,可...

新一代开源爬虫平台:SpiderFlow

SpiderFlow:新一代爬虫平台,以图形化方式定义爬虫流程,不写代码即可完成爬虫。-精选真开源,释放新价值。概览Spider-Flow是一个开源的、面向所有用户的Web端爬虫构建平台,它使用Ja...

通过 SQL 训练机器学习模型的引擎

关注薪资待遇的同学应该知道,机器学习相关的岗位工资普遍偏高啊。同时随着各种通用机器学习框架的出现,机器学习的门槛也在逐渐降低,训练一个简单的机器学习模型变得不那么难。但是不得不承认对于一些数据相关的工...

鼠须管输入法rime for Mac

鼠须管输入法forMac是一款十分新颖的跨平台输入法软件,全名是中州韵输入法引擎,鼠须管输入法mac版不仅仅是一个输入法,而是一个输入法算法框架。Rime的基础架构十分精良,一套算法支持了拼音、...

Go语言 1.20 版本正式发布:新版详细介绍

Go1.20简介最新的Go版本1.20在Go1.19发布六个月后发布。它的大部分更改都在工具链、运行时和库的实现中。一如既往,该版本保持了Go1的兼容性承诺。我们期望几乎所...

iOS 10平台SpriteKit新特性之Tile Maps(上)

简介苹果公司在WWDC2016大会上向人们展示了一大批新的好东西。其中之一就是SpriteKitTileEditor。这款工具易于上手,而且看起来速度特别快。在本教程中,你将了解关于TileE...

程序员简历例句—范例Java、Python、C++模板

个人简介通用简介:有良好的代码风格,通过添加注释提高代码可读性,注重代码质量,研读过XXX,XXX等多个开源项目源码从而学习增强代码的健壮性与扩展性。具备良好的代码编程习惯及文档编写能力,参与多个高...

Telerik UI for iOS Q3 2015正式发布

近日,TelerikUIforiOS正式发布了Q32015。新版本新增对XCode7、Swift2.0和iOS9的支持,同时还新增了对数轴、不连续的日期时间轴等;改进TKDataPoin...

ios使用ijkplayer+nginx进行视频直播

上两节,我们讲到使用nginx和ngixn的rtmp模块搭建直播的服务器,接着我们讲解了在Android使用ijkplayer来作为我们的视频直播播放器,整个过程中,需要注意的就是ijlplayer编...

IOS技术分享|iOS快速生成开发文档(一)

前言对于开发人员而言,文档的作用不言而喻。文档不仅可以提高软件开发效率,还能便于以后的软件开发、使用和维护。本文主要讲述Objective-C快速生成开发文档工具appledoc。简介apple...

macOS下配置VS Code C++开发环境

本文介绍在苹果macOS操作系统下,配置VisualStudioCode的C/C++开发环境的过程,本环境使用Clang/LLVM编译器和调试器。一、前置条件本文默认前置条件是,您的开发设备已...