Spring 原理
它是一个全面的、企业应用开发一站式的解决方案,贯穿表现层、业务层、持久层。但是 Spring 仍然可
以和其他的框架无缝整合。
Spring 特点
轻量级
控制反转
面向切面
容器
框架集合
Spring 核心组件
Spring 总共有十几个组件核心容器(Spring core) Spring上下文(Spring context)Spring面向切面编程(Spring AOP)Spring DAO模块Spring ORM模块Spring Web模块Spring MVC框架(Spring WebMVC)。
**但是真正核心的组件只有三个Core、Context 和 Bean。**它们构建起了整个 Spring的骨骼架构没有它们就不可能有 AOP、Web 等上层的特性功能。
Bean
通过Bean注入类的属性和参数,同过Bean实现类的实例化。
Spring 就是面向 Bean 的编程(BOP,Bean Oriented Programming),Bean 在 Spring 中才是真正的主角。
Bean在Spring 中作用就像 Object 对 OOP 的意义一样,没有对象的概念就像没有面向对象编程,Spring 中没有 Bean 也就没有 Spring 存在的意义。
就像一次演出舞台都准备好了但是却没有演员一样。为什么要 Bean 这种角色 Bean 或者为何在 Spring 如此重要,
这由 Spring 框架的设计目标决定,Spring 为何如此流行,我们用 Spring 的原因是什么,
想想你会发现原来 Spring 解决了一个非常关键的问题他可以让你把对象之间的依赖关系转而用配置文件来管理。
也就是他的依赖注入机制。而这个注入关系在一个叫 Ioc 容器中管理,那 Ioc 容器就是被 Bean 包裹的对象。
Spring 正是通过把对象包装在 Bean 中而达到对这些对象的管理以及一些列额外操作的目的。
Core
Spring框架最基础的部分,它提供了依赖注入(DependencyInjection)特征来实现容器对Bean的管理。
采用Factory(工厂模式)实现了IoC(控制反转)将应用的配置和依赖性规范与实际的应用程序代码分开;
提供了框架的基本组成部分包括控制反转(Inversion of Control,IoC)和依赖注入(Dependency Injection,DI)功能。
Context
是一个配置文件,向Spring框架提供上下文信息;
构建与core封装包基础上的context封装包,提供了一种框架式的对象访问方法。
Context 作为 Spring 的 Ioc 容器
细说Bean
从广义上 Spring 注解可以分为两类:
一类注解是用于注册 Bean
假如 IoC
容器是一间空屋子,首先这间空屋子啥都没有,我们要吃大餐,我们就要从外部搬运食材和餐具进来。这里把某一样食材或者某一样餐具搬进空屋子的操作就相当于每个注册
Bean 的注解作用类似。注册 Bean 的注解作用就是往 IoC容器中放(注册)东西!用于注册 Bean 的注解:比如
@Component、@Repository、@Controller、@Service、@Configuration 这些注解就是用于注册
Bean,放进 IoC 容器中,一来交给 Spring
管理方便解耦,二来还可以进行二次使用,啥是二次使用呢?这里的二次使用可以理解为:在你开始从外部搬运食材和餐具进空屋子的时候,一次性搬运了猪肉、羊肉、铁勺、筷子四样东西,这个时候你要开始吃大餐,首先你吃东西的时候肯定要用筷子或者铁勺,别说你手抓,只要你需要,你就会去找,这个时候发现你已经把筷子或者铁勺放进了屋子,你就不同再去外部拿筷子进屋子了,意思就是
IoC 容器中已经存在,就可以直接拿去用,而不必再去注册!而拿屋子里已有的东西的操作就是下面要讲的关于使用 Bean 的注解!
一类注解是用于使用 Bean
用于使用 Bean 的注解:比如 @Autowired、@Resource
注解,这些注解就是把屋子里的东西自己拿来用,如果你要拿,前提一定是屋子(IoC)里有的,不然就会报错。比如你要做一道牛肉拼盘需要五头牛做原材料才行,你现在锅里只有四头牛,这个时候你知道,自己往屋子里搬过五头牛,这个时候就直接把屋子里的那头牛直接放进锅里,完成牛肉拼盘的组装。是的这些注解就是需要啥,只要容器中有就往容器中拿,就是这么豪横!而这些注解又有各自的区别,比如
@Autowired 用在筷子上,这筷子你可能想用木质的,或许只想用铁质的,@Autowired 作用在什么属性的筷子就那什么筷子,即是按类型的。
@Resource 如果用在安格斯牛肉上面,就指定要名字就是安格斯牛肉的牛肉,即是按照名字的。
一、定义
Bean 是 Spring 框架中最核心的两个概念之一(另一个是面向切面编程 AOP)
Spring 官方文档对 bean 的解释是:
In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container.
翻译过来就是:
在 Spring 中,构成应用程序主干并由 Spring IoC 容器管理的对象称为 bean。bean 是由Spring IoC
容器实例化、组装和管理的对象。
从上面翻译过来意思来看:
bean 是对象,一个或者多个不限定
bean 由 Spring 中一个叫 IoC 的东西管理的
我们的应用程序由一个个 bean 构成
控制反转(IoC)
控制反转英文全称:Inversion of Control,简称就是 IoC。控制反转通过依赖注入(DI)方式实现对象之间的松耦合关系。程序运行时,依赖对象由辅助程序动态生成并注入到被依赖对象中,动态绑定两者的使用关系。Spring IoC 容器就是这样的辅助程序,它负责对象的生成和依赖的注入,然后再交由我们使用。
1、什么是依赖注入与控制反转呢?先通过一个例子来理解一下
首先有一个类叫做 Student,里面有两个成员变量分别是 id 和 name,并提供 set、get方法。
public class Student {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
另外一个类叫做 StudentManager
public class StudentManager {
private Student student;
public void setStudent(Student student) {
this.student = student;
}
public void show() {
System.out.println(student.getId());
System.out.println(student.getName());
}
}
这个 StudentManager 类有个成员是 Student 的一个对象,然后它的 show 方法能够打印这个 student 的 id 以及 name,并提供了 setStudent 方法初始化 Student 对象。我们可以说,StudentManager(被依赖对象) 是依赖于 Student(依赖对象)的。
有一个问题,StudentManager 与 Student 之间的耦合非常紧密,假如我们还没有来的及对 StudentManager 的 student 绑定对象,却调用了 show 方法的话,那么程序将会抛出空指针异常。所以 Spring 提供了一套叫做控制反转与依赖注入这套机制,目的就是为了解耦。
在 Spring 中,**你不需要自己创建对象,**你只需要告诉 Spring,哪些类我需要创建出对象,然后在启动项目的时候 Spring 就会自动帮你创建出该对象,并且只存在一个类的实例。这个类的实例在 Spring 中被称为 Bean。**而这种模式,我们称之为“单例模式”。也就是一个类只有一个实例的意思。**
那么 Spring 是靠什么来了解究竟哪些类需要帮我们创建呢,这里介绍最常用的两种方式------Java 注解配置,Java 代码配置。之前还有 XML 配置等,但是之前的现在已经不推荐使用了。
首先介绍的是 Java 注解配置,这是最简单也是最常用的一种方式。
以上四种声明方式效果完全一致,使用不同的关键词是为了给阅读的人能够快速了解该类属于哪一层。
使用方法为:在定义的实体类前使用该注解。让我们看下面一段代码
@Component
public class Student {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
我们在刚才的 Student 类前面,加上了 @Component 注解,成功告诉 Spring:你要在项目创建运行时帮我创建 Student 类的 Bean (对象)。
好了,这时候添加“依赖”就已经做完了,但是还没完,我们虽然让 Spring 帮我们创建了对象,但是 StudentManager 怎么知道这个对象在哪呢?所以接下来,我们要告诉 StudentManager 刚才 Spring 帮我们创建的 Bean (对象)到底在哪,也就是使用(“注入”)这个 Bean。
我们来看看注入注解的语法:
使用方法:在我们需要注入依赖的成员变量前使用该注解,看一下下面一段代码
@Component
public class StudentManager {
@Autowired
private Student student;
public void show() {
System.out.println(student.getId());
System.out.println(student.getName());
}
}
可以看到,在声明成员变量 Student 的前面我们使用了 @Autowired,所以 Spring 会自动帮我们使用(注入)一个 Bean,我们就再也不用担心忘记绑定对象而出现空指针了。但是可以发现,虽然我们告诉了 Spring 哪些类是需要添加依赖,哪些类是需要注入 Bean,但是 Spring 还需要我们做一次配置,来真正完成这样一个操作。
2、让 Spring 控制类构建过程
不用 new,让 Spring 控制 new 过程。在 Spring 中,我们基本不需要 new 一个类,这些都是让 Spring 去做的。Spring 启动时会把所需的类实例化对象,如果需要依赖,则先实例化依赖,然后实例化当前类。因为依赖必须通过构建函数传入,所以实例化时,当前类就会接收并保存所有依赖的对象。这一步也就是所谓的依赖注入。
3、这就是 IOC
在 Spring 中,类的实例化、依赖的实例化、依赖的传入都交由 Spring Bean 容器控制,而不是用 new 方式实例化对象、通过非构造函数方法传入依赖等常规方式。实质的控制权已经交由程序管理,而不是程序员管理,所以叫控制反转。
@Bean 注解的使用
1、使用说明
@Bean 注解作用在方法上,产生一个 Bean 对象,然后这个 Bean 对象交给 Spring 管理,剩下的你就不用管了。产生这个 Bean 对象的方法 Spring 只会调用一次,随后这个 Spring 将会将这个 Bean 对象放在自己的 IOC 容器中。
@Bean 方法名与返回类名一致,首字母小写。
@Component、@Repository、@Controller、@Service 这些注解只局限于自己编写的类,而 @Bean 注解能把第三方库中的类实例加入 IOC 容器中并交给 Spring 管理。
@Bean 一般和 @Component 或者 @Configuration 一起使用
2、Bean 名称
2.1、默认情况下 Bean 名称就是方法名(首字母小写),比如下面 Bean 名称便是 myBean
@Bean
public MyBean myBean() {
return new MyBean();
}
2.2、@Bean 注解支持设置别名。比如下面除了主名称 myBean 外,还有个别名 myBean1(两个都可以使用)
@Bean("myBean1")
public MyBean myBean() {
return new MyBean();
}
2.3、@Bean 注解可以接受一个 String 数组设置多个别名。比如下面除了主名称 myBean 外,还有别名 myBean1、myBean2(三个都可以使用)
@Bean({"myBean1","myBean2"})
public MyBean myBean() {
return new MyBean();
3、@Bean 与其他注解产生的火花
@Bean 注解常常与 @Scope、@Lazy、@DependsOn 和 @link Primary 注解一起使用
3.1、@Profile 注解
为在不同环境下使用不同的配置提供了支持,如开发环境和生产环境的数据库配置是不同的
@Bean
@Profile("!dev") // 不是dev环境的能使用这个bean
public MyBean myBean() {
MyBean myBean = new MyBean();
myBean.setPort("8080");
return myBean;
}
3.2、@Scope 注解
在 Spring 中对于 bean 的默认处理都是单例的,我们通过上下文容器.getBean方法拿到 bean 容器,并对其进行实例化,这个实例化的过程其实只进行一次,即多次 getBean 获取的对象都是同一个对象,也就相当于这个 bean 的实例在 IOC 容器中是 public 的,对于所有的 bean 请求来讲都可以共享此 bean。@Scope 注解将其改成 prototype 原型模式(每次获取 Bean 的时候会有一个新的实例)
@Bean
@Scope("prototype")
public MyBean myBean() {
MyBean myBean = new MyBean();
myBean.setPort("8080");
return myBean;
@SpringBootApplication
@MapperScan("com.example.quartzdemo.dao")//使用MapperScan批量扫描所有的Mapper接口;
public class QuartzDemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(QuartzDemoApplication.class, args);
MyBean myBean = (MyBean) context.getBean("myBean");
System.out.println(myBean);
MyBean myBean2 = (MyBean) context.getBean("myBean");
System.out.println(myBean2);
}
}
打印输出结果:
com.example.quartzdemo.config.MyBean@49601f82
com.example.quartzdemo.config.MyBean@23e44287
将 @Scope(“prototype”) 删除掉,再运行启动类,打印结果如下:
com.example.quartzdemo.config.MyBean@4cdd2c73
com.example.quartzdemo.config.MyBean@4cdd2c73
3.3、@Lazy 注解:
在 Spring 框架中,默认会在启动时会创建所有的 Bean 对象,但有些 bean 对象假如长时间不用,启动时就创建对象,会占用其内存资源,从而造成一定的资源浪费,此时我们可以基于懒加载策略延迟对象的创建。
@Bean
@Lazy
public MyBean myBean() {
MyBean myBean = new MyBean();
myBean.setPort(“8080”);
return myBean;
Bean 的生命周期
对于普通的 Java 对象,new 的时候会去创建对象,而当它没有任何引用的时候则被垃圾回收机制回收。相较于前者,由Spring IoC 容器托管的对象,它们的生命周期完全由容器控制。Spring 中每个 Bean 的生命周期如下:
总体分为四个阶段:
①实例化 CreateBeanInstance
②属性赋值 PopulateBean
③初始化 Initialization
④销毁 Destruction**
其中多个增强接口贯穿了这四个阶段!
三、SpringBean生命周期中的增强接口PostProcessor:
在上图里有多种后置处理器接口,它们贯穿了Bean的生命周期,且它们的实现类都会在SpringIOC容器进行初始化的时候进行实例化,让我们来做一个区分:
四、实例展示
SpringBeanDemo
package com.rx.spring;
import com.rx.spring.domain.Person;
import com.rx.spring.domain.Student;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class SpringBeanDemo {
public static void main(String[] args) throws Exception {
System.out.println("****开始启动****");
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);
System.out.println("****启动完毕****");
Person person = applicationContext.getBean("person", Person.class);
Student student = applicationContext.getBean("student", Student.class);
System.out.println("=============================================");
System.out.println("person:" + person);
System.out.println("student:" + student);
person.destroy();
System.out.println("============现在开始关闭容器======================");
applicationContext.registerShutdownHook();
}
}
Config
package com.rx.spring;
import org.springframework.context.annotation.*;
@Configuration
@ComponentScan("com.rx.spring")
@ImportResource("classpath:spring.xml")
public class Config {
}
Person
package com.rx.spring.domain;
import lombok.Data;
import org.springframework.beans.factory.DisposableBean;
@Data
public class Person implements DisposableBean {
private String name;
private String address;
private String tel;
public Person(String name, String address, String tel) {
System.out.println("Person--->>>有参构造方法");
this.name = name;
this.address = address;
this.tel = tel;
}
public Person() {
System.out.println("Person--->>>无参构造方法");
}
private void raoInitMethod() {
System.out.println("person--->>>InitMethod...");
}
private void raoDestroyMethod() {
System.out.println("person--->>>DestroyMethod...");
}
@Override
public void destroy() throws Exception {
System.out.println("【DisposableBean接口】调用DisposableBean.destroy()");
}
}
Student
package com.rx.spring.domain;
import lombok.Data;
import org.springframework.beans.factory.DisposableBean;
@Data
public class Student implements DisposableBean {
private String username;
private String password;
public Student(String username, String password) {
System.out.println("student--->>有参构造方法");
this.username = username;
this.password = password;
}
public Student() {
System.out.println("student--->>>无参构造方法");
}
private void raoInitMethod() {
System.out.println("student--->>>InitMethod...");
}
private void raoDestroyMethod() {
System.out.println("student--->>>DestroyMethod...");
}
@Override
public void destroy() throws Exception {
System.out.println("【DisposableBean接口】调用DisposableBean.destroy()");
}
}
RaoInstantiationAwareBeanPostProcessor
package com.rx.spring.beanpostprocessor;
import com.rx.spring.domain.Person;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.stereotype.Component;
@Component
public class RaoInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if ("person".equals(beanName) || "student".equals(beanName)) {
System.out.println(beanName + "--->>>InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation....");
}
return null;
}
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
if ("person".equals(beanName) || "student".equals(beanName)) {
System.out.println(beanName + "--->>>InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation....");
}
return bean instanceof Person;
}
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
System.out.println(beanName + "--->>>InstantiationAwareBeanPostProcessor.postProcessProperties...");
PropertyValue[] propertyValues = pvs.getPropertyValues();
for (PropertyValue propertyValue : propertyValues) {
if ("name".equals(propertyValue.getName())) {
propertyValue.setConvertedValue("改后rx");
}
}
return pvs;
}
}
RaoBeanPostProcessor
package com.rx.spring.beanpostprocessor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
@Component
public class RaoBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if ("person".equals(beanName) || "student".equals(beanName)) {
System.out.println(beanName + "--->>>BeanPostProcessor.postProcessBeforeInitialization...");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if ("person".equals(beanName) || "student".equals(beanName)) {
System.out.println(beanName + "--->>>BeanPostProcessor.postProcessAfterInitialization....");
}
return bean;
}
}
spring.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">
<bean id ="person" class="com.rx.spring.domain.Person" init-method="raoInitMethod" destroy-method="raoDestroyMethod">
<property name="name" value="rx"/>
<property name="address" value="beijing"/>
<property name="tel" value="157********"/>
</bean>
<bean id ="student" class="com.rx.spring.domain.Student" init-method="raoInitMethod" destroy-method="raoDestroyMethod">
<property name="username" value="rx"/>
<property name="password" value="1234"/>
</bean>
</beans>