Spring 如何入门

Spring 入门

现在很多企业级的项目都是基于 spring 框架开发的,而这两年很火的微服务概念就有基于 springboot springcloud 等框架,spring 框架解决企业应用的复杂性和耦合性,对于一个Java 程序员来说,要想学习 springboot 和 springcloud,掌握 spring 的基础用法是一件必须做的事情。那么本文就带领大家一起来理解下 spring 的基本概念以及通过一些案例来快速配置 spring,从而理解 spring 的 IoC 和 DI 概念。

学习 Springboot 和 Springcloud 要不要跳过 spring?我的建议是不要,应该先学习 spring 和 spring mvc,然后进阶去学习 Spring其他框架,会更便于你理解。

什么是 spring

Spring 是一个以 IoC (英文 Inversion of Control,控制反转)和 AOP(Aspect Oriented Programming)为内核的框架,那么啥是 IoC?AOP 又是个什么鬼?

什么是 IoC

IoC 是 spring 的基础,通过 IoC 可以实现控制,在学 Java 基础的时候我们知道调用 new 关键词来构造一个方法创建对象,而在 Spring 中创建对象就是用 IoC。spring 中的 IoC 方式对象的生命周期管理由Spring 框架提供的 IoC 容器来管理,我们可以直接从 IoC 容器中获取对象,控制权从应用程序交给了 IoC 容器。

什么是 DI

而 DI(Dependency Inject,依赖注入)与 IoC 其实含义是一样的,DI 就是对象的属性,已经被注入好相关的 value,直接使用即可。所谓的依赖注入就是由IoC 容器在运行期间动态的将某种依赖关系注入到对象之中。

什么是 AOP

而AOP 是面向切面编程,它是通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术,AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。

我相信你对于上面的话每一个字你都能看懂,但是连在一起你就不知道是什么意思了,没关系,我们继续往下看。

在我们学习 Java 面向对象的时候,我们都知道,如果你写的代码出现重复了可以将重复的代码做如下操作:

  • 抽取成方法
  • 抽取类

抽取成类的方式我们称之为纵向抽取,我们可以通过继承的方式实现纵向抽取。但是在负载的业务场景下即使抽取成类还是会出现重复的代码,因为一些逻辑(开始、结束、提交事务)依附在我们业务类的方法逻辑中!

而AOP的理念则是将分散在各个业务逻辑代码中相同的代码通过横向切割的方式抽取到一个独立的模块中!

使用 Ioc/DI 的优势是啥

  • 可维护性好,方便进行单元测试和故障诊断,因为代码中的每一个 class 都可以单独测试,彼此之间没有联系,组件之间低耦合或者无耦合。
  • 每个开发团队不需要关心别人写的业务逻辑,便于分工协助。
  • 可复用性好,我们可以把具有普遍意义的组件独立出来反复使用到其他部门或其他项目中,其实这也是面向对象的思想。
  • 生成对象的方式转为外置方式,在 Spring 中,我们把生成的对象直接写在配置文件中去定义使用,当我们需要更换一个实现类时,只需要修改配置文件即可,具有热插拔的特点。

入门 spring

spring 框架不断的升级,目前最新版本已经更新到了 5.2.5 了。但是在本文中我们使用的是4.3.6 来讲解,为了更好的创建一个 spring 项目我们使用的工具有下面:

  • maven
  • idea 社区版

同时由于 spring 的 jar 包 都在国外服务器,下载速度慢,所以我们使用阿里云的仓库进行加速

打开maven的配置文件(windows机器一般在maven安装目录的conf/settings.xml),在标签中添加mirror子节点插入如下代码就可以使用阿里云的镜像加速下载 jar 包了:

1
2
3
4
5
6
<mirror>
<id>aliyunmaven</id>
<mirrorOf>*</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>

使用 idea 创建一个 maven 项目

并在 pom.xml 的dependencies中添加如下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>

其中

  • spring-core 包含了 spring 框架基本的核心工具类,spring 其他组件,比如 spingboot、springcloud 都要用到它。
  • spring-beans 所以应用都要用到的 jar 包,它包含了访问配置文件、创建和管理 bean 以及进行 Ioc和 DI 操作的相关类。
  • spring-context 提供了基础的 IoC 功能上的扩展服务。还提供了远程访问、缓存、邮件服务等各种试图层框架的封装。

spring 的核心容器

spring 框架提供了两个最基本最重要的包 org.springframework.beans.factoryorg.springframework.context。前者的主要接口是 BeanFactory,后者的主要接口是 ApplicationFactory

IoC 框架的主要组件有 Beans、配置文件 applicationcontext.xml、Beanfactory 接口和相关类、ApplicationContext 接口和相关类。

Beans 是项目中为业务提供功能的 Bean,就是容器要管理的 Bean,也就是我们场景的 JavaBean、Java 类。

在 Spring 中 Bean 的管理是基于配置文件配置的,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?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-4.3.xsd">
<!--
xmlns 即 xml namespace xml 使用的命名空间
xmlns:xsi 即 xml schema instance xml 遵守的具体规范
xsi:schemaLocation 本文档 xml 遵守的规范 官方指定
-->
<bean id="userService" class="org.example.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userDao" class="org.example.UserDaoImpl"></bean>
</beans>

配置文件的名称可以是 applicationcontext.xml,也可以是其他,通常我们习惯使用 applicationcontext。

如上所示,在配置文件中xml 标签内的内容是指将定义xml 所遵循的规范。

Spring 要取得该 Bean 类,是根据bean标签中的 id、class 和 property name 和 ref 找到实例对象,从而获取对象的相关属性和值。通常多个 bean 包含在 beans 标签内,而 property 是 bean 对应的子元素。

BeanFactory 使用工厂涉及模式,负责读取 Bean 的配置文件,管理对象的生命周期和依赖关系,包括创建、加载和维护等。

org.springframework.beans.Factory.BeanFactory 是 BeanFactory 的顶级实现类。它会根据配置文件中的定义装载 Bean。Beanfactory 的常用方法有:

  • getBea(String name) 可根据 Bean 的 id 生成 Bean 对象。
  • getBean(String name,Class requiredType) 可根据 Bean 的 id 和响应类生成 Bean 的对象。

ApplicationContext 接口提供了高级功能的容器,基本与BeanFactory 一致,不同之处是:

  • 提供访问资源更方便。
  • 支持国际化消息。
  • 提供文件消息解析的方法
  • 可以发布事件。

ApplicationContext 接口实现类有:

  • FileSystemXmlApplicationContext,从文件使用绝对路径引入 xml 文件加载上下文。
  • ClassPathXmlApplicationContext,从类路径的 XML 中加载上下文。
  • XmlWebApplicationContext,从 Web 系统中的 XML 加载上下文

上面我们说了依赖注入和控制反转是对同一件事件的不同说明,依赖注入是在使用 spring 框架创建对象时,动态的将其所依赖的对象注入 Bean 组件中,一般通过量子方式

  • 属性 setter 方法注入
  • 通过构造方法注入

我们通过具体的案例来讲解

在 idea maven 项目中的 src 目录下建立如下目录结构

首先,创建一个 UserDao 接口,定义一个 login()方法

1
2
3
4
5
6
package org.example;


public interface UserDao {
public void login();
}

然后创建一个 UserDao 接口的实现类UserDaoImpl

1
2
3
4
5
6
7
8
package org.example;

public class UserDaoImpl implements UserDao{
@Override
public void login() {
System.out.println("UserDao login");
}
}

在src 目录下创建一个 applicationContext.xml 文件的 beans 标签下配置

1
<bean id="UserDao" class="org.example.UserDaoImpl"></bean>

然后创建一个IoC,如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package org.example;

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

public class IoC {
public static void main(String[] args) {
//1.初始化 Spring 容器,加载配置文件
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("applicationContext.xml");
//2.通过容器获取 UserDao实例
UserDao userDao = (UserDao) applicationContext.getBean("UserDao");
userDao.login();
}
}

执行后如下所示,我们可以看到通过 Spring 加载配置文件就可以操作就可以调用 userDao 的 login()方法。

接下来演示 DI

首先,我们创建UserService接口

1
2
3
4
5
package org.example;

public interface UserService {
public void login();
}

然后创建一个UserServiceImpl 实现类并在类中调用 userDao 的 login()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package org.example;

public class UserServiceImpl implements UserService {
//声明 UserDao 属性
private UserDao userDao;
//添加 userDao 属性的 setter()方法,用于实现依赖注入
public void setUserDao(UserDao userDao){
this.userDao = userDao;
}
//实现接口中的方法
@Override
public void login() {
this.userDao.login(); //调用 userDao 中的 login() 方法
System.out.println("userService login");
}
}

然后在 applicationContext.xml 中配置property name 设置为

1
2
3
4
<bean id="userService" class="org.example.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
<bean id="UserDao" class="org.example.UserDaoImpl"></bean>

最后我们创建一个 DI.java,代码如下所示

1
2
3
4
5
6
7
8
9
10
11
12
package org.example;

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

public class DI {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) applicationContext.getBean("userService");
userService.login();
}
}

执行 DI,如下所示,我们可以看到Spring 通过userService实现类中的user.login()方法调用了 userDao 的 login()方法。