Spring 创建和使用
大家好 , 这个专栏给大家介绍一下 Java 家族的核心产品 - SSM 框架
Java 语言能走到现在 , 仍然屹立不衰的原因 , 有一部分就是因为 SSM 框架的存在
接下来 , 博主会带大家了解一下 Spring、Spring Boot、Spring MVC、MyBatis 相关知识点
并且带领大家进行环境的配置 , 让大家真正用好框架、学懂框架
目标 :
- Spring (Core) 项目创建
- 将对象存储到 Spring 中
- 将对象从 Spring 里面取出
在 Spring 里面 , 把对象叫做 Bean
一 . 创建 Spring 项目
我们接下来用 Maven 创建一个 Spring 项目
- 创建一个 Maven 项目
- 添加 Spring 框架支持 (spring-context : 上下文 spring-beans : 对象)
- 添加启动类
我们这里使用的环境是 IDEA 2021.2 社区版 , JDK 1.8
1.1 创建一个 Maven 项目
1.2 添加 Spring 框架依赖
添加两个引用 :
- spring - content : spring 上下文
- spring - beans : spring 对象
这一步很简单 , 我们只需要把下面这段代码复制到 pom.xml 里面
当然也可以去中央仓库里面找
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
</dependencies>
在这一步 , 我们一定要保证修改镜像源为国内的 , 要不然 超级大概率 会失败
我们还可以通过左侧来检验下载是否成功
我们点击上面的刷新按钮
但是我们下载的依赖只有 spring - content 和 spring - beans 啊 ? 这怎么多出来几个 ?
是这样的 , 我们的 spring - content 和 spring - beans 也是需要额外的依赖包的 , 如果单纯使用 spring - content 和 spring - beans , 也需要把它的几个依赖包导入进来 , 其实就相当于结婚的嫁妆
1.3 添加启动类
实际上就是添加 main 方法
那我们之前写 Servlet 程序 , 也没有 main 方法啊 ?
实际上 , Servlet 是隐藏 main 方法的 , 是不展示给用户的
我们这里的 Spring 程序就是一个最简单的程序 , 还是需要 main 方法的
在 java 目录底下新建一个类
二 . 将 Bean 对象存储到 Spring
存储 Bean 对象需要两步 :
- 存储 Bean 之前 , 先得有 Bean , 所以先创建一个 Bean 对象
- 将创建的 Bean 注册到 Spring 容器中
2.1 创建 Bean 对象
public class User {
// 普通的类,普通的方法
public void sayHi() {
System.out.println("say hi!");
}
}
2.2 将 Bean 存储到 Spring
在 Spring 里面 , 并不是所有的 Bean 都会被存储到 Spring 里面 , 只有需要的才会存储到 Spring 里面
而这个存储 , 也不是真正意义上的存储 , 只是告诉 Spring , 这个 Bean 需要托管给 Spring
那么我们接下来需要在 resources 里面新建一个文件 , 叫做 spring-config.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">
</beans>
目前的只是准备工作 , 我们接下来才是将 Bean 存储到 Spring 的核心操作
<?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">
<!-- 注册一个对象到 Spring 中 -->
<!-- 先写一个 bean 标签 -->
<bean id="user" class="User"></bean>
</beans>
里面的 class 也是需要写包名的 , 但是因为我们没创建包 , 所以就没写
那么我们可以改一下 , 把代码放到包里面
目前为止 , 我们只是声明存储对象到 Spring 里面了 , 并不是真正存储到 Spring 里面了 , 正常情况下 , 我们的 Spring 是懒加载 , 在启动 Spring 的时候 , 他并不会立即存储 Spring 对象 , 但是从广义上来理解 , 我们的 Spring 已经存储这个对象了
2.3 从 Spring 中取出 Bean 对象
2.3.1 先得到 Spring 对象(Spring 上下文对象)
对象都交给 Spring 管理了,所以获取对象要从 Spring 中获取,那么就得先得到 Spring 的上下文。
Spring 上下文对象可使用 ApplicationContext
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
// 1. 得到 Spring 上下文对象
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
}
}
2.3.2 从 Spring 上下文当中取出对象
先不管选哪个 , 我们注意 : getBean 返回值是 Object , 但是我们的对象是 User 类型 , 所以我们需要强转一下
import com.ethan.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
// 1. 得到 Spring 上下文对象
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
// 2. 从 Spring 里面获取对象
// User user = new User();//在当前类当中创建 User 对象,原始方式不推荐[存在代码耦合]
User user = (User)context.getBean("user");//只有第一次执行,才会创建.因为是单例模式,创建过后会一直留着,下次用直接拿走
}
}
2.3.3 使用 bean (非必须)
之前怎么使用 , 现在就怎么使用
import com.ethan.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
// 1. 得到 Spring 上下文对象
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
// 2. 从 Spring 里面获取对象
// User user = new User();//在当前类当中创建 User 对象,原始方式不推荐[存在代码耦合]
User user = (User)context.getBean("user");//只有第一次执行,才会创建.因为是单例模式,创建过后会一直留着,下次用直接拿走
// 3. 使用 bean
user.sayHi();//跟以前一样
}
}
运行一下 :
2.4 特殊情况
2.4.1 xml 配置文件一定要写正确
我们之前说 resources 目录底下的配置文件推荐大家使用 spring-config.xml
那么这个名字其实叫什么都行
那我们改成 spring 看看还能不能成功
成功了
但是这种虽然合法但是不规范
那我们试一下这种情况 :
配置文件名称叫 spring-config.xml , 我们的代码里面写成了 spring.xml
那么运行一下 , 看看会不会运行起来
那么我们帮大家排查一下错误
那么就是 spring.xml 没有找到
因为我们的配置文件叫 spring-config.xml , 不是 spring.xml , 所以就会报错
而且下面还有行数提醒
2.4.2 bean 标识一定要写正确
我们运行一下
2.5 getBean 方法的更多用法
2.5.1 getBean(String s)
我们之前介绍的方法 :getBean(String s)
: 根据名称来获取 bean 对象
这种方法需要进行强转 , 但是他有一个好处 : 我们注册多个 bean , 他会精准定位
比如 :
那么我们的这种方法能正常识别吗 ?
那么这种情况还是挺常见的 : 在公司协同开发 , 张三使用了 user , 李四不知道 , 李四也使用了 user , 这样就产生了重复
原则上来说 , 一个 key 只能出现一次 , 我们就可以把这个第二个 user 改成 user2
那有的人就问了 , 怎么证明 User 和 User2 是不一样的呢 ? 因为他们打印的结果相同啊 , 就会有人以为他们是不是一个东西起了两个名字啊 , 那很好证明
那么我们还可以去证明 user 是不是单例模式
为 true 就代表是单例模式 , 因为 Spring 中创建出一个对象就会一直存储到 Spring 中 , 方便后续使用 , 就不用再去重新创建了
2.5.2 getBean(class<T> aclass)
getBean(class<T> aclass)
: 根据类型获取 bean 对象
import com.ethan.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
// 1. 得到 Spring 上下文对象
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
// 2. 从 Spring 里面获取对象
// 2.1 根据一个 String 参数获取 bean
//User user = (User)context.getBean("user");//只有第一次执行,才会创建.因为是单例模式,创建过后会一直留着,下次用直接拿走
// 2.2 根据一个 Class 参数获取 bean
User user = context.getBean(User.class);//这种方法不用强转了
// 3. 使用 bean
user.sayHi();//跟以前一样
}
}
这种情况是 spring-config.xml 里面只有一个 bean 对象
那么有两个呢 ?
再运行一下 :
看一下异常
我们再看一下 spring-config.xml
import com.ethan.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
// 1. 得到 Spring 上下文对象
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
// 2. 从 Spring 里面获取对象
// 2.1 根据一个 String 参数获取 bean
/*User user = (User)context.getBean("user");
User user1 = (User)context.getBean("user");
User user2 = (User)context.getBean("user2");
System.out.println("user:" + user);
System.out.println("user1:" + user1);
System.out.println("user2:" + user2);
System.out.println("user == user1 -> " + (user == user1));
System.out.println("user == user2 -> " + (user == user2));//为true说明他们地址相同,是一个对象;为false说明地址不同,不是同一个对象*/
// 2.2 根据一个 Class 参数获取 bean
User user = context.getBean(User.class);//这种方法不用强转了
// 3. 使用 bean
user.sayHi();//跟以前一样
}
}
2.5.3 getBean(String s , class<T> aclass)
getBean(String s , class<T> aclass)
: 这种方法更加稳健 , 虽然他比第一种也没差哪去 , 不过如果是第一种方法 , 万一内容为 null , 对 null 进行强转就会出现问题 , 但是你通过这种方法的话 , 整体就会返回一个 null , 就不存在空指针问题了
import com.ethan.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
// 1. 得到 Spring 上下文对象
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
// 2. 从 Spring 里面获取对象
// 2.1 根据一个 String 参数获取 bean
/*User user = (User)context.getBean("user");
User user1 = (User)context.getBean("user");
User user2 = (User)context.getBean("user2");
System.out.println("user:" + user);
System.out.println("user1:" + user1);
System.out.println("user2:" + user2);
System.out.println("user == user1 -> " + (user == user1));
System.out.println("user == user2 -> " + (user == user2));//为true说明他们地址相同,是一个对象;为false说明地址不同,不是同一个对象*/
// 2.2 根据一个 Class 参数获取 bean
//User user = context.getBean(User.class);//这种方法不用强转了
// 2.3 根据 String(bean id) 和 Class 参数获取 bean
User user = context.getBean("user",User.class);//更加的稳健
// 3. 使用 bean
user.sayHi();//跟以前一样
}
}
同样的方式 , 我再去读 user2 也是没问题的
2.6 面试题 : ApplicationContext VS BeanFactory
Spring 上下文对象可使用 ApplicationContext , 我们还可以使用 BeanFactory (ApplicationContext的父类) 来作为 Spring 的上下文
不过 IDEA 已经不推荐我们使用这种方法了
import com.ethan.User;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class App {
public static void main(String[] args) {
// 1. 获取上下文对象
BeanFactory context = new XmlBeanFactory(new ClassPathResource("spring-config.xml"));
// 2. 从 Spring 里面获取对象
User user = context.getBean("user2", User.class);
// 3. 使用方法
user.sayHi();
}
}
运行一下 :
那么既然他们都能运行成功 , 那有什么区别呢 ?
- 共同点 : 都是用来获取 Spring 的上下文对象
- 不同点 :
- 继承关系和功能 : ApplicationContext 属于 BeanFactory 的子类 , ApplicationContext 具备了 BeanFactory 所有的功能 , 而 BeanFactory 只具备最基础访问 Bean 的功能 . Application 还具备了更多的能力 , 比如 : 国际化支持 资源访问支持 事件传播 …
- 从性能方面来说 , ApplicationContext 是一次性加载并初始化所有的 Bean 对象 , 而 BeanFactory 是按需加载 , 需要使用 Bean 的时候才去加载
三 . 总结
- 操作容器之前,先要有容器,所以先要得到容器 , 就需要我们创建 Spring 项目
- 存对象
- 创建 Bean(普通类)。
- 将 Bean 注册(配置)到 spring-confing.xml 中。
- 取对象
- 得到 Spring 上下文,并读取到 Spring 的配置文件。
- 获取某⼀个 Bean 对象。
- 使用 Bean 对象。