写点什么

Spring IOC 和 AOP

作者:Studying_swz
  • 2022-10-21
    天津
  • 本文字数:6041 字

    阅读完需:约 20 分钟

Spring IOC和AOP

IOC 部分:

1.Spring 是什么?

Spring 是一个生态,可以构建 java 应用所需要的一切基础设施。通常 spring 指的是 spring frameWork.一般的生态来说:


  • 1.Spring FrameWork

  • 2.SpringBoot 增加了自动配置总结一下,其实就是 Spring Boot 在启动的时候,按照约定去读取 Spring Boot Starter 的配置信息,再根据配置信息对资源进行初始化,并注入到 Spring 容器中。这样 Spring Boot 启动完毕后,就已经准备好了一切资源,使用过程中直接注入对应 Bean 资源即可

  • 3.SpringMVC

  • 4.SpringCloud 分布式的 springboot,适用于分布式


而 spring 是一个轻量级开源框架:IOC:控制翻转 AOP:面向切面编程


<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

2 .IOC 控制反转

本意: 对象的创建由我们自己 new 转到了 spring ioc 容器中,由 spring ioc 容器统一管理、这样的好处: 降低对象间的耦合度底层分析: xml 配置文件、反射、简单工厂模式我们通过读取 xml 配置文件(bean id+class),通过读取类的全路径,来进行反射创建对象,然后这个过程是由 spring ioc 完成的,我们只需要调用它提供的方法即可获得对象,这是简单工厂模式。


<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

3.IOC 的两种实现

1)BeanFactory 接口这个接口是基本实现,一般不提供开发人员使用,并且加载配置文件的时候是懒加载,不会创建对象。2)ApplicationContext 接口(其中实现的是 BeanFactory 接口)这个接口是提供给开发人员使用的,有相对多的功能,加载配置文件的就会创建对象,所以相对式慢启动、快响应。


<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

4.Bean 的管理

1)创建对象 2)为对象设置属性一般分为基于 xml 的配置文件和基于注解方式的实现:xml 文件:创建对象:在 xml 文件配置 bean 注入属性(DI):构造注入/设置注入(set),可以注入简单属性的值,也可以注入其他类的对象,包括注入外部 bean、注入内部 bean、


基于注解:创建对象:在 xml 文件配置 component-scan,然后再要创建对象的类上加入注解:@component、@service、@Reposity、@Controller 注入属性(DI):在属性上加入注解:@Autowire、@Vlaue、@Qualifier、@Resource


<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

5:DI 和 IOC

可以看到 IOC 式控制翻转,主要是创建对象的思想,而真正的实现是由反射+简单工厂,然后通过 DI 技术实现对象的属性赋值


<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

6.scope

一般是单例、可以设置多例、还有不常用 session、request,一次请求创建一个 bean,一个会话创建一个 bean<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

7.spring 的单例安全

单例:一般如果涉及到无状态的 bean(没有属性),那么肯定用单例,并且安全但是如果有状态的 bean,单例则不安全了,我们一般用多例模式;当然还可以在单例模式下设置属性 ThreadLocal 来保证每个线程有自己的属性。<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

8.spring bean 的常见生命周期

简单来说:创建对象---->注入属性---->后置处理器的 Before 方法------>初始化方法-------->后置处理器的 After 方法------>获得对象销毁对象------>调用 destory 方法(写了才有用)



为了更好理解,可以重构成实例化、属性注入、初始化、销毁四个主要部分,其余部分相当于使用 AOP 技术,如果实现了相关接口,才会有这些过程。


  • 1.BeanFactoryPostProcessor 的 bean 定义

  • BeanFactoryPostProcessor 接口是 Spring 初始化 BeanFactory 时对外暴露的扩展点,Spring IoC 容器允许 BeanFactoryPostProcessor 在容器实例化任何 bean 之前读取 bean 的定义,并可以修改它。说通俗一些就是可以管理我们的 bean 工厂内所有的 beandefinition(未实例化)数据,可以随心所欲的修改属性

  • 2.InstantiationAwareBeanPostProcessor 的 bean 定义

  • postProcessBeforeInstantiation 方法

  • 3.实例化

  • 4.InstantiationAwareBeanPostProcessor 的 bean 定义

  • postProcessAfterInstantiation 方法

  • 5.注入属性

  • 6.Aware 接口【BeanNameAware 接口】调用 BeanNameAware.setBeanName()【BeanFactoryAware】调用 BeanFactoryAware.setBeanFactory()【ApplicationContextAware】调用 ApplicationContextAware.setApplicationContext()

  • 7.BeanPostProcessor 的 bean 定义

  • postProcessBeforeInitialization 方法

  • 8.InitializingBean 的接口

  • InitializingBean.afterPropertiesSet()方法

  • 9.初始化 init

  • 10.BeanPostProcessor 的 bean 定义

  • postProcessAfterInitialization 方法

  • 11.DisposableBean 接口

  • destroy()方法

  • 12.自定义 init_destory()方法<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

9.Spring 怎么解决循环依赖问题?

  • 三级缓存

  • singletonObjects 一级缓存

  • earlySingletonObjects 二级缓存

  • singletonFactories 三级缓存

情景一(没有循环依赖)----一级缓存

情景二(有循环依赖,没有 Aop) -----二级缓存


最后把半成品池(二级缓存)的对象销毁;



注:无法解决 Aop 代理对象

情景三(有循环依赖,有 Aop)----三级缓存






步骤:(待)


https://blog.csdn.net/lkforce/article/details/97183065https://blog.csdn.net/m0_43448868/article/details/113578628https://www.bilibili.com/video/BV1ET4y1N7Sp?p=1参考链接:https://www.nowcoder.com/discuss/747711https://blog.csdn.net/qq_36714200/article/details/111240510

AOP 部分

1.AOP

面向切面编程,主要本质: 对业务逻辑的各个部分进行分离,从而业务逻辑和一些其他非业务逻辑:事务、日志等分隔开,同理降低耦合度,并且代码的复用能力。


<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

2.静态代理和动态代理

静态代理:对一个类,我们利用其对象来调用其方法,但是我们生成一个代理类,这个代理类作为其访问的接口,并且我们可以在其对象方法前后加上自己的新的逻辑功能,进行增强。--- 编译时完成


动态代理:不同于静态代理,我们在运行时进行动态代理类的生成,主要分为两种:1)基于 JDK 接口+实现类主要是反射实现的,代理类 Proxy,主要是三个参数(实现类的类加载器、实现类的接口、实现 invocationhandler 接口的对象(实现类对象)


2)基于 CGLIB 没有接口,只有实现类,通过 ASM 开源包对代理对象类的 class 文件加载进来,通过修改字节码生成子类处理。因此如果被代理类被 final 关键字所修饰,会失败。内部是实现”MethodInterceptor“接口,而这个接口内部获得实现类的元对象,并把它设置为 Enhance 类的父类,所以这是继承的思想,在其字节码中的指定对象前后加入自己处理逻辑。


3)对比 cglib 的创建效率低,调用效率高;而 spring 默认是 jdk 代理,但是如果没有实现接口,则强制使用 CGLib 来实现动态代理。


<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

3.spring 的常用设计模式

1)简单工厂模式---IOC2)单例模式---bean3)动态代理----AOP


<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

4.AOP 的实现


术语:连接点:类中的方法(不用记)切入点:这个才有用,真正被增强的方法切面:将通知应用到切入点的过程,是一个动作通知/增强:实际增强的逻辑部分,自己写的代码部分


  • 类型:前置通知、后置通知、环绕通知、异常通知、最终通知

  • @Before、@AfterReturning、@Around、@AfterThrouing、@After


注:就是定义好切入点、通知,然后利用切面来将通知加入到切入点上。


配置:可以通过切入点表达式 @PointCut


代码实现:


  • 引入依赖


    <dependency>           <groupId>org.aspectj</groupId>           <artifactId>aspectjweaver</artifactId>           <version>1.9.4</version>       </dependency>
复制代码


  • 1.xml 配置实现


  <beans xmlns="http://www.springframework.org/schema/beans"             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"             xmlns:context="http://www.springframework.org/schema/context"             xmlns:aop="http://www.springframework.org/schema/aop"             xsi:schemaLocation="http://www.springframework.org/schema/beans          http://www.springframework.org/schema/beans/spring-beans.xsd          http://www.springframework.org/schema/aop          http://www.springframework.org/schema/aop/spring-aop.xsd">      <!--配置aop:需要导入aop的命名空间-->      <aop:config>          <!--配置切入点 -->          <aop:pointcut id="point" expression="execution(* dao.*.*(..))"/>         <!-- 配置通知点,需要log类实现相关的接口-->          <aop:advisor advice-ref="log" pointcut-ref="point"/>      </aop:config>
复制代码


  /*  Before(方法执行前) :org.apringframework.aop.MethodBeforeAdvice  AfterReturning(方法返回后) :org.springframework.aop.AfterReturningAdvice  After-throwing(异常抛出后) :org.springframework.aop.ThrowsAdviceArround  环绕,即方法前后 :org.aopaliance.intercept.MethodInterceptor  */  public class Log implements MethodBeforeAdvice, AfterReturningAdvice,MethodInterceptor  {        public void before(Method method, Object[] objects, Object o) throws Throwable {          System.out.println("BeforeAdvice方法执行前");          System.out.println(method.getName()+";"+o.getClass());      }        public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {          System.out.println("AfterAdvice方法执行前");          System.out.println(method.getName()+";"+o.getClass());      }        public Object invoke(MethodInvocation methodInvocation) throws Throwable {          System.out.println("Roundadvice方法执行前");          System.out.println(methodInvocation.getArguments()[0]);//可以获取目标方法的参数值          Object result=methodInvocation.proceed();//调用目标对象的方法          System.out.println("RoundAdvice方法执行完成了");          return result;      }    }
复制代码


  • 2.注解实现

  • xml 配置


  <!-- JDK(默认proxy-target-class="false") cglib(proxy-target-class="true")-->      <aop:aspectj-autoproxy/>
复制代码


  //定义切面  //定义bean  @Aspect  @Component  public class MyAspect {      //配置切点,切点的这个方法不需要实现,只是为了给切面的方法作为锚点      @Pointcut("execution(* testExecution(..))")      public void anyTestMethod() {}       //**配置前置通知,使用在anyTestMethod上注册的切入点**很重要!!!     @Before("ric.study.demo.aop.MyAspect .anyTestMethod()")      public void doAccessCheck(JoinPoint joinPoint) {          // ... 实现代码      }      // returnVal 就是相应方法的返回值     // 注意可以通过这样去为你的aop方法获得更多的参数打印详细信息,只要是该注解提供的参数都是这样获取      @AfterReturning(          pointcut="ric.study.demo.aop.MyAspect .anyTestMethod()",          returning="returnVal")      public void doAccessCheck(Object returnVal) {          //  ... 实现代码      }        // 这种最灵活,既能做 @Before 的事情,也可以做 @AfterReturning 的事情      @Around("ric.study.demo.aop.MyAspect .anyTestMethod()")      public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {           //  target 方法执行前... 实现代码          Object retVal = pjp.proceed();          //  target 方法执行后... 实现代码          return retVal;      }    }  
复制代码


https://zhuanlan.zhihu.com/p/127792838


<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

5.spring 事务的配置

  • 1.编程式事务管理编程式事务管理是侵入性事务管理,使用 TransactionTemplate 或者直接使用 PlatformTransactionManager,对于编程式事务管理,Spring 推荐使用 TransactionTemplate。

  • 2.声明式事务管理声明式事务管理建立在 AOP 之上,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目标方法之后根据执行的情况提交或者回滚。

  • 3.比较编程式事务每次实现都要单独实现,但业务量大功能复杂时,使用编程式事务无疑是痛苦的,而声明式事务不同,声明式事务属于无侵入式,不会影响业务逻辑的实现,只需要在配置文件中做相关的事务规则声明或者通过注解的方式,便可以将事务规则应用到业务逻辑中。显然声明式事务管理要优于编程式事务管理,这正是 Spring 倡导的非侵入式的编程方式。唯一不足的地方就是声明式事务管理的粒度是方法级别,而编程式事务管理是可以到代码块的,但是可以通过提取方法的方式完成声明式事务管理的配置(黑体不懂)


<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

6.spring 事务的传播?

事务的传播性一般用在事务嵌套的场景,比如一个事务方法里面调用了另外一个事务方法,那么两个方法是各自作为独立的方法提交还是内层的事务合并到外层的事务一起提交,这就是需要事务传播机制的配置来确定怎么样执行。常用的事务传播机制如下:


  • PROPAGATION_REQUIREDSpring 默认的传播机制,能满足绝大部分业务需求,如果外层有事务,则当前事务加入到外层事务,一块提交,一块回滚。如果外层没有事务,新建一个事务执行

  • PROPAGATION_REQUES_NEW 该事务传播机制是每次都会新开启一个事务,同时把外层事务挂起,当当前事务执行完毕,恢复上层事务的执行。如果外层没有事务,执行当前新开启的事务即可

  • PROPAGATION_SUPPORT 如果外层有事务,则加入外层事务,如果外层没有事务,则直接使用非事务方式执行。完全依赖外层的事务

  • PROPAGATION_NOT_SUPPORT 该传播机制不支持事务,如果外层存在事务则挂起,执行完当前代码,则恢复外层事务,无论是否异常都不会回滚当前的代码

  • PROPAGATION_NEVER 该传播机制不支持外层事务,即如果外层有事务就抛出异常

  • PROPAGATION_MANDATORY 与 NEVER 相反,如果外层没有事务,则抛出异常

  • PROPAGATION_NESTED 该传播机制的特点是可以保存状态保存点,当前事务回滚到某一个点,从而避免所有的嵌套事务都回滚,即各自回滚各自的,如果子事务没有把异常吃掉,基本还是会引起全部回滚的。


传播规则回答了这样一个问题:一个新的事务应该被启动还是被挂起,或者是一个方法是否应该在事务性上下文中运行


<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

发布于: 刚刚阅读数: 5
用户头像

Studying_swz

关注

还未添加个人签名 2020-12-23 加入

还未添加个人简介

评论

发布
暂无评论
Spring IOC和AOP_spring_Studying_swz_InfoQ写作社区