Spring Framework
Spring是什么?
- Spring是什么呢?我们可看一下官网的定义:
- Spring快速、容易、安全,专注速度,开发效率高,Spring是全世界最流行的Java开发框架。
Spring Framework框架
Spring Framework是Spring中的核心框架,结构如下所示:
Core Container(核心容器)模块主要是四部分构成:
Sping-Beans
Spring-Core
Spring-Context
Spring-Expression Language
1.IoC容器
IoC(Inversion of Control)称为控制反转,是面向对象编程中的一种设计原则,主要用来降低代码之间的高耦合度问题。
- 从使用上看,使用IoC之前,创建对象需要手动new对象,并设置对象属性,控制权掌握在应用程序自身(对象的创建与销毁都掌握在我们自己手中);使用IoC之后,对象的控制权交给了第三方进行管理,这里的第三方就是容器,因而对象由容器来管理。因为发生了控制权的反转,所以叫控制反转。
2.DI(依赖注入)
DI(Dependency)称为依赖注入,是实现IoC的方法之一,所谓依赖注入就是指IoC容器运行期间,动态地将某种依赖关系注入到对象到中。
3.IoC与DI关系
实际上,IoC与DI是从不同角度描述的同一件事情,当引入IoC容器后,使用依赖关系注入的方式,实现了对象之间的解耦。
接下来主要针对Sping-Beans与Spring-Context进行介绍:
- Spring容器的API有
BeanFactory
和ApplicationContext
两大类,二者都是顶级接口,其中ApplicationContext是BeanFactory的顶级接口。接下来的代码演示部分,我们都基于ApplicationContext应用上下文接口去实现容器。
我们先基于Maven创建Spring项目,具体步骤如下:
1.创建Maven项目后,在pom.xml配置文件中引入外部依赖,刷新Maven面板
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>SpringProject</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 注解 -->
<properties>
<java.version>1.8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-framework.version>5.2.10.RELEASE</spring-framework.version>
</properties>
<!-- 引入外部依赖 -->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring-framework.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
</dependencies>
</project>
2.准备项目启动类入口
将beans.xml文件作为参数对ClassPathXmlApplicationContext类进行实例化操作,构建一个ApplicationContext对象。
package org.example; //自定义启动类路径
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("beans.xml");
//关闭容器
((ClassPathXmlApplicationContext)context).close();
}
}
3.定义Spring配置文件(这里指的就是beans.xml)
在src/main/resources/下创建beans.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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 包扫描路径 -->
<context:component-scan base-package="org.example"/>
</beans>
<context:component-scan base-package="org.example"/>
base-package:表示的是Spring启动时候扫描的包路径,该路径下带有类注解的类将被注册到容器里。
4.扫描Spring类注解注册Bean对象
类注解方式:共有4种类注解,它们虽然对应不同的业务,但是都是为Spring容器服务的;类注解定义方式默认会注册类名首字母小写的Bean对象到容器中。
@Controller
:前端业务交互(参数校验)@Service
:业务处理中间类(业务逻辑处理)@Repository
:数据持久层(增删查改)@Component
:通用对象工具类(比如数据序列化、反序列化操作)
package org.example.controller;
import org.springframework.stereotype.Controller;
@Controller
public class LoginController1 {
}
5.得到容器中Bean对象
第一种得到Bean对象方式:
需要特别注意的是:
getBean方法中的参数首字母一定是小写的(源码表示启动类名首字母小写),否则代码引发异常
。
第二种得到Bean对象方式:
从代码执行的结果可以看出:Bean对象被正常打印出来
两种方式拿到的对象是否一样呢?
从结果可以看出两种方式获得的对象是同一个Bean对象,这是因为Spring使用了单例模式。
特殊情况:当注解类前两个字母都为大写时,传入的参数为原来的类名
package org.example.controller;
import org.springframework.stereotype.Controller;
@Controller
public class URLController {
}
从代码执行结果来看,此时getBean方法传入的参数要求是原来的类名。
Lombok依赖
当在beans.xml中添加了Lombok依赖时,需要安装Lombok插件
Lombok使用演示:
package org.example.model;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Setter
@Getter
@ToString
public class User {
private String name;
private String password;
}
对上面代码build操作后,可以发现自动生成了三种方法
Lombok原理
:它是自定义注解的方式,在代码编译时期,将自定义的注解更换成了JVM可以执行的方法(就是get、set、toString方法)。
注册Bean的方式
1.类注解
上面已经阐述,此处不再赘述。
2.@Bean注解
当前类被Spring扫描时,可以在方法上使用@Bean注解,通过方法返回类型,可以定义、注册Bean对象,默认使用方法名作为Bean的名称。
在LoginController中定义两个User方法:
//返回User类型
@Bean
public User user1(){
User user = new User();
user.setName("小唐");
user.setPassword("0627");
return user;
}
@Bean
public User user2(){
User user = new User();
user.setName("康康");
user.setPassword("0215");
return user;
}
从结果可以看出@Bean方法注解成功注册了Bean对象。
那么能不能以同样的方式(或者Use.class方式)传入user2拿到Bean对象?
答案:不行,因为user1和user2方法返回的都是User类型,容器中注册的Bean对象有两个。
如何得到user2的Bean对象呢?
首先在LoginController类上加入Getter注解
3.@Configuration注解
当类被Spring扫描时,使用@Configuration注解,可以注册一个配置类到容器中,配置类一般用来自定义配置某些资源。
从结果可以看出,使用Configuration注解注册了Bean对象。
依赖注入的方式
1.属性注入
package org.example.service;
import org.springframework.stereotype.Service;
@Service //存储Bean
public class LoginService {
public void sayHi(){
System.out.println("666666666666666666");
}
}
package org.example.controller;
import org.example.service.LoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
@Getter
public class LoginController {
//第一种方式:属性注入(查询Bean)
@Autowired
//这里变量名不能随意起,当同一个类有多个Bean对象时,会引发异常
public LoginService loginService;
public void say(){
loginService.sayHi();
}
结果说明在LoginController类中,通过属性注入的方式,可以得到Bean对象。
2.set方法注入
//第二种:set方式
public LoginService loginService; //这里的变量名可随意起
@Autowired
public void setLoginService(LoginService loginService) {
this.loginService = loginService;
}
public void say(){
loginService.sayHi();
}
3.构造方法注入
//第三种:构造方法注入
private LoginService loginService;
@Autowired //加不加都行
public LoginController(LoginService loginService){
this.loginService = loginService;
}
public void say(){
loginService.sayHi();
}
三种注入方式比较来看,属性注入的方式是最简洁的。
4.指定注入:@Qualifier
当同类型的Bean有多个时,注入该类型Bean需要指定Bean的名称:
- 属性名或方法参数名设置为Bean的名称
- 属性名或方法参数设置@Qualifier(“名称”)注解,注解内的字符串是Bean对象的名称。
Bean的作用域
Spring容器在初始化一个Bean对象时,同时会指定该对象的作用域,一共有6种作用域,接下来我们分别来看:
1.singleton
该作用域为单例模式作用域,Bean在IoC容器中只有一个实例对象,获取Bean(getBean)以及装配Bean(@Autowired)都是同一个对象。Spring容器默认默认选择的是singleton作用域
。
2.prototype
每次该作用域下Bean的请求都会创建新的实例,也就是说每一次的获取Bean和装配Bean都是新的对象。通常有状态的Bean使用该作用域
。
3.request
每次http请求都会创建新的Bean实例;适用场景:一次http请求和响应的共享Bean;限定在SpringMVC中使用。
4.session
在一个http的session中定义一个Bean实例;场景:http的session会话中记录一个用户的登录信息;限定在SpringMVC中使用。
5.application
在一个http servlet Context中,定义一个Bean实例;场景:Web应用的全局上下文信息,比如记录一个应用的共享信息;限定在SpringMVC中使用。了解即可
6.websocket
在一个http WebSocket的生命周期中,定义一个Bean实例;场景:WebSocket的每次会话中,保存了一个Map结构的头信息,用来包裹客户端的消息头,第一次初始化后,直到WebSocket结束都是一个Bean;限定在Spring WebSocket中使用。
Bean生命周期
- 生命周期图
- Bean的生命周期主要步骤:
1.实例化Bean:通过反射机制调用构造方法实例化对象;
2.依赖注入:装配Bean的属性;
3.实现了Aware接口的Bean,执行接口的方法:顺序执行BeanNameAware、BeanFactoryAware、ApplicationContextAware的接口方法;
4.Bean对象初始化之前,循环调用实现了BeanPostProcessor接口的预初始化方法postProcessBeforeInitialization;
5.Bean对象初始化:顺序执行@PostConstruct注解方法、InitializingBean接口方法、init-method方法;
6.Bean对象初始化后,循环调用实现了BeanPostProcessor接口的后初始化方法
postProcessAfterInitialization;
7.容器关闭时调用Bean对象的销毁方法,顺序:@PreDestroy注解方法、@DisposableBean接口方法、destory-method。