Day121 深入理解Spring与SpringBoot

Spring基础

Spring是一个 轻量级 的 控制反转(IoC)面向切面(AOP) 的容器框架,意在解决Java企业应用开发的复杂性。

IOC

IOC意在降低程序的耦合度,将对象创建和对象之间调用关系交给IOC容器管理,参与开发的每一成员只要实现自己的类就可以了,不需要依赖其他类。

原理

  • IOC容器就是一个管理对象的工厂

  • IOC原理:工厂模式 + 反射 +(xml解析)

  • 为什么:为了尽可能的降低程序的耦合度

    • new方法:原始方法用new来创建调用对象耦合度太高,一旦调用的对象路径方法发生变化,那么调用者就也得跟着改,并且调用的地方会非常多,修改起来很麻烦。
    • 工厂模式:所以可以把new对象的过程交给工厂,哪里需要调用对象直接往工厂里拿就可以了,有变动只需要修改工厂类就可以了。
    • IOC:在xml中配置对象信息,需要修改不用改程序,只需要改配置文件就可以;在工厂类中读取xml的配置信息,用反射机制来创建对象,做到不用类名就可以实例化对象,进一步降低了耦合度。
// IOC雏形
class BeanFactory {
	public static UserDao getDao(){
		String classValue = class属性值; //xml解析
		Class clazz = Class.forName(classValue); //通过反射创建对象
		return (UserDao)clazz.newInstance();
	}
}

应用

  • 怎么做
  • Spring中的工厂类:类名为BeanFactory,在程序中通常BeanFactory的子类ApplicationContext。Spring相当于一个大的工厂类,在其配置文件中通过元素配置用于创建实例对象的类名和实例对象的属性。
  • BeanFactory:加载配置文件时不会加载对象,获取对象使用才会去创建对象。
  • ApplicationContext:加载配置文件时就会把配置文件中的对象进行创建,在Web项目中把这些耗时的操作先完成,可以有效提升客户端访问的速度。
  • 常用实现类:ClassPathXmlApplicationContext,在classpath下找文件名
public class Client {
	public static void main(String[] args) {
		//1.使用ApplicationContext接口,获取spring容器
		ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
		//2.根据bean的id获取对象
		IAccountService aService = (IAccountService) ac.getBean("accountService");
	}
}
  • IOC操作Bean来实现【对象的创建】与【属性的注入】,有【基于xml】与【基于注解】两种实现方式。
    • 基于xml创建对象id属性是获取对象的唯一标识,class属性代表全限定类名,用于反射创建对象。默认情况下调用无参构造函数。
<bean id="user" class="com.wangc.spring5.User"></bean>
  • 基于注解创建对象:@Component,@Service,@Controller,@Repository
//<bean id="userService" class=".."/>
@Component(value = "userService") 
public class UserService {
}

注入方式

DI:依赖注入,主要指注入属性。它是spring框架核心ioc的具体实现。在当前类需要用到其他类对象,有spring为我们提供,我们只需要在配置文件中说明依赖关系的维护,就称之为依赖注入。

  1. 使用set方法进行注入:最常用,创建对象时没有明确的限制,可以直接使用默认构造函数
<bean id="accountService" class="com.wangc.service.impl.AccountServiceImpl2">
 	<!--使用 property 完成属性注入
 		name:类里面属性名称
		 value:向属性注入的值
	 -->
    <property name="name" value="TEST" ></property>
    <property name="age" value="21"></property>
    <property name="birthday" ref="now"></property>
</bean>
  1. 使用有参构造进行注入:在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功。
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
    <constructor-arg name="name" value="泰斯特"></constructor-arg>
    <constructor-arg name="age" value="18"></constructor-arg>
    <constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>
  1. 基于注解注入属性
    • @Autowired:根据属性类型进行自动装配
    • @Qualifier:根据名称进行注入
    • @Resource:可以根据类型注入,可以根据名称注入
    • @Value:注入普通类型属性
@Service
public class UserService {
	//定义 dao 类型属性
	//不需要添加 set 方法
	//添加注入属性注解
	@Autowired
	private UserDao userDao;
	public void add() {
	}
}

AOP

意在把应用业务逻辑和系统服务分开,就是系统中有很多各不相干的类的方法(业务逻辑),在这些众多方法中要加入某种系统功能的代码,其实也是为了解耦。

  • 为什么:AOP可以看做是OOP的补充和完善,OP允许你定义从上到下的关系,但并不适合定义从左到右的关系,例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系,普通的OOP设计中,会导致了大量代码的重复,不利于各个模块的重用。
  • AOP是通过CGLib动态代理方式实现的,系统功能作为对业务逻辑的增强
  • JDK中的动态代理实现,创建代理对象实现增强的逻辑,传入Proxy.newProxyInstance方法对原方法进行增强。
    在这里插入图片描述
  • 应用:在系统中业务逻辑中要加入某种系统功能的代码,例如,加入日志,加入权限判断,加入异常处理,这种应用称为AOP。

深入理解 Spring

Spring bean 就是被 Spring IOC 容器创建管理 java 对象,这些beans可以以XML文件中的形式定义。理解 Spring bean 的机制对我们深入IOC有着极其重要的作用。

Bean 基础

作用范围

  • 单例对象:scope=“singleton”:一个应用只有一个对象的实例。它的作用范围就是整个引用。(默认)
    • 生命周期:
      • 对象出生:当应用加载,创建容器时,对象就被创建了。
      • 对象活着:只要容器在,对象一直活着。
      • 对象死亡:当应用卸载,销毁容器时,对象就被销毁了。
  • 多例对象:scope=“prototype”:每次访问对象时,都会重新创建对象实例。
    • 生命周期:
      • 对象出生:当使用对象时,创建新的对象实例。
      • 对象活着:只要对象在使用中,就一直活着。
      • 对象死亡:当对象长时间不用时,被java的垃圾回收器回收了。

加载顺序

<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao"></bean>

depends-on是bean标签的属性之一,表示一个bean对其他bean的依赖关系。这manager和accoutDao会先于beanOne被实例化,会慢于beanOne被销毁,而beanOne不引用accountDao(或者说beanOne不会将accountDao注入到自己的属性中)。这就是depends-on的主要作用。

Bean 原理

问题:
bean是如何被spring加载为实例对象的?(加载机制)
bean 从创建到销毁经历了什么样的过程?(生命周期)

普通的Java对象直接new出来就可以了,但JavaBean生命周期就比较复杂了,可以简单理解为 默认构造方法实例化对象—set方法设置属性值—调用初始化方法—获取对象—销毁对象 五步,但实际上Bean实例化之前经历了一系列复杂的解析和加载的过程,大题可简化为下图:
在这里插入图片描述

  1. 解析:xml或注解方式定义bean的信息统一由 BeanDefinitionReader 解析为 BeanDefinition对象
  2. BeanDefinition:非常重要的一步, 里面记录了Bean的所有信息,一些信息决定了Bean将如何加载为对象,比如对象的作用范围是否为 singleton,比如是否为懒加载 lazy-init=ture,再比如是否有dependon属性等
  3. BeanFactoryPostProcessor: bean工厂的bean属性的后置处理器,Spring可扩展性的保证, 可以管理我们的bean工厂内所有的 BeanDefinition 数据。
  4. BeanFactory:这时候拿着记录了所有对象信息的 BeanDefinition,放入工厂根据通过构造器创建反射创建Bean实例。
  5. BeanPostProcessor:Bean实例的后置处理器,Spring可扩展性的保证。
  6. 把Bean实例放入map中容器中管理,到此为止ClassPathXmlApplicationContext("bean.xml");,Bean实例化并且放入IOC容器的过程就完成了。
  7. ac.getBean("xxx"),拿到bean对象。
  8. 当容器关闭时候,调用bean 的销毁的方法(需要进行配置销毁的方法)

在整个流程的最开始是创建BeanFactory(ApplicationContext),在refresh()方法中

问题:Bean是否线程安全、

不一定,这要看Bean的创建模式,单例不保证线程安全,多例每个线程都会有一个Bean实例,不存在数据共享固然安全。
并且要看Bean是否有实例变量,即是否有存储数据能力,如果没有实例变量,称作Bean是无状态的,这时候是线程安全的,但如果有状态,那就要开发者自己去保证线程安全了,最简单的就是改变 bean 的作用域,把“singleton”变更为“prototype”,

@Autowired 原理

  • @Autowired:自动按照类型注入。当使用注解注入属性时,set方法可以省略。它只能注入其他bean类型。当有多个类型匹配时,使用要注入的对象变量名称作为bean的id,在spring容器查找,找到了也可以注入成功,找不到就报错。
    *. 实现原理:Spring在容器启动阶段,会先实例化bean,然后再对bean进行初始化操作。在初始化阶段,会通过调用Bean后置处理来完成对属性的赋值等操作,那么同理,要想实现@Autowired的功能,肯定也是通过后置处理器来完成的。这个后置处理器就是AutowiredAnnotationBeanPostProcessor。

其他

Spring中涉及哪些设计模式?

  • IOC的工厂模式
  • AOP的动态代理模式
  • 监听器的观察者模式
  • bean作用范围的单例模式

深入理解SpringBoot

SpringBoot是框架简化是指简化了Spring众多框架中所需的大量且繁琐的配置文件,要想理解SpringBoot首先要理解Spring的原理,另外理解SpringBoot的自动装配原理和启动流程。

  • 核心注解:启动类上面的@SpringBootApplication,包含
    • @SpringBootConfiguration:组合了 @Configuration 注解
    • @EnableAutoConfiguration:打开自动配置的功能
    • @ComponentScan:Spring 组件扫描。

启动过程

在这里插入图片描述

  • 初始化:构造SpringApplication的时候会进行初始化的工作
    • 判断Web应用类型,None,Servlet或响应式(Spring5新增)
    • setInitializers在 META-INF/spring.factories 中找到所有初始化器 ApplicationListener,设置到initializers属性中
    • setListener在 META-INF/spring.factories 中找到所有监听器 ApplicationListener,设置到listeners属性中
    • 找出运行的主函数
  • 启动run,进入refresh方法之前都是准备工作
  • 构造一个StopWatch,进行计时
  • 找到并启动所有 SpringApplicationRunListener,用于监听run方法的执行。
  • 配置环境模块
  • 应用上下文模块
  • 构造Spring容器,进入refresh方法

SpringApplicationRunListener看名字也知道用于监听SpringApplication的run方法的执行。

它定义了5个步骤:

started(run方法执行的时候立马执行;对应事件的类型是ApplicationStartedEvent)
environmentPrepared(ApplicationContext创建之前并且环境信息准备好的时候调用;对应事件的类型是ApplicationEnvironmentPreparedEvent)
contextPrepared(ApplicationContext创建好并且在source加载之前调用一次;没有具体的对应事件)
contextLoaded(ApplicationContext创建并加载之后并在refresh之前调用;对应事件的类型是ApplicationPreparedEvent)
finished(run方法结束之前调用;对应事件的类型是ApplicationReadyEvent或ApplicationFailedEvent)

自动配置原理

资料
https://www.bilibili.com/video/BV1nD4y1Q7ep?t=4047
https://www.bilibili.com/video/BV1ME411e7k8?p=19
https://fangjian0423.github.io/2017/04/30/springboot-startup-analysis/
https://www.bilibili.com/video/BV1Rz4y197uX?p=2
https://thinkwon.blog.csdn.net/article/details/104397516

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值