简述JWT?
答:
概述:JWT是基于Token验证方式的一种基本实现
Header :描述 JWT 的元数据。定义了生成签名的算法以及 Token 的类型。
Payload(负载):用来存放实际需要传递的数据
Signature(签名):服务器通过Payload、Header和一个密钥(secret)使用 Header 里面指定的签名算法(默认是 HMAC SHA256)生成。
在基于 Token 进行身份验证的的应用程序中,服务器通过Payload、Header和一个密钥(secret)创建令牌(Token)并将 Token 发送给客户端,客户端将 Token 保存在 Cookie 或者 localStorage 里面,以后客户端发出的所有请求都会携带这个令牌。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP Header 的 Authorization字段中:Authorization: Bearer Token。
spring如何实现依赖注入?
答:spring实现依赖注入有两种方式:构造函数注入和setter注入
依赖注入可以理解Spring如何帮我们把我们在配置文件写的依赖关系实现。IOC可以认为是在中间加了一个代理,我们叫他做什么,他就做什么,原来new的方式可以理解为,我自己去做,而IOC是我叫你去做。
Spring如何解决循环依赖?
答:循环依赖如下图,就是A依赖B的同时B也依赖了A,或者C依赖了自己,在实例化的时候可能会导致,开始实例化A的时候发现A里面有B,然后又去实例化B,然后B里面又有A,无限循环
@Component
public class A {
// A中注入了B
@Autowired
private B b;
}
---
@Component
public class B {
// B中注入了A
@Autowired
private A a;
}
---
// 自己依赖自己
@Component
public class C {
// C中注入了C
@Autowired
private C c;
}
-
解决方法:
三级缓存:1、singletonObjects:用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用 2、earlySingletonObjects: 存放原始的 bean 对象(尚未填充属性),用于解决循环依赖 3、singletonFactories:存放 bean 工厂对象,用于解决循环依赖
-
Spring 解决循环依赖的过程:
- 首先 A 完成初始化第一步并将自己提前曝光出来(通过 ObjectFactory 将自己提前曝光),在初始化的时候,发现自己依赖对象 B,此时就会去尝试 get(B),这个时候发现 B 还没有被创建出来
- 然后 B 就走创建流程,在 B 初始化的时候,同样发现自己依赖A,于是尝试 get(A),这个时候由于 A 已经添加至缓存中(一般都是添加至三级缓存
singletonFactories
),通过 ObjectFactory 提前曝光,所以可以通过ObjectFactory.getObject()
方法来拿到 A 对象,B拿到 A 对象后顺利完成初始化,然后将自己添加到一级缓存中 - 回到 A,A 也可以拿到 B 对象,完成初始化,到这里整个链路就已经完成了初始化过程了。
其实两重依赖就能解决循环依赖的问题了,比如A-B循环,A先初始化,然后发现A里面有B,那么就去初始化B,并把半成品的A放进二级缓存里,然后B里面发现有A,去缓存里找,找到,B成功初始化,放进一级缓存,然后A创建也就完成了。那么为什么还要三重缓存呢,这里主要是为那些有AOP的Bean对象考虑,我们假设A是有经过代理,我们可以在二重循环中发现在B从缓存获取A的时候,获取的是半成品的A,而这个A还没有经过代理,所以跟我们想象的是完全不同的,所以Spring定义了第三级缓存,在第三级缓存中判断返回半成品引用的时候,是否需要进行代理,这里会返回有代理和没有代理的引用。
Spring中bean的作用域?
答:
在spring配置文件定义Bean时,通过声明scope配置项,可以定义Bean的作用域,一共有五种:
1、singleton:IOC容器仅创建一个Bean实例,IOC容器每次返回的是同一个Bean实例。(默认的作用域)
2、prototype:IOC容器可以创建多个Bean实例,每次返回的都是一个新的实例。
3、request:该属性仅对HTTP请求产生作用,使用该属性定义Bean时,每次HTTP请求都会创建一个新的Bean,适用于WebApplicationContext环境。
4、 session:该属性仅用于HTTP Session,同一个Session共享一个Bean实例。不同Session使用不同的实例。
5、global-session:该属性仅用于HTTP Session,同session作用域不同的是,所有的Session共享一个Bean实例。
Spring中bean的生命周期?
答:
1. 实例化Bean
- 对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。
- 对于ApplicationContext容器,当容器启动结束后,便实例化所有的bean。
- 容器通过获取BeanDefinition对象中的信息进行实例化。并且这一步仅仅是简单的实例化,并未进行依赖注入(还没有为成员变量赋值)。
- 实例化对象被包装在BeanWrapper对象中,BeanWrapper提供了设置对象属性的接口,从而避免了使用反射机制设置属性。
2. 设置对象属性(依赖注入)
- 实例化后的对象被封装在BeanWrapper对象中,并且此时对象仍然是一个原生的状态,并没有进行依赖注入。
- 紧接着,Spring根据BeanDefinition中的信息进行依赖注入。
- 并且通过BeanWrapper提供的设置属性的接口完成依赖注入。
3. 注入Aware接口(让Bean对象知道Spring的存在,比如BeanNameAware,就是让Bean知道自己在Spring里的ID)
- 紧接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给bean。
4. BeanPostProcessor
当经过上述几个步骤后,bean对象已经被正确构造,但如果你想要对象被使用前再进行一些自定义的处理,就可以通过BeanPostProcessor接口实现。
该接口提供了两个函数:
- postProcessBeforeInitialzation( Object bean, String beanName ) 当前正在初始化的bean对象会被传递进来,我们就可以对这个bean作任何处理。 这个函数会先于InitialzationBean执行,因此称为前置处理。 所有Aware接口的注入就是在这一步完成的。
- postProcessAfterInitialzation( Object bean, String beanName ) 当前正在初始化的bean对象会被传递进来,我们就可以对这个bean作任何处理。 这个函数会在InitialzationBean完成后执行,因此称为后置处理。
5. InitializingBean与init-method
当BeanPostProcessor的前置处理完成后就会进入本阶段。 InitializingBean接口只有一个函数:
afterPropertiesSet()
这一阶段也可以在bean正式构造完成前增加我们自定义的逻辑,但它与前置处理不同,由于该函数并不会把当前bean对象传进来,因此在这一步没办法处理对象本身,只能增加一些额外的逻辑。 若要使用它,我们需要让bean实现该接口,并把要增加的逻辑写在该函数中。然后Spring会在前置处理完成后检测当前bean是否实现了该接口,并执行afterPropertiesSet函数。当然,Spring为了降低对客户代码的侵入性,给bean的配置提供了init-method属性,该属性指定了在这一阶段需要执行的函数名。Spring便会在初始化阶段执行我们设置的函数。init-method本质上仍然使用了InitializingBean接口。
6. DisposableBean和destroy-method
和init-method一样,通过给destroy-method指定函数,就可以在bean销毁前执行指定的逻辑。
Spring 事务中哪几种事务传播行为?
答:
事务传播行为是为了解决业务层方法之间互相调用的事务问题。
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
正确的事务传播行为可能的值如下:
1.TransactionDefinition.PROPAGATION_REQUIRED
使用的最多的一个事务传播行为,我们平时经常使用的@Transactional注解默认使用就是这个事务传播行为。如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
2.TransactionDefinition.PROPAGATION_REQUIRES_NEW
创建一个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部方法是否开启事务,Propagation.REQUIRES_NEW修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干扰。
3.TransactionDefinition.PROPAGATION_NESTED
如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
4.TransactionDefinition.PROPAGATION_MANDATORY
如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)
这个使用的很少。
Mybatis的映射配置文件中,动态传递参数有两种方式:#{}和${},它们有什么区别?
答:
1、#{}为参数占位符?即sql预编译,${}为字符串替换,即sql拼接
2、变量替换后,#{} 对应的变量自动加上单引号,${} 对应的变量不会加上单引号
3、#{}能防止sql注入,${}不能防止sql注入
简述Shiro?
答:首先我们的权限认证基本上基于下图。
先验证你是否有这个用户,然后再获取该用户相关的权限(这里有两种划分形式,将某一个特定的权限分配给用户,还是将特定的权限集合在一起,成为一个角色,再分配给用户)
而shiro以三个主要的对象来实现这些功能,分别是subject、SecurityManager、Realm
- subject:这个主要是跟我们的外层逻辑交互的对象,也就是说我们可以根据这个对象的一些方法来判断是否有权限,是否是某个角色,来进行资源管理
- SecurityManager:他负责所有的subject的管理,比如认证和授权、还有缓存等相关管理
- Realm:这个也就是我们大部分代码的集中区域了,这里主要有两个方法,认证方法和授权方法,subject的角色与权限都是再这里进行逻辑产生的。
Springboot自动装配原理?
答:
- 什么是自动装配:通过注解或者一些简单的配置就能在 Spring Boot 的帮助下实现某块功能。
-
原理:在我们每一个SpringBoot应用中,有一个注解@SpringBootApplication,大概可以把 @SpringBootApplication看作是 @Configuration、@EnableAutoConfiguration、@ComponentScan 注解的集合。这三个注解的作用分别是:
1、@EnableAutoConfiguration:启用 SpringBoot 的自动配置机制。
2、@Configuration:允许在上下文中注册额外的 bean 或导入其他配置类。
3、@ComponentScan: 扫描被@Component (@Service,@Controller)注解的 bean,注解默认会扫描启动类所在的包下所有的类 ,可以自定义不扫描某些 bean。如下图所示,容器中将排除TypeExcludeFilter和AutoConfigurationExcludeFilter。
而这里的enableAutoConfiguration是实现自动装配的关键,这里注解会导入一个类AutoConfigurationImportSelector,他实现了一个接口ImportSelector,接口定义了一个方法selectImports,该方法主要用于获取所有符合条件的类的全限定类名,这些类需要被加载到 IoC 容器中。而在具体的这个方法的实现中,有个叫getAutoConfigurationEntry()的方法被调用。这个方法第一步会判断自动装配是否开启,第二步获取Exclude和Excludename。第三步获取需要自动装配的所有配置类,读取META-INF/spring.factories。这个文件里有我们需要自动装配的所有类的全类名。接着就利用反射将对应的类注入Spring容器即可。而且也并不是每次自动装配都是完完全全装配所有的类的。有一个注解@ConditionalOnXXX,他可以做一些逻辑判断,在某些时候一些是没有必要装配的。另外引入一个starter,他在meta-inf/spring.factories的配置就会被读取,然后自动装配相应的类 - 如何实现一个 Starter:
- 首先我们要引入Spring的相关依赖,比如Spring给我们定义Starter的基本版本,我们在Maven中加dependence即可。
- 将相应的类加上@Bean和@ConditionalOnClass(如果不设置条件也可以不添加)
- 最后在mete-inf/的Spring.factory中写上自动装配类的全类名即可
- 这样一个Starter就完成了
总结:也就是说我们Spring的启动注解中,包含了一个enableAutoConfiguration的这个注解,这个注解会导入一个实现自动装配的类,他有一个方法,会扫描所有Starter的Spring.factory文件里的相关类,而且并不是每次都加载全部,在ConditionOnclass的注解下可能只会装配部分。
Spring IOC的理解?
答:中间人,门面模式,从你自己做,到你叫别人帮你做,其实本质上我认为就是,将那些通用或者难实现的代码,交给哪些最厉害的程序员去写,而我们只注重我们的个性化需求,但是你如果没有通过容器,直接new这样,无从下手,所以采用门面模式
将类交给spring容器的3种办法
答:
- 注解方式:
1@Bean
2)@Component
3)@Import方式 - XML方式:
4)xml注册Bean,如果spring配置元数据的方式是xml时,可以手动在xml注册第三方jar包中的类。
spring源码aop怎么实现的,aop用在哪里?
答:原理就是当我们从Spring容器中获取bean的时候,他根据动态代理返回一个代理对象,具体代理在调用我们真正的方法的时候用了什么,是基于我们自己的注解配置。动态代理参考其他
拦截器和过滤器的区别?
@AutoWired、@Qualified、@Resource三个注解的区别 ?
答:
一、@Autowired,@Qualifier
// @Autowired只按照类型注入,如果想按照名称注入,配合@Qualifier使用
@Autowired
@Qualifier("studentDao")
二、@Resource
// 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
@Resource(name = "studentDao",type = StudentDao.class)
// 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
@Resource(name = "studentDao")
// 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
@Resource(type = StudentDao.class)
// 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;
@Resource
本质上两者都是标志bean自动注入,只是标准不同,Resource是JDK中的标准,假如有其他DI框架也同样适用
事务传播机制底层原理?
事务传播机制基本概念
事务传播机制具体例子
事务底层实现原理
什么是事务的传播?
Spring 除了封装了事务控制之外,还抽象出了 事务的传播 这个概念,事务的传播并不是关系型数据库所定义的,而是Spring在封装事务时做的增强扩展,可以通过@Transactional 指定事务的传播,具体类型如下为什么会有传播机制?
spring 对事务的控制,是使用 aop 切面实现的,我们不用关心事务的开始,提交 ,回滚,只需要在方法上加 @Transactional 注解,这时候就有问题了。场景一: serviceA 方法调用了 serviceB 方法,但两个方法都有事务,这个时候如果 serviceB 方法异常,是让 serviceB 方法提交,还是两个一起回滚。
场景二:serviceA 方法调用了 serviceB 方法,但是只有 serviceA 方法加了事务,是否把 serviceB 也加入 serviceA 的事务,如果 serviceB 异常,是否回滚 serviceA 。
场景三:serviceA 方法调用了 serviceB 方法,两者都有事务,serviceB 已经正常执行完,但 serviceA 异常,是否需要回滚 serviceB 的数据。
传播机制生效条件
因为 spring 是使用 aop 来代理事务控制 ,是针对于接口或类的,所以在同一个 service 类中两个方法的调用,传播机制是不生效的
1、PROPAGATION_REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。Spring的默认事务传播类型
2、PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
3、PROPAGATION_MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。
4、PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起(暂停)。
5、PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
6、PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
7、PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。