1 Star 0 Fork 0

Paul_Zhen / java-review

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
spring.md 17.56 KB
一键复制 编辑 原始数据 按行查看 历史
zhenpeng 提交于 2023-12-11 13:34 . 分布式等篇章总结

谈谈IOC?

1.ioc也就是控制反转,首先是一种设计思想,用来降低类代码的耦合度,是一种设计模式中好莱坞原则的体现,我不需要调用你,等你来调用我。

实现方式是依赖查找和依赖注入(DI),优点是降低耦合性。

2.spring的ioc,通过IOC容器管理bean的生命周期,降低耦合性,通过配置bean也简化开发难度。IOC容器有beanFactory 和applicationContext。

IOC和DI区别?

ioc是控制反转,di是依赖注入,依赖注入的方式有构造器注入、setter注入等。DI是IOC的实现方式之一。

IOC容器的初始化步骤

极客时间 (geekbang.org)

生病期间肝了3万字的Spring容器启动流程 (qq.com)

public void refresh() throws BeansException, IllegalStateException {
 synchronized (this.startupShutdownMonitor) {
  // Prepare this context for refreshing.
  // 1. 刷新前的预处理
  prepareRefresh();

  // Tell the subclass to refresh the internal bean factory.
  // 2. 获取 beanFactory,即前面创建的【DefaultListableBeanFactory】
  ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

  // Prepare the bean factory for use in this context.
  // 3. 预处理 beanFactory,向容器中添加一些组件
  prepareBeanFactory(beanFactory);

  try {
   // Allows post-processing of the bean factory in context subclasses.
   // 4. 子类通过重写这个方法可以在 BeanFactory 创建并与准备完成以后做进一步的设置
   postProcessBeanFactory(beanFactory);

   // Invoke factory processors registered as beans in the context.
   // 5. 执行 BeanFactoryPostProcessor 方法,beanFactory 后置处理器
   invokeBeanFactoryPostProcessors(beanFactory);

   // Register bean processors that intercept bean creation.
   // 6. 注册 BeanPostProcessors,bean 后置处理器
   registerBeanPostProcessors(beanFactory);

   // Initialize message source for this context.
   // 7. 初始化 MessageSource 组件(做国际化功能;消息绑定,消息解析)
   initMessageSource();

   // Initialize event multicaster for this context.
   // 8. 初始化事件派发器,在注册监听器时会用到
   initApplicationEventMulticaster();

   // Initialize other special beans in specific context subclasses.
   // 9. 留给子容器(子类),子类重写这个方法,在容器刷新的时候可以自定义逻辑,web 场景下会使用
   onRefresh();

   // Check for listener beans and register them.
   // 10. 注册监听器,派发之前步骤产生的一些事件(可能没有)
   registerListeners();

   // Instantiate all remaining (non-lazy-init) singletons.
   // 11. 初始化所有的非单实例 bean
   finishBeanFactoryInitialization(beanFactory);

   // Last step: publish corresponding event.
   // 12. 发布容器刷新完成事件
   finishRefresh();
  }

  ...
  
 }
}

refreash方法

1.BeanFactory的实例化

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

2.BeanFacory的初始化 prepareBeanFactory(beanFactory);

3.通过继承和组合两种方式,对beanFactory做后置处理

// 子类通过重写这个方法可以在 BeanFactory 创建并与准备完成以后做进一步的设置 postProcessBeanFactory(beanFactory); // 执行 BeanFactoryPostProcessor 方法,beanFactory 后置处理器 invokeBeanFactoryPostProcessors(beanFactory);

4.注册BeanPostProcessors,bean 后置处理器 registerBeanPostProcessors(beanFactory);

5.国际化、事件发布相关 ``initMessageSource(); initApplicationEventMulticaster();`

6.启动tomcat容器

onRefresh();

7.把BeanDefinition的bean的初始化

finishBeanFactoryInitialization(beanFactory);

8.发布容器刷新完成事件

finishRefresh();

BeanFactory和ApplicationContext的区别?

它们都是IOC容器,但是,beanFactory是底层的IOC容器,而ApplicationContext是个超集,还提供了企业级的一些特性比如国际化、aop、Environment的功能。

源码角度讲ApplicationContext继承了beanFactory接口,但同时组合了beanFactory接口的实例对象,也就是DefaultListableBeanFactory,也就是使用了代理模式。

Environment是什么?

用于存储和处理占位符的加载外部化配置和profiles。

BeanFactory和FactoryBean区别?

BeanFactory接口:管理Bean的容器,Spring中生成的Bean都是由这个接口的实现来管理的。

FactoryBean接口:通常是用来创建比较复杂的bean,一般的bean 直接用xml配置即可,但如果一个bean的创建过程中涉及到很多其他的bean 和复杂的逻辑,直接用xml配置比较麻烦的时候,这时可以考虑用FactoryBean,可以隐藏实例化复杂Bean的细节。

什么是 BeanDefinition? 以及 Spring 怎么创建一个 bean_beandefinition 创建bean-CSDN博客

bean的生命周期

BeanDefinition注册阶段 : registerBeanDefinition 缓存Bean的定义到Map中

BeanDefinition合并阶段:(父子关系的 BeanDefinition 属性合并起来,子bean继承父bean的属性) - getMergedBeanDefinition RootBeanDefinition

实例化阶段:注入容器对象

设置 Bean的 属性(解析比如xml方式里面的property标签,把里面的value赋值给Bean的成员变量)。

如果我们通过各种 Aware 接口声明了依赖关系,则会注入 Bean 对容器基础设施层面的依赖。具体包括 BeanNameAware、BeanFactoryAware 和 ApplicationContextAware,分别会注入 Bean id、Bean Factory 或者 ApplicationContext 等等容器对象。

初始化阶段:

调用 BeanPostProcessor 的前置初始化方法 (postProcessBeforeInitialization)。

如果实现了 InitializingBean 接口,则会调用 afterPropertiesSet 方法。

调用 Bean 自身定义的 init 方法(执行一些初始化操作)。

调用 BeanPostProcessor 的后置初始化方法( postProcessAfterInitialization)。

销毁阶段:

关闭spring容器对时候,Spring Bean 的销毁过程会依次调用 DisposableBean 的 destroy 方法

和 Bean 自身定制的 destroy 方法。

循环依赖怎么解决

首先构造器注入的循环依赖,只能通过@Lazy解决,而单例模式下属性注入的循环依赖处理思路是利用bean的生命周期解决问题,先实例化,后初始化:实际会用到三级缓存singletonFactories(对象工厂)去解决循环依赖。

例如A依赖B,B又依赖A。

1.首先在A完成实例化后,并添加到三级缓存singletonFactories中

2.之后B先进行实例化,然后再初始化的时候发现自己依赖A,尝试从一级二级缓存去拿,发现拿不到就用getSingleton方法,就可以从三级缓存中获取到A的对象工厂,拿到A的实例对象进行填充,进而完成B的初始化。

3.当B创建完后,会将B再注入到A中完成A的初始化。循环依赖就解决了。

注:Spring通过三级缓存解决了循环依赖,其中一级缓存为单例池(singletonObjects),二级缓存为早期曝光对象earlySingletonObjects,三级缓存为对象工厂(singletonFactories)。

为什么不用二级缓存?

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
   Object singletonObject = this.singletonObjects.get(beanName);
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      synchronized (this.singletonObjects) {
      //通过earlySingletonObjects拿相同的实例
         singletonObject = this.earlySingletonObjects.get(beanName);
         if (singletonObject == null && allowEarlyReference) {
            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
            if (singletonFactory != null) {
               singletonObject = singletonFactory.getObject();//拿不到就会通过这个步骤获取实例,这个是重点,使用二级缓存避免重复走这个步骤
               this.earlySingletonObjects.put(beanName, singletonObject);
               this.singletonFactories.remove(beanName);
            }
         }
      }
   }
   return singletonObject;
}

有这个情况:a实例同时依赖于b实例和c实例,b实例又依赖于a实例,c实例也依赖于a实例。

getSingleton方法里面有一个先判断二级缓存里是否有对象,如果没有则从三级缓存里获取对象工厂并进行相关SmartInstantiationAwareBeanPostProcessor处理的过程,有了二级缓存,就避免下次做相同的处理,能够直接再次拿到a,提高了效率。

spring bean的作用域

单例singleton( beanFactory里面是单例的)

多例prototype

Request

Session

怎么实现单例的?

Spring源码解析之-doCreateBean() 详解-CSDN博客

BeanDefintion类里面有isSingleton接口、然后创建Bean的时候首先 调用这个接口判断 是否是单例模式 ,如果是单例模式, 从缓存获取并清除。

spring 单例的情况下,怎么保证的并发访问?

首先,在@Controller/@Service等容器中,默认情况下,scope值是单例-singleton的,也是线程不安全的;

尽量不要在@Controller/@Service等容器中定义静态变量,不论是单例(singleton)还是多实例(prototype)他都是线程不安全的。

如果单例Bean,是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。

解决线程安全的方法为:

方法一、使用ThreadLocal,ThreadLocal会为每一个线程提供一个独立的变量副本,这样在多线程对数据访问就不会出现冲突。因为每一个线程都拥有自己的变量副本,因此也就不需要同步该变量。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。

SpringMVC单例Bean线程安全怎么解决

方法二、如果时web应用,可以使用Spring Bean的作用域中的@Scope("prototype")@Scope("request"),在controller类前面加上@Scope("****"),表明每次请求都会生成一个新的Bean对象。这样也能起到线程安全的作用。

spring事务传播特性

PROPAGATION_REQUIRED 0 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择,也是Spring默认的事务的传播。
PROPAGATION_SUPPORTS 1 支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY 2 支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW 3 新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED 4 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER 5 以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED 6 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。

常用的主要有Required,RequiresNew,Nested三种。

  • Required:简单理解就是事务方法会判断是否存在事务,有事务就用已有的,没有就重新开启一个。
  • RequiresNew:简单理解就是开启新事务,若当前已有事务,挂起当前事务。新开启的事务和之前的事务无关,拥有自己的锁和隔离级别,可以独立提交和回滚,内层事务执行期间,外层事务挂起,内层事务执行完成后,外层事务恢复执行。
  • Nested:简单理解就是嵌套事务,如果外部事务回滚,则嵌套事务也会回滚!!!外部事务提交的时候,嵌套它才会被提交。嵌套事务回滚不会影响外部事务。子事务是上层事务的嵌套事务,在子事务执行之前会建立savepoint,嵌套事务的回滚会回到这个savepoint,不会造成父事务的回滚。

如果想事务一起执行可以用Required满足大部分场景,如果不想让执行的子事务的结果影响到父事务的提交可以将子事务设置为RequiresNew。

spring事务的传播原理

Spring事务传播实现原理_事务的传播行为实现原理-CSDN博客

通过threadLocal 管理数据库连接,然后每次事务的方法调用的时候,会先获取是否存在事务,然后有事务就判断怎么根据事务传播特性处理当前的事务。

spring事务失效?

a普通方法、b事务方法,a调用b不会启动事务,因为相当于this.事务方法(),没有走代理对象。

@Service
public class UserService {
 
    @Autowired
    private UserMapper userMapper;
 
  
    public void add(UserModel userModel) {
        userMapper.insertUser(userModel);
        updateStatus(userModel);
    }
 
    @Transactional
    public void updateStatus(UserModel userModel) {
        doSameThing();
    }
}

我们看到在事务方法 add 中,直接调用事务方法 updateStatus。从前面介绍的内容可以知道,updateStatus 方法拥有事务的能力是因为 spring aop 生成代理了对象,但是这种方法直接调用了 this 对象的方法,所以 updateStatus 方法不会生成事务。

由此可见,在同一个类中的方法直接内部调用,会导致事务失效。

spring事务的隔离级别

ISOLATION_DEFAULT -1 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。另外四个与JDBC的隔离级别相对应
ISOLATION_READ_UNCOMMITTED 1 这是事务最低的隔离级别,它充许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻读。
ISOLATION_READ_COMMITTED 2 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。
ISOLATION_REPEATABLE_READ 4 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻读。
ISOLATION_SERIALIZABLE 8 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻读。

只是比数据库事务的隔离级别多了一个default

AOP用来做什么?Spring的AOP是怎么实现的?它的两种实现方式,并且说一下哪一个效率更高一些,为什么。

我们在项目中主要使用aop来做日志操作还有权限管理。

springAOP主要有两种实现,1.JDK动态代理,2.cgLib

它们是有一点区别的,就是JDK动态代理它主要是可以生成语言接口,就是实现相同接口的,这么一个类。

但是原来类没有实现接口的话,它就不合适。

如果是cgLib的话,他会使用一种ASM框架的字节码的编辑器,asm是直接通过字节码来生成和修改class文件。cgLib可以生成一个目标类的子类,去实现类似代理的一个功能。

性能上讲,cgLib,它在创建对象的过程中,可能就是做的更慢一点,但是在运行的时候,效率要更高一些。

总结: 两种代理中,每次通过类也好 接口也好 都会生成.class文件,并指定这个生成的代理类的名称(如JDK方式的类名:com.sun.proxy.$Proxy0),加载到内存中,生成一个class对象,拿到class对象就可以走new Instance方法拿到实例,最后就可以拿到这个实例进行在比如(InvocationHandler里的invoke方法)里面进行回调,调用的前后可以进行自定义的控制。

项目里面是怎么用AOP的

权限模块,维护的基本单位是员工和权限的关系表,有几个场景,比如员工岗位变动,权限禁用,都会变动这个员工权限关系表.权限变动还需要下放给海康威视的门禁系统.下放的逻辑写到了aop实现的.

如何设计一个统计API访问次数的功能

统计接口调用量 - 掘金 (juejin.cn)

接口统计表+自定义注解@Trend+aop(以自定义注解作为切面,只对定义@Trend注解的方法进行统计)

@Pointcut("@within(com.xxx.annotation.Trend)")

aop通知?

前置通知(Before):在目标方法被调用之前调用通知功能;

后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;

返回通知(After-returning ):在目标方法成功执行之后调用通知;

异常通知(After-throwing):在目标方法抛出异常后调用通知;

环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的逻辑。

面试官:说一下Java反射机制的应用场景 - 知乎 (zhihu.com)

你什么地方用到了反射,或者java本身哪里用到了反射?

1.jdbc class.forName("com.mysql.jdbc.Driver");

2.spring里面的 读取bean 的xml ,解析路径,并实例化对应bean。

1
https://gitee.com/Paul_Zhen/java-review.git
git@gitee.com:Paul_Zhen/java-review.git
Paul_Zhen
java-review
java-review
master

搜索帮助