框架源码知识点(spring)一

什么是框架?框架就是为了能够在某一领域简化开发流程,提升开发效率,某些个人或组织定义了一系列的类或接口,并提前定义好了一些通用的实现,用户可以在此基础上,迅速开发自己的应用。从而简化了开发的过程,提高了开发的效率。

java主流架构演变:1.servlet+jsp+javabean 2.mvc(在1的基础上进行分层) 3.使用EJB 4.SSH 5.SSM 6.springboot

Spring

什么是spring?Spring是一个轻量级java开发框架,用于解决开发过程中各层间的耦合问题,主要思想是IOC和AOP,其目的都是为了解耦

Spring IOC知识点

IOC(控制反转)是思想,DI(依赖注入)是实现方式

IOC优点除了解耦外,还有1.集中管理 2.功能可复用 3.使得整个程序体系可维护性、灵活性、扩展性变高。

IOC容器概述:spring的ApplicationContext是一个接口,是ioc思想的实现的代表,其包含多个实现类。主要负责实例化、配置和组装bean。是spring的顶层核心接口

ApplicationContext的实现类包含:1.ClassPathXmlApplicationContext 读取项目xml 2.FileSystemXmlApplicationContext 读取磁盘xml配置3.AnnotationConfigApplicationContext 读取javaConfig来实现spring ioc容器

ApplicationContext实现了MessageSource接口表示拥有国际化功能,实现了ResourcePatternResolver接口表示拥有了资源加载功能,ListableBeanFactory表示展示BeanFactory的内容,比如获取bean定义数量,名字, 其它类似。

元数据指应用程序组件、配置和其他相关信息的数据,通常包含了关于类、方法、字段等元素的描述信息。
配置元数据用于声明实例化、配置和组装bean,ioc通过读取配置元数据来进行相应的实例化、配置和组装操作。可以通过xml、java注解或javaconfig的方式来配置元数据。其中java注解的方式包含 @Compont(@serivce @controller @repository) @Autowired(spring 2.5支持 ) ,javaconfig 包含@Configuration @Bean @Import ,javaconfig也是使用的注解

实例化bean方式包含:使用构造器实例化(默认)、使用静态工厂方法实例化、使用实例工厂方法实例化

依赖注入方式包含:基于setter方法的依赖注入、基于构造函数的依赖注入

自动注入用于当一个对象引用另外一个对象时,可以通过配置进行自动注入,无需通过之前的ref标签来配置依赖。默认是不自动注入的,需要配置。

@AutoWired是spring中提供的注解,默认按类型进行匹配,要求依赖的对象必须存在,不存在则抛出异常,如果存在多个类型则会按照变量名作为id继续匹配,也可以使用@Qualifier来指定id,只适合spring框架。
@Resource是jdk中定义的注解,默认按照名字来进行匹配,可以指定name属性,扩展性比@AutoWired更好。

javaconfig在spring3时开始支持,在spring4,springboot1.0开始完全使用

@Configuration相当于创建了一个xml文件,@ComponentScan(“com.john”)相当于<context:component-scan base-package=“com.john”> @Bean相当于 @Import用于引入其它配置类,相当于导入另一个 xml文件。@PropertySource(“classpath:db.properties”)用于引入外部属性资源文件 @Scope用于设置bean作用域

@Component, @Controller,@Service,@Repository是将实例化交给spring去做,而@Bean是自己实例化,需要自己new
@Bean // 使用 @Bean 注解标记方法
public MyBean myBean() {
return new MyBean(); // 创建 MyBean 对象并返回
}

@Controller也可以用@Component来标记,区分那么清,是为了精细化管理,比如进行排除
Import除了可以引入其它配置类(@Import(SecondJavaConfig.class)),还可以通过引入实现了ImportSelector接口的类来注册多个Bean,如@Import(MyImportSelector.class)

有参构造方法的参数可以不加@Autowired。
意思是说下面的代码不用加@Autowired,也能注入Cup。
public Car(Cup cup) {
System.out.println(“2222”);
System.out.println(cup);
int i = 0;
}
构造函数的参数注入默认根据参数类型而不是参数名来从容器中找,如果根据找到多个相同参数类型的bean,则继续按参数名来找,如果没有找到则报错。(实际上是如果容器中如果有多个类型的实例,如果根据类型找到多个则判断是否有@Primary注解,如果没有则判断是否有@Priority注解,如果没找到,则会根据方法名去找)

@Bean不要声明在构造参数所在类中,否则可能出现实例化顺序问题。(即可能出现循环依赖)

使用aop时,在没有调用代理对象UserServiceProxy.test()方法前或调用代理对象UserServiceProxy.test()方法时orderService会为空(因为此时调用的是代理对象且ioc设计时没有给代理对象的属性注入值),只有真正调用到target UserService的实例而非代理对象时orderService才有值。

使用事务时,事务的代码被加入到的是代理对象,而非普通对象。所以事务方法内调用事务方法时,要自己注入自己来调用,确保使用代理对象的方法。否则会出现事务失效。

Aware是针对单个Bean的,BeanPostProcessor针对的是所有bean的
Spring Aware是Spring框架提供的一种机制,通过这种机制,Spring容器内的一些资源可以注入到我们的bean中。
常见的Spring Aware接口包括:
BeanFactoryAware:让bean获取当前的BeanFactory。
ApplicationContextAware:让bean获取当前的ApplicationContext。
MessageSourceAware:让bean获取消息源,如Spring提供的国际化支持。
ApplicationEventPublisherAware:让bean获取应用事件发布器,可以发布事件。
EnvironmentAware:让bean获取环境信息,如配置文件、系统属性等。
使用Spring Aware的场景:
需要访问Spring容器资源:如获取其他bean、获取配置文件中的值等。
需要进行事件发布:如监听容器启动、关闭等。
需要获取环境信息:如读取系统属性、环境变量等。
在这里插入图片描述
spring中使用SimpleMetadataReader来读取解析类的元数据信息,比如类名、类方法、类注解,SimpleMetadataReader去解析类的时候使用的是ASM技术。即asm除了字节码增强,还可以解析字节码。
asm是一个Java字节码操控框架。它能够以二进制形式修改已有类或者动态生成类。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
为什么要使用asm技术来读取元数据:可以无需提前加载Class进JVM。
如果不用asm可以使用反射:但反射依赖于Class,使用时需要将Class加载进来。这样不好。

通过@Bean也可以自己生成一个对象作为Bean,那么和FactoryBean的区别是什么呢?
区别很明显,@Bean定义的Bean是会经过完整的Bean生命周期的。但是通过FactoryBean这种方式创造出来的Bean,只会走初始化回调,其他Spring的生命周期步骤是不会经过的,比如依赖注入。
FactoryBean这个对象是在容器启动的时候就缓存在单例缓存,即单例池。
FactoryBean里面的注入对象,是在第一次获取对象的时候调用getObject方法初始化的。
UserService会放到另外Cache。
但是可以通过实现SmartFactoryBean,并且重写isEagerInit()为返回true,这样除了会创建XXFactoryBean实例之外,还会创建User实例到ioc容器。
如果 isEagerInit() 返回 true,那么在容器启动时,该 FactoryBean 创建的 bean 将会立即初始化;如果返回 false,则在第一次访问该 bean 时才会进行初始化。

loadClass和Class.forName的区别?
即loadClass只是进行类加载,而Class.forName除了会加载类,还会进行初始化。

testFactoryBean表示要获取目标对象Test &testFactoryBean符号表示要获取FactoryBean本身

spring framework scope只包含singleton和prototype两种,spring mvc包含singleton、prototype、request、session四种。
UserService的OrderService不用@Autowired,直接在注入UserService的时候 public UserService userService() 上加上@Bean(autowire=Autowire.BY_NAME)就能依赖注入。

bean懒加载是通过@Lazy去实现的,@Lazy原理是先返回一个代理对象,然后调用代理方法的时候才真正创建真正实例。即@Lazy是通过代理去实现的。

如果容器中如果有多个类型的实例,如果根据类型找到多个则判断是否有@Primary注解,如果没有则判断是否有@Priority注解,如果没找到,则会根据方法名去找,
也就是说如果下面的注入属性名字如果为cup123的话,因为容器有2个Cup类型的实例,则会找实例名称beanName为cup123的实例,没有就会报错。
@Autowried
Cup cup123;

如果是自己注入自己,找到多个,则不使用自己,用其它。如果只找到一个就用自己,以避免循环依赖。

注入的时候还会判断类能否进行依赖注入逻辑,判断时先父类能否进行依赖注入,能的话再判断自己能否进行依赖注入,只有父类和子类都能依赖注入时才可进行依赖注入 autowireCandidate

@Autowired依赖注入有6个判断 ,依次是
类型即autowireCandidate,泛型,@Qualifier,@Primary,@Priority,名字
当我们的service按照类型注入时,就无法确定要注入哪个daoBean就报错。
我们可以在其中某一个上配置autowire-candidate=false,这时spring在查找accountDAO时就会忽略这个bean。

关于缓存,第一次创建找注入点属性的时候会将每个AutowireElement对应的beanName缓存起来,第二次创建的时候从缓存当中去取。这个在原型bean会用到缓存(原型是指bean是原型,而不是注入点属性是原型),在单例不会。因为在单例的时候,只会创建一次bean,第二次直接从单例池拿bean,不需要执行到缓存的代码。
缓存的是每个AutowireElement对应的的beanName(通过DependencyDescriptor封装),而不是对象
比如下面UserService为原型,取OrderService就会触发缓存,有了缓存就不用总是执行上面6个找bean的流程。
@Component
@Scope(“prototype”)
public class UserService {
@Autowired
private OrderService orderService
public void test(){System.out.println(orderService)}
}
为什么缓存的是名字,而不是对象?如果属性bean是prototype的话,是不读取缓存的,每次都是一个新的实例。
如果OrderService是单例的话,不管UserService是单例还是prototype,多个UserService下面的orderService对象是同一个对象。

@Resource由CommonAnnotationBeanPostProcessor处理和Autowired不一样的地方
1.其属性如果是static的话会抛异常
2.它生成的是ResourceElement,其注入点inject方法是在ResourceElement其父类的
3.@Resource如何找bean? 先byName,再byType,而Autowired相反
name=属性名,type是属性类型
4.如何选用@Resouce,@Autowired
@Autowired是spring的 @Resource是java的

循环依赖解决思路
什么是循环依赖?
假设创建完对象后,才将对象放到单例池,即singletonObjectsMap。
那么创建一个对象的时候,如果A依赖B,B依赖A。就会出现类似deadlock情况。

怎么解决?
可以通过引入临时缓存(名称定为xxMap),在创建A实例的时候,还没等A初始化完,就将对象放到临时缓存。这样创建B的时候就能拿到A实例(但这个机制不完善,下面会讲)。
下面先看看bean的大概生成步骤:

  1. Spring扫描class得到BeanDefinition
  2. 根据得到的BeanDefinition去生成bean
  3. 首先根据class推断构造方法
  4. 根据推断出来的构造方法,反射,得到一个对象(暂时叫做原始对象)
  5. 填充原始对象中的属性(依赖注入)
  6. 如果原始对象中的某个方法被AOP了,那么则需要根据原始对象生成一个代理对象
  7. 把最终生成的代理对象放入单例池(源码中叫做singletonObjects)中,下次getBean时就直接 从单例池拿即可。
    即:A类—>生成一个普通对象(原始对象)–>属性注入–>基于切面生成一个代理对象–>把代理对 象放入singletonObjects单例池中。
    但是为什么不能用这个机制来去实现循环依赖呢?
    这是难点,基于上面的场景想一个问题:如果A的原始对象注入给B的属性之后,A的原始对象进行了 AOP产生了一个代理对象,此时就会出现,对于A而言,它的Bean对象其实应该是AOP之后的代理对象,而B的A属性对应的并不是AOP之后的代理对象,这就产生了冲突。
    即A对象最终应该是一个代理对象,B中的A属性应该也需要是一个代理对象才行。但是在A变成代理对象前,是把原始对象放入临时缓存的,此时B注入的A属性就为一个原始对象,这是不对的(即B依赖的A和最终的A不是同一个对象)。所以我们要确保放入临时缓存的应该是一个代理对象才行,即需要在放入临时缓存前进行AOP。
    这样就解决了循环依赖(不完美,继续看下面)。

另外,提前AOP不是说所有AOP时都需要提前AOP,只有在出现循环依赖才需要进行提前,那么这就要判断是否出现循环依赖。那么怎么判断呢?在哪里判断?
应该在B中去单例池中没找到A时去判断,需要借助另一个集合来存储正在创建的集合(creatingSet)来判断循环依赖,如果AService在正在创建中集合creatingSet,说明AService存在循环依赖。A–>B–>C–>D–>A。这个creatingSet=singletonsCurrentlyInCreation。
这样就判断了是否出现循环依赖。
这样就解决了循环依赖(还是不完美)。

singletonFactories是啥?主要是将是否需要进行aop以及创建bean对象/代理对象的逻辑放在这里来实现。这个为第三级缓存。
singletonFactories中存的是某个beanName对应的ObjectFactory,在bean的生命周期中, 生成完原始对象之后,就会构造一个ObjectFactory存入singletonFactories中。这个ObjectFactory 是一个函数式接口,所以支持Lambda表达式:() -> getEarlyBeanReference(beanName, mbd, bean)。
Spring所用的singletonFactories,为了调和不同的情况,在 singletonFactories中存的是lambda表达式,这样的话,只有在出现了循环依赖的情况,才会执行 lambda表达式,才会进行AOP,也就说只有在出现了循环依赖的情况下才会打破Bean生命周期的设 计,如果一个Bean没有出现循环依赖,那么它还是遵守了Bean的生命周期的设计的。
这样才就解决了循环依赖。

上面这个机制就是三级缓存。
三级缓存是通用的叫法。 一级缓存为:singletonObjects(单例池,存储完全初始化的单例Bean实例)
二级缓存为:earlySingletonObjects(用于存储早期暴露的单例Bean实例,即在Bean的创建过程中,尚未完成初始化的Bean实例)
三级缓存为**:singletonFactories**(相当于创建bean对象的工厂,封装了是否需要进行AOP的逻辑)

spring ioc原理

spring ioc的原理,就是理解1.spring ioc包含哪些主要接口、类,2.以及ioc容器的初始化过程,如何加载bean定义,生成bean,3.bean的生命周期等

首先是包含的主要接口:1.BeanDefinition:封装定义了一切用来生产bean的方式。2.BeanFactory:顶层核心接口,规范子工厂及生产bean。ApplicationContext继承它。 3.BeanFactoryPostProcessor:bean工厂后置处理器,用来修改bean定义。

ioc对应的是spring-context包

ioc容器的初始化过程:
xml定义bean方式:
1.调用new springApplication()或ioc=new ClassPathXmlApplicationContext(“classpath:spring.xml”) 2.加载xm配置文件路径,调用AbstractApplicationContext ->refresh->obtainFreshBeanFactory->refreshBeanFactory方法,创建bean工厂DefaultListableBeanFactory,然后调用loadBeanDefinitions方法,loadBeanDefinitions方法里面会通过XmlBeanDefinitionReader(bean定义解析处理类)来加载bean定义
3.调用bean工厂后置处理器invokeBeanFactoryPostProcessors方法
4.调用finishBeanFactoryInitialization结束bean工厂初始化,并开始生产bean,创建时会判断是否符合生产标准,即是否不是抽象,不是懒加载且是单例的,如果是单例的化会去单例池判断是否已经创建,如果已经创建则从单例池中返回,如果没有就去判断是否正在创建中(通过多级缓存将正在创建的bean放到singletonsCurrentlyInCreation Set集合中来标识bean正在创建来解决循环依赖),如果不是正在创建就进行创建,如果是则等待。
5.创建实例化完后注入bean属性值,比如对声明了@AutoWired的属性进行赋值(默认调用无参构造函数来实例化)
6.初始化bean(创建是创建,初始化是初始化),初始化分为多个步骤,具体看下面的bean生命周期3-6。
7.判断是否需加入到单例池,如果需要则将bean加入到单例池

注解定义bean方式:
1.new AnnotationConfigApplicationContext(AppConfig.class),其首先会在其父类GenericApplicationContext构造方法里面创建bean工厂DefaultListableBeanFactory,然后在其自身构造方法中实例化AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner,在AnnotatedBeanDefinitionReader里面会调用registerAnnotationConfigProcessors注册所有相关的注解处理器annotationPostProcessors,包含了beanPostProcessor和beanFactoryPostProcessor。比如和依赖注入相关的注解处理器AutowiredAnnotationBeanPostProcessor,还有用于解析bean定义相关的注解处理器ConfigurationClassPostProcessor。注册是指创建BeanDefinition,还没到实例化。
2.遍历调用bean工厂后置处理器invokeBeanFactoryPostProcessors方法,在ConfigurationClassPostProcessor这个bean工厂后置处理器被调用的时候,这个bean工厂后置处理器会通过ClassPathBeanDefinitionScanner扫描及创建bean定义,创建时会初步判断是否符合标准,符合的话怎创建并添加到候选bean定义集合Set candidates,之后再做进一步判断和默认值赋值、判断spring容器中是否存在该beanName等,最后将bean定义注册到容器。
3.如上5…

ConfigurationClassPostProcessor这个bean工厂后置处理器会通过其解析器ConfigurationClassParser,去解析@ComponentScan和@ImportResource @Import这些注解,比如会去获取@ComponentScan(“com.willard.beans”)中的包名,然后通过ClassPathBeanDefinitionScanner扫描包名下的bean及创建bean定义。

注解方式bean定义是在bean工厂后置处理器中通过ClassPathBeanDefinitionScanner加载,即invokeBeanFactoryPostProcessors ,xml是在obtainFreshBeanFactory方法中加载。ClassPathXmlApplicationContext的父类是AbstractXmlApplicationContext,AnnotationConfigApplicationContext的父类是GenericApplicationContext。

BeanFacotry和FactoryBean的区别?
BeanFactory用于生产bean是一个工厂。FactoryBean是一个bean,是一个修饰对象如何生产的工厂bean。BeanFactory底层用new生产bean,上层用getBean方法返回。FactoryBean底层通过调用getObject方法来返回bean,但是上层还是用getBean方法获取bean

BeanFactory和ApplicationContext的区别?BeanFactory是spring顶层核心接口,用来生产bean.
相同:spring提供了两种ioc容器。一个是BeanFactory,一个是ApplicationContext.他们都是接口。ApplicatonContext继承于BeanFactory.是BeanFactory的扩展。

Spring Bean生命周期?
1.实例化bean 2.设置对象属性,依赖注入。 3.如果bean实现了BeanNameAware接口则调用其setBeanName方法 4.如果bean实现了BeanFacotryAware接口则调用其setBeanFactory方法,将BeanFactory容器实例传入进去 5.如果bean实现了ApplicationContextAware接口则调用setApplicationContext方法,将上下文传入进去 6.初始化前,如果实现了BeanPostProcessor,则调用BeanPostProcessor postProcessBeforeInitialization 5.如果配置了init-method或实现了InitializingBean则调用对应的初始化方法。 6.初始化后,如果实现了BeanPostProcessor,则调用postProcessAfterInitialization,这时bean就正确创建了,可以开始使用。AOP就是在这一步实现的。 7.当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方 法。 8.如果配置了destroy-method,则调用对应的方法。

bean工厂后置处理器是在加载完bean定义后调用的,bean后置处理器是在创建完bean实例,注入bean属性值后调用的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值