# Spring 介绍

官方文档:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html

spring 是一个开源的 java 框架,一个轻量级的、非入侵式的框架。

spring 框架是以 interface21 框架为基础,经过重新设计,并且不断丰富内容。于 2004 年 3 月 24 日发布了 1.0 正式版

spring 理念:使现有点技术更加容易使用,它本身是一个大杂烩,整合了现有的技术框架!

spring 核心技术:IOC (控制反转), AOP (面向切面编程)

Spring 特点

  • 方便解耦,简易开发
  • AOP 编程的支持
  • 方便程序测试
  • 方便和其他框架整合,像 SSM(SpringMvc + Spring + Mybatis)
  • 方便进行事务的操作
  • 降低 API 的使用难度

# Spring 7 大模块

image-20211116192833518

核心容器(Spring Core):核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转 (IOC)模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。

Spring 上下文(Spring Coutext):Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。

AOP:通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。

Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。

Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。

Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。

Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。

# IOC 概念

IOC,Inversion of Control : 控制反转, 是一个理论,一个指导思想。 指导开发人员如何使用对象,管理对象的。 把对象的创建,属性赋值,对象的声明周期都交给代码之外的容器管理。

在 Spring 之前的业务中,用户的需求可能会影响我们原来的代码,需要根据用户的需求去修改代码,如果程序代码量很大,修改一次的成本就会变得十分昂贵

例如:

// 定义 DAO 接口
public interface UesrDAO {
    void getUser();
}
//DAO 接口实现类
public class UserDAOImpl implements UesrDAO{
    @Override
    public void getUser() {
        System.out.println("默认获取用户的信息");
    }
}
// 业务层接口
public interface UserService {
    void getUser();
}
// 实现业务层调用 DAO 层
public class UserServiceImpl implements UserService{
    private UesrDAO uesrDAO=new UserDAOImpl();
    public void getUser() {
        uesrDAO.getUser();
    }
}
public class MyText {
    public static void main(String[] args) {
        // 用户实际接触的是业务层,dao 层是不需要接触的
        UserService userService=new UserServiceImpl();
        userService.getUser();
    }
}
// 控制台输出:默认获取用户的信息

这时候有个需求说要增加一个实现 mysql 获取用户信息,这时候我们就需要添加 DAO

// 实现 MySQL 获取用户数据
public class UserDAOMySQLImpl implements UesrDAO{
    public void getUser() {
        System.out.println("MySQL获取用户数据!");
    }
}

为了让客户使用到 MySQL 获取用户信息就一定要去业务层修改

// 实现业务层调用 DAO 层
public class UserServiceImpl implements UserService{
    // 这里原本的 UserDAOImpl 要改成 UserDAOMySQLImpl
    private UesrDAO uesrDAO=new UserDAOMySQLImpl();
    public void getUser() {
        uesrDAO.getUser();
    }
}

当代码量十分庞大的时候,实现了多个对象,我们就需要改动很多的地方,这是一个很大的代价,并且十分麻烦,这就造成了用户需求变了,就得去改动大量的代码

所以需要一个方法来解决这个问题,使对象生成不在写死,而是改成由用户需求来生成对象

private UesrDAO uesrDAO;
    public void setUesrDAO(UesrDAO uesrDAO) {
        this.uesrDAO = uesrDAO;}

使用 set 进行动态实现值的注入,这使得程序可以实现需要什么实现就用对应的接口

之前,程序是主动创建对象!控制权在程序员手上!所以用户的改动需求就需要程序员去改动代码。

使用了 set 注入后,程序不再具有主动性,而是变成了被动的接受对象!

这种思想,从本质上解决了问题,我们程序猿不用再去管理对象的创建了,系统的耦合性大大降低,可以更加专注的在业务都实现上!这是 IOC 的原型!

IOC 本质:控制反转 IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现 IoC 的一种方法,也有人认为 DI 只是 IOC 的另一种说法。没有 IOC 的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。

控制反转是一种通过描述(XML 或注解)并通过第三方去生产或获取特定对象的方式。在 Spring 中实现控制反转的事 IoC 容器,其实现方法是依赖注入(Dependency Injection,DI)。

# AOP 概念

AOP (Aspect Orient Programming),直译过来就是 面向切面编程。AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。

为什么需要 AOP:在开发的多个模块过程中,这些模块会有某段代码是重复的,我们经常把这段代码抽象成 一个方法,然后再我们需要这段代码的时候去调用这个方法,这样的话但需要改变这段代码的时候直接改变这个方法就好了。然而需求总是变化的,有一天,新增了一个需求,需要再多出做修改,我们需要再抽象出一个方法,然后再在需要的地方分别调用这个方法,又或者我们不需要这个方法了,我们还是得删除掉每一处调用该方法的地方。实际上涉及到多个地方具有相同的修改的问题我们都可以通过 AOP 来解决。

AOP 要达到的效果是,保证开发者不修改源代码的前提下,去为系统中的业务组件添加某种通用功能。

按照 AOP 框架修改源代码的时机,可以将其分为两类:

  • 静态 AOP 实现, AOP 框架在编译阶段对程序源代码进行修改,生成了静态的 AOP 代理类(生成的 *.class 文件已经被改掉了,需要使用特定的编译器),比如 AspectJ。
  • 动态 AOP 实现, AOP 框架在运行阶段对动态生成代理对象(在内存中以 JDK 动态代理,或 CGlib 动态地生成 AOP 代理类),如 SpringAOP。

Spring 中的 AOP 是通过动态代理实现的。

# 第一个 Spring 程序

构建一个 maven 项目

然后再 pom.xml 文件中加入

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.10</version>
</dependency>

导入 org.springframework 包会自动帮我们导入其他的包,像 spring-beans、spring-core 之类的组件会自动导入

// 创建一个类
public class Hello {
    private String str;
    public String getStr() {
        return str;
    }
    public void setStr(String str) {
        this.str = str;
    }
    
    public String toString() {
        return "Hello{" +
                "str='" + str + '\'' +
                '}';
    }
}

配置元数据(现在的 Spring 已经可以使用注解去配置了,但这里展示的还是使用 xml 配置)

在 resource 文件夹下创建 beans.xml (官方的名字是:applicationContext.cml,但这里命名无所谓)

<?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:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
    <!-- 使用 Spring 来创建对象,在 Spring 这些都成为 bean-->
    
    <!-- <bean id="自己命名" class="想要的类对应的路径" -->
    
    <!-- property 中 value 对应的是具体的值  ref 对应的是这个 spring 容器中创建好的对象(eg. 此处的 hello)-->
    
    <bean id="hello" class="com.Spring.Hello"> 
        <!--<property name="想要设置字段的名字" value="设置的值"></property> -->
        <property name="str" value="Spring"></property>
    </bean>
</beans>
public class MyText {
    public static void main(String[] args) {
        // 获取 Spring 的上下文对象,拿到 Spring 容器
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        // 我们的对象现在都在 Spring 管理了,我们需要使用就直接去里面取出来就好了
        Hello hello = (Hello) context.getBean("hello");
        System.out.println(hello.toString());
    }
}
// 然后控制台就输出了:Hello {str='Spring'}
  • hello 是谁创建的?
    • hello 是由 Spring 创建的
  • hello 对象的属性怎么设值?
    • hello 对象的属性是由 Spring 容器控制的
<!--
    类型 变量名 = new 类型 ();
    Hello hello = new Hello ():
    
    id = 变量名
    class = new 的对象
    property 相当于给对象中的属性设置一个值
 -->

这个过程就叫控制反转:

控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用 Spring 后,对象由 Spring 来创建

反转:程序本身不创建对象,而变成被动的接收对象。

依赖注入:就是利用 set 方法来进行注入。

IOC 是一种编程思想,由主动点编程编程被动的接收。

要实现不同的操作,只需要在 xml 配置文件中进行修改,所谓的 IOC 就是:对象由 Spring 来创建,管理,装配!

# IOC 创建对象的方式

  1. 它是使用无参的构造方法来创建对象的,也是它的默认实现
public class User {
    private String name;
    public  User(){
        System.out.println("无参构造方法");
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void show() {
        System.out.println("name=" + name);
    }
}

配置文件

<?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:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
    <bean id="user" class="com.Spring.User">
        <property name="name" value="张三"></property>
    </bean>
</beans>

测试

public class MyText {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        User user = (User) context.getBean("user");
        user.show();
    }
}
// 控制台输出的是:
//				无参构造方法
//				name = 张三

和我们普通创建对象一样,在没有参数的构造方法的时候,它默认走的是无参的构造方法来创建对象

  1. 假设我们使用有参构造来创建对象

    1. 通过下标赋值
    public class User {
        private String name;
    // 当设立一个有参构造方法时,如果不再设置无参构造方法,那就不会有无参的构造方法
    // 当完全没有构造方法时,系统默认有个无参的构造方法
        public  User(String name){
            this.name = name;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public void show() {
            System.out.println("name=" + name);
        }
    }
    <?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:util="http://www.springframework.org/schema/util"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
        <bean id="user" class="com.Spring.User">
             <constructor-arg index="0" value="李四"></constructor-arg>
        </bean>
    </beans>
    1. 用过参数类型来赋值
    <?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:util="http://www.springframework.org/schema/util"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
        <bean id="user" class="com.Spring.User">
              <constructor-arg type="java.lang.String" value="王五"></constructor-arg>
        </bean>
    </beans>

    当参数类型一样的时候,就会按照顺序赋值

    1. 通过参数名来赋值
    <?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:util="http://www.springframework.org/schema/util"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
        <bean id="user" class="com.Spring.User">
              <constructor-arg name="name" value="李六"></constructor-arg>
        </bean>
    </beans>

总结:在配置文件加载的时候,容器中管理的对象就已经初始化了!

有待更新...