Java总结-3
六、Spring
1. IOC和AOP?
首先声明:IoC & AOP 不是 Spring 提出来的,它们在 Spring 之前其实已经存在了,只不过当时更加偏向于理论。Spring 在技术层次将这两个思想进行了很好的实现。
1.1 IOC?
IoC (Inversion of control )控制反转/反转控制。它是一种思想不是一个技术实现。描述的是:Java 开发领域对象的创建以及管理的问题。
例如:现有类 A 依赖于类 B
- 传统的开发方式 :往往是在类 A 中手动通过 new 关键字来 new 一个 B 的对象出来
- 使用 IoC 思想的开发方式 :不通过 new 关键字来创建对象,而是通过 IoC 容器(Spring 框架) 来帮助我们实例化对象。我们需要哪个对象,直接从 IoC 容器里面过去即可。
从以上两种开发方式的对比来看:我们 “丧失了一个权力” (创建、管理对象的权力),从而也得到了一个好处(不用再考虑对象的创建、管理等一系列的事情)
为什么叫控制反转?
控制:指的是对象创建(实例化、管理)的权力,反转:控制权交给外部环境(Spring 框架、IoC 容器)
解决了什么问题?
IoC 的思想就是两方之间不互相依赖,由第三方容器来管理相关资源。这样有什么好处呢?首先对象之间的耦合度或者说依赖程度降低;然后资源变的容易管理;比如你用 Spring 容器提供的话很容易就可以实现一个单例。
1.2 AOP
什么是AOP?
AOP:Aspect oriented programming 面向切面编程,AOP 是 OOP(面向对象编程)的一种延续。切 :指的是横切逻辑,原有业务逻辑代码不动,只能操作横切逻辑代码,所以面向横切逻辑;面 :横切逻辑代码往往要影响的是很多个方法,每个方法如同一个点,多个点构成一个面。
AOP解决了什么问题?
通过上面的分析可以发现,AOP 主要用来解决:在不改变原有业务逻辑的情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复。
1.3 AOP实现:JDK动态代理,CGLIB
两种方式:一种是JDK动态代理,另一种是CGLib的方式。
1.3.1 JDK动态代理具体实现原理
- 通过实现InvocationHandlet接口创建自己的调用处理器;
- 通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理;
- 通过反射机制获取动态代理类的构造函数,其唯一参数类型就是调用处理器接口类型;
- 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数参入;
JDK动态代理是面向接口的代理模式,如果被代理目标没有接口那么Spring也无能为力,Spring通过Java的反射机制生产被代理接口的新的匿名实现类,重写了其中AOP的增强方法。
1.3.2 CGLib动态代理
CGLib是一个强大、高性能的Code生产类库,可以实现运行期动态扩展java类,Spring在运行期间通过 CGlib继承要被动态代理的类,重写父类的方法,实现AOP面向切面编程呢。
1.3.3 两者对比
JDK动态代理是面向接口的。
CGLib动态代理是通过字节码底层继承要代理类来实现(如果被代理类被final关键字所修饰,那么抱歉会失败)。
1.3.4 性能
关于两者之间的性能的话,JDK动态代理所创建的代理对象,在以前的JDK版本中,性能并不是很高,虽然在高版本中JDK动态代理对象的性能得到了很大的提升,但是他也并不是适用于所有的场景。
主要体现在如下的两个指标中:
- CGLib所创建的动态代理对象在实际运行时候的性能要比JDK动态代理高不少,有研究表明,大概要高10倍;
- 但是CGLib在创建对象的时候所花费的时间却比JDK动态代理要多很多,有研究表明,大概有8倍的差距;
- 因此,对于singleton的代理对象或者具有实例池的代理,因为无需频繁的创建代理对象,所以比较适合采用CGLib动态代理,反正,则比较适用JDK动态代理。
2.Spring事务管理?
3.Spring启动的过程?
4.Spring Bean的生命周期?
graph TB st(( ))--通过getBean调用某一个Bean-->a(*调用InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation方法) a-->b(实例化) b-->c(*调用InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation方法) c-->d(*调用instantiationAwarebeanPostProcessor的postProcessPropertyValues方法) d-->e(设置属性值) e-->f(调用BeanNameAware的setBeanName方法) f-->g(调用BeanFactoryAware的setBeanFactory方法) g-->h(*调用BeanPostProcessor的postProcessBeforeInitialization方法) h-->i(调用InitializingBean的afterPropertiesSet方法) i-->j(通过init-method属性配置的初始化方法) j-->k(*调用BeanPostProcessor的postProcessAfterInitialization方法) k-->l(Spring缓存池中准备就绪的Bean) k-->m(将准备就绪的Bean交给调用者) l-->n(调用DisposableBean的destroy方法) n-->p(通过destroy-method属性配置的销毁方法) p-->pp(( ))
ps:ApplicationContext中的Bean的生命周期
graph TB st(( ))--启动容器-->aa(调用BeanFactoryPostProcessor的postProcessBeanFactory方法对工厂定义信息进行处理) aa--通过getBean调用某一个Bean-->a(*调用InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation方法) a-->b(实例化) b-->c(*调用InstantiationAwareBeanPostProcessor的postprocessAfterInstantiation方法) c-->d(*调用instantiationAwarebeanPostProcessor的postProcessPropertyValues方法) d-->e(设置属性值) e-->f(调用BeanNameAware的setBeanName方法) f-->g(调用BeanFactoryAware的setBeanFactory方法) g-->gg(调用ApplicationContextAware的setApplicationnContext方法) gg-->h(*调用BeanPostProcessor的postProcessBeforeInitialization方法) h-->i(调用InitializingBean的afterPropertiesSet方法) i-->j(通过init-method属性配置的初始化方法) j-->k(*调用BeanPostProcessor的postProcessAfterInitialization方法) k-->l(Spring缓存池中准备就绪的Bean) k-->m(将准备就绪的Bean交给调用者) l-->n(调用DisposableBean的destroy方法) n-->p(通过destroy-method属性配置的销毁方法) p-->pp(结束)
5.Spring AOP和Spring Bean的生命周期有什么关系?
- AOP本身是可以对一个pojo进行操作,比如希望在一个类中的方法执行前和执行后添加日志信息。
- 在Spring中,Spring AOP、IOC和AspectJ被整合在一起了。
- Spring AOP使用了JDK动态代理和CGLIB动态代理。(PS:JDK动态代理只支持接口代理,不支持类代理)
- JDK动态代理主要涉及的类:Proxy和InvocationHandler
- CGLIB主要涉及到的接口:MethodInterpreter,Enhance,MethodProxy
- 对于增强,CGLIB手动建立的,AOP定义了5种增强类型:前置增强、后置增强、环绕增强、异常抛出增强、引介增强。
- 如何管理植入切面的增强?
- 直接使用ProxyBean
- 通过Spring配置来定义:ProxyFactoryBean
- Spring提供了自动代理机制,让容器自动生成代理,避免上述繁琐的配置方式,在内部,Spring使用BeanPostProcessor自动完成这个工作。
6.接收参数的几种方法?
7.@Bean和@Component的区别?
(1)@Component注解表明一个类会作为组件类,并告知Spring要为这个类创建bean。
(2)@Bean注解告诉Spring这个方法将会返回一个对象,这个对象要注册为Spring应用上下文中的bean。通常方法体中包含了最终产生bean实例的逻辑。
两者的目的是一样的,都是注册bean到Spring容器中。
区别:
- @Component(@Controller、@Service、@Repository)通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中。
- 而@Bean注解通常是我们在标有该注解的方法中定义产生这个bean的逻辑。
- @Component 作用于类,@Bean作用于方法。
总结:
@Component和@Bean都是用来注册Bean并装配到Spring容器中,但是Bean比Component的自定义性更强。可以实现一些Component实现不了的自定义加载类。
8. Spring中的设计模式
https://juejin.cn/post/6844903849849962509
(1)单例模式
1 | /** Cache of singleton objects: bean name to bean instance. */ |
(2)工厂模式
Spring使用工厂模式可以通过 BeanFactory 或 ApplicationContext 创建 bean 对象。
(3)观察者模式
- Spring 的事件流程总结: ApplicationEvent,ApplicationListener,ApplicationEventPublisher。
graph TB st(( ))-->a(1. 定义一个事件, 实现一个继承自ApplicationEvent, 并且写相应的构造函数) a-->b(2. 定义一个事件监听者: 实现ApplicationListener接口, 重写onApplicationEvent方法) b-->c(3. 使用事件发布者发布消息: 可以通过ApplicationEventPublisher的publishEvent方法发布消息)
1 | // 定义一个事件,继承自ApplicationEvent并且写相应的构造函数 |
(4)代理模式
(5)模板方法
(6)适配器模式
(7)装饰者模式
9. Spring 的启动过程是怎样的?
(1)SpringApplication.java注释
1 | /** |
10.Spring的自动装配?
11.ApplicationContext通常的实现是什么?
12.AOP实现流程?
(1)基于接口的增强
以下是通过接口实现增强的使用,用起来很笨重。后面来讲基于@AspectJ的方式就简单很多!
graph TB A1(获取spring提供的代理工厂)-.->B1(设置目标代理) B1-.->C1(为代理目标设置增强) C1-.->D1(生成代理实例)举一个例子,如何自行定义一个权限控制的注解?注解+AOP实现
- 先定义一个注解,比如叫@AuthCheck
- 在接口上添加@AuthCheck注解
- 对一个类添加@AspectJ注解,在这个类中通过@PointCut来扫描@AuthCheck,就是切点;然后定义一些增强,比如使用@Before搞一个前置增强,这个增强对切点进行处理,可以将切点的参数一并携带,进行一些逻辑处理!
13.Springboot自动配置的流程?
(1)refresh()
以下refresh()定义了Spring容器在加载配置文件后的各项处理过程。
1 |
|
(2)spring从配置文件创建完整bean的流程
graph TB st((配置文件))--ResourceLoader装载配置文件-->a(Resource) a--BeanDefinitoinReader解析配置信息-->b(BeanDefinitionRegistry加工前的BeanDefinition) b-->c(BeanDefinitionRegistry加工后的BeanDefinition) b-->d(PropertyEditorRegistry存放的自定义的PropertyEditor) c--InstantiationStrategy实例化Bean对象-->e(Bean实例未设置属性) d--BeanWrapper设置Bean属性-->f(Bean实例已设置属性) e--BeanWrapper设置Bean属性-->f f--BeanPostprocessror对Bean进行加工-->g(Bean实例准备完成)
1 | (1) ResourceLoader从存储介质加载Spring配置信息,并使用Resource表示这个配置文件资源。 |
(3)Spring boot自动配置
举例:在引入MyBatis的starter后,MyBatis相关的Bean自动装配进入容器,如何仅仅通过starter来实现?
1)SpringBoot启动的时候加载主配置类,开启了自动配置功能@EnableAutoConfiguration
2)@EnableAutoConfiguration 作用:利用EnableAutoConfigurationImportSelector给容器中导入一些组件可以查看selectImports()方法的内容;
List configurations = getCandidateConfigurations(annotationMetadata, attributes);
获取候选的配置
1
2
3
4
5 > SpringFactoriesLoader.loadFactoryNames()
> 扫描所有jar包类路径下 META‐INF/spring.factories
> 把扫描到的这些文件的内容包装成properties对象
> 从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把他们添加在容器中
>
将 类路径下 META-INF/spring.factories 里面配置的所有EnableAutoConfiguration的值加入到了容器中;每一个这样的 xxxAutoConfiguration类都是容器中的一个组件,都加入到容器中;用他们来做自动配置;
3)、每一个自动配置类进行自动配置功能;
14.BeanFactory和FactoryBean的区别?
- BeanFactory是工厂,我们通过getBean获取Bean。
- FactoryBean是一个工厂类接口,可以用来定制Bean的逻辑,Spring提供了很多FactoryBean的实现类,隐藏了实例化一些复杂Bean的细节。
15.@Resource和@Autowired的作用的相同的么?
- 这是一个sb的问题,没啥子实际区别!
16.BeanFactory和ApplicationContext的区别?
- 生命周期上有区别
- 对Bean的初始化有区别
17.在 Spring中如何注入一个java集合?
18.MyBatis 相关
(1)简单谈谈你对 Mybatis 的理解?
(2)MyBatis 接口绑定的优点是什么?
(3)实现 MyBatis 接口绑定分别有哪几种方式?
(4)MyBatis 如何实现一对一关联关系?
(5)MyBatis 如何实现一对多关联关系?
(6)说说 MyBatis 动态 SQL 的具体使用步骤?
(7)MyBatis 与 Hibernate 的区别是什么?
(8)MyBatis 如何实现模糊查询?
19.Nginx 反向代理实现高并发的具体步骤是什么?
20.Nginx 搭建 Tomcat 集群的核心配置应该怎么写?
21.bean之间的关系?(继承,依赖)
22.配置外部数据源?context:property-placeholder,使用${var}
23.SpEL的使用
(1)SpEL的作用?
24.bean的配置方式?
(1)xml文件
(2)注解(可以使用value修改名字)
25.Spring事务管理
(1)编程式事务管理(注释)
(2)声明式事务管理(XML)
26.Spring Boot事务管理
27.Spring事务的传播行为
28.Spring的事务隔离级别
29. 注解
30. Spring 问题?
30.1 Spring 循环依赖?
循环依赖:一个或多个对象实例之间存在直接或间接的依赖关系,这种依赖关系构成了构成一个环形调用。
循环依赖总结一下(假设A,B之间循环依赖):
一级缓存singletonObject,也就是常说的单例池,是个Map
二级缓存earlySingletonObjects,也就是提前一点的单例池,哈哈,字面翻译,也是Map
三级缓存singletonFactories,这个Map有点特殊,因为这个Map的value存放的是一个lambda表达式
1、单例池不能存放原始对象,只能存放经过完整生命周期的对象,也就是java bean
2、A,B在注入属性都会执行一个addSingletonFactory方法,这个方法里面三级缓存就出现了,三级缓存put了key为beanName,value为一个lambda表达式
3、其实最容易绕晕的地方是,当B注入属性A的时候,执行populateBean注入一个bean的属性的时候会执行getSingleton这个方法,一定要记得!!populateBean方法体中没有直接调用getSingleton这个方法,但一定要记得,执行了这个方法
4、getSingleton这个方法,会依次到一级缓存,二级缓存,三级缓存中get(beanName),很显然当B注入A属性的时候,一级,二级里面都没有内容,只有三级有,这时会执行lambda表达式,lambda表达式的作用就是生成代理对象!!然后把生成的代理对象存入二级缓存,并返回这个代理对象,B就会得到这个代理对象A,B就会认为这个代理对象A就是A的最后的bean对象,因此也就完成了对A的属性注入这步操作,接着依次执行B后续的操作,最后就完成了B的生命周期,B就成功变成了bean对象,B也就完成了使命
5、当B完成使命之后,A就会继续注入B,这时就会注入属性成功了,接下来开始执行AOP操作,因为上一步中A已经生成了代理对象A,也就是相当于完成了AOP,所以B就不执行AOP操作了,此时A就会执行最后一步操作,将代理对象A放入到单例池中去,这时A就会执行方法getSingleton,从二级缓存中获得了代理对象A,最后将其存入单例池,也就是一级缓存!好了,现在A和B都放入了单例池,圆满结束!!!!
30.2 Spring为什么要有三级缓存?
三级缓存的实现在代码中的 SingletonBeanRegistry 中,其中有以下几个核心属性:
singletonObjects:一级缓存, 用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用。
earlySingletonObjects:二级缓存,用于存放提前曝光的单例对象的cache,原始的 bean 对象(尚未填充属性)。
singletonFactories:三级缓存,用于存放 bean 工厂对象(ObjectFactory)。三级缓存中用到了 ObjectFactory,这里的 ObjectFactory 可以理解为三级缓存中 Bean 的代理对象,其 getObject() 方法描述了如何获取这个三级缓存的对象。设计成这样除了方便实现三级缓存解决循环依赖,另外也是方便 Spring 在 ObjectFactory 中做一些拓展。
singletonsCurrentlyInCreation:用于存放正在被创建的 Bean 对象。
二级和三级只有在产生循环依赖的时候用到。
31. Spring单例实现
使用ConcurrentHashMap存放Bean
1 | /** Cache of singleton objects: bean name to bean instance. */ |
七、Spring Boot
1.Spring boot重要的构成部分
2.Spring Boot启动过程
https://www.e-learn.cn/content/qita/2004793
graph TB A1(加载resources/META-INF/spring.factories中配置的ApplicationContextInitializer ApplicationListener)-.->B1(加载 resources/META-INF/spring.factories 中配置的 SpringApplicationRunListener) B1-.->C1(创建 StandardServletEnvironment 并完成初始配置) C1-.->D1(通过 EventPublishingRunListener发布ApplicationEnvironmentPreparedEvent事件) D1-.->E1(加载resources/META-INF/spring.factories中配置的EnvironmentPostProcessor,
并触发其 postProcessEnvironment调用,完成Environment的后处理,主要用于加载属性源到 Environment中) E1-.->F1(将Environment绑定到SpringApplication 中) F1-.->G1(创建ApplicationContext:AnnotationConfigServletWebServerApplicationContext) G1-.->H1(加载 resources/META-INF/spring.factories 中配置的 SpringBootExceptionReporter'FailureAnalyzers') H1-.->I1(执行所有已加载的 ApplicationContextInitializer,附加自定义配置) I1-.->J1(通过EventPublishingRunListener发布 ApplicationContextInitializedEvent事件) J1-.->K1(创建 BeanDefinitionLoader, 将应用程序主类解析为 BeanDefinition 并注册到 DefaultListableBeanFactory中) K1-.->L1(通过 EventPublishingRunListener 发布 ApplicationPreparedEvent 事件) L1-.->M1(prepareRefresh:清除类路径扫描器缓存写入closed, active 标识,验证所有必须的属性是否都能解析) M1-.->N1(obtainFreshBeanFactory:写入 refreshed 标识, 获取 DefaultListableBeanFactory) N1-.->O1(prepareBeanFactory:写入 BeanPostProcessor写入可以忽略的依赖注入接口, 注册部分内部可解析的依赖接口等) O1-.->P1(postProcessBeanFactory:注册 WebApplicationScope, 注册部分内部可解析的依赖接口) P1-.->Q1(invokeBeanFactoryPostProcessors:执行
SharedMetadataReaderFactoryContextInitializer$CachingMetadataReaderFactoryPostProcessor
注册用于获取MetadataReader的org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory的BeanDefinition) Q1-.->R1(registerBeanPostProcessors:按照 PriorityOrdered, Ordered, 普通的顺序将 BeanPostProcessor添加到 BeanFactory 中) R1-.->S1(initMessageSource:注册 messageSource 单例'DelegatingMessageSource') S1-.->T1(注册applicationEventMulticaster单例'SimpleApplicationEventMulticaster', 用于广播 ApplicationEvent) T1-.->U1(onRefresh:设置主题, 创建内嵌服务器'WebServer',
获取 ServletContextInitializer DispatcherServletRegistrationBean初始化器
并注册DispatcherServlet到 javax.servlet.ServletContext和org.apache.catalina.core.ApplicationContext中.
注册 OrderedCharacterEncodingFilter,OrderedHiddenHttpMethodFilter,
OrderedFormContentFilter, OrderedRequestContextFilter等过滤器,
也可注册自定义过滤器. 写入 ServletContextPropertySource, ServletConfigPropertySource到Environment中) U1-.->V1(registerListeners:注册内置的 ApplicationListener, 自定义的 ApplicationListener 到 ApplicationEventMulticaster中) V1-.->W1(finishBeanFactoryInitialization:冻结 DefaultListableBeanFactory 中注册的BeanDefinition 信息,
并实例化所有非延迟初始化的单例 bean.
在所有 eager 单例初始化完成之后,
如果其实现了 SmartInitializingSingleton 接口, 则触发其 afterSingletonsInstantiated 调用完成后处理) W1-.->X1(注册 DefaultLifecycleProcessor,查找所有实现 SmartLifecycle 接口的 bean && 如果它是 isAutoStartup,则触发其start方法调用) X1-.->Y1(通过 EventPublishingRunListener 发布 ContextRefreshedEvent 事件) Y1-.->Z1(启动 WebServer) Z1-.->A2(通过 EventPublishingRunListener 发布 ServletWebServerInitializedEvent 事件) A2-.->B2(清理各种缓存) B2-.->C2(注册 Application 的 ShutdownHook) C2-.->D2(触发 SpringApplicationRunListener 的 started 通知) D2-.->E2(通过 EventPublishingRunListener 发布 ApplicationStartedEvent 事件) E2-.->F2(按照 Order 对 ApplicationRunner,CommandLineRunner 进行排序,并顺序执行) F2-.->G2(触发 SpringApplicationRunListener 的 running 通知) G2-.->H2(通过 EventPublishingRunListener 发布 ApplicationReadyEvent 事件) H2-.->I2(应用程序启动完成)
3. 自定义spring-boot-starter
- 写一个@Enable*的注解,@Import相关的@Configuration
- 写具体的实现
- 写@Configuration,创建Bean
八、RocketMq
1. 事务机制
(1)half机制
2. Dledger(外部机制实现高可用)
(1)Raft协议
- Leader
- follower
- candidate(竞选状态)
3. 私信队列
- 一直没有消费———>Retry队列———->死信队列————->加入另外的消费者消费