Spring框架概览

149 阅读8分钟

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中事务的使用以及常见的失效场景

  1. 方法访问权限问题,只支持public
  2. 方法用final修饰,动态代理不能代理final方法
  3. 方法内部调用,同一对象内调用没有使用代理,未被aop事务管理器控制
  4. 未被spring管理
  5. 多线程调用,事务管理内部使用threadLocal,不同线程间不在同一事务
  6. 表不支持事务
  7. 未配置事务,事务不回滚
  8. 错误的传播属性
  9. 自己吞了异常
  10. 手动抛了别的异常
  11. 自定义了回滚异常与事务回滚异常不一致
  12. 嵌套事务回滚多了,需要局部回滚的地方未做异常控制