IOC系列
1、什么是Spring
一个全面的, 可编程可配置的模型,针对基于Java的企业应用。易于开发,大量模块
2、spring framework有哪些模块?
spring-core:基础API模块,如资源管理,泛型处理。
spring-beans:Spring Bean 相关,依赖查找,依赖注入
Spring-aop:动态代理,aop字节码提升。
spring-context:事件驱动,注解驱动,模块驱动
spring-expression: spring表达式语音模块
3、IOC和DI区别
IOC的实现有四种方式,
1、一种是服务定位也就是依赖查找,调用容器的bean查找接口查找bean实例,缺点,有侵入性,性能低。
2、一种是依赖注入,也就是DI,注入有4种方式,构造器注入,熟悉注入,Setter注入,接口注入。性能高,侵入小。
3、上下文依赖查询
4、模板方法涉及模式,JdbcTemplate的使用,不需要关心具体callback来源
4、IOC的职责
依赖处理:依赖查找(主动),依赖注入(大部分被容器处理)
生命周期管理:容器(启动,暂停,终止),托管资源(不止是baen,事件监听器等)
配置:容器(控制容器的行为,比如定时任务),外部化配置(属性配置,xml配置),托管的资源(bean配置,线程池配置等)
5、Spring 中有多少种 IoC 容器?
1、BeanFactory是底层的一个容器(懒加载),更轻量级.
常用的BeanFactory是XmlBeanFactory,根据xml定义的内容创建bean。
2、ApplicationContext是在这个基础上增加了一些特性(是预加载的)。
常用的ApplicationContext:
ClassPathXmlApplicationContext ---从classPath的xml配置中读取上下文,并生成上下文定义。
FileSystemXmlApplicationContext---文件系统的xml
XmlWebApplicationContext---web应用中定义的xml
6、IOC轻量级容器
方便使用,配置少,代码耦合度小。
7、依赖注入与依赖查找
依赖处理:查找是主动获取数据,相当于拉的方式。注入则是由容器环境环境被动的推送到我这里来。
spring中主要还是依赖注入,通过setter和构造方法两种方式注入;
依赖查找的应用是实现ApplicationContextAware获取ApplicationContext来查找自己想要的bean对象也就是JNDI
比如说通过beanFactory或者context的getBean()方法是依赖查找,通过@Autowired @Resource是依赖注入。
8、简述 Spring IoC 的实现机制?
其实就是工厂模式加反射机制。
分为低级容器和高级容器,其实就是BeanFactory,首先加载配置文件,解析成BeanDefinition放在map里。
调用getBean方法,从BeanDefinition所属的map里,拿出Class对象进行实例化,同时如果存在依赖关系,将递归调用getBean方法。
高级容器的话就是ApplicationContext,包含了低级容器的功能,当执行refersh的方法,刷新整个容器的bean,还有其他功能,访问文件资源,事件发布通知等。
9、BeanDefintion是什么?
顾名思义,就是bean的定义,根据xml或者注解的配置信息生成相应的bean详情对象。BeanDefinition和xml有个对应关系。
10、FactoryBean与BeanFactory的区别
FactoryBean 是一种特殊的 Bean,需要注册到 IoC 容器,通过容器 getBean 获取 FactoryBean#getObject() 方法的内容,而 BeanFactory#getBean 则是依赖查找,如果 Bean 没有初始化,那么将从底层查找或构建。 BeanFactory是IOC的底层容器,FactoryBean是创建Bean的一种方式,帮助实现复杂的初始化逻辑。
11、IOC依赖来源
1、自定义bean:业务方面的bean 2、容器内创建bean:初始化的bean 3、容器内建依赖:BeanFactory这些
12、spring有哪些不同类型的事件?
1、上下文更新---容器被初始化或者更新时发布,也可以在调用ConfigurableApplicationContext的refresh方法发布。
2、上下文开始---调用ConfigurableApplicationContext的start方法
3、上下文停止---调用start方法
4、上下文关闭--容器被关闭时触发,所有的bean都被销毁。
5、请求处理--http请求结束触发
13、ApplicationContext除了IOC容器还有什么作用
1、AOP
2、配置元信息
3、资源管理
4、事件、国际化、注解。
5、Environment抽象
14、Spring Bean 在容器的生命周期是什么样的?
1、实例化对象--根据配置中的BeanDefinition实例化bean对象。
2、设置bean相关的属性。
3、检查Aware相关接口并设置相关依赖
比如BeanNameAware这个接口,可以让bean感知到自身在spring容器中的id。
其他的Aware接口也是类似功能,可以让bean感知到自身的一些属性。
4、BeanPostProcessor前置处理。
在bean初始化前加一些自己想要做的逻辑。postProcessBeforeInitialization这个方法
5、BeanPostProcessor后置处理。
在bean初始化后加一些自己想要做的逻辑。postProcessAfterInitialization这个方法
6、bean 对象处于就绪状态,可以使用了。
7、销毁bean
1、是否实现了DisposableBean接口,spring容器关闭会调用destory方法。
2、是否配置自定义的destory方法销毁。
15、spring内部bean?
xml配置元数据里面标签配置,其实就相当于一个类内部有一个属性,这个属性其实也是一个类。
16、什么是spring装配?
装配其实和依赖注入是一个东西,Bean和spring容器组合在一起,容器需要知道需要什么bean以及如何使用依赖注入将bean绑定在一起,同时装配bean。分为自动装配和非自动装配,默认是非自动装配。
17、什么是延迟加载?
通过Bean 中设置 lzay-init = "true" 。
这样就不会在容器启动的时候就创建,用的时候才会创建。
18、spring中单例bean是线程安全的吗?
spring没有对单例bean做任何多线程的封装处理,但是大部分单例bean没有可变的状态,所以某种程度上是线程安全的,如果有可变状态的话,需要自行保证,最简单就是由单例变成多例。
19、Spring怎么解决循环依赖的问题?
A依赖B B依赖A(简单理解就是A中有个属性B,B中有个属性A)
1、A创建的时候先初始化,初始化的时候通过ObjectFactory提前曝光,并且一般都是将这个曝光的半成品放到三级缓存里面,然后发现自己有个B的属性,就尝试get(B),发现B还没有被创建。
2、这个时候B就会去进行创建,创建的过程发现有个属性A,就会去尝试get(A),这个时候A已经在缓存里面了,通过ObjectFactory.getObject方法拿到这个A的半成品,进行创建。
3、B创建成功后放到一级缓存,然后A再从这里拿到B完成创建。
20、如何在 Spring 中启动注解装配?
在spring配置文件中通过配置<context:annotation-config>,springboot 默认开启。
21、常用的注解
@Component ---标记为一个bean ,@Service,@Repository都作用差不多,加了个标记的特点。
@Required---应用于Bean属性的setter方法。
@Autowired/@Resource---自动注入一个bean
@Qualifier---多个相同的bean,将属性注入到这个注解下的
AOP系列
1、Aspect(切面)
简单可以认为使用@Aspect注解的类就是切面和class同级,由pointcount和advice组成,主要有两个任务: 1、如何通过pointcut和advice定位到特定的joinpoint上。 2、如何在advice上编写切面代码。
2、advice
其实就是一些增强的方法,分为前置,后置,以及环绕还有异常停止,当切点匹配到了连接点,请求会根据配置的advice对该请求执行的方法进行增强。
3、连接点join point
程序运行的一些时间点,例如一个方法的执行。
4、切点point cut
要匹配到对应的连接点
切点标志符:execution 匹配方法签名
// 匹配指定包中的所有的方法
execution(* com.xys.service.*(..))
// 匹配当前包中的指定类的所有方法
execution(* UserService.*(..))
// 匹配指定包中的所有 public 方法
execution(public * com.xys.service.*(..))
// 匹配指定包中的所有 public 方法, 并且返回值是 int 类型的方法
execution(public int com.xys.service.*(..))
// 匹配指定包中的所有 public 方法, 并且第一个参数是 String, 返回值是 int 类型的方法
execution(public int com.xys.service.*(String name, ..))
匹配类型签名
// 匹配指定包中的所有的方法, 但不包括子包
within(com.xys.service.*)
// 匹配指定包中的所有的方法, 包括子包
within(com.xys.service..*)
// 匹配当前包中的指定类中的方法
within(UserService)
// 匹配一个接口的所有实现类中的实现的方法
within(UserDao+)
匹配 Bean 名字
// 匹配以指定名字结尾的 Bean 中的所有方法
bean(*Service)
// 匹配以 Service 或 ServiceImpl 结尾的 bean
bean(*Service || *ServiceImpl)
// 匹配名字以 Service 结尾, 并且在包 com.xys.service 中的 bean
bean(*Service) && within(com.xys.service.*)
@annotation
匹配由指定注解所标注的方法
@Pointcut("@annotation(com.xys.demo1.AuthChecker)")
public void pointcut() {}
5、AOP 有哪些实现方式?
1、静态代理:指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强。
• 编译时编织(特殊编译器实现)
• 类加载时编织(特殊的类加载器实现)
2、动态代理(spring aop实现)
• jdk动态代理,目标代理对象必须实现至少一个接口,所有该目标类型实现的接口都将被代理
JDK 动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是 InvocationHandler 接口和 Proxy 类。
• cglib代理,没有实现任何接口,无法通知advice的final方法,不能被覆盖(不推荐)
6、Spring AOP and AspectJ AOP 有什么区别?
1、代理方式不同,一个是静态一个是动态
2、切点point cut支持的力度不同
springAop仅支持方法级别的,aspect支持了完全的aop,还支持属性级别。
7、Spring 如何使用 AOP 切面?
• 基于 XML 方式的切面实现。
• 基于 注解 方式的切面实现
Spring 事务
1、事务的特性(ACID)指的是?
原子性:要么全发成,要么全不完成。
一致性:事务开始和结束之前,数据库的完整性没有被破坏
隔离性:防止多个事务交叉执行而导致的数据不一致,分为读未提交,读提交,可重复读,串行化
持久性:事务结束后对数据的修改是永久的
2、spring支持的事务管理类型
声明式事务:通过注解或者项xml配置事务,使事务管理与业务代码分离
编程式事务:通过编码实现,需要在代码显式的调用事务的获得,提交,回滚。
3、事务的回滚原则
1、默认情况只有遇到运行期异常才会回滚,遇到检查时异常是不会回滚的。
2、也可以声明事务在遇到特定的检查时异常也回滚,也可以声明遇到特定的异常不回滚。
4、事务不生效的几种情况
1、子类有事务,不能传播到父类;父类的事务可以传播到子类。
2、注解在接口上必须要动态代理,一般不建议标注在接口上。
3、只能应用在public上,否则不会生效,主要是外部调用,自家调用可以通过aop增强实现。
4、自我调用也会失效,没有走aop增强。
5、事务的隔离级别
1、读未提交,最低级别,导致脏读,幻读不可重复读。
2、读已提交,阻止脏读,幻读不可重复读仍有可能会发生
3、重复读,多次读取的结果是一致的,除非数据是被事务本身修改的,幻读仍有可能。
4、序列化,级别最高,性能低,可避免上上三种情况。
6、事务的传播(当前带有事务配置的方法,需要怎么处理事务)级别
三类7种
一、支持当前事务的情况。
1、如果当前存在事务,则继续执行;如果不存在事务,则新建一个事务。
2、如果当前存在事务,则继续执行;如果不存在事务,则以非事务的方式运行。
3、如果当前存在事务,则继续执行;如果不存在事务,则抛出异常。
二、不支持当前的事务情况。
4、创建一个新的事务,如果当前存在事务,则把当前事务挂起。
5、以非事务方式运行, 如果当前存在事务,则把当前事务挂起。
6、以非事务方式运行, 如果当前存在事务,则抛出异常
三、其他
7、如果当前存在事务,则创建一个事务作为当前事务的嵌套事务;如果没有事务,则新建一个事务。
7、事务的超时属性
就是一个事务允许执行的最长时间,如果过了这个时间还没执行完就自动回滚。
8、事务的只读属性。
对事务性资源标为只读可以提高事务处理的性能。
9、SpringBoot中使用@Transactional注解遇到的问题
1、不建议在接口上添加@Transactional注解,一般在service类标签上添加@Transactional即可
2、@Transactional注解只能应用到public可见度的方法上。如果应用到protected、private或者package可见度的方法上时,不会报错,但事务也不会起作用.
3、默认情况下,spring会对uncheck异常进行事务回滚的;如果是checked异常则不会回滚,可添加注解 @Transactional(rollbackFor=Exception.class) 是的checked异常回滚。
uncheck异常:java里面将派生于Error或者RuntimeException(比如空指针,1/0)的异常
checked异常:其他继承自java.lang.Exception得异常统称为Checked Exception,如IOException、TimeoutException等
4、同一类中methodA()方法没有@Transactional 注解,在其内部调用有@Transactional 注解的方法,有@Transactional 注解的方法methodB()的事务被忽略,不会开启新的事务,也不会发生回滚。例如:
@Service
public class TransactionService {
public void methodA(){
this.methodB();
}
@Transactional
public void methodB(){
}
}
原因: Spring采用动态代理(AOP)实现对bean的管理和切片,它为我们的每个class生成一个代理对象。只有在代理对象之间进行调用时,可以触发切面逻辑。在 Spring 的 AOP 代理下,只有目标方法由外部调用,目标方法才由 Spring 生成的代理对象来管理。
详解: Spring的事务管理是通过AOP实现的,其AOP的实现对于非final类是通过cglib这种方式,即生成当前类的一个子类作为代理类,然后在调用其下的方法时,会判断这个方法有没有@Transactional注解,如果有的话,则会开启一个新的事务,并通过动态代理实现事务管理(拦截方法调用,执行事务等切面)。当methodA()中调用methodB()时,并不是使用的代理对象,而是普通的javabean,从而导致this.methodB()时也不是代码对象,从而导致@Transactional失败,即发现methodA()上并没有@Transactional注解,所以整个AOP代理过程(事务管理)不会发生。
解决方法:
1、把这两个方法分开到不同的类中;
2、把注解@Transactional加到类名上面去;
3、把注解@Transactional加到methodA()方法上,methodB()不添加注解,在调用methodB()时两个方法的事务都会生效,因为methodA()默认的事务传播属性为PROPAGATION_REQUIRED,此时methodB()会加入到methodA()中
4、获取本对象的代理对象,再进行调用。具体操作如:
• Spring-content.xml上下文中,增加配置:<aop:aspectj-autoproxy expose-proxy="true"/>
• 在TransactionService 中,用(transactionService )(AopContext.currentProxy()),获取到TransactionService 的代理类,再调用事务方法,强行经过代理类,激活事务切面。