SpringAOP知识拓展

先赞后看,养成习惯!!!❤️ ❤️ ❤️
码字不易,如果喜欢可以关注我哦!
如果本篇学习笔记对你有所启发,欢迎访问我的个人博客了解更多内容:链接地址

springAOP

什么是动态代理?

在了解动态代理之前,我现在简单介绍下什么代理。代理:按照字面意思,就是代替你去做一些事情。代替你去完成一些功能,或者做一些本来应该你来做的事情。

这是字面意思理解,在面向对象的程序设计语言里:动态代理是在你原有的功能基础之上,对功能进行增强的一种实现手段。通过动态代理,并且符合开闭原则的前提增强方法的功能。这也是AOP的思想,通过不修改原有代码,把你的代码织入到指定的方法中。

代理模式:

代理模式(Proxy):为其他对象提供一个代理以控制对这个对象的访问。

主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(

比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,

我们可以在访问此对象时加上一个对此对象的访问层。

代理模式的元素是:共同接口、代理对象、目标对象。

代理模式的行为:由代理对象执行目标对象的方法、由代理对象扩展目标对象的方法。

代理模式的宏观特性:对客户端只暴露出接口,不暴露它以下的架构。

JDKProxDynameic 和 CGlibProxDynameic区别

jdk的动态代理是代理的接口,并且只能代理有 接口的类。如果这个类没有实现任何接口,jdk的动态代理是无法代理的。

这个时候就可以使用cglib去对类的字节码进行底层的继承代理,通过继承被代理对象。也就是JDKProxDynameic代理的接口CGlibProxDynameic代理的类

(如果类被final修饰就不能被代理成功)

AOP概念

AOP概念:面向切面编程,一种编程范式,知道开发者如何组织程序结构

Java是面向对象编程,这都是一种思想,一种编程形式

作用:

在不惊动原始设计的基础上为其进行功能增强(不改方法的源代码却可以使用到其他方法的功能) ->实现Spring倡导的:无入侵式编程

定义:

  • 我们在类里面实现的每一个功能(方法)叫做连接点
  • 如果我们将其他方法也需要的方法代码抽取出来做成一个方法去执行切入点,这个方法叫做通知
  • 单独抽取出来的方法不能独立存在,所以我们要写一个类专门存放这种方法(通知),类叫做通知类
  • 那么这些被追加其他功能的方法叫做切入点(一个或多个)
  • 通知肯定不止一个,那么如何确定通知与切入点的关系呢,这种关系叫做切面

注意:AOP代理类不能通过其实现类获取Bean,应该通过其接口获取

AOP工作流程:(本质:代理模式)

1.Spring容器启动

2.0

3.初始化bean,判定bean对应的类中方法是否匹配到任意切入点 匹配失败->创建原始对象(匹配失败说明不需要增强,直接调用原始对象的方法即可) / 匹配成功->创建原始对象(目标对象)的代理对象

4.获取bean执行方法 ->获取bean,调用方法并执行 *当获取的bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容

AOP切入点表达式

execution(void com.itheima.dao.Dao.daoAction3(int a))

        ↓           ↓                 ↓                          ↓                   ↓

原始方法    返回值         包名           接口/实现类        原始方法参数

可是,如果每一个切入点都写上这种表达式,过于繁琐

优化:使用通配符

*表示单个独立任意符号    execution(* com.itheima.dao.*.daoAction3(int a))
..表示多个任意符号      execution(* com.itheima.*.*Service.save(..))  //表示增强com.Zezai包下所有Service的save方法

AOP通知类型

1.前置通知 @Before

2.后置通知 @After

3.环绕通知(*) 前后都执行

@Around("pt()")
  public Object method(ProceedingJoinPoint pjp){ //如果没有使用该参数,将会直接跳过原始方法执行(不执行原始代码,而是执行通知的)
   System.out.println(before.....);
   Object ret=pjp.proceed();//在这个位置执行原方法(注意,proceed方法就是原方法,如果原方法有返回值,则该返回值就是proceed方法的返回值,记得最后要return给方法)
   System.out.println(after......);
   return ret;
   }

如何从AOP里获取原始方法参数?

->之前我们已经知道了如何在AOP里获取原始方法的返回值(即pjp.proceed的返回值),那么如果原始方法需要参数,那么在AOP里怎么将这个参数传给它呢

方法:通过在AOP里传入JoinPoint对象,调用它的get.Arg();

IOC拓展

核心概念

  • 目标:充分解耦
    • 使用IoC容器管理bean (IoC)
    • 在IoC容器内将有依赖关系的bean进行关系绑定(DI)
  • 最终效果
    • 使用对象时不仅可以直接从IoC容器中获取,并且获取到的bean已经绑定了所有的依赖关系

---------------------------------------------问题探讨与拓展------------------------------------------------------------

IOC/DI(目的就是为了充分解耦)

Mybatis开发时的现状:Service层里的一个方法对应Mapper层的一个接口方法,在调用mapper里方法时必须创建对象,如果一个已经部署的项目想修改Mapper层的方法时,

Service层里的对象也要进行修改再重新部署,耦合度较IO高

解决方案:

Service层不主动创建对象,直接使用"外部"提供的对象(这样一种思想称为IoC->控制反转)

->Spring为我们的思想进行了实现:Spring为我们提供了一个名为IoC的容器,充当上文的"外部"

  • IOC容器中不仅可以存放mapper对象,还可以存放Service对象(毕竟Controller层也需要创建service对象)
  • IOC容器负责对象的创建,初始化等一系列工作,容器内的对象统称为Bean

-------------------------------------------------------------------------------------------------------------------------

耦合度仍然较高的问题:

IoC在Controller层可以直接为我们提供service对象,Service层又为我们直接提供mapper对象,而一个service服务可能依赖一个mapper的方法也可能是多个;这样导致耦合度仍然较高

解决方案:我们事先将service对象与需要的mapper进行绑定(这样一种思想称为DI[Dependency Injection])->依赖注入),通过配置的方式

-------------------------------------------------------------------------------------------------------------------------

新的项目结构问题:为什么service层和dao层都要编写接口和实现类?

答:归根结底就是要解耦,直接写类固然会节省很多代码,但是灵活度没有前者高,如果是比较简单的业务需求(比如简单的增删查改),直接写会比较方便,若是比较复杂的业务逻辑,或者说用户的需求发生了很大的变化,我们与其修改整个类,不如修改接口的实现类,这样提高了效率。

-------------------------------------------------------------------------------------------------------------------------

Idea集成Spring->在pom.xml里添加Spring依赖即可

配置方法:要想把service和dao对象传给IoC管理,我们要先创建配置文件告诉Spring->在resources文件夹里创建applicationContext.xml->配置bean

使用方法:

1.获取IoC容器:ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml"); 参数里放入我们写好的配置文件"说明书"

2.获取bean:通过我们获取的容器,直接使用它的getBean(bean名)方法即可

Bean的别名:在bean标签里添加name字段即可,这样就可以在取出容器里的对象时自由度更高

正常来说,如果我们用两个对象接收同一个容器里的对象时地址是相同的(单例),这样做为了节省内存 使之不同的方法:在bean标签里加入scope字段,参数为prototype

-------------------------------------------------------------------------------------------------------------------------

适合交给容器进行管理的bean类型

1.表现层对象

2.业务层对象

3.数据层对象

4.工具类对象

不适合交给容器进行管理的bean类型:封装实体的域对象

-------------------------------------------------------------------------------------------------------------------------

实例化bean的方式(Spring如何获取IoC容器里的对象)

1.通过调用对应类的构造方法获取对象

2.静态工厂获取bean

-------------------------------------------------------------------------------------------------------------------------

bean生命周期的控制

先在实现类里编写好init和destroy方法,再通过配置文件里对应的bean标签里添加init-method和destroy-method字段,参数里填实现类里的方法名即可

在java虚拟机关闭之前需要先关闭容器,而Spring为我们提供了两种关闭方法:

  • 直接使用父类ClassPathXmlApplicationContext的close方法强制关闭
  • 在获取容器后,使用registerShutdownHook()方法添加容器钩子,它会使虚拟机关闭前自动关闭容器
  • 更好的方法:在Service里直接实现InitializingBean,DisposableBean类,重写他们的方法即可

-------------------------------------------------------------------------------------------------------------------------

Bean初始化的步骤:

  • 初始化容器
    • 1.创建对象
    • 2.执行构造方法
    • 3.执行属性注入(执行构造的Set方法)
    • 4.执行bean初始化方法()->InitializingBean类的afterPropertiesSet()方法
  • 使用bean:指向业务操作
  • 关闭/销毁容器:执行bean的销毁方法

-------------------------------------------------------------------------------------------------------------------------

依赖注入的方式

1.普通方式(编写set方法)->setter注入(基本数据类型/引用数据类型)推荐!!!!!!!

setter注入基本数据类型时,只需要在实现类定义好属性名,写好set方法,并在配置文件里写好对应的标签,将原来的ref改为value即可

引用数据类型前文已讲

2.构造方法->构造器注入(基本数据类型/引用数据类型)->在实现类里添加一个构造器(有参构造->参数就是需要注入的对象),构造体里的内容也和setter注入一致。

不过在配置文件里不再用标签,而是用,字段名一样是name,ref(基本)/value(引用)

-------------------------------------------------------------------------------------------------------------------------

配置Bean的思考

虽然通过配置文件的形式吧对象之间的关系进行了绑定,但是如同的书写方式

耦合度仍然很高,如果实现类参数发生了改变,配置文件也得变,所以有人提出了方案 (直接不写参数名,用类型来辨别)

虽然解决了形参变名的耦合性问题,但如果有两个同类型的参数时也无法使用,所以需要更好的方案.

↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓ ↓↓

依赖的自动装配(只针对引用数据类型装配)

如果我们可以不用自己动手去配置bean的注入的话(也就是ref/value参数的值)可以提高很多效率

->为此Spring为我们提供了依赖的自动装配机制

-------------------------------------------------------------------------------------------------------------------------

自动装配的方式:仍然需要我们提供set方法,但就不需要property标签了

1.按类型(常用)

2.按名称(不推荐)

->想要ServiceImpl里自动装填DaoImpl对象的话只需在serviceImpl里添加autowire标签即可

注意:一般来说,一个bean标签对应一个实现类(DaoImpl),所以按类型type装配是有效的 即一个id参数对应一个class参数

如果一个实现类出现了两个bean标签的话,需要按名称id装配 即两个id参数都对应一个class参数 (但是id后的参数必须和实现类里的对象名保持一致)

当自动装配和setter注入/构造器注入同时存在时,系统会默认普通注入,自动装配将会失效

-------------------------------------------------------------------------------------------------------------------------

思考:我们已经会注入引用数据类型和基本数据类型,他们都只是单一的数据,那如果我们需要注入集合,该怎么操作?(用的较少)

答:和之前的两种注入类型方法大差不差,同样要在实现类里创建成员变量以及对应的set方法,并在配置文件中添加标签,但里面的格式需要注意(以数组array为例)

   (这个是我们自己定义的在实现类里的数组名)
      (这个是标签类型)
          100
          200
          300
      
   

-------------------------------------------------------------------------------------------------------------------------

第三方资源配置管理

->我们不仅可以通过IoC管理我们自己创建的实现类对象,同时第三方来的对象同样可以进行管理

方法: //添加druid数据源数据进行管理

    
         //因为druid没有提供构造器,所以只能用setter注入(我们需要填上连接数据库的基本信息)
        
        
        
        
    

//添加其他数据源的格式一样,参数不同,可以在网上查找资料

优化:如果将这些数据库连接信息全写在配置文件里不易阅读,所以我们可以将标签里的信息抽取出来全部放到一个单独的文件jdbc.properties里

方法:在Spring内开辟空间,使其可以加载到jdbc.properties这个文件(在配置文件里配置)

-------------------------------------------------------------------------------------------------------------------------

容器补充知识点(了解)

1.创建容器方式:->加载类路径下的配置文件(通过写入配置文件.xml方式)(常用)

->通过文件加载的方式

ClassPathXmlApplicationContext ctx=new FileSystemXmlApplicationContext("绝对路径")

ApplicationContext接口使Spring容器的核心接口,初始化时Bean立即加载

ClassPathXmlApplicationContext是它的实现类

你可能感兴趣的:(学习笔记,spring,java,学习,笔记)