写在前面:
朋友分享了一个Spring的知识体系图片,把它整理成文字,以便阅读;
1、基本概念Spring优点: 1、轻量级,非侵入式,对现有的类结构没有影响; 2、可以提供众多服务,如事务管理、WS等 3、AOP的很好支持,方便面向切面编程,使得业务逻辑和系统服务分开 4、对主流的框架提供了很好的集成支持 5、使用Spring IOC容器,将对象之间的依赖关系交给Spring,降低组件之间的耦合性,让我们更专注于应用逻辑 5、Spring DI机制降低了业务对象替换的复杂性 6、Spring的高度可开放性,可不强制依赖于Spring,开发者可以自由选择Spring部分或全部 Spring缺点: 1、缺少一个公用控制器 2、没有SpringBoot好用 3、Spring像一个胶水,将框架黏在一起,后面拆分就不容易拆分2、AOP
基本概念: 1、核心业务功能和切面功能分别独立进行开发,然后把切面功能和核心业务功能“编织在一起”,这就叫AOP 2、让关注点代码与业务代码分离 3、面向切面编程就是指:对很多功能都有很多的重复代码抽取,再在运行的时候往业务方法上动态植入“切面类代码” 4、应用场景:日志、事务管理、权限控制
实现原理:
JDK动态代理:
主要通过Proxy:newProxyInstance()和InvocationHandler这两个类和方法实现
实现过程:
1、创建代理类Proxy实现invocation接口,重写invoke()方法,调用被代理类方法时默认调用此方法
2、将被代理类作为构造函数的参数传入代理类Proxy
3、调用Proxy:newProxyInstance(classloader,interfaces,handler)方法生成代理类(生成代理类 -> 代理对象会实现用户提供的的这组接口,因此可以将这个代理对象强制类型转化为这组接口中的任意一个 -> 通过反射生成对象)
实现过程总结:代理类调用自己的方法时,通过自身持有的中介类对象调用中间类对象的invoke方法,从而达到代理执行被代理对象的方法。
CGLIB动态代理:
1、生成对象类型为Enhancer
2、实现原理类似于JDK动态代理,只是他在运行期间生成的代理对象是针对目标类拓展的字类
3、Spring在运行期间通过CGLIB继承要被动态代理的类,重写父类的方法,实现AOP面向切面编程
静态代理:
在编译的时候就直接生成代理类
缺点:
1、如果要代理一个接口的多个实现的话需要定义不同的代理类
2、代理类和被代理类必须实现同样的接口,万一接口有变动,代理、被代理类都得修改,难以维护
JDK动态代理和CGLIB的对比:
1、CGLIB所创建的动态代理对象在实际运行时候的性能要比JDK动态代理高(1.6和1.7的时候,CGLIB更快;1.8的时候,JDK更快)
2、CGLIB在创建对象的时候所花费的时间比JDK动态代理多
3、singleton的代理对象或者具体实例池的代理,因为无需频繁的创建代理对象,所以比较适合CGLIB动态代理,反之,则适合用JDK动态代理
4、JDK动态代理是面向接口的,CGLIB动态代理是通过字节码底层继承代理类来实现(如果被代理类被final关键字所修饰,那么会失败)
5、JDK生成的代理类类型是Proxy(因为继承的是Proxy),CGLIB生成的代理类类型是Enhancer类型
6、如果要被代理类类型是个实现类,那么Spring会使用JDK动态代理来完成操作(Spring默认采用JDK动态代理实现机制);如果被代理对象不是实现类,那么Spring会强制使用 CGLIB来实现动态代理。
配置方式: 1、XML方式 2、注解方式 3、基于JAVA类配置,通过@Configuration过和@Bean这两个注解实现的,@Configuration作用于类上,相当于一个Xml配置文件;@Bean作用于方法上,相当于Xml配置中的3、事务管理标签
基本概念:
如果需要某一组操作具有原子性,就用注解的方式开启事务,按照给定的事务规则来执行提交或者回滚操作
事务控制:
编程式事务控制:
用户通过代码的形式手动控制事务;
Conn.setAutoCommite(false);//设置手动控制事务
粒度较细,比较灵活,但开发起来比较繁琐,每次开启都要开启、提交、回滚
声明式事务控制:
Spring提供事务的控制管理:XML方式、注解方式
Spring事务管理接口
PlatformTransactionManager:
事务管理器;
Spring并不直接管理事务,而是提供了多种事务管理器,通过PlatformTransactionManager这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了
TransactionDefinition:
事务定义属性(事务隔离级别、传播行为、超时、只读、回滚规则)
TransactionStatus:
事务运行状态
事务一般在Service层:
1、如果在Dao层,回滚的时候只能回滚到当前方法,但一般我们的Service层的方法都是由很多Dao层的方法组成的;
2、如果在Dao层,Commit的次数会过多
事务属性:
事务传播行为:
REQUIED<默认>:没有事务就创建一个新事物,存在则加入该事务;
REQUIED_NEWS:创建新事物,无论存不存在都创建新事物;
NESTED:如果当前存在事务,嵌套事务内执行,没有则新建;
SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果不存在则已非事务执行;
MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果不存在就抛出异常;
NOT_SUPPORTS:以非事务方式执行,存在事务的话,就把事务挂起;
NEVER:以非事务执行,如果当前存在事务,就抛出异常
数据库隔离级别:
ISOLATION_DEFAULT:使用后端数据库默认的隔离级别
ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复度
ISOLATION_READ_COMMITTED:允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或者不可重读仍有可能发生
ISOLATION_REPEATABLE_READ:对一个字段的多次读取结果都是一致的,除非数据库是被本身事务所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别,所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读,但是这将严重影响程序的性能,通常情况下也不会用到该级别。
事务超时属性:
指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。
事务只读属性:
对事务资源是否执行制度操作
回滚规则:
定义了哪些异常会导致事务回滚而哪些不会,默认情况下,事务只有遇到运行期异常时才会回滚,而在遇到检查型异常时不会回滚,也可以由用户自己定义
4、相关设计模式
1、单例模式
实现方式:Spring依赖注入Bean实例默认是单例的;
2、工厂模式:
实现方式:
静态工厂,beanFactory;
工厂模式,factoryBean;
3、代理模式:
实现方式:AOP中的JDK动态代理和CGLIB
4、适配器模式
实现方式:SpringMVC中的适配器HandlerAdatper
实现原理:HandlerAdatper根据Handler规则执行不同的Handler
5、装饰器模式:
实现方式:Spring中用到的包装器模式在类名上有两种表现:一种是类名中含有Wrapper,另一种是类名中含有Decortor
6、观察者模式:
实现方式:SPring的事件驱动模型使用的是观察者模式,Spring中Observer模式常用的地方是listener的实现
具体实现:事件机制的实现需要三个部分,事件源,事件,事件监听器
7、策略模式:
实现方式:Spring框架的资源访问Resource接口,该接口提供了更强的资源访问能力,Spring框架本身大量使用了Resource接口来访问底层资源
8、模板方法模式
5、IOC
依赖注入:
装配方式(依赖注入的具体行为):
基于注解的自动装配:
实现方式:
注解:
@Autowired:
1、优先按照ByType方式进行查找
2、如果查询的结果不止一个,而且没有设置名称的话,那么会报错
3、否则Autowired会按照ByName方式来查找
4、如果查询结果为空,那么会抛出异常,可以使用require=false来解决
@Resource:
1、默认按照ByName当方式来进行装配,名称可以通过name属性进行指定
2、如果没有制定name属性,当注解写在字段上时,默认去字段名字进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配
3、当找不到与名称匹配的bean时才通过ByType进行装配。但是需要注意的是,如果name属性一旦制定,就只会按照名称进行装配
4、只指定@Resource注解的type属性,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常
自动扫描:
标签会开启SPring Beans的自动扫描,并可设置base-package属性,表示Spring将会扫描该目录以及子目录下所有被@Component标注修饰的类,对他们进行装配
基于XML配置的显示装配:
基于JAVA配置的显示装配:
能够在编译时就发现错误
依赖注入的方式:
构造器方式注入:
1、保证依赖不可变(final关键字)
2、保证依赖不为空:
3、避免循环依赖:在构造器注入参数时,比如在A中注入B,在B中注入A,先初始化A,那么需要传入B,这个时候发现需要B没有初始化,那么就要初始化B,这个时候就出现了问题,会排除循环依赖错误,而使用field注入方式则只能在运行时才会遇到这个问题。
setter方式注入:通过找到类的对应的setter方法,再进行对应的注入;
Setter方法注入是容器通过调用无参构造器或无参statuc工厂方法实例化bean之后,调用该bean的setter方法,即实现了基于setter的依赖注入
循环依赖:
构造器方式无法解决,只能抛出异常:因为加入三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决;
多例方法无法解决,只能抛出异常:因为Spring容器不缓存prototype作用域的bean,因此无法提前暴露一个创建中的bean
单例模式可以解决:
通过三级缓存解决;
在createBeanInstance()之后会调用addSingleton()方法将bean注册到singletonFactorles中,通过提前暴露一个单例工厂方法,从而使其他bean能够引用到该bean提前暴露一个正在创建中的bean
举例:1、A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象;2、A首先完成了初始化的第一步,并且将自己提前曝光到singletonFactories中;3、A发现自己依赖对象B,此时就尝试去get B,发现B还没有背create,所以走create流程4、B在初始化第一步的时候发现自己依赖了对象A,于是尝试get A,尝试一级缓存没有,因为A还没有初始化完全,尝试二级缓存也没有,尝试三级缓存,由于A通过ObjectFactory将自己提前曝光了,所以B能够通过ObjectFactory.getObject拿到A对象;5、B拿到A对象后顺利完成初始化阶段1、2、3,完全初始化后将自己放入到一级缓存singletonObjects中;6、返回A中,A此时能够拿到B的对象顺利完成自己的初始化阶段2、3,最终A也完成了初始化,放到一级缓存中;7、由于B拿到了A的对象引用,所以B类中的A对象完成了初始化。
容器的初始化过程: 1、Resource定位:指对BeanDedinition的资源定位过程,通俗地讲,就是找到定义JavaBean信息的XML文件,并将其封装成Resource对象 2、BeanDefinition的载入:把用户定义好的JavaBean表示为IOC容器内部的数据结构,这个容器内部的数据结构就是BeanDefinition 3、向IOC容器注册这些BeanDefinition
Bean的知识:
Bean的生命周期:
1、实例化bean
2、设置Bean的属性
3、检查Aware相关接口并设置相关依赖
4、检查BeanPostProcessor接口并进行前置处理
5、检查Bean在Spring配置文件中配置的init-method属性并自动调用其配置的初始化方法
6、检查beanPostProcessor接口并进行后置处理
7、当Bean不再需要时,会进过清理阶段,如果Bean实现了DisPosableBean这个接口,会调用那个其实现的destory方法;
8、最后,如果这个Bean的Spring配置中配置了destory- method属性,会自动调用其配置的销毁方法。
Bean的作用域:
singleton:
是Bean的默认作用域,默认情况下是容器初始化的时候创建,但也可设定运行时再初始化Bean
Spring容器可以管理singleton作用域下bean的生命周期,在此作用域下,Spring能够精确地知道bean何时被创建,何时初始化完成,以及何时被销毁
prototype:
每次通过Spring容器获取prototype定义的bean时,容器都将创建一个新的Bean实例,每个Bean实例都有自己的属性和状态;当容器创建了bean的实例后,bean的实例将交给客户端的代码管理,Spring容器将不会再跟踪其生命周期,并且不会管理那些配置成prototype作用域的生命周期;对有状态的bean使用prototype作用域,而对无状态的bean使用singleton作用域。
request:
再一次http请求中,容器会返回Bean的同一实例,而对不同的Http请求则会产生新的Bean,而且该Bean仅在当前Http Request内有效
session:
在一次Http Session中,容器会返回该Bean的同一实例,而对不同的Http请求则会产生新的Bean,而且该Bean实例仅会在当前Session内有效
Global Seesion:
在一个全局的Http Session中,容器会返回该Bean的同一个实例,仅在使用portlet context时有效。
Bean的创建过程:
1、进入getBean方法;
2、判断当前bean的作用域是否是单例,如果是,则去对应缓存中查找,没有查找的话则新建实例并保存,如果不是单例,则直接新建实例(createBeanInstance)
3、新建实例后再注入属性(popilateBean),并处理回调(创建Bean,找到@Autowired的对象,创建注入对象,并赋值)
IOC大致流程: 1、首先根据配置文件找到对应的包,读取包中的类,找到所有含有@Bean,@Service等注解的类,利用反射解析他们,包括解析构造器,方法,属性等等,然后封装成各种信息方法哦Container(其实是一个map)里6、Spring MVC2、获取类时,首先从Container中查找是否有这个类,如果没有创建,如果有,则通过构造器新奇将这个类new出来; 3、如果这个类含有其他需要注入的属性,则进行依赖注入,如果有则还是从COntailer找到对应的解析类,new出对象,并通过之前解析出来的信息类找到setter方法(setter方法注入),然后用该方法注入对象(这就是依赖注入)。如果其中有一个类Container里没找到,则抛出异常 4、如果有嵌套Bean的情况,则通过递归解析; 5、如果Bean的scope时singleton,则会重用这个bean不用重新创建,将这个bean放到一个map里面找,如果作用域时session,则该bean会放到session里面 总结:通过解析xml文件,获取bean的属性里面的内容,利用反射原理创建配置文件里面的类的实例对象,存入到Spring的bean容器中
执行流程: 1、用户发送请求至前端控制器DispatcherServlet 2、DIspatcherServlet收到请求调用HandlerMapping处理器映射器 3、处理器映射器根据请求url找到具体的处理器,生成处理器对象以及处理器拦截器一并返回给DispatherServlet 4、DispathcherServlet通过HandlerAdapter处理器适配器调用处理器 5、执行处理器(Controller,也叫后端处理器) 6、Controller执行完成饭后ModelView 7、HandlerAdapter将Controller执行结果返回ModelAndView返回给DispathcherServlet 8、DispatcherServlet将ModelAndView传给View Resource视图解析器 9、ViewReslover解析后返回具体View 10、Dispatcher Servlet对View进行渲染视图(即将模型数据填充至视图中)。 11、DispatcherServlet响应用户 常用注释: @Compoent(@Controller、@Service、@Repository) @RequestMapping @RequesParam @Autowired @Resource Servlet的生命周期: 1、加载和实例化 2、初始化 3、请求处理 4、服务终止 7、Spring类
ApplicationContext: FileSystemXmlApplicationContext ClassPathXmlApplicationContext WebXmlApplicationContext



