一:什么是Spring,IoC,DI
Spring是包含了众多工具方法的IoC容器。既然是Ioc容器就包含了两个最核心最基础的概念。
最核心的功能就是:一是把对象存储到Spring当中,第二个就是把对象从Spring当中取出来。
IoC(Inversion of Control 控制反转),使用控制反转的思路可以实现依赖类之间的解耦,让我们不必去关心依赖类之间的具体实现和生命过程,只需要再使用它的时候把它注入进来就可以。
优点是实现类和依赖类之间的解耦。在底层依赖类发生变化的时候,无需关心它的实现。因为我们的Ioc(控制反转)把之前需要自己创建的对象交给了其它人。我们不需要关系具体的细节,需要使用的时候直接用就可以了。
DI(Dependency Injection 依赖注入)是IoC的一种具体的实现手段,指的是在程序运行的时候,动态的将对象注入到类的一种实现机制,让当前类可以使用依赖类。
IoC是一种指导思想,DI是具体实现。
二:Spring的创建与使用
1.创建Sprig项目
创建Spring项目就是使用Maven项目,添加Spring框架的依赖。
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.3.RELEASE</version>
</dependency>
最后在Java文件下创建一个启动类,目的是为了测试 Spring 的核心功能:存对象和取对象。
public class App {
public static void main(String[] args) {
}
}
2.存储Bean对象
第一步:存储 Bean 对象,先创建一个Bean。
public class User {
public void Name(String name){
System.out.println("姓名:"+name);
}
}
第二步:将创建的 Bean 注册到 Spring 容器中。
在 resources 文件夹下创建 Spring.xml 配置文件,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">
</beans>
将 User 对象注册到 Spring 中,具体操作是在 中添加如下配置
<beans>
<bean id="user" class="User"></bean>
</beans>
此处的 id 为 Bean 的标识符,后面读取到这个对象的时候会用到。
class 代表保存到 Spring 当中的那个对象。
3.从 Spring 框架中读取存储的业务对象
第一步:先要获得 Spring 上下文的对象。
我们在之前创建的 App 类的 main 方法中通过如下两种方式的代码 都可以 获取 Spring 的上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("Spring.xml");
ClassPathXmlApplicationContext 属于 ApplicationContext 的子类,拥有 ApplicationContext 的所有功能,是通过 xml 的配置来获取所有的 Bean 容器的。
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("Spring.xml"));
ApplicationContext VS BeanFactory
- 二者来自的 jar 包不同;BeanFactory 来自 spring.beans.jar;ApplicationnContext 来自 spring.context.jar 下。
- BeanFactory 和 ApplicationContext 都是属于 Spring下的顶级接口;ApplicationContext 属于 BeanFactory 的子类,BeanFactory 的所有功能 ApplicationContext 都是拥有的,除此之外,ApplicationContext 还有对国际化支持,支持资源的访问,支持事件的传播…
- 对于执行性能来说,ApplicationContext 是一次性加载并初始化所有的 bean 的,所以它的启动过程可能比较慢,但是后续的执行比较快;而 BeanFactory 是需要哪个类才去加载哪个类,因此 BeanFactory 占用的资源更少,启动更快,单后续的还行可能会慢一些。
第二步:获取指定的 Bean 对象的 getBean() 的三种用法
a)通过 xml 文件中的 bean 的 id 来获取。需要类型强转
User user = (User) context.getBean("user");
b)通过类型获取
User user = context.getBean(User.class);
优点是无需类型强转,缺点是对于多个对象的同一种类型的 Bean 获取会报错。
c)通过 id+类型 的方式来获取 bean
User user = context.getBean("user",User.class);
三:Sping 更简单的读取和存储对象
1.先创建一个 Maven 项目
2.添加Spring的依赖包
3.配置 Spring xml 文件 =》配置Spring 组件扫描路径(所有需要存储在 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"
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">
<!--配置 Spring 扫描的根路径(所有需要存储在Spring中的对象都需要放在此目录)-->
<content:component-scan base-package="com.baidu"></content:component-scan>
</beans>
4.添加注解存储 Bean 对象
想要将对象存储在 Spring 中,有两种注解类型可以实现。
类注解及作用:@Controller【业务逻辑层(数据校验)】、@Service【业务逻辑层(数据组装)】、@Repository【数据库的交互,表的CRUD操作】、@Component【组件(公共组件/class)】、@Configuration【系统配置信息】
方法注解:@Bean
两种注解的区别与联系
- 两种注解类型作用都是一样的,都是注册bean到Spring容器中。
- 引用第三方库中的类需要装配到Spring容器时,则只能通过@Bean来实现
- Spring 默认情况下是类扫描,因此如果使用的是方法注解 @Bean,那么必须要配合类注解(@Component)一起使用,才能将方法返回对象顺利存储到 Spring 中。
注意:Spring 中获得存入的对象,类的id默认的规则是将存入的类首字母小写、
方法 id 默认规则是方法名,首字母小写;@Bean 可以重命名,方便读取对象。
5.获取 Bean 对象(对象装配)
获取 bean 对象也叫做对象装配,是把对象取出来放到某个类中,有时候也叫对象注入。
对象注入(对象装配)的实现方法有三种
- 属性注入
属性注入是使用 @Autowired 实现的
从 Spring 中获取一个对象,并注入到当前类
@Autowired
private UserService userService;
- 构造方法注入
构造方法注入是在类的构造方法中实现注入
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
public User getUser(Integer age, String name){
return userService.getUser(age,name);
}
- Setter注入
private UserService userService;
@Autowired
public void setUserService(UserService userService){
this.userService = userService;
}
注意:如果当前类里面只有一个构造方法,那么 @Autowired 注解是可以被省略的(和Setter注入的不同)
6.另一种注入关键字:@Resource
它的用法和 @Autowired 类似。有属性注入和使用 Setter 两种方式,但是不能使用在构造方法上进行注入。
当出现多个 @Bean 方法注解,返回的对象是同一类型的时候。有两种解决办法
- 使用 @Resource(name=“XXX”)
需要给 @Bean 起别名。 例如 @Bean(name=“uesr1”) @Bean(name=“user2”) - 使用 @Autowired + @Qualifier(value=“user1”)
@Autowired 和 @Resource 的区别
- @Autowired 来自 Spring 框架,而 @Resource 来自于JDK
- 作用范围不同,使用 @Autowired 可以进行属性注入,Setter 注入,构造器注入;而 @Resource 只能进行属性注入和Setter 注入
- 功能不同,@Resource 可以配合更多的属性进行使用,而 @Autowired 支持的属性较少,比如使用 @Resource 可以配合 name 属性进行使用,从而完成对象的别名注入。
四:Bean的作用域和生命周期
4.1 Bean作用域
Spring 容器在初始化一个 Bean 的实例时,同时会指定该实例的作用域。Spring有 6 种作用域,
最后四种是基于 Spring MVC 有效的:
- singleton:单例作用域(Spring默认选择该作用域)
- prototype:原型作用域(多例作用域)
- request:请求作用域
- session:会话作用域
- application:全局作用域
- websocket:HTTP WebSocket 作用域
注意:后4种状态是 Spring MVC中的值,在普通的 Spring项目中只有前两种。
singleton
- 描述:该作用域下的Bean在IoC容器中只存在一个实例:获取Bean(即通过
applicationContext.getBean等方法获取)及装配Bean(即通过@Autowired注入)都是同一
个对象。 - 场景:通常无状态的Bean使用该作用域。无状态表示Bean对象的属性状态不需要更新
- 备注:Spring默认选择该作用域
prototype
- 描述:每次对该作用域下的Bean的请求都会创建新的实例:获取Bean(即通过
applicationContext.getBean等方法获取)及装配Bean(即通过@Autowired注入)都是新的
对象实例 - 场景:通常有状态的Bean使用该作用域
设置作用域注解 代码
@Bean(name = {"u1"})
// 直接设置值
@Scope("prototype")
// 使用枚举设置
//@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public User user1(){
User user = new User();
user.setAge(18);
user.setName("鲁硕");
return user;
}
4.2 Bean执行流程(Spring的执行流程)
1.启动Spring容器
2.加载Spring配置文件
3.加载配置文件bean,或者是根据配置文件中的组件(Bean)根路径,进行Bean对象的扫描五大类注解,如果有注解直接加载
4.加载的对象存储到Spring
5.其他需要使用Bean对象的地方就可以直接获取并使用
6.执行完之后,执行销毁操作
4.3 Bean生命周期
所谓的生命周期指的是一个对象从诞生到销毁的整个生命过程,我们把这个过程就叫做一个对象的生命周期。
Bean的声明周期分为以下5大部分
- 实例化Bean(为Bean分配内存空间)
- 设置属性(Bean注入和装配)
- Bean初始化
实现了各种Aware通知的方法,如BeanNameAware、BeanFactoryAware、ApplicationContextAware的接口方法;
执行BeanPostProcessor初始化前置方法
执行@PostConstruct初始化方法,依赖注入操作之后被执行
执行自己制定的init-method方法(如果有指定的话,在xml中设置)
执行BeanPostProcessor初始化后置方法 - 使用Bean
- 销毁Bean
销毁容器的各种方法,如@PreDestroy、DisposableBean接口方法、destroy-method。
@Component
public class BeanLifeComponent implements BeanNameAware {
@Override
public void setBeanName(String s) {
System.out.println("执行通知"+s);
}
@PostConstruct
public void PostConstructor(){
System.out.println("执行@PostConstructor方法");
}
// init 和 destory方法名要和xml中的保持一致
public void init(){
System.out.println("执行init方法");
}
@PreDestroy
public void preDestory(){
System.out.println("执行@PreDestroy方法");
}
public void destory(){
System.out.println("执行destory方法");
}
}
ApplicationContext context = new ClassPathXmlApplicationContext("Spring.xml");
BeanLifeComponent beanLifeComponent = context.getBean("beanLifeComponent",BeanLifeComponent.class);
beanLifeComponent.preDestory();
beanLifeComponent.destory();