Spring框架
IOC/DI
什么是SPRING IOC?
- IOC是INVERSION OF CONTROL 控制反转,将你设计好的对象交给容器去新建和管理,而不是在你对象内部进行控制。
- SPRING 实现IOC的方式是DI(DEPEDENCY INJECT)依赖注入,依赖注入的方式有接口注入,SETTER注入,构造器注入。
IOC有什么好处?
- 对象之间的依赖关系由容器控制,从而降低了耦合(耦合就是指对象之间的依赖关系程度)和系统复杂度,好的设计系统是高内聚低耦合的
- 创建对象只需要依赖注入,更好地测试性
- 更适合模块化编程,提高扩展性
IOC实例化BEAN的三种方式
- XML文件中配置BEAN
- JAVA类中配置BEAN,@CONFIGURATION +@BEAN
- 使用注解 @COMPONENT @ REPOSITY @CONTROLLER @SERVICE @MAP等注解
DI依赖注入BEAN的注解有哪些?
-
@Autowied
- 是SPRING自带注解,默认根据BEAN的TYPE来注入,也可以和@Qualifier变成根据name注入
-
@Resource
- 默认根据name来注入
-
@Inject
- 也是默认根据TYPE来注入,实现name注入需要@Named注解
为什么推荐构造器注入?
-
首先DI是实现IOC的方式,主要有三种:接口注入、setter注入、构造器注入
-
构造器注入好处
- 依赖不可变,SETTER注入依赖就可变,可以运行时用set方法
- 依赖不为空,参数不正确就会报错
- 完全的初始化状态,循环依赖就会直接报错【接口注入只有在你使用它的时候才会报错】,@Lazy懒加载解决循环依赖的问题
IOC初始化流程或者说创建一个bean的过程
-
综述:
- 扫描和解析配置文件和注解信息(XML配置文件、自定义JAVA BEAN 、注解),将其转为内部的对象定义和依赖关系
- 根据对象定义和依赖关系,利用反射去创建 设置属性,aware,beanPostProcessor
-
具体步骤:
-
读取XML配置文件、Java的配置类、类似于@COMPONENT注解 转化为BEAN DEFINITION(内部的对象定义和依赖关系)
-
利用反射去创建对象
-
结合BEAN DEFINITION 设置初始化对象的属性
-
调用AWARE接口,获取SPRING的资源【AWARE接口可以感知容器的一些特殊能力,比如ApplicationContextAware能获取ApplicationContext】
- BeanNameAware:调用setBeanName方法 根据bean的id和name进行传递
-
调用BeanPostProcessor的前置处理(PostProcessorBeforeInitialization)【BeanPostProcessor是对bean的一些额外处理,比如打印日志或者bean的三级缓存】
-
进行初始化
-
调用BeanPostProcessor的后置处理
-
初始化完成
-
Bean
BEAN的生命周期
-
SingleTon【单例对象】
- 与容器生命周期相同
-
ProtoType【多例对象】
- 由容器创建
- 由Java虚拟机进行对象回收
Bean Factory和ApplicationContext的关系
- Bean Factory是Spring框架中最基本的容器,提供最基础的IOC和DI,ApplicationContext基于BeanFactory并扩展,从而提供了更多的特性,比如资源加载访问。
BEAN的作用域
- 单例对象 SingleTon ,默认作用域,每个bean只对应一个实例
- 多例对象 Prototype,每次获取都会创建新的bean
- request 每次request请求都会创建新的bean
- session 一次会话一个实例
- global-session 一个集群会话一个实例
SPRING如何解决循环依赖的问题
主要使用三级缓存去解决循环依赖的问题,循环依赖主要带来的问题是重复调用,反复创建bean 导致内存溢出。
- 首先从一级缓存中去查找是否有创建的对象,如果有直接返回
- 如果一级缓存中没有创建的对象,去二级缓存中找是否有早期对象(刚刚通过反射创建的对象,还没有根据BeanDefinition进行设置属性的对象),如果有返回早期对象
- 如果二级缓存中也没有对象,则将正在创建的对象放入三级缓存并进行依赖注入,如果此时其他对象也依赖了这个对象,则直接从三级缓存中返回这个正在创建的对象,而不是重新创建
- 当bean对象创建完成后,从三级缓存中删除,并复制到一级缓存中,以备下次使用。
- Spring通过检查缓存中对象,返回bean的提前引用来解决循环依赖
AOP
谈谈对AOP的理解
面向切入编程,将与主业务逻辑无关的代码抽离出来,比如日志、权限控制等
- 连接点:被AOP控制的对象的方法
- 目标对象:连接点里面的对象,切入点表达式的对象
- 通知:重复的操作,日志,权限控制等
- 切入点:执行通知的时机,比如@Around、@before等,与切入点表达式连用【比如根据特定注解来实现通知或者特定的类】
- 切面:切入点+通知构成切面
使用:
- @Aspect @Component
动态代理了解吗?
-
代理模式:
- 主要角色:抽象主题、真实主题、代理,真实主题实现抽象主题的方法,代理对真实主题进行方法加强,客户端访问代理对象来调用真实主题里的操作
-
基于Innocation接口的动态代理:
-
基于接口的动态代理,需要一个实现InnocationHandler接口的类,重写invoke方法,通过Java的方法反射invoke() 调用真实主题里面的方法
-
通过Proxy.newInsatnce来调用代理对象,主要参数是CLASSLOADER,抽象主题数组(抽象接口),真实主题(接口实现类)
-
代理类:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class DynamicProxy implements InvocationHandler { private Object realSubject; public DynamicProxy(Object realSubject) { this.realSubject = realSubject; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before Request"); Object result = method.invoke(realSubject, args); System.out.println("After Request"); return result; } }客户端类
import java.lang.reflect.Proxy; public class Main { public static void main(String[] args) { RealSubject realSubject = new RealSubject(); Subject proxy = (Subject) Proxy.newProxyInstance( Subject.class.getClassLoader(), new Class[] { Subject.class }, new DynamicProxy(realSubject) ); // 调用代理对象的方法 proxy.request(); } } -
基于CGLB(code generation libiary)类的动态代理,基于字节码生成技术,运行时扩展Java类
-
CGLB可以用于没有实现接口的那些类,但是生成字节码也会带来性能开销
-
MVC
-
主要部件
- 前端控制器:DispacherServlet
- HandlerMapping
- HandlerExcutionChain
- HandlerAdapter
- Handler
- ModelAndView
- ViewResolver
-
过程
- 客户端发送HTTP请求给前端控制器DispacherServlet
- DispacherServlet响应请求,调用HandlerMapping
- HandlerMapping根据请求,返回给前端控制器DispacherServlet一个HandlerExecutionChain和Interceptor链
- DispacherServlet调用HandlerAdapter去找对应的Handler执行
- 对应的Handler执行完之后,返回ModelAndView给HandlerAdapter
- HandlerAdapter再把ModelAndView返回给前端控制器DispacherServlet
- 前端控制器DispacherServlet调用ViewResolver解析ModelAndView
- ViewResolver返回View给前端控制器啊DispacherServlet
- 前端控制器调用view的render方法进行渲染,返回给客户端
SPRING的设计模式
-
代理模式:
- 原生代理模式:客户端通过调用代理类去实现相关功能,不暴露具体的功能类,代理类可以对原有功能进行增强,主要类有抽象主题类、真实主题类,代理类
- 应用场景,AOP面向切入编程,实现了InnocationHandler接口,重写invoke方法,利用Java的反射去增强真实主题类中的方法,并用Proxy.newInstancequ去调用代理类
-
单例模式:
-
原生单例模式:确保一个类只有一个实例
-
应用场景:bean对象作用域为单例对象【默认】
public class Singleton { private static Singleton instance; // 私有构造函数 private Singleton() { } // 获取实例的静态方法 public static Singleton getInstance() { // 懒汉式实现:在第一次调用时才创建实例 if (instance == null) { instance = new Singleton(); } return instance; } }
-
-
工厂模式:
- 原生工厂模式包括抽象工厂、具体工厂、抽象产品、具体产品,将父类的实例化操作延迟到子类中完成,由子类决定具体实例化那个产品
- SPRING中的应用:beanfactory,applicationcontext
-
观察者模式:
- 原生观察者模式包括:抽象主题,主题,抽象观察者,观察者,当目标主题发生变化时,会通知所有的注册的观察者
- SPRING中的观察者模式:事件驱动模型【用事件来处理生命周期相关的内容,比如MVC中请求开始和请求结束的时候】
-
适配器模式:
- 原生的适配器模式:将一个类的接口转为期望的接口,增加兼容性,包括目标接口target、适配器adapter、适配者apaptee
- spring中的适配器模式,mvc里面的handleradapter
-
Spring中事务的使用以及常见的失效场景
- 方法访问权限问题,只支持public
- 方法用final修饰,动态代理不能代理final方法
- 方法内部调用,同一对象内调用没有使用代理,未被aop事务管理器控制
- 未被spring管理
- 多线程调用,事务管理内部使用threadLocal,不同线程间不在同一事务
- 表不支持事务
- 未配置事务,事务不回滚
- 错误的传播属性
- 自己吞了异常
- 手动抛了别的异常
- 自定义了回滚异常与事务回滚异常不一致
- 嵌套事务回滚多了,需要局部回滚的地方未做异常控制