Spring 学习笔记 IOC AOP

Spring的核心架构图

什么是IoC?

IoC Inversion of Control (控制反转),注意它是⼀个技术思想,不是⼀个技术实现
描述的事情:Java开发领域对象的创建,管理的问题

实例化Bean的三种方式?

方式一: 使用无参构造。在默认情况下,它会通过反射调⽤⽆参构造函数来创建对象。如果类中没有⽆参构造函数,将创建失败。

<!--配置service对象-->
<bean id="userService" class="com.lagou.service.impl.TransferServiceImpl"> </bean> 

⽅式⼆:使⽤静态⽅法创建

<!--使⽤静态⽅法创建对象的配置⽅式-->
<bean id="userService" class="com.lagou.factory.BeanFactory"
    factory-method="getTransferService"></bean>    

⽅式三:使⽤实例化⽅法创建 

此种⽅式和上⾯静态⽅法创建其实类似,区别是⽤于获取对象的⽅法不再是static修饰的了,⽽是类中的⼀ 个普通⽅法。此种⽅式⽐静态⽅法创建的使⽤⼏率要⾼⼀些。 在早期开发的项⽬中,⼯⼚类中的⽅法有可能是静态的,也有可能是⾮静态⽅法,当是⾮静态⽅法时,即可 采⽤下⾯的配置⽅式

<bean id="beanFactory" 
class="com.lagou.factory.instancemethod.BeanFactory"></bean> <bean id="transferService" factory-bean="beanFactory" factory-method="getTransferService"></bean>

 

Bean的作用范围及生命周期

作⽤范围的改变 在spring框架管理Bean对象的创建时,Bean对象默认都是单例的,但是它⽀持配置的⽅式改变作⽤范围。作⽤范围官⽅提供的说明如下图:

<!--配置service对象-->
<bean id="transferService" 
class="com.lagou.service.impl.TransferServiceImpl" scope="singleton"> </bean>

单例模式:singleton
对象出⽣:当创建容器时,对象就被创建了。 对象活着:只要容器在,对象⼀直活着。
对象死亡:当销毁容器时,对象就被销毁了。 ⼀句话总结:单例模式的bean对象⽣命周期与容器相同。

多例模式:prototype 对象出⽣:当使⽤对象时,创建新的对象实例。
对象活着:只要对象在使⽤中,就⼀直活着。 对象死亡:当对象⻓时间不⽤时,被java的垃圾回收器回收了。
⼀句话总结:多例模式的bean对象,spring框架只负责创建,不负责销毁。 Bean标签属性

依赖注入

方式一:构造函数注入,类中提供的构造函数参数个数必须和配置的参数个数⼀致,且数据类型匹配。同时需要注意的是,当没有⽆参构造时,则必须提供构造函数参数的注⼊,否则Spring框架会报错。

<bean id ="userDao" class="com.lgl.study.dao.impl.UserDaoImpl" scope="singleton">
   <!-- 按照参数名称-->
    <constructor-arg name="connectionUtils" ref="connectionUtils">
    <constructor-arg name="money" value="100.6">

   <!-- 按照参数位置-->
    <constructor-arg index="1" ref="connectionUtils">
    <constructor-arg index="2" value="100.6">
</bean>

方式二:使用set方法注入

<bean id="userService" class="com.lgl.study.impl.UserServiceImpl">
    <property name="userDao" ref="userDao"></property>
</bean>

 复杂类型属性注入

 

 

方式三: 注解注入

@Autowired
@Qualifier("userDAO")
private UserDAO userDAO;

 

后置处理器

Spring提供了两种后处理bean的扩展接⼝,分别为 BeanPostProcessor 和BeanFactoryPostProcessor,两者在使⽤上是有所区别的。 ⼯⼚初始化(BeanFactory)—> Bean对象 在BeanFactory初始化之后可以使⽤BeanFactoryPostProcessor进⾏后置处理做⼀些事情 在Bean对象实例化(并不是Bean的整个⽣命周期完成)之后可以使⽤BeanPostProcessor进⾏后置处理做⼀些事情。

 IOC容器初始化主流程

核心方法:AbstractApplicationContext.refresh()

1. 刷新前的预处理   prepareRefresh();

2. 获取BeanFactory;默认实现是DefaultListableBeanFactory 加载BeanDefition 并注册到 BeanDefitionRegistry  ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

3. BeanFactory的预准备⼯作(BeanFactory进⾏⼀些设置,⽐如context的类加载器等)prepareBeanFactory(beanFactory);

4. BeanFactory准备⼯作完成后进⾏的后置处理⼯作 postProcessBeanFactory(beanFactory);

5. 实例化并调⽤实现了BeanFactoryPostProcessor接⼝的Bean   invokeBeanFactoryPostProcessors(beanFactory);

6. 注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执⾏ registerBeanPostProcessors(beanFactory);

7. 初始化MessageSource组件(做国际化功能;消息绑定,消息解析) initMessageSource();

8. 初始化事件派发器 initApplicationEventMulticaster();

9. ⼦类重写这个⽅法,在容器刷新的时候可以⾃定义逻辑   onRefresh();

10.注册应⽤的监听器。就是注册实现了ApplicationListener接⼝的监听器 bean  registerListeners();

11. 初始化所有剩下的⾮懒加载的单例bean 初始化创建⾮懒加载⽅式的单例Bean实例(未设置属性)填充属性初始化⽅法调⽤(⽐如调⽤afterPropertiesSet⽅法、init-method⽅法)

调⽤BeanPostProcessor(后置处理器)对实例bean进⾏后置处  finishBeanFactoryInitialization(beanFactory);

12. 完成context的刷新。主要是调⽤LifecycleProcessor的onRefresh()⽅法,并且发布事件  finishRefresh();

 

什么是AOP

AOP:Aspect oriented Programming ⾯向切⾯编程/⾯向⽅⾯编程

解决的问题: 在不改变原有业务逻辑情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复。

为什么叫面向切面编程:

「切」:指的是横切逻辑,原有业务逻辑代码我们不能动,只能操作横切逻辑代码,所以⾯向横切逻辑 「⾯」:横切逻辑代码往往要影响的是很多个⽅法,每⼀个⽅法都如同⼀个点,多个点构成⾯,有⼀个⾯的概念在⾥⾯

 

 

 

AOP术语:

Joinpoint(连接点):它指的是那些可以⽤于把增强代码加⼊到业务主线中的点,这些点指的就是⽅法。在⽅法执⾏的前后通过动态代理技术加⼊增强的代码。在Spring框架AOP思想的技术实现中,也只⽀持⽅法类型的连接点。

Pointcut(切⼊点):它指的是那些已经把增强代码加⼊到业务主线进来之后的连接点。由上图中,我们看出表现层 transfer⽅法就只是连接点,因为判断访问权限的功能并没有对其增强。

Advice(通知/增强):它指的是切⾯类中⽤于提供增强功能的⽅法。并且不同的⽅法增强的时机是不⼀样的。⽐如,开启事务肯定要在业务⽅法执⾏之前执⾏;提交事务要在业务⽅法正常执⾏之后执⾏,⽽回滚事务要在业务⽅法执⾏产⽣异常之后执⾏等等。那么这些就是通知的类型。其分类有:前置通知 后置通知 异常通知 最终通知 环绕通知。

Target(⽬标对象):它指的是代理的⽬标对象。即被代理对象。

Proxy(代理):它指的是⼀个类被AOP织⼊增强后,产⽣的代理类。即代理对象。

Weaving(织⼊):它指的是把增强应⽤到⽬标对象来创建新的代理对象的过程。spring采⽤动态代理织⼊,⽽AspectJ采⽤编译期织⼊和类装载期织⼊。

Aspect(切⾯): 它指定是增强的代码所关注的⽅⾯,把这些相关的增强代码定义到⼀个类中,这个类就是切⾯类。例如,事务切⾯,它⾥⾯定义的⽅法就是和事务相关的,像开启事务,提交事务,回滚事务等等,不会定义其他与事务⽆关的⽅法。我们前⾯的案例中 TrasnactionManager就是⼀个切⾯。

 

实现方式:

Spring 实现AOP思想使⽤的是动态代理技术
默认情况下,Spring会根据被代理对象是否实现接⼝来选择使⽤JDK还是CGLIB。当被代理对象没有实现任何接⼝时,Spring会选择CGLIB。当被代理对象实现了接⼝,Spring会选择JDK官⽅的代理技术,不过我们可以通过配置的⽅式,让Spring强制使⽤CGLIB。

AOP核心配置及应用

<!--Spring基于XML的AOP配置前期准备:
  在spring的配置⽂件中加⼊aop的约束
  xmlns:aop="http://www.springframework.org/schema/aop"
  http://www.springframework.org/schema/aop   
  https://www.springframework.org/schema/aop/spring-aop.xsd

  Spring基于XML的AOP配置步骤:
  第⼀步:把通知Bean交给Spring管理第⼆步:使⽤aop:config开始aop的配置第三步:使⽤aop:aspect配置切⾯
  第四步:使⽤对应的标签配置通知的类型
  ⼊⻔案例采⽤前置通知,标签为aop:before
 -->

 <bean id="logUtil" class="com.lagou.utils.LogUtil"></bean>
 <!--开始aop的配置-->
 <aop:config>
 <!--配置切⾯-->
    <aop:aspect id="logAdvice" ref="logUtil">   <!--配置前置通知-->
        <aop:before method="printLog" pointcut="execution(public *com.lagou.service.impl.TransferServiceImpl.updateAccountByCardNo(com.lagou .pojo.Account))"></aop:before>
    </aop:aspect>  
 </aop:config>
execution表达式使用示例
全限定⽅法名 访问修饰符  返回值  包名.包名.包名.类名.⽅法名(参数列表) 全匹配⽅式:
public void com.lagou.service.impl.TransferServiceImpl.updateAccountByCardNo(com.lagou.pojo.Account)

访问修饰符可以省略
void com.lagou.service.impl.TransferServiceImpl.updateAccountByCardNo(com.lagou.pojo.Account)

返回值可以使⽤*,表示任意返回值
com.lagou.service.impl.TransferServiceImpl.updateAccountByCardNo(com.lagou.pojo.Account)

包名可以使⽤.表示任意包,但是有⼏级包,必须写⼏个
....TransferServiceImpl.updateAccountByCardNo(com.lagou.pojo.Account)

包名可以使⽤..表示当前包及其⼦包
..TransferServiceImpl.updateAccountByCardNo(com.lagou.pojo.Account )

类名和⽅法名,都可以使⽤.表示任意类,任意⽅法* ...(com.lagou.pojo.Account)
参数列表,可以使⽤具体类型
基本类型直接写类型名称 : 
引⽤类型必须写全限定类名:java.lang.String
参数列表可以使⽤*,表示任意参数类型,但是必须有参数
*    *..*.*(*)
参数列表可以使⽤..,表示有⽆参数均可。有参数可以是任意类型
*    *..*.*(..)
全通配⽅式:
* *..*.*(..)

 五种通知配置

1. aop:before标签 前置通知永远都会在切⼊点⽅法(业务核⼼⽅法)执⾏之前执⾏。前置通知可以获取切⼊点⽅法的参数,并对其进⾏增强。

<!--作⽤:⽤于配置前置通知。出现位置:它只能出现在aop:aspect标签内部
属性: method:⽤于指定前置通知的⽅法名称 pointcut:⽤于指定切⼊点表达式 pointcut
-ref:⽤于指定切⼊点表达式的引⽤ --> <aop:before method="printLog" pointcut-ref="pointcut1"> </aop:before>

2.正常执⾏时通知

<!--作⽤:⽤于配置正常执⾏时通知出现位置:它只能出现在aop:aspect标签内部
属性:
method:⽤于指定后置通知的⽅法名称
pointcut:⽤于指定切⼊点表达式
pointcut-ref:⽤于指定切⼊点表达式的引⽤
-->
<aop:after-returning method="afterReturningPrintLog" pointcutref="
pt1"></aop:after-returning>

3. 异常通知 执⾏时机是在切⼊点⽅法(业务核⼼⽅法)执⾏产⽣异常之后,异常通知执⾏。如果切⼊点⽅法执⾏没有产⽣异常,则异常通知不会执⾏。

异常通知不仅可以获取切⼊点⽅法执⾏的参数,也可以获取切⼊点⽅法执⾏产⽣的异常信息。

<!--
作⽤:⽤于配置异常通知。出现位置:它只能出现在aop:aspect标签内部
属性:
method:⽤于指定异常通知的⽅法名称
pointcut:⽤于指定切⼊点表达式
pointcut-ref:⽤于指定切⼊点表达式的引⽤
-->
<aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"
></aop:after-throwing>

4. 最终通知  

最终通知的执⾏时机是在切⼊点⽅法(业务核⼼⽅法)执⾏完成之后,切⼊点⽅法返回之前执⾏。换句话说,⽆论切⼊点⽅法执⾏是否产⽣异常,它都会在返回之前执⾏。

最终通知执⾏时,可以获取到通知⽅法的参数。同时它可以做⼀些清理操作。

<!--作⽤:⽤于指定最终通知。出现位置:它只能出现在aop:aspect标签内部
属性:
method:⽤于指定最终通知的⽅法名称
pointcut:⽤于指定切⼊点表达式
pointcut-ref:⽤于指定切⼊点表达式的引⽤
-->
<aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after>

5. 环绕通知 

环绕通知,它是有别于前⾯四种通知类型外的特殊通知。前⾯四种通知(前置,后置,异常和最终)它们都是指定何时增强的通知类型。⽽环绕通知,它是Spring框架为我们提供的⼀种可以通过编码的

⽅式,控制增强代码何时执⾏的通知类型。它⾥⾯借助的ProceedingJoinPoint接⼝及其实现类,实现⼿动触发切⼊点⽅法的调⽤

<!--作⽤:⽤于配置环绕通知。出现位置:它只能出现在aop:aspect标签的内部
属性:
method:⽤于指定环绕通知的⽅法名称
pointcut:⽤于指定切⼊点表达式
pointcut-ref:⽤于指定切⼊点表达式的引⽤
-->
<aop:around method="aroundPrintLog" pointcut-ref="pt1"></aop:around>

 

posted on 2020-09-27 16:42  lvguoliang(学无止境)  阅读(170)  评论(0)    收藏  举报