目录
Spring是一个开源的轻量级控制反转【IOC-Inversion Of Control--思想,DI依赖注入是实现】和面向切面编程【Aop-Aspect-oriented programming】的容器框架。轻量级是说它开发使用简单,功能强大。
控制反转【IOC】,以前创建对象我们是通过new的方式进行创建,包括依赖关系(Controller层依赖于service业务层,service--数据层/持久化层)也是自己注入,使用Spring后,将bean对象的【生命周期】创建,属性设置,初始化,销毁和依赖关系交给Spring容器进行管理,然后我们通过注解的方式进行注入对象,作用就是降低耦合度,提高系统的可扩展性和可维护性**。
DI依赖注入是IOC思想的具体实现,就是将所有具有依赖关系的类放在IOC容器(SqlSessionFactory-bean工厂)中,然后IOC容器就会根据依赖关系,给你创建这个类的实例,这就是注入**【Spring中依赖注入的方式包括:构造方式注入,setter注入。注解注入@Autowired(常用)】
1在配置文件中定义一个Bean
2加载Spring的配置,并对xml进行解析。
3针对Bean的定义属性,会创建一个对象BeanDefination(注意不是Bean实例)
4存储多个BeanDefination(Bean的定义对象)
要用ConcurrentHashMap
装起来,Bean的注册。 【因为基本上使用在多线程的环境中,读取的性能就会更高--JDK1.8过后采用的数据+链表+红黑树存储】环境中,使用ConcurrentHashMap依赖于他的特性,查询效率就会更高。
5如果是单例的,【默认是饿汉,在Bean注册完成之后(4步)就会去concurrentHashMap拿到BeanDefination,根据BeanDefination
使用反射或代理的方式进行创建真正的实例对象。】第一次使用的时候进行创建
6接下来就是对Bean进行初始化,以及属性注入。
7将创建好的Bean存储在另外一个ConcurrentHashMap
8使用的时候直接去ConcurrentHashMap中取,根据Id(name)类型获取Bean。【@Autowired通过类型注入,类型没有就会采用名字注入,@Resources通过名字进行注入】。
==========================底层比较重要的类=================================
BeanFactory:创建Bean的工厂,提供了基本的获取Bean的方法
ApplicationContext:是BeanFactory的子接口,功能增强了如读取Resources,对消息的支持,如国际化支持等
ClasspathXmlApplicationContext:是ApplicationContext的子实现,可以自动从ClassPath类路径下加载xml配置,然后完成IOC的启动【getBean,getType】
AnnotationConfigApplicationContext:是针对注解配置的IOC容器,在SpringBoot中使用
XmlWebApplicationContext:是Spring整合了SpringMVC,在Web环境中使用的IOC工厂
=========================================================================
循环依赖指的是两个类中的属性各自依赖对方。
举例:比如A类中有B属性,B类中有A属性,
这样子就造成了spring在实例化其中一个类A的时候,填充属性的时候要去实例化另外一个类B,而填充另外一个类B属性A的时候又发现需要原来的那个类A,导致循环引用,无限创建。
1构造器注入循环依赖
2setter 注入循环依赖
3prototype模式Bean的循环依赖
Spring中利用缓存机制解决循环依赖问题,但仅限于单例情况,对于构造器循环依赖,和 prototype模式的循环依赖是无法解决的,在创建Bean的时候就会抛出异常BeanCurrentlyInCreationException 。
那么问题来了?
Spring中利用缓存机制解决循环依赖问题,核心是利用一个map。来解决这个问题,这个map就是缓存。
为什么可以这么做,因为我们的Spring创建的bean是默认单例的,而且是字段注入(setter注入)的,单例意味着只需要创建一次对象,然后将创建的对象存储到缓存中,后面就可以从缓存中取出来。
如果是原型bean,那么就意味着每次都要去创建对象,无法利用缓存;
如果是构造方法注入,那么就意味着需要调用构造方法注入,也无法利用缓存。
一级缓存,用来缓存已经(创建)实例化好的bean,即单例Bean缓存池
二级缓存,用来缓存正在创建的bean
三级缓存,用来缓存创建(实例化)bean的实例工厂ObjectFactory
====================我觉得最细的解释了===============================
假设有两个bean,A依赖B,B依赖A
1当A的ObjectFactory正在实例化(创建)A bean过后,需要给A中进行属性【成员变量】注入,发现A中依赖了B bean。
2此时B bean还没有进行实例化,所以需要暂停A,然后将A的ObjectFactory工厂存储在三级缓存中。
3然后使用创建B的ObjectFactory工厂会先去实例化(创建)B bean。
4此时new出来的A对象存储在那个地方?直接放在map容器中显然不合适,因为A的属性还没有注入B,所以要A存储在二级缓存【正在创建的bean的缓存】当中。
5然后就可以提供A bean(虽然没有创建成功)供其他bean进行依赖.
6而如果对A所依赖的B进行实例化过程中进行属性注入时,发现B也以依赖了A(然后就出现了循环依赖)。
7这时候B bean就可以从二级缓存中直接获取到A bean对象(虽然没有创建成功)进行注入到属性中。
8然后完成B的实例化(创建成功),然后将创建好的B实例存储到一级缓存中,移除B bean的二级三级缓存。
9然后A bean就可以获取到B bean的实例进行属性注入,最终A就能够实例化完成,然后将创建完成的A bean存储到一级缓存中,移除A bean的二级三级缓存。然后A和B都能够成功的创建成功。
具体流程图如下图,但是我不建议看。

Spring在获取Bean实例时首先会尝试从一级缓存中获取。就会走二级缓存尝试获取,如果也没有,就会走三级缓存拿到Bean的ObjectFacory创建Bean,然后把Bean放入二级缓存。
概念:
面向切面编程【AOP】是指将相同的逻辑代码横向抽取出来,比如事务,日志,异常处理等进行集中管理。AOP的主要作用就是严格遵守了OCP原则(面向扩展开放,面向修改封闭),在不改变原有代码的情况下,增加新的功能。
因为在我们传统面向对象开发过程中,我们的主要**业务逻辑【http-controller-service-mapper】是自上而下的,但是在自上而下的过程中,会出现很多横切性问题【下面有解释-想增加代码扩展新的功能】,比如说异常的统一处理,日志记录,事务,权限验证,这些横切性问题跟我们主要业务逻辑没有直接的关系。我们面向切面编程:就是将这些横切性问题模块化成一个切面aspect(公共的代码),我们程序员去关注这个切面(公共代码)的执行时机(就是这个代码需要切入到那个位置,什么时候切入)和顺序。
AOP的实现原理是基于动态代理,动态代理就是在运行时期,动态的为原生类生成一个代理类,该代理类是持有原生类的,从而可以对原生类的一个功能增强。
动态代理分为JDK动态代理和CGLIB代理,CGLIB代理需要导入相关的jar包,两者的区别是JDK动态代理要求目标类需要实现至少一个接口。而CGLIB则是基于继承进行代理,不需要实现任何接口
Spring中默认采用JDK动态代理(目标对象(原生类)实现了接口),如果目标对象没有实现任何接口,Spring会选择CGLIB代理,或者你可以在配置文件中强制指定使用CGLIB代理
常用设计模式+代理模式【Design Pattern】【我终于懂设计模式了】_GuGuBirdXXXX的博客-CSDN博客
下面我们使用AOP来做一个事务管理案例:在每个Service方法执行前后添加事务的代码
1.创建一个普通类,这个类的方法需要有事务
- @Service
- public class UserServiceImpl implements IUserService {
-
- public void insert() {
- System.out.println("UserServiceImpl.insert:保存User...");
- }
-
- public void delete() {
- System.out.println("UserServiceImpl.delete:删除User");
- }
- }
-
2.创建一个切面类,这个类里面提供公共的业务代码,即:事务代码
- @Component
- @Aspect
- public class TranscationManager {
-
- //定义切点,表达式作用于所有到service的所有的方法
- @Pointcut("execution(* cn.xxx.*.service.*.*(..))")
- public void pointcut(){}
-
- //前置通知 , 方法执行前执行,用来开启事务
- @Before("pointcut()")
- public void begin(JoinPoint joinPoint){
-
- System.out.println("TranscationManager.begin:开启事务...:");
- }
- //后置返回通知 ,方法正常返回后执行, 用来提交事务
- @AfterReturning("pointcut()")
- public void commit(){
- System.out.println("TranscationManager.commit:提交事物...");
- }
-
- //异常通知,方法出现异常调用,用来回滚事务
- @AfterThrowing(value = "pointcut()",throwing="e")
- public void rollback(JoinPoint joinPoint,Throwable e){
- System.out.println("TranscationManager.rollback:回滚事物咯...:"+e.getMessage());
- }
-
- //最终通知,不管方法会不会出现异常,都会执行,用来关闭资源
- @After("pointcut()")
- public void close(){
- System.out.println("TranscationManager.close:关闭连接...");
- }
-
-
- //环绕通知,拦截原始类的类的方法,可以通过 joinPoint调用原始的类
- @Around("pointcut()")
- public Object around(ProceedingJoinPoint joinPoint){
-
-
- return null;
- }
-
- }
3.配置Spring支持AOP注解
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
- http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-4.0.xsd">
-
- <context:component-scan base-package="cn.xx" />
-
- <aop:aspectj-autoproxy />
- beans>
4.测试代码
- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration("classpath:application_aop.xml")
- public class AopTest {
-
- @Autowired
- private IUserService userService ;
-
- @Test
- public void testAop(){
- userService.insert();
- System.out.println("=====================================");
- userService.delete();
- }
- }
控制台效果
TranscationManager.begin:开启事务…:
UserServiceImpl.insert:保存User…
TranscationManager.commit:提交事物…
TranscationManager.close:关闭连接…
======================================
TranscationManager.begin:开启事务…:
UserServiceImpl.delete:删除User…
TranscationManager.commit:提交事物…
TranscationManager.close:关闭连接…
(1)连接点(Join point):指程序运行过程中所执行的方法。在Spring AOP中,一个连接点总代表一个方法的执行。
(2)切面(Aspect):被抽取出来的公共模块,可以用来会横切多个对象。Aspect切面可以看成 Pointcut切点 和 Advice通知 的结合,一个切面可以由多个切点和通知组成。
在Spring AOP中,切面可以在类上使用 @AspectJ 注解来实现。
(3)切点(Pointcut):切点用于定义 要对哪些Join point进行拦截。
切点分为execution方式和annotation方式。execution方式可以用路径表达式指定对哪些方法拦截,比如指定拦截add*、search*。annotation方式可以指定被哪些注解修饰的代码进行拦截。
(4)通知(Advice):指要在连接点(Join Point)上执行的动作,即增强的逻辑,比如权限校验和、日志记录等。通知有各种类型,包括Around、Before、After、After returning、After throwing。
(5)目标对象(Target):包含连接点的对象,也称作被通知(Advice)的对象。 由于Spring AOP是通过动态代理实现的,所以这个对象永远是一个代理对象。
(6)织入(Weaving):通过动态代理,在目标对象(Target)的方法(即连接点Join point)中执行增强逻辑(Advice)的过程。
(7)引入(Introduction):添加额外的方法或者字段到被通知的类。Spring允许引入新的接口(以及对应的实现)到任何被代理的对象。例如,你可以使用一个引入来使bean实现 IsModified 接口,以便简化缓存机制。
=================================================================
Transactionnal事务流程 【文档】
Spring Bean的生命周期?
Spring中Controller是单例还是多例
IOC容器是如何保证Bean的单例的?