-
Spring框架是J2EE编程领域的一个轻量级开源框架,是针对
bean的生命周期
进行管理的轻量级容器
;依赖注入IOC和面向切面编程AOP
是Spring的灵魂;常见的配置方式:XML配置、注解配置、Java配置。Spring的几个核心模块:
- Spring Core:核心类库,提供IOC服务
- Spring Context:提供框架式的Bean访问方式,以及企业级功能
- Spring AOP:AOP服务
- Spring DAO:对JDBC的抽象,简化了数据访问异常的处理
- Spring ORM:对现有的ORM框架的支持
- Spring WEB:提供了基本的面向WEB的综合特性,如文件上传
- Spring MVC:提供面向WEB应用的Model-View-Controller实现
-
Spring优点
- 属于
低侵入式
设计,IOC机制将对象之间的依赖关系交由框架处理,减少组件耦合性 - AOP技术支持将一些通用任务,如安全、事务、日志、权限等进行集中式管理,从而更好地
实现复用
- 对
主流的应用框架提供了集成支持
,主流的ORM框架,如mybatis/struct/hibernate等
- 属于
-
Spring的IOC和AOP机制:
- IOC:控制反转(依赖注入),利用工厂模式将对象交给容器管理,spring容器启动时会将配置文件中的bean都初始化好,使用的时候,只要将被使用的bean注入到调用方类中即可,不需要再new被调用者对象;
- AOP:将程序中的
交叉业务逻辑
(例如安全
、日志
、事务
),封装成一个切面
,注入到目标对象中去;可以减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性;可用于权限认证、日志、事务处理。
-
Spring的几种注入方式:
- 属性注入:使用@Autowired(Spring注解)、@Resource(JAVA注解)都可实现注入
- @Autowired按照type注入,如果存在多个,则按照name匹配(按照@
Qualifier
指定的name或者变量名); - @Resource如果同时指定了
name
和type
,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常;如果指定了name
,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常;如果指定了type
,则从上下文中找到类型匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常;如果既没有指定name
,又没有指定type
,则默认按照byName
方式进行装配;如果没有匹配,按照byType
进行装配
- @Autowired按照type注入,如果存在多个,则按照name匹配(按照@
- set方法注入:灵活,可以选择性的注入对象,prototype模式下会存在循环依赖问题,抛异常;singleton模式下spring会通过二级缓存和三级缓存自动处理掉
- 构造方法注入:不够灵活,当需要注入多个对象时,构造器参数列表很长,或者需要多个重载构造器,存在循环依赖问题,抛异常
- 属性注入:使用@Autowired(Spring注解)、@Resource(JAVA注解)都可实现注入
-
AOP代理模式:分为静态代理和动态代理,区别在于生成AOP代理对象的时机不同
- 静态代理的代表为AspectJ:所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强;会在编译阶段将AspectJ(切面)织入到Java字节码中,运行时候就是增强之后的AOP对象;设计模式里边的案例就是静态代理,在编译期间会将接口、实现类和代理类编译成class文件
- Spring AOP使用的是动态代理:所谓动态代理,就是AOP框架在每次运行时在内存中临时为方法生成一个AOP对象,这个对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法;
-
Spring的动态代理主要有两种方式:JDK动态代理和CGLIB动态代理
1. JDK动态代理只提供接口的代理,不支持类的代理;核心InvocationHandler接口和Proxy类
2. 如果代理类没有实现InvocationHandler接口,那么SpringAOP会选择使用CGLIB来动态代理目标类;可以在运行时动态生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP;CGLIB是通过继承的方式做的动态代理,因此无法动态代理某个被final标记的类。
-
Spring AOP几个概念
- 连接点:指程序运行过程中所执行的方法
- 切面:被抽取出来的公共模块,在Spring AOP中,切面可以在类上使用 @AspectJ 注解来实现
- 切点:切点用于定义要对哪些连接点进行拦截,切点分为execution方式和annotation方式;execution方式可以用
路径表达式
指定对哪些方法拦截,比如指定拦截add*、search*;annotation方式可以指定被哪些注解修饰的代码进行拦截 - 通知:指要在连接点上执行的动作,即
增强的逻辑
;通知有各种类型,包括Around、Before、After、Around、After returning、After throwing
-
拦截器 VS 过滤器 VS Aspect
-
先说明一下:AOP是一种技术、思想,而Aspect、拦截器和过滤器是AOP技术、思想的具体实现;Aspect是针对具体的方法进行
增强
;而拦截器主要是针对Controller接口进行拦截 -
来源:过滤器来自于java的servlet包;拦截器来自于Spring框架
-
触发时机:请求进入容器 -> 过滤器 -> Servlet -> 拦截器 -> 执行控制器(Controller)
-
实现原理:
- 过滤器是基于
方法回调
实现的:doFilter方法中,我们要执行下一个过滤器或者流程时,需调用FilterChain对象的doFilter方法进行回调执行 - 拦截器是基于
动态代理
(底层是反射
)实现的:代理目标Controller
- 过滤器是基于
-
支持项目类型:
- 过滤器是依赖于Servlet容器的,只能用于WEB项目
- 拦截器是Spring的组件,可以应用在WEB、APPLICATION中
-
使用场景:
- 拦截器更接近于业务系统,主要用来实现项目中业务判断,比如:
登录校验
、权限判断
、日志记录
等 - 过滤器通常用来实现
通用功能
的过滤,比如:敏感词过滤
、字符编码集
设置、响应数据压缩等
- 拦截器更接近于业务系统,主要用来实现项目中业务判断,比如:
-
-
BeanFactory和ApplicationContext有什么区别
- BeanFactory是Spring里面最底层的接口,是IoC的核心,定义了IoC的基本功能,包含了各种Bean的定义、加载、实例化,依赖注入和生命周期管理
- ApplicationContext接口作为BeanFactory的子类,还提供了国际化支持、资源文件访问
- BeanFactroy采用的是延迟加载形式来注入Bean的,只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化;ApplicationContext是在容器启动时,一次性创建了所有的Bean
-
Bean的生命周期:实例化->属性赋值->初始化->销毁
-
实例化Bean对象:通过BeanFactory/ApplicationContext容器实例化Bean
-
设置对象属性(依赖注入):Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成属性设置与依赖注入
-
处理Aware接口:我们的Bean实现Spring的某些容器Aware接口,为了利用这些容器对象提供的能力/服务
- BeanNameAware接口:会调用它实现的setBeanName(String beanId)方法,传入Bean的名字
- BeanClassLoaderAware接口:调用setBeanClassLoader()方法,传入ClassLoader对象的实例
- BeanFactoryAware接口:会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身
- ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文
-
BeanPostProcessor前置处理:如果想对Bean进行一些自定义的前置处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj, String s)方法
-
InitializingBean:如果Bean实现了InitializingBean接口,执行afterPropertiesSet()方法
-
init-method:如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法
-
BeanPostProcessor后置处理:如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术
-
DisposableBean:当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法
-
destroy-method:最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法
-
-
@PostConstruct注解
- Java自己的注解,被用来修饰非静态void方法
- 被@PostConstruct修饰的方法会在服务器
加载Servlet
的时候运行,并且只会被服务器执行一次 - PostConstruct在构造函数之后执行,init()方法之前执行
- PostConstruct方法在Bean初始化过程中的顺序:
Constructor(构造方法)
->@Autowired(依赖注入)
->@PostConstruct
(注释的方法)
-
bean的作用域
- singleton:默认单例
- prototype:每一个bean请求创建一个实例
- request:为每一个request请求创建一个实例
- session:类似request,同一个session会话共享一个实例
- global-session:全局作用域,所有会话共享一个实例
-
spring中的bean线程安全吗?
- Spring容器本身并没有提供Bean的线程安全策略,因此可以说Spring容器中的Bean本身不具备线程安全的特性
- 对于prototype作用域的Bean,每次都创建一个新对象,也就是线程之间不存在Bean共享,因此不会有线程安全问题
- 对于singleton作用域的Bean,所有的线程都共享一个单例实例的Bean,因此是存在线程安全问题的;但是如果单例Bean是一个无状态Bean,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例Bean是线程安全的。比如Controller类、Service类和Dao等,这些Bean大多是无状态的,只关注于方法本身
-
spring如何处理
线程并发
问题?- ThreadLocal:空间换时间,为每一个线程提供
独立副本
,隔离
多个线程对数据的访问冲突; - 同步机制:时间换空间,多个线程在访问同一份数据时,需要获取
锁
- ThreadLocal:空间换时间,为每一个线程提供
-
spring中的一些设计模式
- 工厂模式:Spring使用工厂模式,通过BeanFactory和ApplicationContext来创建对象
- 单例模式:Bean默认为单例模式
- 代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术
- 策略模式:例如Resource的实现类,针对不同的资源文件,实现了不同方式的资源获取策略
-
springmvc
-
对springmvc的理解:
- 基于Java实现了
MVC设计模式
的请求驱动类型的轻量级Web
框架; - 通过Model、View、Controller分离,将
web层
进行职责解耦
,纵向分层,简化开发
- 基于Java实现了
-
springmvc的主要组件:
前端控制器
DispatcherServlet:接收请求、响应结果,相当于转发器- 处理器
映射器
HandlerMapping:根据请求的URL来查找Handler - 处理器
适配器
HandlerAdapter:执行具体Handler 处理器
Handler:程序员开发,处理具体web逻辑视图解析器
ViewResolver:进行视图解析,根据试图逻辑名解析成真正的视图- 视图View:程序员开发的jsp
-
springmvc注解:
- @RequestMapping:用于处理请求url, 把web请求
映射
到相应的处理函数
- @RequestBody:实现接收http请求的json数据,将json转为java
- 原理:根据不同的
content-type
,spring-mvc会采用不同的消息转换器HttpMessageConverter
进行数据解析;使用jackson转换器进行反序列化
- 原理:根据不同的
- @ResponseBody:实现将conreoller方法返回对象转化为json对象响应给客户
- @RequestMapping:用于处理请求url, 把web请求
-
springmvc流程:核心是前端控制器DispatcherServlet
- 用户发送请求至
前端控制器DispatcherServlet
- DispatcherServlet收到请求后,调用
HandlerMapping处理器映射器
,请求获取Handler - 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet
- DispatcherServlet 调用 HandlerAdapter处理器适配器,调用具体处理器
- Handler执行完成返回ModelAndView;HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet
- DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析;ViewResolver解析后返回具体View
- DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
- 用户发送请求至
-
-
springboot有哪些优点?
-
可快速构建独立的 Spring 应用
- Spring Boot是一个依靠大量注解实现自动化配置的全新框架
- 在构建Spring应用时,我们只需要添加相应的场景依赖,Spring Boot就会根据添加的场景依赖自动进行配置
-
直接嵌入Tomcat、Jetty 和Undertow 服务器
-
通过依赖starter启动器简化构建配置
- 例如,在Web开发时,只需在构建项目时选择对应的Web场景依赖启动器spring-boot-starter-web
- Spring Boot项目便会自动导入spring-webmvc、spring-web、spring-boot-starter-tomcat等子依赖,并自动下载和获取Web开发需要的相关JAR包
-
自动化配置Spring和第三方库,简化了XML配置
- 在提供了各种场景依赖启动器的基础上,内部还默认提供了各种自动化配置类
- 使用Spring Boot开发项目时,一旦引入了某个场景的依赖启动器,Spring Boot内部提供的默认自动化配置类就会生效
-
提供生产就绪功能
- Spring Boot提供了一些用于生产环境运行时的特性,例如指标、监控检查和外部化配置
-
-
springboot监视器是什么?
- Spring boot actuator是spring启动框架中的重要功能之一
- 可帮助访问生产环境中正在运行的应用程序的当前状态
- 监视器模块公开了一组可直接作为HTTP URL访问的REST端点来检查状态
-
@SpringBootApplication注解是如何工作的?
- 它是三个注解的组合ComponentScan、EnableAutoConfiguration和Configuration
- @Configuration:添加该注解的类被视为springboot的配置类,会被初始化创建bean并放入IOC容器中管理
- @ComponentScan:指定扫描注解的包
- @EnableAutoConfiguration:给容器导入META-INF/spring.factories 里定义的自动配置类,
筛选有效的自动配置类
,每一个自动配置类结合对应的xxxProperties.java
读取配置文件进行自动配置功能
。
-
springboot的run启动逻辑,它是如何把项目启动起来的?
- 调用SpringApplication的run静态方法
- run静态方法会先调用SpringApplication构造方法new一个SpringApplication实例,然后调用实例的run方法
- 然后通过debug启动看构造方法中属性值
- 将当前类作为入参传入构造方法
- 设置应用类型webApplicationType为SERVLET,即web应用类型
- 设置初始化器:通过getSpringFactoriesInstances方法获取/META-INF/spring.factories中配置的ApplicationContextInitializer初始化器(实现类)
- 设置监听器:通过getSpringFactoriesInstances方法获取/META-INF/spring.factories中配置的ApplicationListener监听器(实现类)
- 最后一步就是推断应用程序的主类,就是我们的入口类
- 然后分析run实例方法
- StopWatch计时器:StopWatch实例的start方法用来记录任务执行时间
- configureHeadlessProperty设置无领导属性:为了能够在不支持显示器、键盘鼠标的环境中也能正常启动
- 获取SpringApplicationRunListener监听器:通过getSpringFactoriesInstances方法获取/META-INF/spring.factories中配置的SpringApplicationRunListener监听器(实现类):返回子类EventPublishingRunListener,用来在整个启动流程中接收不同执行点事件通知的监听者,SpringApplicationRunListener接口规定了SpringBoot的生命周期,在各个生命周期广播相应的事件
- 准备环境变量:准备ConfigurableEnvironment环境变量,包括配置属性、系统属性等
- 打印banner信息:在没有自定义banner图或者文本的情况下,默认调用SpringBootBanner的printBanner方法打印banner信息(将自定义文本banner.txt或图片banner.png放到resources目录下,可以在application.properties配置中指定spring.banner.location或者spring.banner.imgage.location位置)
- 创建application上下文:createApplicationContext方法创建ConfigurableApplicationContext实例,也是run方法最终返回的对象
- 准备上下文prepareContext方法:初始化ApplicationContextInitializer;执行Initializer的contextPrepared方法并发布事件;如果延迟加载,添加延迟加载上下文处理器;执行Initializer的contextLoaded方法并发布事件
- 更新上下文refreshContext方法:真正加载bean到容器中
- 更新上下文的后置处理:无逻辑
- StopWatch计时器:StopWatch实例的stop方法,打印初始化时间
- 发布上下文开始事件:调用SpringApplicationRunListener的started方法发布事件
- 调用Runner执行器:从ApplicationContext上下文中获取ApplicationRunner和CommandLineRunner执行器,调用其run方法执行
- 发布上下文执行事件:调用SpringApplicationRunListener的running方法发布事件
- 总结:Springboot的启动过程大量使用了SPI和事件发布
- SPI全称Service Provider Interface,一种服务发现机制
- 通过java.util.ServiceLoader类解析classPath和jar包的META-INF/services/目录 下的以接口全限定名命名的文件,加载该文件中指定的接口实现类,以此完成调用
-
如果自己写一个starter,怎么做?
- 创建一个starter项目,命名可以按照官方推荐的springboot-starter-xxx
- 创建一个@ConfigurationProperties用于获取application.yml中的配置信息
- 创建一个AutoConfiguration,引用定义好的配置信息;在AutoConfiguration中实现所有starter应该完成的操作,并且把这个类加入spring.factories配置文件中进行声明:
- org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.liyuan.hello.configuration.HelloAutoConfiguration
- install打包项目,之后在一个SpringBoot项目中引入该项目依赖,然后就可以使用该starter了
-
springboot读取配置几种方式
- @Value注解:适用于读取
单一配置
属性 - @ConfigurationProperties:提供了将多个配置选项注入
复杂对象
的能力;它要求我们指定配置的共同前缀
;一般配合@Component
注解交给IOC容器,以便在其他Bean中注入使用 - @PropertySource:可以加载指定路径的配置文件,默认只支持.properties格式,一般需要配合@Value或者@ConfigurationProperties注解
- 使用Environment读取配置文件:将该对象注入到Bean中,然后使用getProperty方法读取配置
- 使用原生的Properties方式读取:使用文件流方式读取配置文件智Properties对象中,然后通过getProperty方法读取配置
- @Value注解:适用于读取
-
如何将配置读取到static静态变量中
-
实现InitializingBean接口,在它的afterPropertiesSet方法中将使用@Value读取的变量赋值给static静态变量
-
或者使用@PostConstruct注解的方法中将使用@Value读取的变量赋值给static静态变量
-
总结:
- 先用
@Value
注解读取到配置文件中的值 - 然后在spring加载到某一步时,将上面读取的配置值赋值给static静态变量
- 所以,在使用静态变量时,不能太早,最好等spring加载完成后再使用
- 如果一定要在spring加载之前获取static变量,那可以在静态方法中直接读取配置文件信息,给static赋值
- 先用
-
-
eureka和zookeeper区别
- zookeeper保证的是CP,在Zookeeper集群中,当发生网络故障导致master节点和slave节点失联时,剩余的slave节点会进行leader选举,而在选举的过程中,zookeeper集群不可用,不能对外提供注册和查询的服务。
- eureka保证的是AP,在eureka集群中,某些节点挂掉,只要有一个eureka节点存在,就可以对外提供注册和查询服务,但是可能注册信息不是最新的。
-
eureka和nacos
-
共同点:都支持
服务注册
和服务拉取
;都支持服务提供者心跳
的方式做健康检测 -
不同点:
-
临时实例心跳不正常会被剔除,非临时实例则不会被剔除
-
spring.cloud.nacos.discovery.ephemera = false
# 设置为非临时实例 -
nacos支持服务列表变更的消息推送模式,服务列表更新及时
-
nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;eureka采用AP方式
-
nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式(一般情况下都使用临时实例,主动检测消费的服务器资源较大,服务器压力大)
-
-
-
ribbon的负载均衡算法有哪些?
- 轮训(RoundRobinRule)、随机(RandomRule)、根据响应时间分配权重(WeightedResponseTimeRule)、重试等
- 原理:@LoadBalanced注解和RestTemplate Bean实现,RestTemplate发送请求,LoadBalancerInterceptor拦截
- 自定义:实现AbstractLoadBalancerRule抽象类,重写choose方法
-
ribbon和feign区别
-
Ribbon原理:先去本地获取从Eureka缓存下来的服务列表,然后使用负载均衡算法选取一个服务,使用HttpClient进行调用
-
Feign的原理:
- 扫描所有加了@FeginClient 的接口,然后针对这个注解的接口生成动态代理
- 然后你针对feign的动态代理去调用他方法的时候,此时会在底层生成http协议格式的请求,使用HttpURLConnection进行调用
-
-
hystrix什么是?
- Hystix是分布式系统的一个延迟和容错的开源库
- 可以进行熔断、降级、限流、监控
- 可以避免分布式系统的
级联故障
和雪崩效应
- 服务熔断:熔断是直接调用降级方法,不调用目标方法,无需等待接口调用超时才返回结果
- 服务降级:降级是调用目标方法,由于目标方法调用超时或者异常,才调用降级方法
- 使用:服务降级是在消费端和feign一起使用,默认降级的配置不是开启的(feign.hystrix.enabled=false)
- 服务熔断是在服务端使用,对服务端的controller进行熔断,默认熔断的配置是开启的
-
spring声明式事务原理
- spring
容器在启动
时,会为@Transactional标注的类或方法创建一个代理类
,在方法被调用的时候,实际调用的是TransactionInterceptor类中的invoke
方法,该方法作用就是在目标方法之前开启事务,方法执行过程中如果遇到异常的时候回滚事务,方法调用完成之后提交事务
- spring
-
spring中的事务和传播机制
-
spring中的事务操作分为两种
编程式事务管理
:通过API接口
实现,使用TransactionTemplate或者TransactionManager手动管理事务- 优点:显式的管理事务,灵活性强,易于调试;可以在代码中根据具体业务需要来控制事务的具体范围和属性
- 缺点:代码复杂度高
声明式事务
:通过AOP技术
实现,在方法上添加@Transactional注解就可以实现了,进入方法时自动开启事务,方法执行完会自动提交事务【使用PlatformTransactionManager创建事务】- 优点:简化代码;可配置性强
- 缺点:限制较大,事务属性需要在方法或类级别进行声明,会导致某些情况下难以满足特定的业务需求;难以调试,由于事务是在AOP层面进行管理的,在调试时可能难以追踪事务管理的具体细节
-
spring的事务隔离级别使用后端数据库默认的隔离级别
-
spring事务的传播机制
-
定义:指的是Spring中多个事务在相互调用时, 他们之间的行为方式是如何执行的
-
七种传播机制:
- propagation.required:Spring默认的传播机制,能满足绝大部分业务需求,如果外层有事务,则当前事务加入到外层事务,一块提交,一块回滚。如果外层没有事务,新建一个事务执行——特点:一个类调用另外两个类的时候,调用类(一个类)当中有事务,被调用类(其他两个类)如果存在事务,就加入,如果被调用类中出现了错误,就全部回滚,如果没有出现错误,就添加成功。如果不存在事务,就创建一个事务
- propagation.requires_new:该事务传播机制是每次都会新开启一个事务,同时把外层事务挂起,当当前事务执行完毕,恢复上层事务的执行。如果外层没有事务,执行当前新开启的事务即可
- propagation.supports:如果外层有事务,则加入外层事务,如果外层没有事务,则直接使用非事务方式执行,完全依赖外层的事务
- propagation.not_supported:该传播机制不支持事务,如果外层存在事务则挂起,执行完当前代码,则恢复外层事务,无论是否异常都不会回滚当前的代码
- propagation.never:该传播机制不支持外层事务,即如果外层有事务就抛出异常
- propagation.mandatory(mandatory:强制性):与NEVER相反,如果外层没有事务,则抛出异常
- propagation.nested:该传播机制的特点是可以保存状态保存点,当前事务回滚到某一个点,从而避免所有的嵌套事务都回滚,即各自回滚各自的,如果子事务没有把异常吃掉,基本还是会引起全部回滚的
-
嵌套事务nested和加入事务required的区别
- 整个事务如果全部执行成功,二者结果是一样的
- 如果事务执行到一半失败了,加入事务:整个事务会全部回滚;而嵌套事务会局部回滚,不会影响到上一个方法中执行的结果
-
-
spring中事务不生效的可能原因
- 手动抛出Exception异常,而Exception是RuntimeException的父类:Spring事务默认支持RuntimeException异常,抛出的异常为RuntimeException异常及其子类异常事务均可生效
- 使用了try/catch:异常被try catch块捕获,导致事务失效
- 事务方法为private/final/static方法:Spring声明式事务基于动态代理实现,private/final/static方法不能被代理,事务不会生效
- 类未被spring管理:Spring实现对象的动态代理,首先这个对象要交由Spring管理
- 一个方法调用本类中另一个方法,事务失效:@Transactional基于AOP实现,而AOP又是基于动态代理实现,直接调用本类方法或使用this调用本类方法,均不是Spring的代理对象,无法实现动态代理,事务也就不会生效
- 数据表不支持事务:Spring事务基于数据库事务实现,有些数据表本身不支持事务,如MySql的MyISAM引擎,事务自然不生效
-
多线程中如何实现spring事务
- spring的声明式事务在使用多线程时,并不会生效
- 可以使用spring提供的编程式事务解决;核心思路是为每一个线程都开辟一个事务
-
-
gateway的过滤器和zuul的过滤器底层有啥不一样
- gateway
- zuul
-
hystrix两种
隔离模式
- 信号量
- 线程池(默认)
-
Spring Cloud中如何保证各个微服务之间调用的安全性?
- spring security安全认证机制
- 请求体Body中加入参数校验 => SDK集成场景较多(微信一些接口,会有签名)
- API网关转换外部请求时打上标签tag
- 请求头Header中加入认证信息 动态token(一定程度上能够防止刷接口)
- 公共的认证服务使用JWT安全标准,来制定我们的认证机制
- 在网关出通过滤器来统一添加token至请求头中,该token可以通过定时任务调用认证服务生成,有效期15min,每隔15分钟重新生成Token
- 各个下游业务微服务通过拦截器,调用认证服务,校验token有效性
-
Spring Cloud中
Hystrix 线程隔离导致ThreadLocal数据丢失
-
需求:传统微服务中,内部通讯默认为安全,不需要鉴权,但我们项目要求内部接口不能被外部访问,需要做鉴权;所有请求通过
gate-way
网关进入,转换为内部请求时,在网关全局过滤器GlobalFilter
中动态生成校验microToken,添加到request-header中;下游微服务通过HandlerInterceptor
进行拦截,并获取microToken,校验通过之后,放到ThreadLocal
中;内部微服务调用时,采用feignclient拦截器RequestInterceptor
获取到ThreadLocal中的microToken,再次添加到request-header
中,调用其他微服务。 -
问题复现:发现在
RequestInterceptor
拦截器中获取不到之前放入ThreadLocal中的microToken数据 -
原因排查:通过日志发现两边的线程发生了变化,并不是同一个线程了,致使取不到值,致使线程发生变化的原因就是显示开启了feign.hystrix.enabled,Hystrix 线程隔离导致ThreadLocal数据丢失
-
处理方法:如何让ThreadLocal变量信息在HystrixCommand执行时能在Hystrix线程中正确的传递?
-
方法一:就是将hystrix的隔离模式改为信号量模式,就不存在线程池复用导致线程发生变化的问题,但是信号量只能实现限流,没法实现超时熔断
-
实现:
hystrix.command.default.execution.isolation.strategy = SEMAPHORE
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests = 100
-
-
方法二(无效):InheritableThreadLocal:可以将当前线程中的线程变量信息共享到当前线程所创建的「子线程」中
- 但是Hystrix中的线程模式底层使用的是自己维护的一个线程池,会出现复用的情况,那么就会出现每个线程所共享的信息都是之前首次获取到的「父线程」的共享信息,如果线程池中线程长期存活,那么我们就没法获取到定时更新的microToken
-
方法三:使用hystrix插件,HystrixConcurrencyStrategy(hystrix并发策略,包含真正创建线程池的逻辑):
-
实现:
自定义hystrix策略继承HystrixConcurrencyStrategy,重写wrapCallable方法,手动在新线程中设置ThreadLocal
重置Hystrix插件,并将自定义并发策略注册到插件上
-
-
-
-
JWT
-
全称JSON WebToken,一个轻量级身份认证规范,方便用户和服务器之间传递安全可靠的信息
-
原理:解决传统的跨域认证问题,传统方式是共享session,JWT服务端无状态,不保存用户信息,而是生成token下发给客户端进行保存,为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名
-
JWT组成:头部header、透传数据payload、签名signature,三个部分之间.连接
- header:描述JWT最基本信息,包括typ(声明类型,JWT)和alg(声明签名使用的算法,HMAC-SHA256)两部分信息
- payload:存放有效信息,包括标准中标注的(issuer/issuerAt/expiration)、公共声明和私有声明(自定义claim)
- signature:由Base64后的header、Base64 后的payload以及秘钥
-
注意
- 一般JWT生成签名采用HS256的AES对称加密算法,秘钥secret是保存在服务端的,用于签名的加密和解密
- JWT本身默认不加密的,只是采用base64算法,拿到JWT字符串后可以转换回原本的JSON数据,上述的加密仅仅是指签名,可以对生成的Token在进行一次加密
- 生成的token保存在客户端,每次请求服务端会携带token,在服务端进行校验
- 由于服务端不保存session状态,因此无法在使用过程中废止某个 token,一旦token签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑
-
spring和springboot和springcloud专题
于 2023-08-28 10:37:17 首次发布