Spring再次复习之相关笔记

实例化建BeanDefinition读取器: AnnotatedBeanDefinitionReader:其主要做了2件事情
  1.注册内置BeanPostProcessor
  2注册相关的BeanDefinition.

Spring涉及的设计模式:
简单工厂、工厂方法、单例模式、适配器模式、装饰器模式、代理模式、观察者模式、策略模式、模板方法模式、责任链模式

//根据参数类型可以知道,其实可以传入多个annotatedClasses,但是这种情况出现的比较少
    public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
        //调用无参构造函数,会先调用父类GenericApplicationContext的构造函数
        //父类的构造函数里面就是初始化DefaultListableBeanFactory,并且赋值给beanFactory
        //本类的构造函数里面,初始化了一个读取器:AnnotatedBeanDefinitionReader read,一个扫描器ClassPathBeanDefinitionScanner scanner
        //scanner的用处不是很大,它仅仅是在我们外部手动调用 .scan 等方法才有用,常规方式是不会用到scanner对象的
        this();
        //把传入的类进行注册,这里有两个情况,
        //传入传统的配置类
        //传入bean(虽然一般没有人会这么做
        //看到后面会知道spring把传统的带上@Configuration的配置类称之为FULL配置类,不带@Configuration的称之为Lite配置类
        //但是我们这里先把带上@Configuration的配置类称之为传统配置类,不带的称之为普通bean
        register(annotatedClasses);
        //刷新
        refresh();
    }

 refresh()方法的核心源码:

// Prepare this context for refreshing.
//刷新预处理,和主流程关系不大,就是保存了容器的启动时间,启动标志等
prepareRefresh();

//DefaultListableBeanFactory
// Tell the subclass to refresh the internal bean factory.
//和主流程关系也不大,最终获得了DefaultListableBeanFactory,
// DefaultListableBeanFactory实现了ConfigurableListableBeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

// Prepare the bean factory for use in this context.
//还是一些准备工作,添加了两个后置处理器:ApplicationContextAwareProcessor,ApplicationListenerDetector
//还设置了 忽略自动装配 和 允许自动装配 的接口,如果不存在某个bean的时候,spring就自动注册singleton bean
//还设置了bean表达式解析器 等
prepareBeanFactory(beanFactory);

// Allows post-processing of the bean factory in context subclasses.
//这是一个空方法
postProcessBeanFactory(beanFactory);

// Invoke factory processors registered as beans in the context.
//执行自定义的BeanFactoryProcessor和内置的BeanFactoryProcessor
invokeBeanFactoryPostProcessors(beanFactory);

// Register bean processors that intercept bean creation.
// 注册BeanPostProcessor
registerBeanPostProcessors(beanFactory);

// Initialize message source for this context.
initMessageSource();

// Initialize event multicaster for this context.
initApplicationEventMulticaster();

// Initialize other special beans in specific context subclasses.
// 空方法
onRefresh();

// Check for listener beans and register them.
registerListeners();

// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);

// Last step: publish corresponding event.
finishRefresh();

血泪史,其实Spring源码的相关东东是看过一遍的,可是好像忘得差不多了。

 

什么是Spring? 谈谈你对iOC和AOP的理解.
Spring: 是一个企业级java应用框架,他的作用主要是简化软件的开发以及配置过程,简化项目部署环境
Spring的有点:
1、Spring低侵入设计,对业务代码的污染非常低
2、Spring的DI机制将对象之间的关系交由框架处理,减少组件的耦合
3、Spring提供了AOP技术,支持将一些通用的功能进行集中式管理,从而提供更好的复用。
4、Spring对于主流框架提供了非常好的支持。

IOC就是控制反转,指创建对象的控制权转移给Spring来进行管理。简单来说,就是应用不用去new对象了,而全部交由Spring自动生产。
IOC有三种注入方式: 1、构造器注入 2、setter方法注入 3、根据注解注入。
AOP面向切面。用于将那些与业务无关,但却对多个对象产生影响的公共行为。抽取并封装成一个可重用的模块。AOP的核心就是动态代理。JDK的动态代理 和 CGLIB动态代理。

 

什么是bean的自动装配,有哪些方式?

 开启自动装配,只需要在xml配置文件中定义“autowire”属性。 

<bean id="cutomer" class="com.xxx.xxx.Customer" autowire="" />

autowire属性有五种装配的方式: 

no – 缺省情况下,自动配置是通过“ref”属性手动设定 。 

手动装配:以value或ref的方式明确指定属性值都是手动装配。

需要通过‘ref’属性来连接bean。

byName-根据bean的属性名称进行自动装配。

Cutomer的属性名称是person,Spring会将bean id为person的bean通过setter方法进行自动装配。 

<bean id="cutomer" class="com.xxx.xxx.Cutomer" autowire="byName"/>
<bean id="person" class="com.xxx.xxx.Person"/>

byType-根据bean的类型进行自动装配。 

Cutomer的属性person的类型为Person,Spirng会将Person类型通过setter方法进行自动装配。

<bean id="cutomer" class="com.xxx.xxx.Cutomer" autowire="byType"/>
<bean id="person" class="com.xxx.xxx.Person"/>

constructor-类似byType,不过是应用于构造器的参数。如果一个bean与构造器参数的类型形 同,则进行自动装配,否则导致异常。

Cutomer构造函数的参数person的类型为Person,Spirng会将Person类型通过构造方法进行自动装配。

<bean id="cutomer" class="com.xxx.xxx.Cutomer" autowire="construtor"/>
<bean id="person" class="com.xxx.xxx.Person"/>

autodetect-如果有默认的构造器,则通过constructor方式进行自动装配,否则使用byType方式 进行自动装配。

如果有默认的构造器,则通过constructor方式进行自动装配,否则使用byType方式进行自动装配。 

@Autowired自动装配bean,可以在字段、setter方法、构造函数上使用。 

如何理解 Spring Boot 中的 Starter?

使用spring + springmvc使用,如果需要引入mybatis等框架,需要到xml中定义mybatis需要的beanstarter就是定义一个starter的iar包,写一个@Configuration配置类、将这些bean定义在里面,然后在starter包的META-INF/spring.factories中写入该配置类,springboot会按照约定来加载该配置类开发人员只需要将相应的starter包依赖进应用,进行相应的属性配置(使用默认配置时,不需要配置),就可以直接进行代码开发,使用对应的功能了,比如mybatis-spring-boot--starter,spring-boot-starter-redis

如何实现一个IOC容器?

1、配置文件配置包扫描路径;
2、递归包扫描获取.class文件;
3、反射、确定需要交给OC管理的类;
4、对需要注入的类进行依赖注入;

  • 配置文件中指定需要扫描的包路径;
  • 定义一些注解,分别表示访问控制层、业务服务层、数据持久层、依赖注入注解,获取配置文件注解;
  • 遍历这个set集合,获取在类上有指定注解的类,并将其交给I0C容器,定义一个安全的Map用来存储这些对象;
  • 遍历这个IOC容器,获取到每一个类的实例,判断里面是有有依赖其他的类的实例,然后进行递归注入;

Spring中Bean是线程安全的吗?

Spring本身并没有针对Bean做线程安全的处理,所以:
1.如果Bean是无状态的,那么Bean则是线程安全的;
2.如果Bean是有状态的,那么Bean则不是线程安全的;
另外,Bean是不是线程安全,跟Bean的作用域没有关系,Bean的作用域只表示Ba的生命周期范用,对于任何生命周期的Bean都是一个对象,这个对象是不具线程安全的,还是得看这个Bean对象本身。

谈谈你对IOC的理解

容器概念、控制反转、依赖注入

ioc容器: 实际上就是个map (key,value) ,里面存的是各种对象 (在xml里配置的bean节点、@repository.@service、 @controller、@component) ,在项目启动的时候会读取配置文件里面的bean节点,根据全限定类名使用反射创建对象放到map里、扫描到打上上述注解的类还是通过反射创建对象放到map里。

这个时候map里就有各种对象了,接下来我们在代码里需要用到里面的对象时,再通过DI注入 (autowiredresource等注解,xml里bean节点内的ref属性,项目启动的时候会读取xml节点ref属性根据id注入,也会扫描这些注解,根据类型或id注入;id就是对象名)。

 

控制反转:
没有引入I0C容器之前,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B,控制权都在自己手上。
引入IOC容器之后,对象A与对象B之间失去了直接联系,当对象A运行到需要对象B的时候,IOC容器会主动创建一个对象B注入到对象A需要的地方。
通过前后的对比,不难看出来: 对象A获得依赖对象B的过程,由主动行为变为了被动行为,控制权颠倒过来了,这就是“控制反转”这个名称的由来。
全部对象的控制权全部上缴给"第三方”OC容器,所以,10C容器成了整个系统的关键核心,它起到了一种类似“合剂”的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个"粘合剂",对象与对象之间会彼此失去联系,这就是有人把IOC容器比喻成”粘合剂”的由来。

依赖注入:
获得依赖对象的过程被反转了”。控制被反转之后,获得依赖对象的过程由自身管理变为了由IOC容器主动注入。
依赖注入是实现IOC的方法,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中。

介绍一下Spring,读过源码介绍一下大致流程

1. Spring是一个快速开发框架,Spring帮助程序员来管理对象;
2. Spring的源码实现的是非常优秀的,设计模式的应用、并发安全的实现、面向接口的设计等;
3. 在创建Spring容器,也就是启动Spring时:
a.首先会进行扫描,扫描得到所有的BeanDefinition对象,并存在一个Map中
b.然后筛选出非懒加载的单例BeanDefinition进行创建Bean,对于多例Bean不需要在启动过程中去进行创建,对于多例bean会在每次获取Bean时利用 BeanDeinition
c.利用BeanDefinition创建Bean就Bean的创边生命周期,这期间包括了合#BeanDefinition、推斯构造法、实例化、属性填充、初始化前、初始化、初始化后等步骤,其中AOP就是发生在初始化后这一步骤中
4.单例Bean创建完了之后,Spring会发布一个容器启动事件5.Spring启动结束6.在源码中会更复杂,比源码中会提供一些模板方法,让子类来实现,比如源中还涉及到一BeanFatoryPostProess和eanPostProcesor的注册,Spring的扫描就是通过BenaFactorvPostProcessor来实现的,依赖注入就是通过BeanPostProcessor来实现的7.在Spring启动过程中还会去处理@Import等注解。

描述一下Spring Bean的生命周期?

1、解析类得到BeanDefinition
2、如果有多个构造方法,则要推断构造方法
3、确定好构造方法后,进行实例化得到一个对象
4、对对象中的加了@Autowired注解的属性进行属性填充
5、回调Aware方法,比如BeanNameAware,BeanFactoryAware6、调用BeanPostProcessor的初始化前的方法
7、调用初始化方法
8、调用BeanPostProcessor的初始化后的方法,在这里会进行AOP9、如果当前创建的bean是单例的则会把bean放入单例池
10、使用bean
11、Spring容器关闭时调用DisposableBean中destory()方法

谈谈你对AOP的理解

系统是由许多不同的组件所组成的,每一个组件各负责一块特定功能。除了实现自身核心功能之外,这些组件还经常承担着额外的职责。例如日志、事务管理和安全这样的核心服务经常融入到自身具有核心业务逻辑的组件中去。这些系统服务经常被称为横切关注点,因为它们会跨越系统的多个组件
当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。
日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。
在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
AOP:将程序中的交叉业务逻辑(比如安全,日志,事务等),封装成一个切面,然后注入到目标对象(具体业务逻辑)中去。AOP可以对某个对象或某些对象的功能进行增强,比如对象中的方法进行增强,可以在执行某个方法之前额外的做一些事情,在某个方法执行之后额外的做一些事情。

说一下Spring的事务机制?

1.Spring事务底层是基于数据库事务和AOP机制的;
2.首先对于使用了@Transactional注解的Bean,Spring会创建一个代理对象作为Bean;
3.当调用代理对象的方法时,会先判断该方法上是否加了@Transactional注解;
4.如果加了,那么则利用事务管理器创建一个数据库连接;
5.并且修改数据库连接的autocommit属性为false,禁止此连接的自动提交,这是实现Spring事务非常重要的一步;
6.然后执行当前方法,方法中会执行sql;
7.执行完当前方法后,如果没有出现异觉就直接提交事务;
8.如果出现了异常,并且这个异常是需要回滚的就会回滚事务,否则仍然提交事务;
9.Spring事务的隔离级别对应的就是数据库的隔离级别;
10.Spring事务的传播机制是Spring事务自己实现的,也是Spring事务中最复杂的;
1.Spring事务的传播机制是基于数据库连接来做的,一个数据库连接一个事务,如果传播机制配置为需要新开一个事务,那么实际上就是先建立一个数据库连接,在此新教据库连接上执行sql。

说说常用的SpringBoot注解,及其实现?
1.@SpringBootApplication注解: 这个注解标识了一个SpringBoot工程,它实际上是另外三个注解的组合,这三个注解是:
  a.@SpringBootConfiguration: 这个注解实际就是一个@Configuration,表示启动类也是一个配置类;
  b.@EnableAutoConfiguration:向pring容器中入了一个Selector,用来加载ClasPath下SpringFactories中所定义的自动配置类,将这些自动协载为配置Bean;
  c.@ComponentScan:标识扫描路径,因为默认是没有配置实际描路径,所以SpringBoot扫描的路径是启动类所在的当前目录;
2.@Bean注解:用来定义Bean,类似于XML中的<bean>标签,Spring在启动时,会对加了@Bean的法进行解折,将法的名字做为beanName,并通过执行方法得到bean对象;
3.@Controller、@Service、 @ResponseBody、@Autowired都可可以说。

什么时候@Transactional失效?

因为spring事务是基于代理来实的,所以某个加了@Transactional的方法只有是被代理对象调用时,那么这个注解才会生效,所以如果不是由代理对象来调用这个方法,那么@Transactional是不会生效的。
同时如里某方法是prnvate的,那么@Transactional也会失效,因为底层是基于子来实现的,子类是不能里我的private法的,所以没有利用代理,也导致@Transactianal失效。另一种说法是@Transactional是基于动态代理的,Spring的代理工厂在启动时会扫描所有的类和方法,并检查方法的修饰符是否为public,非public时不会获取@Transactional的属性信息,这时@Transactional的动态代理对象为空。
2、同一个类中,非@Transactional方法调用@Transactional方法。还是动态代理的原因,类内部方法的调用是通过this调用的,不会使用动态代理对象,事务不会回滚。
3、异常被“吃了”:Spring是根据抛出的异常来回滚的,如果异常被捕获了没有抛出的话,事务就不会回滚。
4、rollbackFor属性设置不对:Spring默认抛出unchecked异常或Error时才会回滚事务,要想其他类型异常也回滚则需要设置rollbackFor属性的值。

Spring中对事务定义了不同的传播级别: Propagation1、PROPAGATION REQUIRED: 默认传播行为。如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入到事务中。

Spring如何处理事务?

Spring当中支持编程式事务管理和声明式事务管理两种方式:

1、编程式事务可以使用TransactionTemplate
2、声明式事务:是Spring在AOP基础上提供的事务实现机制。他的最大优点就是不需要在业务代码中添加事务管理的代码,只需要在配置文件中做相关的事务规则声明就可以了。但是声明式事务只能针对方法级别,无法控制代码级别的事务管理。

2、PROPAGATION SUPPORTS: 如果当前存在事务,就加入到该事务。如果当前不存在事务,就以非事务方式运行。
3、PROPAGATION MANDATORY: 如果当前存在事务,就加入该事务。如果当前不存在事务,就抛出异常。
4、PROPAGATION REQUIRES NEW: 无论当前存不存在事务,都创建新事务进行执行。
5、PROPAGATION NOT SUPPORTED: 以非事务方式运行。如果当前存在事务,就将当前事务挂起,6、PROPAGATION NEVER : 以非事务方式运行。如果当前存在事务,就抛出异常。
7、PROPAGATION NESTED: 如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,则按REQUEIRED属性执行。

编程式事务参考:

https://blog.csdn.net/lyh59/article/details/126134146
https://blog.csdn.net/weixin_36994098/article/details/125859864

Spring中事务的隔离级别:

1、ISOLATION DEFAULT: 使用数据库默认的事务隔离级别。
2、ISOLATION READ UNCOMMITTED: 读未提交。允许事务在执行过程中,读取其他事务未提交的数据。
3、ISOLATION READ COMMITTED: 读已提交。允许事务在执行过程中,读取其他事务已经提交的数据。
4、ISOLATION REPEATABLE READ: 可重复读。在同一个事务内,任意时刻的查询结果是一致的。
5、ISOLATION SERIALIZABLE: 所有事务依次执行。

解释下Spring支持的几种bean的作用域

singleton: 默认,每个容器中只有一个bean的实例,单例的模式由BeanFactory自身来维护。该对象的生命周期是与SpringIOC容器一致的 (但在第一次被注入时才会创建)。
prototype: 为每一个bean请求提供一个实例。在每次注入时都会创建一个新的对象
request: bean被定义为在每个HTTP请求中创建一个单例对象,也就是说在单个请求中都会复用这一个单例对象。
session: 与request范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。
application: bean被定义为在ServletContext的生命周期中复用一个单例对象。
websocket: bean被定义为在websocket的生命周期中复用一个单例对象。
global-session: 全局作用域,global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同。

SpringMVC中的控制器是不是单例模式? 如果是,如何保证线程安全?

控制器是单例模式。
单例模式下就会有线程安全问题。
Spring中保证线程安全的方法:
将scop设置成非singleton。 prototype, request。
2、最好的方式是将控制器设计成无状态模式。在控制器中,不要携带数据。但是可以引用无状态的service和dao。

简述 Mybatis 的插件运行原理如何编写一个插件
答: Mybatis 只支持针对ParameterHandler、ResultSetHandler、StatementHandler、Executor 这 4种接口的插件,Mybatis 使用]DK 的动态代理,为需要拦截的接口生成代理对象以实现接口万法拦截功能,每当执行这4 种接口对象的方法时,就会进入拦截方法,具体就是 InvocationHandler 的 invoke() 方法,拦截那些你指定需要拦截的方法。
编写插件: 实现 Mybatis 的 nterceptor 接口并复写 intercept()方法,然后在给插件编写注解,指定要拦截哪个接口的哪些方法即可,在配置文件中配置编写的插件。

 

End!

posted @ 2023-02-07 06:59  君莫笑我十年游  阅读(22)  评论(0编辑  收藏  举报