文章目录
Spring是什么?
Spring指的是Spring Framework(Spring框架),它是一个开源框架,有非常活跃和庞大的社区支持,Spring支持广泛的应用场景,可以让Java企业级开发更简单。Spring是包含众多工具方法的IoC容器,Spring最核心的功能就是容器管理。
IoC容器与DI
我们需要理解一下Spring是包含众多工具方法的IoC容器这句话,什么是IoC呢?IoC = Inversion of Control
翻译成中文就是控制反转,我们通过一个例子来解释一下这个名词。
构建⼀辆⻋(Car Class),然⽽⻋需要依赖⻋身(FrameWork Class),⽽⻋身需要依赖底盘(BottomClass),⽽底盘需要依赖轮胎(Tire Class),此时我们传统开发的方法来完成这个需求:
class Car {
private Framework framework;
public Car(int size) {
this.framework = new Framework(size);
}
public void init() {
System.out.println("构造Car");
framework.init();
}
}
class Framework {
private Bottom bottom;
public Framework(int size ) {
this.bottom = new Bottom(size);
}
public void init() {
System.out.println("构造Framework");
bottom.init();
}
}
class Bottom {
private Tire tire;
public Bottom(int size) {
this.tire = new Tire(size);
}
public void init() {
System.out.println("构造Bottom");
tire.init();
}
}
class Tire {
int size = 15;
public Tire(int size) {
this.size = size;
}
public void init() {
System.out.println("车轮子 = " + size);
}
}
public class Test {
public static void main(String[] args) {
int size = 20;
Car car = new Car(size);
car.init();
}
}
运行代码完成了需求,但是当我们需要增加一个属性的时候会非常困难,比如我们增加一个轮子的颜色属性。
可以看到,当我们更改了底层的代码,整个调用链上的所有代码都需要修改,如何解决这个问题呢?我们可以模拟一下IoC
容器,将传统的new对象的方式改为注入传递的方式。
class Car {
private Framework framework;
/*public Car(int size) {
this.framework = new Framework(size);
}*/
public Car(Framework framework) {
this.framework = framework;
}
public void init() {
System.out.println("构造Car");
framework.init();
}
}
class Framework {
private Bottom bottom;
/*public Framework(int size ) {
this.bottom = new Bottom(size);
}
*/
public Framework(Bottom bottom) {
this.bottom = bottom;
}
public void init() {
System.out.println("构造Framework");
bottom.init();
}
}
class Bottom {
private Tire tire;
/* public Bottom(int size) {
this.tire = new Tire(size);
}*/
public Bottom(Tire tire) {
this.tire = tire;
}
public void init() {
System.out.println("构造Bottom");
tire.init();
}
}
class Tire {
int size = 15;
String color;
public Tire(int size,String color) {
this.size = size;
this.color = color;
}
public void init() {
System.out.println("车轮子 = " + size);
}
}
public class Test {
private Car car;
private Framework framework;
private Bottom bottom;
private Tire tire;
public Test() {
this.tire = new Tire(20,"blue");
this.bottom = new Bottom(this.tire);
this.framework = new Framework(this.bottom);
this.car = new Car(this.framework);
}
public static void main(String[] args) {
Test test = new Test();
test.car.init();
}
}
经过调整,现在更改底层代码是不会影响整个调用链的,这样完成对代码的解耦。我们发现,传统的代码是上层代码依赖下层代码,由上级对象创建下级对象,而更改后的代码,是把下层代码注入到上层代码,这样下级代码的控制权不在由上级控制,自然下级代码更改不会影响上级代码了,这就是所谓的控制权反转,也是IoC容器的思想。
所以Spring 是包含了多个⼯具⽅法的 IoC 容器,可以理解为Spring是一个控制反转容器,既然是容器那么就可以存东西和取东西。所以对象的创建和销毁的权利都交给 Spring 来管理了,它本身⼜具备了存储对象和获取对象的能⼒。也就是,创建类的时候将类存到Spring容器中,在创建对象时不需要new了,直接去Spring中取对象。所以学习Spring最核心的功能就是如何将对象存到Spring中,再从Spring中获取对象的过程。
DI与IoC的区别
上面我们知道了IoC
是控制权反转的一种思想,那么DI又是什么呢?DI
的 Dependency Injection
的缩写,翻译成中文是依赖注入。他与IoC
是从不同角度描述的同一件事,IoC
是一种思想,而思想执行需要方法,IoC
就是思想,DI
就是具体得到实现方式。
Spring项目的创建
配置maven国内源
在设置中勾选setters.xml配置文章和本次仓库文件。
在settings.xml中配置国内源
<mirror>
<id>alimaven</id>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<mirrorOf>central</mirrorOf>
</mirror>
删除本地所有的jar包,重新下载
创建Spring项目
首先创建一个maven项目
添加Spring依赖
<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>
有关Bean的操作
存储Bean
创建一个启动类
存储Bean对象分为两个步骤:
1、创建一个Bean对象。
2、将创建的Bean对象注册到Spring容器中。
创建一个Bean对象,就是一个普通的类对象。
添加Spring配置文件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"
xmlns:content="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="student" class="com.java.demo.model.Student"></bean>
</beans>
使用Bean
使用Bean对象,分为三步:
1、得到Spring上下文对象,因为对象都交给了Spring管理了,所以对象需要从Spring中获取。
2、通过Spring上下文,获取某一个指定的Bean对象。
3、使用Bean对象。
使用 ApplicationContext
获取上下文对象,使用getBean()
方法获取Bean对象。
此时运行代码我们看到打印出了Student对象的信息。
ApplicationContext和BeanFactory的区别
BeanFactory
和ApplicationContext
的作用一样都是获取上下文对象的,他俩的区别可以从两个方面说:
1、从继承关系来说,
ApplicationContext
是BeanFactory
的子类,他有更多的功能,比如对国际化的支持、资源访问支持、以及事件传播等方面的支持。
2、从性能放面来说:ApplicationContext
是一次性加载所有Bean对象,BeanFactory
是懒加载,只加载使用的Bean对象。
getBean()的三种使用方法
使用图中的三种方法都可以获取到Bean对象,注意如果有多个相同的Bean对象,使用类名获取Bean对象的方法就会报错。
更简单的存储和获取对象
我们发现使用框架写代码竟然比传统开发的代码还要复杂,有没有方法使存储Bean对象和获取Bean对象的过程便简单呢?
我们可以通过两个方法实现更简单的存储Bean:
1、使用类注解的方法实现Bean对象的存储。
2、使用方法注解的方法实现Bean对象的存储。
首先同样要添加Spring配置文件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"
xmlns:content="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<content:component-scan base-package="com.java.demo"></content:component-scan>
</beans>
类注解
五大类注解分别是:
1、@Controller
控制器,检测数据的合法性
2、@Service
服务层,进行业务组装
3、@Repository
数据持久层,实际业务的处理
4、@Component
组件 工具类层
5、@Configuration
配置层 进行配置
使用这些类注解都可以将类储存到IoC容器中,那为什么要分这么多注解呢?原因就是让程序员看到类注解之后,就可以了解到当前类的用途。
此时会有一个问题,我们在配置文件中并没有配置Bean对象,也就是说没有设置Bean对象在IoC中id,那我们如何获取Bean对象呢?
使用类注解存储Bean的命名规则如下:
1、当类的命名为默认大驼峰(首字母大写第二个字母小写),可以使用类名首字母小写获取到Bean对象。
2、如果不满足上面的大驼峰的情况,直接使用类名就可以获取到Bean对象。
方法注解
除了使用类注解的方法进行存储,还可以使用方法注解的方式。
@Bean的默认命名方式为方法名,我们还可以进行重命名。
还可以指定多个名称
@Bean获取时的一些注意事项:
1、如果多个Bean使用相同的名称,执行程序不会报错,只会将第一个Bean对象存储到容器中,后面的会自动忽略。
2、如果对@Bean进行了重命名,那么默认使用方法名获取Bean的方法将会失效。
获取Bean对象的简单方法——依赖注入
之前我们获取Bean对象的方法称为依赖查找,很麻烦,我们还有更简单的方法依赖注入。
我们有三种方式进行对象注入:
1、属性注入
2、构造方法注入
3、Setter注入
属性注入:
使用属性注入的优缺点:
优点:代码简单
缺点:无法注入final修饰的变量,可能违反单一性设计原则
构造方法注入:
使用属性注入的优缺点:
优点:可以注入一个final修饰的变量,注入的对象不会被修改,构造方法可以保证注入对象完全初始化,构造方法注入通用性更好。
缺点:代码更加复杂,无法解决循环依赖的问题。
setter方法注入:
使用属性注入的优缺点:
优点:Setter注入更符合单一设计原则
缺点:无法注入final修饰的变量,Setter注入的对象可以被修改。
除了使用@Autowired
注解进行注入,还可以使@Resource进行注入。
@Autowired和@Resource的区别
1、
@Autowired
来自于Spring,而@Resource
来自于JDK
2、使用时可以设置的参数不同,@Resource
有更多的参数
3、@Resource
只能用于Setter注入和属性注入,不能用于构造函数。
关于设置的参数我们举个例子来说明解释一下:
我们使用@Bean注入两个返回相同对象类型的Bean:
此时使用@Autowired@Resource
获取Bean对象会报错
报错原因是使用@Autowried
进行依赖注入的过程是先根据getType获取对象,如果只获取到一个,那么会直接注入到属性上去,如果有多个会使用getName进行匹配。
所以此处我们有三种方法解决Bug
Bean的作用域和生命周期
作用域
我们通过一个例子来观察一下Bean的作用域:现在有一个Student类通过@Bean的方式进行了注入,此时有两个类都需要使用这个Bean对象,但是其中一个类需要进行修改Bean对象中的属性,一个不需要任何改动,此时会发生什么呢?
我们的预期是,公共的Bean对象在各自的类中可以被修改,但是不能影响其他类,但是结果不尽人意。
造成这个结果的原因就是Bean的作用域,Bean在默认的情况下是单例的状态,也就是所有人都是同一个对象,自然我们在一个类中进行修改,另一个类中的对象也会更改。解决这个问题的方式就是改变Bean对象的作用域,Bean的作用域有哪些呢?
Bean的作用域有6种:
singleton
:单例作⽤域 (默认)prototype
:原型作⽤域(多例作⽤域)request
:请求作⽤域session
:回话作⽤域 一个会话共享一个Beanapplication
:全局作⽤域 一个Context容器共享一个作用域websocket
:HTTP WebSocket 作⽤域
前两个作用域适用于普通Spring项目,后四个都是Spring MVC的项目。
设置作用域
我们可以使用@Scope来设置作用域
再次运行项目此时完成我们的需求。
生命周期
Bean的执行流程
1、启动Spring容器
2、实例化Bean
3、Bean注册到Spring中
4、将Bean装配到需要的类中
Bean的生命周期
1、实例化Bean(为Bean分配内存空间)
2、设置属性(Bean注入和装配)
3、Bean初始化
3.1、实现了各种Aware通知的方法,如BeanNameAware、BeanFactoryAware、ApplicationContextAware
的接口方法
3.2、执行BeanPostProcessor
初始化前置方法
3.3、执行@PostConstruct
初始化方法,依赖注入操作后被执行
3.4、执行自己指定的init-method
方法
3.5、执行BeanPostProcessor
初始化后置方法
4、使用Bean
5、销毁Bean
Bean的初始化比较复杂简单总结就是:
1、执行各种通知
2、初始化的前置方法
3、初始化方法
4、初始化的后置方法