• Spring【IOC+AOP】待完善....勿看


    目录

    SpringIOC

    SpringIOC底层原理  

    Spring如何解决Bean的循环依赖?

    到底什么是循环依赖?

    有哪些循环依赖?

    Spring为什么解决不了多例和构造器的循环依赖?

    setter循环依赖的解决主要使用了三级缓存

    Spring如何解决Bean的循环依赖的执行流程

    从Concurrent缓存中获取Bean的流程

    SpringAOP  

    SpringAOP实现原理

    AOP案例【事务】

     Spring AOP里面的几个名词的概念:

    SpringBoot


             Spring是一个开源的轻量级控制反转【IOC-Inversion Of Control--思想,DI依赖注入是实现】面向切面编程【Aop-Aspect-oriented programming】容器框架轻量级是说它开发使用简单功能强大

    SpringIOC

            控制反转【IOC】,以前创建对象我们是通过new的方式进行创建,包括依赖关系(Controller层依赖于service业务层,service--数据层/持久化层)也是自己注入,使用Spring后,将bean对象的【生命周期】创建,属性设置,初始化,销毁和依赖关系交给Spring容器进行管理,然后我们通过注解的方式进行注入对象,作用就是降低耦合度,提高系统的可扩展性和可维护性**。

    DI依赖注入是IOC思想的具体实现,就是将所有具有依赖关系的类放在IOC容器(SqlSessionFactory-bean工厂)中,然后IOC容器就会根据依赖关系,给你创建这个类的实例,这就是注入**【Spring中依赖注入的方式包括:构造方式注入,setter注入。注解注入@Autowired(常用)】
        

    SpringIOC底层原理  

    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通过名字进行注入】。

    什么是ConcurrentHashMap?_GuGuBirdXXXX的博客-CSDN博客

    ==========================底层比较重要的类=================================

    BeanFactory:创建Bean的工厂,提供了基本的获取Bean的方法

    ApplicationContext:是BeanFactory的子接口,功能增强了如读取Resources,对消息的支持,如国际化支持等

    ClasspathXmlApplicationContext:是ApplicationContext的子实现,可以自动从ClassPath类路径下加载xml配置,然后完成IOC的启动【getBean,getType】

    AnnotationConfigApplicationContext:是针对注解配置的IOC容器在SpringBoot中使用

    XmlWebApplicationContext:是Spring整合了SpringMVC,在Web环境中使用的IOC工厂

    =========================================================================

    Spring如何解决Bean的循环依赖?

    到底什么是循环依赖?

    循环依赖指的是两个类中的属性各自依赖对方。

    举例:比如A类中有B属性,B类中有A属性,

    这样子就造成了spring在实例化其中一个类A的时候,填充属性的时候要去实例化另外一个类B,而填充另外一个类B属性A的时候又发现需要原来的那个类A,导致循环引用,无限创建

    有哪些循环依赖?

    1构造器注入循环依赖

    2setter 注入循环依赖

    3prototype模式Bean的循环依赖

    Spring中利用缓存机制解决循环依赖问题,但仅限于单例情况,对于构造器循环依赖,和 prototype模式的循环依赖是无法解决的,在创建Bean的时候就会抛出异常BeanCurrentlyInCreationException 。

    那么问题来了?

    Spring为什么解决不了多例和构造器的循环依赖?

            Spring中利用缓存机制解决循环依赖问题,核心是利用一个map。来解决这个问题,这个map就是缓存。

    为什么可以这么做,因为我们的Spring创建的bean是默认单例的,而且是字段注入(setter注入)的,单例意味着只需要创建一次对象,然后将创建的对象存储到缓存中,后面就可以从缓存中取出来。

    如果是原型bean,那么就意味着每次都要去创建对象,无法利用缓存;
    如果是构造方法注入,那么就意味着需要调用构造方法注入,也无法利用缓存。

    setter循环依赖的解决主要使用了三级缓存

    • 一级缓存,用来缓存已经(创建)实例化好的bean,即单例Bean缓存池

    • 二级缓存,用来缓存正在创建的bean

    • 三级缓存,用来缓存创建(实例化)bean的实例工厂ObjectFactory

    Spring如何解决Bean的循环依赖的执行流程

    ====================我觉得最细的解释了===============================

    假设有两个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都能够成功的创建成功。

    具体流程图如下图,但是我不建议看。

           

    从Concurrent缓存中获取Bean的流程

            Spring在获取Bean实例时首先会尝试从一级缓存中获取。就会走二级缓存尝试获取,如果也没有,就会走三级缓存拿到Bean的ObjectFacory创建Bean,然后把Bean放入二级缓存。

    SpringAOP  

            概念:

            面向切面编程【AOP】是指将相同的逻辑代码横向抽取出来,比如事务日志,异常处理等进行集中管理AOP的主要作用就是严格遵守了OCP原则(面向扩展开放,面向修改封闭),在不改变原有代码的情况下,增加新的功能

            因为在我们传统面向对象开发过程中,我们的主要**业务逻辑【http-controller-service-mapper】是自上而下的,但是在自上而下的过程中,会出现很多横切性问题【下面有解释-想增加代码扩展新的功能】,比如说异常的统一处理,日志记录,事务,权限验证,这些横切性问题跟我们主要业务逻辑没有直接的关系。我们面向切面编程:就是将这些横切性问题模块化成一个切面aspect(公共的代码),我们程序员去关注这个切面(公共代码)执行时机(就是这个代码需要切入到那个位置,什么时候切入)顺序。     

    SpringAOP实现原理

    AOP的实现原理是基于动态代理,动态代理就是在运行时期,动态的为原生类生成一个代理类,该代理类是持有原生类的,从而可以对原生类的一个功能增强。

    动态代理分为JDK动态代理和CGLIB代理,CGLIB代理需要导入相关的jar包,两者的区别是JDK动态代理要求目标类需要实现至少一个接口。而CGLIB则是基于继承进行代理,不需要实现任何接口

    Spring中默认采用JDK动态代理(目标对象(原生类)实现了接口),如果目标对象没有实现任何接口,Spring会选择CGLIB代理,或者你可以在配置文件中强制指定使用CGLIB代理

    常用设计模式+代理模式【Design Pattern】【我终于懂设计模式了】_GuGuBirdXXXX的博客-CSDN博客




     

    AOP案例【事务】

    下面我们使用AOP来做一个事务管理案例:在每个Service方法执行前后添加事务的代码

    1.创建一个普通类,这个类的方法需要有事务

    1. @Service
    2. public class UserServiceImpl implements IUserService {
    3. public void insert() {
    4. System.out.println("UserServiceImpl.insert:保存User...");
    5. }
    6. public void delete() {
    7. System.out.println("UserServiceImpl.delete:删除User");
    8. }
    9. }

    2.创建一个切面类,这个类里面提供公共的业务代码,即:事务代码

    1. @Component
    2. @Aspect
    3. public class TranscationManager {
    4. //定义切点,表达式作用于所有到service的所有的方法
    5. @Pointcut("execution(* cn.xxx.*.service.*.*(..))")
    6. public void pointcut(){}
    7. //前置通知 , 方法执行前执行,用来开启事务
    8. @Before("pointcut()")
    9. public void begin(JoinPoint joinPoint){
    10. System.out.println("TranscationManager.begin:开启事务...:");
    11. }
    12. //后置返回通知 ,方法正常返回后执行, 用来提交事务
    13. @AfterReturning("pointcut()")
    14. public void commit(){
    15. System.out.println("TranscationManager.commit:提交事物...");
    16. }
    17. //异常通知,方法出现异常调用,用来回滚事务
    18. @AfterThrowing(value = "pointcut()",throwing="e")
    19. public void rollback(JoinPoint joinPoint,Throwable e){
    20. System.out.println("TranscationManager.rollback:回滚事物咯...:"+e.getMessage());
    21. }
    22. //最终通知,不管方法会不会出现异常,都会执行,用来关闭资源
    23. @After("pointcut()")
    24. public void close(){
    25. System.out.println("TranscationManager.close:关闭连接...");
    26. }
    27. //环绕通知,拦截原始类的类的方法,可以通过 joinPoint调用原始的类
    28. @Around("pointcut()")
    29. public Object around(ProceedingJoinPoint joinPoint){
    30. return null;
    31. }
    32. }

    3.配置Spring支持AOP注解

    1. <beans xmlns="http://www.springframework.org/schema/beans"
    2. xmlns:aop="http://www.springframework.org/schema/aop"
    3. xmlns:context="http://www.springframework.org/schema/context"
    4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    6. http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
    7. http://www.springframework.org/schema/context
    8. http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    9. <context:component-scan base-package="cn.xx" />
    10. <aop:aspectj-autoproxy />
    11. beans>

    4.测试代码

    1. @RunWith(SpringJUnit4ClassRunner.class)
    2. @ContextConfiguration("classpath:application_aop.xml")
    3. public class AopTest {
    4. @Autowired
    5. private IUserService userService ;
    6. @Test
    7. public void testAop(){
    8. userService.insert();
    9. System.out.println("=====================================");
    10. userService.delete();
    11. }
    12. }

    控制台效果

    TranscationManager.begin:开启事务…:
    UserServiceImpl.insert:保存User…
    TranscationManager.commit:提交事物…
    TranscationManager.close:关闭连接…
    ======================================
    TranscationManager.begin:开启事务…:
    UserServiceImpl.delete:删除User…
    TranscationManager.commit:提交事物…
    TranscationManager.close:关闭连接…
     

    Spring AOP里面的几个名词的概念:

    (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事务流程 【文档】

    SpringBoot

    Spring Bean的生命周期?

    Spring中Controller是单例还是多例

    IOC容器是如何保证Bean的单例的?

    Spring常见面试题总结(超详细回答)_张维鹏的博客-CSDN博客_spring面试题

  • 相关阅读:
    方法的使用
    第九届世界渲染大赛什么时候开始举办?
    Collection,Map与String互相转换
    看源码方法
    IOS自动化之批量解锁&锁定屏幕
    java计算机毕业设计springboot+vue网络体检服务系统
    [Django-1] 快速建立项目
    用于 syslog 收集的协议:TCP、UDP、RELP
    wireshark解析达梦数据库协议
    umask计算创建文件、目录的默认权限
  • 原文地址:https://blog.csdn.net/m0_64210833/article/details/126313071