一、Spring简介

1、Spring介绍

Spring 是一款主流的 Java EE 轻量级开源框架 ,Spring 由“Spring 之父”Rod Johnson 提出并创立,其目的是用于简化 Java 企业级应用的开发难度和开发周期。Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。Spring 框架除了自己提供功能外,还提供整合其他技术和框架的能力。

Spring 自诞生以来备受青睐,一直被广大开发人员作为 Java 企业级应用程序开发的首选。时至今日,Spring 俨然成为了 Java EE 代名词,成为了构建 Java EE 应用的事实标准。

官网:https://spring.io/

2、使用Spring的原因

官方解释:https://spring.io/why-spring

Spring使Java编程对每个人来说更快、更容易、更安全。 Spring对速度、简单性和生产率的关注使它成为世界上最流行的Java框架。 spring给整个行业带来等了春天,为我们软件的开发带来了极大的便利。

参考:snyk发布的2021年JVM 生态系统报告(2022年的还没有发布)

明解Spring6(一)------邂逅

Java 世界仍然是 Spring 主导的世界,超过一半的市场使用 Spring Boot,几乎三分之一使用 Spring MVC。总的来说,我们看到我们生活在一个高度由 Spring 主导的宇宙中,这似乎表明 Spring 的人们在为社区服务方面做得很好。

Spring 框架提供灵活的类库受到世界各地开发人员的信任。无论是流媒体电视、在线购物、还是无数其他创新的解决方案,Spring每天都为数百万终端用户提供愉快的体验。Spring也有来自所有科技巨头的贡献,包括阿里巴巴、亚马逊、谷歌、微软等。

Spring 灵活而全面的扩展能力和第三方库让开发人员可以构建几乎任何可以想象到的应用程序。 Spring框架的控制反转(IoC)和依赖注入(DI)特性为一系列广泛的特性和功能提供了基础。 无论您是在为web构建安全的、响应式的、基于云的微服务,还是为企业构建复杂的流数据流,Spring都有工具可以提供帮助。

Spring Boot 改变了您处理Java编程任务的方式,从根本上简化了您的体验。 Spring Boot结合了应用程序上下文和自动配置的嵌入式web服务器等必要条件,使microservice开发变得轻而易举。 为了更快,您可以将Spring Boot与Spring Cloud丰富的支持库、服务器、模式和模板组合在一起,以创纪录的时间将整个基于微服务的架构安全地部署到云中。

我们的工程师非常关心性能。 在Spring中,默认情况下,您会注意到快速启动、快速关闭和优化执行。 Spring项目也越来越多地支持reactive(nonblocking)编程模型,以获得更高的效率。 开发人员的生产力是Spring的超级力量。 Spring Boot帮助开发人员轻松地构建应用程序,而且比其他竞争范式要轻松得多。

Spring在处理安全问题方面十分可靠。 Spring代码的贡献者与安全专业人员一起修补和测试任何报告的漏洞。 第三方依赖关系也被密切监控,并定期发布更新,以帮助您的数据和应用程序尽可能安全。 此外,Spring Security使您更容易集成行业标准的安全方案,并交付可靠的默认安全解决方案。

Spring社区是一个庞大的、全球性的、多样化的社区,涵盖了所有年龄和能力的人,从完全的初学者到经验丰富的专业人士。 无论你处在人生的哪个阶段,你都能找到帮助你进入下一个阶段的支持和资源。

3、Spring提供的功能

明解Spring6(一)------邂逅

总结:Spring发展到现在已经不再是一个单纯的应用框架,而是逐渐发展成为一个由多个不同子项目(模块)组成的成熟技术,例如 Spring Framework、SpringBoot、Spring Cloud、Spring Data、Spring Security 等。

Spring提供的所有项目组成:https://spring.io/projects

这些子项目涵盖了从企业级应用开发到云计算等各方面的内容,能够帮助开发人员解决软件发展过程中不断产生的各种实际问题,给开发人员带来了更好的开发体验。

Spring Framework 是其它子项目的基础,可以理解成 Spring 的项目也只是在 Spring Framework 的基础上对其进行扩展实现的。故我们也称 Spring Framework 为 Spring 框架。框架本身的好处是重用性,其它项目不用重复开发Spring Framework已经实现的功能,对其添砖加瓦,形成自己特色的项目就好。

而且我们所说的Spring MVC其实也属于Spring Framework核心项目之一;只是因为使用频率最高且强调web功能,所以我们会拆出来成为Spring MVC 框架。

4、Spring Framework组成

官网介绍:https://spring.io/projects/spring-framework#overview

明解Spring6(一)------邂逅

明解Spring6(一)------邂逅

上图中包含了 Spring 框架的所有模块,这些模块可以满足一切企业级应用开发的需求,在开发过程中可以根据需求有选择性地使用所需要的模块。下面分别对这些模块的作用进行简单介绍。

  1. Spring Core(核心容器)

spring core提供了IOC,DI,Bean配置装载创建的核心实现。核心概念: Beans、BeanFactory、、ApplicationContext。

  1. Spring AOP
  1. Spring Data Access
  1. Spring Web
  1. Spring Message
  1. Spring test

5、Spring Framework特点

Spring框架版本:Spring6要求JDK最低版本是JDK17

二、IoC概述

1、背景介绍

介绍IOC之前我们先对其背景进行介绍,也即对象的创建史!

1) 普通new实例

在实际项目设计过程中,接口是一个重要组成元素,不同层之间的操作需要通过接口来调用。利用接口,可以实现子类的隐藏,也可以更好地描述不同层之间的操作标准。Java中要想获得接口对象,需要通过关键字new来实现。下面的案例不要研究其业务意义,只是一个测试demo!

public interface ConnectionMessage {
    public String select();
}
public class MySQLMessageImpl implements ConnectionMessage {
    @Override
    public String select() {
        System.out.println("连接---查询MySQL数据库信息");
        return "";
    }
}
public class MyTest {
    public static void main(String[] args) {
        ConnectionMessage message = new MySQLMessageImpl();
        message.select();
    }
}

明解Spring6(一)------邂逅

以上是使用MySQL操作的模拟程序,如果切换为PostgreSQL就需要修改其使用类!

public class MyTest {
    public static void main(String[] args) {
        //ConnectionMessage message = new MySQLMessageImpl();
        ConnectionMessage message = new PostgreSQLMessageImpl();
        message.select();
    }
}

明解Spring6(一)------邂逅

这就是所谓的耦合,调用的时候要修改代码,即直接new , 显然不符合面向对象的设计原则---------开闭原则

2.1)普通工厂

针对上面的情况我们可以使用工厂方式,创建一个BeanFactory.java文件

public class BeanFactory {
    public static ConnectionMessage select(){
        return new MySQLMessageImpl();
    }
}

这样使用的时候就变成下面这样

public class MyTest {
    public static void main(String[] args) {
//        ConnectionMessage message = new MySQLMessageImpl();
//        ConnectionMessage message = new PostgreSQLMessageImpl();
        ConnectionMessage message = BeanFactory.select();
        message.select();
    }
}

明解Spring6(一)------邂逅

上图的思想没问题,但是我们的代码还尚有问题,即BeanFactory里面还是有代码耦合

明解Spring6(一)------邂逅

2.2)反射工厂1.0

我们接下来可以利用反射完善一下!

public class BeanFactory {
    private static ConnectionMessage message;
    public static ConnectionMessage select() throws Exception {
        Class<?> clazz = Class.forName("com.ty.demo.dao.impl.MySQLMessageImpl");
        message = (MySQLMessageImpl) clazz.getDeclaredConstructor().newInstance();
        return message;
    }
}

这样比直接new好多了,减少了工厂内获取对象的耦合,但是获取类限定名的时候还是需要进行修改!

明解Spring6(一)------邂逅

2.3)反射工厂2.0

下面我们可以用properties属性配置文件存储文件限定名,把具体的类限定名和代码相分离,未来只需要修改此配置文件即可!

明解Spring6(一)------邂逅

public class BeanFactory {
    private static ConnectionMessage message;
    //创建Properties对象来加载properties属性配置文件
    private static Properties environment = new Properties();
    private static InputStream resource;
    static {
        //加载bean.properties配置文件
        resource = BeanFactory.class.getResourceAsStream("/bean.properties");
        try {
            environment.load(resource);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    public static ConnectionMessage select() throws Exception {
        Class<?> clazz = Class.forName(environment.getProperty("mysqlMessage"));
        message = (MySQLMessageImpl) clazz.getDeclaredConstructor().newInstance();
        return message;
    }
}

2.3)通用工厂

上面我们的工厂方法基本设计完成了,但是还有点小问题,那就是每次都需要在BeanFactory工厂类写一个与其对应的对象创建工厂方法,这时我们设计一个通用的工厂方法

public class BeanFactory {
    //创建Properties对象来加载properties属性配置文件
    private static Properties environment = new Properties();
    private static InputStream resource;
    static {
        //加载bean.properties配置文件
        resource = BeanFactory.class.getResourceAsStream("/bean.properties");
        try {
            environment.load(resource);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * @param key 配置文件对应的mysqlMessage和postgresqlMessage
     * @return
     */
    public static Object getBean(String key) {
        Object returnedObject = null;
        try {
            Class<?> clazz = Class.forName(environment.getProperty(key));
            returnedObject = clazz.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return returnedObject;
    }
}

明解Spring6(一)------邂逅

一个通用工厂就完成了,但是这种方式你还需要先自己提供工厂类和方法,我们如果用Spring的话,工厂类不用我们手动实现,Spring提供了工厂,使我们可以将更多精力放在核心代码的开发处理上。

2、IoC---控制反转

简介:控制反转(Inversion of Control,缩写为IoC),它不是一门技术,而是面向对象编程中的一种设计思想,可以用来降低代码之间的耦合度,能够指导我们如何设计出松耦合、更优良的程序。

核心: 将对象的创建权交出去,将对象和对象之间关系的管理权交出去,由第三方容器来负责创建与维护 。

目标:降低程序耦合度,提高程序扩展力

控制反转,反转的是什么?

  • 将对象的创建权利交出去,即不在程序中采用硬编码的方式来new对象,而是交给第三方容器负责
  • 将对象和对象之间关系的维护权交出去,交给第三方容器负责

实现方式:依赖注入(Dependency Injection)与依赖查找(Dependency Lookup)

总结:

  1. IOC 就是一种控制反转的思想, 而 DI 是对IoC的一种具体实现
  2. Bean管理:Bean对象的创建,以及Bean对象中属性的赋值(或者叫做Bean对象之间关系的维护)

相关名词

容器:可以管理对象的生命周期、对象与对象之间的依赖关系

实体类:

1、所有属性为private。

2、这个类必须有一个公共的缺省构造函数。即是提供无参数的构造器。

3、这个类的属性使用getter和setter来访问,其他方法遵从标准命名规范。

4、这个类应是可序列化的。实现serializable接口。

因为这些要求主要是靠约定而不是靠实现接口,所以许多开发者把JavaBean看作遵从特定命名约定的POJO。

POJO与JavaBean的区别:

POJO JavaBean
权限控制 没有对成员提供太多控制 提供对成员的完全控制
可序列化 可以实现 应该实现
字段访问 具有任何可见性,可以直接访问 字段只有私人可见性,只能由getter和setter访问
无参构造 可以没有 必须具有
使用场景 当您不想限制成员并让用户完全访问您的实体时使用它 当您要向用户提供您的实体,但仅向实体的一部分提供服务时,将使用它

POJO类和Bean均用于定义Java对象,以提高其可读性和可重用性。POJO没有其他限制,而bean是具有某些限制的特殊POJO。

SpringBean和JavaBean的区别:

JavaBean SpringBean
用处不同 更多地作为值传递参数 用处几乎无处不在,任何组件都可以被称为bean
写法不同 作为值对象,要求每个属性都提供getter和setter方法 只需为接受设值注入的属性提供setter方法
生命周期不同 传统javabean作为值对象传递,不接受任何容器管理其生命周期 spring管理其生命周期行为

Entity Bean:是域模型对象,用于实现O/R映射,负责将数据库中的表记录映射为内存中的Entity对象,事实上,创建一个Entity Bean对象相当于新建一条记录,删除一个 Entity Bean会同时从数据库中删除对应记录,修改一个Entity Bean时,容器会自动将Entity Bean的状态和数据库同步。

三、入门程序

1、引入依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>6.0.4</version>
</dependency>

当引入Spring-webmvc依赖之后,表示将Spring的基础依赖也一起引入了

明解Spring6(一)------邂逅

有的教程可能引得是spring-context依赖,此依赖是除了web部分其余的基础依赖都会引进来,它们一般是在Spring MVC的时候再引入spring-webmvc依赖。

2、创建java类

public class HelloWorld {
    public void sayHello(){
        System.out.println("hello world");
    }
}

3、创建配置文件

在resources目录创建一个 Spring 配置文件 bean.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">
<!--
    配置HelloWorld所对应的bean,表示将HelloWorld的对象交给Spring的IOC容器管理
    通过bean标签配置IOC容器所管理的bean
    属性:
        id:设置bean的唯一标识
        class:设置bean所对应类型的全限定名
    -->
    <bean id="hello" class="com.ty.demo.bean.HelloWorld"></bean>
</beans>

4、测试程序

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        context.getBean("hello", HelloWorld.class).sayHello();
    }
}

5、简单分析

这里我们这是简单介绍一下基本原理,Spring整个Bean的创建过程很复杂,之后再一点点介绍!

  1. 底层利用通过反射机制调用无参数构造方法

明解Spring6(一)------邂逅

明解Spring6(一)------邂逅

  1. Spring创建对象就像之前我们使用Properties的步骤类似,它使用的是dom4j解析beans.xml文件,从中获取class属性值,类的全类名;然后通过反射机制调用无参数构造方法创建对象
Class.forName("com.ty.demo.bean.HelloWorld").getDeclaredConstructor().newInstance()
  1. 创建好的对象存储在ConcurrentHashMap中

bean对象最终存储在spring容器中,在spring源码底层就是一个map集合,存储bean的map在DefaultListableBeanFactory类中:

明解Spring6(一)------邂逅

Spring容器加载到Bean类时 , 会把这个类的描述信息, 以包名加类名的方式存到beanDefinitionMap 中,
Map<String,BeanDefinition>

6、SpringTest测试

前面通过ApplicationContext启动了Spring容器,并实现了配置文件的加载,但这样处理并不能体现出Spring的运行特征。为了更好地还原现实的开发场景,可利用SpringTest依赖库和JUnit实现测试环境下的Spring容器启动,且可以使用@Autowired代替getBean方法实现自动注入。

引入依赖

 <dependency>
     <groupId>org.junit.jupiter</groupId>
     <artifactId>junit-jupiter</artifactId>
     <version>5.8.2</version>
     <scope>test</scope>
</dependency>
<dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-test</artifactId>
     <version>6.0.4</version>
</dependency>

测试程序

@ContextConfiguration(locations = {"classpath:bean.xml"})
@ExtendWith(SpringExtension.class)
public class MyTest {
    @Autowired
    private HelloWorld helloWorld;
    @Test
    void testHelloWorld() {
        helloWorld.sayHello();
    }
}

7、整合logback日志

在项目开发中,日志十分的重要,不管是记录运行情况还是定位线上问题,都离不开对日志的分析。日志记录了系统行为的时间、地点、状态等相关信息,能够帮助我们了解并监控系统状态,在发生错误或者接近某种危险状态时能够及时提醒我们处理,同时在系统产生问题时,能够帮助我们快速的定位、诊断并解决问题。

引入依赖

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.4.5</version>
    <scope>test</scope>
</dependency>

测试程序

明解Spring6(一)------邂逅

明解Spring6(一)------邂逅

发表回复