0.对前面学习的总结
前面初步使用了Spring MVC 和 Spring Boot,大概总结如下:
-
Controller 和 java与页面的交互,都是Spring MVC的功能
M: Model模型,狭义上指实体类, 广义上指除了控制器之外的其它java类
V: View视图,html页面, 即显示给用户看到的界面
C: Controller控制器,Controller类,视图与java进行数据交互的
Spring MVC框架简化了V和C之间的数据交互过程
-
Spring Boot:
boot : 启动
支持Spring Boot框架的项目内置了很多配置,这些配置都是大部分框架的常规配置,当我们使用这些框架时,就免去了那些繁琐的配置。除了这些配置之外,还包含了一些约定:例如static文件夹存放静态资源等。
1.什么是Spring?
Spring本身的意思是春天、泉水,Spring是一个基础框架,Spring MVC、Spring Boot、Spring Data、Spring Security、Spring Validation、Spring Cloud都是Spring框架衍生出来的以及众多以Spring开头的框架的基础。它的出现是现在java能够长盛不衰的主要原因。因为它提供了一个java开发的生态环境,几乎市面上任何通用的常用需求,Spring都有解决方案。
2.Spring框架的功能
-
Ioc\DI (主要讲解内容):Ioc是一种思想,DI是基于Ioc的操作。
-
Aop (最后讲解内容):
2.1 什么是Ioc?
-
Ioc(Inversion of Control) 翻译为:控制反转,正常的控制称之为主动控制
-
主动控制: 我们编写的程序主动控制对象组件的产生,再编写对象组件互相调用,以完成程序功能
-
控制反转: 我们编写的程序需要的对象组件保存在外部容器中,需要时从容器中获取,之后调用方法实现功能
2.2 Spring 实现Ioc
(1)首先创建一个Maven项目,默认配置即可
注意:Maven项目不同于普通项目,因为里面包含pom.xml文件,可以添加组件依赖,方便管理依赖,而普通项目需要单独导入jar包,当jar包较多时,不方便管理,使用不便。Spring Boot内置Maven!
(2)项目的pom.xml中添加依赖
注意:先添加依赖(代码正确),再刷新Maven,等待下载完毕即可!
maven下载资源失败的解决方案:
1.检查setting.xml文件的位置是否设置了阿里\华为的镜像
2.退出所有防火墙或电脑管家等占用电脑资源的程序
3.更换网络再试(手机热点,手机共享网络给电脑)
4.安装maven(可以从苍老师网站下载)
5.删除.m2文件夹下的repository文件夹,再导入依赖,刷新Maven
(3)详细步骤
在src/main/java路径下创建一个包hello,包下创建类Stu,类中编写代码如下:
package hello;
?
/**
* 1.实体类Stu
*/
public class Stu {
private String name;
private Integer age;
?
public String getName() {
return name;
}
?
public void setName(String name) {
this.name = name;
}
?
public Integer getAge() {
return age;
}
?
public void setAge(Integer age) {
this.age = age;
}
?
包下编写配置类Config,常见的将对象保存到Spring容器中的方法有两个
我们先介绍第一个:@Bean方式
package hello;
?
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
?
/**
* 2.Spring配置类
* 配置Spring容器中内容
*/
//标准情况下,Spring配置类要添加下面的注解:@Configuration
包下新建测试类:获得Spring容器中的对象
package hello;
?
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
?
/**
* 3.测试:获得Spring容器中的对象
*/
public class Test {
public static void main(String[] args) {
//初始化Spring容器,参数为配置类的反射
AnnotationConfigApplicationContext acac = new AnnotationConfigApplicationContext(Config.class);//快捷键ACAC,然后回车
?
//从Spring容器中获取对象
//getBean方法两个参数:
//1.想获得的对象在Spring容器中的id(唯一的名字:@Bean时对应方法名)
//2.获得对象类型的反射(不需要强转,直接返回这个类型的对象)
Stu stu = acac.getBean("stu",Stu.class);
System.out.println(stu);
acac.close();//关闭资源
}
}
?
输出结果:Stu{name=‘杨过‘, age=26}
2.3 利用单元测试框架JUnit运行Spring
我们之前使用过Junit来测试程序运行,Junit测试程序有很多好处,例如一个测试类可以编写多个可以直接运行的方法,来减少main方法和类的数量。
在maven项目的pom.xml文件中添加junit的依赖,刷新Maven
<!--JUnit单元测试框架-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
在我们测试Spring的方法中,发现实例化ACAC对象和ACAC对象的关闭是每次都要编写执行的,我们可以利用Junit的@Before和@After这两个注解减少冗余代码,简化编写。
src下面有两个路径:main和test,它们的子文件中都包含java文件夹,其中main下面的java主要存储我们日常编写的程序,test下面的java主要是对上面编写程序的测试,虽然名字相同,但是具有不同的功能。在编写测试类时,尽量与上面的包同名,以便测试类中可以直接调用编写好的类,不需要导包。同时,注意测试类的名字不能为Test,因为测试时需要用到@Test注解,会发生冲突。
在test/java路径下,新建包hello,编写HelloTest的代码如下:
package hello;
?
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
?
public class HelloTest {
//声明ACAC为成员变量,方便每个方法使用(Java中叫做成员变量,有的语言中也叫全局变量)
AnnotationConfigApplicationContext acac;
?
//测试类中的@Before注解,注解下的方法会在@Test标记的方法运行之前自动运行
输出结果:Stu{name=‘杨过‘, age=26}
2.4 将对象保存到Spring容器中方法2:Spring 组件扫描
除了上面章节中给大家介绍的@Bean方式可以将对象保存到Spring容器中,还有组件扫描方式可以将对象保存到Spring容器中。
在main/java路径下,新建包ioc,包中创建类Hero
package ioc;
?
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
//@Component(组件):这个注解一旦标记,表示当前类要自动实例化对象,保存到Spring容器中
//这个对象保存到Spring容器中的id就是当前类名,只是首字母要小写
//此处直接设置对象属性,适用于对象属性不需要改变的情况
包下编写配置类Config
package ioc;
?
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
?
同样按照上面的方法,转到测试类进行测试
package ioc;
?
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
?
public class IocTest {
AnnotationConfigApplicationContext acac;
输出结果:Hero{name=‘关羽‘, job=‘战士‘}
组件扫描流程:
1.实例化ACAC对象时,指定Config类的反射
2.这个Config类上如果有@ComponentScan注解,并指定了包
3.扫描这个包中的所有类(包括子孙包中的所有类)
4.如果某个类上带有支持组件扫描方式保存该类对象到Spring容器的注解(例如@Component)
5.实例化这个对象,并将这个对象保存到Spring容器中,注意:id是这个类名首字母小写
2.5 保存到Spring的细节
-
除了@Component,还有以下常见注解有相同功能
-
@Controller \ RestController 控制器
-
@Service 业务逻辑
-
@Repository 数据访问
-
.....
-
为什么功能一样要设计这么多的注解呢?原因是见到注解名称,就知道这个类的作用(见名知意)
-
关于特殊的类名
我们已经学习使用组件扫描时,当前类型对象的id是当前类名首字母小写,但是有特殊情况:如果有类名连续两个以上字母都是大写,例如:VIPStu,那么它保存到Spring容器中的id就是类名(原类名)。
-
自定义组件ID
如果有特殊原因不能或不希望类名做id,我们可以自定义组件id,例如:
当然,其它支持组件扫描的注解,也可以用这个方法定义id,一般都是被动使用。
3.Spring中对象的作用域
3.1 作用域(Scope)概述
我们的飞机大战项目中,一共有6个类型,这6个类型中就涉及了我们要讲的两种作用域:
单例(singleton):从程序开始到程序结束,某个类型的对象始终只有一个
天空和英雄机就是单例
原型(prototype): 从程序开始到程序结束,某个类型不断出现新的对象,没有数量的限制(需要时就初始化创建新的对象)
小敌机,大敌机,小蜜蜂,子弹
3.2 单例(singleson)作用域
默认情况下,所有保存到Spring容器中的对象都是单例(singleton)(节省内存,原型要创建多个对象,占用内存)
3.3 原型(prototype)作用域
修改默认scope,将对象修改为prototype(原型)的scope
@Bean保存方式中的设置:
组件扫描方式保存的设置:
测试代码:
今后使用Spring框架的时候,如果需要设置Scope,根据这两个特性来设置修改即可。
4.惰性初始化(懒惰初始化)
Spring容器中大部分对象都是单例的,单例对象会在实例化Spring容器时进行对象的实例化,也就是说实例化Spring容器时会有大量对象进行实例化。针对实例化时会消耗较多资源,但是运行过程中又不一定使用到的对象,我们可以设置为懒惰初始化。
使用@Lazy表示懒惰初始化
@Bean对象设置懒惰加载:
组件扫描的方式设置懒惰加载:
测试代码:
package ioc;
?
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
?
public class IocTest {
AnnotationConfigApplicationContext acac;
输出结果:
init方法运行完毕
lazy方法运行
Hero实例化
Hero{name=‘关羽‘, job=‘战士‘}
非Lazy情况下输出结果:
Hero实例化
init方法运行完毕
lazy方法运行
Hero{name=‘关羽‘, job=‘战士‘}
正常情况下,是在init的时候实例化对象,而输出的结果证明我们的设置生效了,是在acac.getBean方法的位置才实例化的Hero对象。如果方法中没有获得Hero对象的代码,这个对象就不会实例化了。所以通过使用惰性初始化可以针对个别对象节省内存。
什么时候需要使用这个对象?什么时候懒惰初始化的对象才会实例化?
惰性初始化可以针对个别对象节省内存,不宜大范围使用,因为大范围使用会引起运行压力大\缓慢。由于prototype是在需要的时候产生对象,所以不能和Lazy一起使用。
5.DI
5.1 什么是DI?
DI(Dependency Injection)依赖注入
Ioc是控制反转,它是一种编程思想,DI是基于控制反转思想的一种操作,要明确依赖注入的原因和作用,我们要先了解"依赖的概念"。
5.2 什么是依赖?
依赖指程序中A类需要使用到B类型对象的情况,那么就说A类依赖B类。
要在Spring中实现这个依赖过程,我们要创建如下类来演示:
青龙偃月刀类
package san;
?
public class DragonBlade {
private String name = "青龙偃月刀";
?
public String getName() {
return name;
}
?
public void setName(String name) {
this.name = name;
}
?
关羽类
package san;
?
public class GuanYu {
private String name = "关云长";
//在GunaYu类中声明了属性DragonBlade
//这就是依赖关系,关羽依赖青龙偃月刀
private DragonBlade dragonBlade;
?
public void fight(){
//dragonBlade自动调用toString输出名字
System.out.println(name+"使用"+dragonBlade+"战斗");
}
public String getName() {
return name;
}
?
public void setName(String name) {
this.name = name;
}
?
public DragonBlade getDragonBlade() {
return dragonBlade;
}
?
public void setDragonBlade(DragonBlade dragonBlade) {
this.dragonBlade = dragonBlade;
}
?
}
?
关羽依赖青龙偃月刀,在配置类中:
package san;
?
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
?
测试类让关羽战斗:
package san;
?
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
?
public class SanTest {
AnnotationConfigApplicationContext acac;
我们经过配置看到测试代码,逻辑和之前主动控制没有什么多大区别,但是使用依赖注入可以简化这个流程。
实现依赖注入:修改Config配置
package san;
?
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
?
测试类修改代码运行: