`

Spring之AOP篇

阅读更多

Spring之AOP篇:

AOP框架是Spring的一个重要组成部分.但是Spring IOC 并不依赖于AOP,这就意味着你有权力选择是否使用AOP,AOP作为Spring IOC容器的一个补充,使它成为一个强大的中间件解决方案。

一、AOP(Aspect-Oriented Programming)

    AOP的目标:横切关注点  这种问题用面向对象很难解决

  AOP是用来解决什么问题的呢?
     横越多个模块的业务关注点,比如记录日志,事务管理
 1、AOP的解决方案——拦截
   1)由谁来拦截?--代理类Proxy 和本类实现同一个接口
          就像是这样:action发出命令--代理类(处理一些操作)--本类
   2)拦截的过程:
       I.将Proxy(如UserServiceProxy)交给调用者(如UserAction),调用者调用需要的方法
       II.Proxy先拦截该方法,执行拦截逻辑
       III.然后执行调用者真正的实现类(如UserServiceImp)
   3)核心概念:
     a、代理对象Proxy:是核心,负责拦截,由工具自动创建
     b、拦截器(Interceptor):实现拦截逻辑的对象
     c、目标对象(Target):是Proxy所代理的对象,是已有的类
  以上三个类实现了一个“金三角”
     Proxy--拦截-->Interceptor--实现拦截逻辑--处理目标对象-->Target
 2、代理如何实现?
      有两种方式:
        1)JDK动态代理(使用这个比较多)
            由JDK自带的动态代码生成技术,可以对实现了接口的类进行处理
        2)CGLib
     对没有实现任何接口的类进行处理
 这两种方式的共同点:(都是在后台自动生成的)
  在程序运行期间,动态的生成代码并进行动态编译和加载

     
    问题:Spring的AOP是如何实现代理的?是自己实现的吗?
        注:Spring借用了JDK动态代理和CGLib来实现代理对象。
     Spring进行如下判断来实现代理:
  i、如果目标类实现了接口,Spring则调用JDK动态代理;
  ii、反之,调用CGLib
     
      Proxy是核心:
      创建Proxy的方法:ProxyFactoryBean
 <bean id="arithProxy"
         class="org.springframework.aop.framework.ProxyFactoryBean">
       <property name="target" ref="arith"/>
       <property name="interceptorNames">
       <list>
        <!--<value>logBefore</value>
           <value>logAfter</value>
           <value>logThrows</value> -->
       <value>logAround</value>
      </list>
     </property>

      JDK动态代理的实现方式:(工厂模式)
  UserDao userDao = new UserDaoImp();
  UserDao proxy = (UserDao)
  Proxy.newProxyInstance(userDao.getClass().getClassLoader(),
    userDao.getClass().getInterfaces(),
    new LogHandler(userDao));

二、Spring中的AOP方式(在Spring中将拦截称为通知)
   拦截逻辑放在Advice之中,Advice按时间将拦截器分为四类:
      1)Before Advice:方法执行之前进行拦截
    public class LogBeforeAdvice implements MethodBeforeAdvice{

 //Method method拦截的方法
 //Object[] args 方法中的参数
 //target 目标对象
 @Override
 public void before
 (Method method, Object[] args, Object target)
   throws Throwable {
  MyLogger logger = new MyLogger();
  System.out.println("Methods:"+method.getName()+
    "  begins, args:"+Arrays.toString(args));
 }
}
    2)After Advice:方法执行之后进行拦截
  public class LogAfterAdvice implements AfterReturningAdvice{
 @Override
 public void afterReturning(Object resultValue, Method method, Object[] args,
   Object target) throws Throwable {
  MyLogger logger = new MyLogger();
  System.out.println("Methods:"+method.getName()+
    "  ends, result:"+resultValue);
 }
}
   3)AfterThrowing Advice:方法异常结束之后进行拦截
  public class LogThrowingAdvice implements ThrowsAdvice {
 
 //ThrowsAdvice 没有方法,但方法名必须是afterThrowing
 public void afterThrowing(IllegalArgumentException e)
 throws Throwable{
  MyLogger logger = new MyLogger();
  logger.log("IllegalArgumentException!");
 }
}
   4)Around Advice:环绕通知
  public class LogAroundAdvice implements MethodInterceptor {

 //MethodInvocation相当于Method的包装
 @Override
 public Object invoke(MethodInvocation methodInvocation)
 throws Throwable {
  MyLogger logger = new MyLogger();
  try {
   //methodInvocation.getMethod()获得方法
   //methodInvocation.getArguments()获得方法的参数
   logger.log("before:"+
     methodInvocation.getMethod().getName()+
     ", args:"+methodInvocation.getArguments());
   
   //在环绕通知里面,必须执行目标方法!!!!!!!!!!!!!!
   Object result = methodInvocation.proceed();
   logger.log("ends:"+
     methodInvocation.getMethod().getName());
   return result;
  } catch (Exception e) {
   logger.log("Exception:"+e.getMessage());
   throw e;
  }
 }

  注意:虽然环绕通知包含了另外三种,但还是要依据业务逻辑来选择,这样有利于代码的编程量
       并且环绕通知最好不要和另外三种混用,并且并许执行目标方法proceed().

 也可以大致分成两组——
 i、BeforeAdvice, AfterReturning, Throwing
 ii、AroundAdvice:需要在内部负责调用目标类的方法。
      注意:AroundAdvice应单独使用


<!-- Target Object -->
<bean id="arith" class="myspring.calculator.ArithmeticCalculatorImp"></bean>

<!-- Advice -->
<bean id="logBefore" class="myspring.aop.LogBeforeAdvice"/>
<bean id="logAfter" class="myspring.aop.LogAfterAdvice"/>
<bean id="logThrows" class="myspring.aop.LogThrowingAdvice"/>
<bean id="logAround" class="myspring.aop.LogAroundAdvice"/>

<!-- Proxy -->
<bean id="arithProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="arith"/>
<property name="interceptorNames">
  <list>
    <!-- <value>logBefore</value>
    <value>logAfter</value>
    <value>logThrows</value> -->
    <value>logAround</value>
  </list>
</property>

</bean>

在test类中这样用:ApplicationContext ac =
   new ClassPathXmlApplicationContext("aop-base.xml");
  IArithmeticCalculator proxy =
   (IArithmeticCalculator)ac.getBean("arithProxy");

 

 面试中可能会问到:
          proxyTargetClass属性
  将其加入到代理bean中去,则Spring将调用CGLib来实现创建Proxy对象。
                   (无论Target有无实现接口)
      形如
     <bean id="" class="....ProxyFactoryBean">
    .....
       <property name="proxyTargetClass" value="true" />
     </bean>
 三、Spring AOP基本方式  AspectJ Spring2.5之后用的

Spring对AspectJ的支持
      应用于有多个target,不想一个一个的配置时,用AspectJ     
1)POJO类(Aspect类),使用@Aspect 
  基本信息:Advice和Pointcut  
2)定义Bean:在Bean定义文件中定义这个Aspect类 
3)启用Spring的自动代理
  <aop:aspectj-autoproxy />  

   1、JoinPoint(连接点):被拦截的方法
                程序执行过程中的点,就好像方法中的一个调用,
  或者一个特别的被抛出的异常
               在Spring AOP中,一个连接点通常是方法调用.
   Spring并不明显地使用"连接点"作为术语,
  连接点信息可以通过MathodInvocation的参数穿过拦截器访问,
  相当于实现org.springframework.aop.Pointcut接口.
 2、JoinPoint对象
  获取连接点信息(获取目标方法以及目标对象的信息) 
 1)目标方法:
  i、getSignature():方法签名
    joinPoint.getSignature().getName()
  
  ii、getArgs():方法的实参(方法的实际参数)
                  joinPoint.getArgs()
  打印输出时:Arrays.toString(joinPoint.getArgs())
 2)目标对象:  getTarget()
    目标对象实际类型:getTarget().getClass()

 3、PointCut(切入点):一组连接点。
                作用:定义了一组被拦截的方法 
  PointCut(切入点):当一个通知被激活的时候,
  会指定一些连接点.一个AOP框架必须允许开发者指定切入点。
  一个AOP框架必须允许开发者指定切入点:例如使用正则表达式.

  只有引入了切入点,才能很好的进行拦截

  PointCut的意义:可以一次性定义多个类的
    多个方法作为拦截目标(即JoinPoint)
  PointCut代表了Target 一组Target组成了PointCut
  一组Target可以理解为一个类的多个方法,或者多个类的多个方法
  代理、拦截器、目标

  PointCut
   在AspectJ中,切入点是用PointCut Expression来
   定义的。(切入点表达式可以理解为过滤条件)
   
   PointCut表达式
    格式  execution(表达式)
    表达式  一个表达式就是一个匹配规则
     * myspring.calculator.IArithmeticCalculator.add(..)
  *理解为public void myspring.calculator.IArithmeticCalculator.add(int a,int b)
  * 取代public void 通配一切字符   
   Spring自动代理(即Spring自动生成Proxy对象)
    用<aop:aspectj-autoproxy />来启用

 Pointcut表达式语法
 1)方法级别  —— execution
  execution(* *.*(..)) :所有类的方法
  execution(public * somepackage.SomeClass.someMethod(..))
  总结:execution是对方法签名进行匹配
 2)对象级别(类级别)—— within
 within(somepackage.*):somepackage包下所有的类
 within(com.dang.service.*Service)
 即 com.dang.service包下所有以Service结尾的类
 within(somepackage..*):somepackage以及所有子包中的类
 within(somepackage.SomeInterface+):SomeInterface接口的所有实现类
 总结:within是对类名(带包名)进行匹配。 拦截这个类的所有方法 


4、Aspect类的组成  一般都用注解 重点
  1)Advice(AspectJ中的通知类型)
   i、Before
   //@Before("LogPointcuts.logAdd()") 方法1
            @Before("execution(* *.*(..))") 方法2
         public void logBefore(JoinPoint joinPoint){
     logger.log("{before} the method: "
       +joinPoint.getSignature().getName()+
       "  args:"+Arrays.toString(joinPoint.getArgs())); 
 }
             注:方法1和方法2都是可以的,方法1的话需要在定义一个方法.
  在同一个类或不同的类都是可以的 直接调用这个方法
                  @Pointcut("execution(* *.*(..))")
          public void logOperation(){}
   ii、AfterReturning
  @AfterReturning(pointcut="execution(* *.*(..))",
   returning="result")
    public void afterReturning(JoinPoint joinPoint,Object result){
   logger.log("{AfterReturning} the method: "
        +joinPoint.getSignature().getName()
        +" result:"+result); 
 }
    注意:方法有两个参数,在注解中的returning的值必须和Object的值完全一样
   iii、AfterThrowing
  @AfterThrowing(pointcut="execution(* *.*(..))",
   throwing="e")
    public void afterThrowing(JoinPoint joinPoint,IllegalArgumentException e){
  logger.log("{AfterThrowing} the method: "
       +joinPoint.getSignature().getName()
       +" e:"+e.getMessage());
 }
   iv、After:相当于finally,它在AfterReturning
     和AfterThrowing之后执行。
     它的作用——一般为释放资源(如关闭Connection)
  @After("execution(* *.*(..))")
 public void after(JoinPoint joinPoint){
  logger.log("{After} method:"+joinPoint.getSignature().getName());
 }

  注意:After是肯定是要在最后执行的,
   v、Around
  @Around("execution(* *.*(..))")
 public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable{
  logger.log("{Arount}Before method:"
    +joinPoint.getSignature().getName());
  try{
                       //一定不要忘记调用目标方法
   Object result = joinPoint.proceed();
   
   logger.log("{Arount}After method:"
     +joinPoint.getSignature().getName()
     +" result:"+result);
         //返回目标方法的返回值
   return result;
  }
  catch(IllegalArgumentException e){
   logger.log("{Around}Exception:Illegal Argument"
     +Arrays.toString(joinPoint.getArgs()));
   throw e;
  }
  注意:Around最好不要和其他四种同时使用

  把处理日志的代码放到一起就是切面,模块化(Aspect)

  5.Introduction(引入)
   1)Introduction的定义
  给正在运行的程序中的对象添加方法。
   其实是一种动态技术
   2)问题:如何给ArithmeticCalculatorImp类加入
   求最大值和最小值的方法
   假设:最大值和最小值的方法已经存在,且分别属于不同的类。
 extends MaxCalculatorImp, MinCalculatorImp 不行,java不允许多继承
   3)要点:
 a、实现一个POJO类
  定义@DeclareParents来"继承"的父类
         @DeclareParents(
  value="myspring.calculator.ArithmeticCalculatorImp",
  defaultImpl=MaxCalculatorImp.class
 )
 private MaxCalculator maxCalculator;
 
 @DeclareParents(
   value="myspring.calculator.ArithmeticCalculatorImp",
   defaultImpl=MinCalculatorImp.class
  )
  private MinCalculator minCalculator;   
 b、定义这个Bean:在Bean定义文件中声明一下。
         <bean class="myspring.calculator.CalculatorIntroduction"></bean>
    在test类中测试时:ApplicationContext ac =
  new ClassPathXmlApplicationContext("aop.xml");
  
  IArithmeticCalculator cal = (IArithmeticCalculator)
  ac.getBean("arith");
  cal.add(2, 3);
  cal.div(4, 3);
  
  MaxCalculator max = (MaxCalculator)cal;
  max.max(3, 6);
  MinCalculator min = (MinCalculator)cal;
  min.min(3, 6);

分享到:
评论

相关推荐

    死磕Spring之AOP篇 - Spring AOP两种代理对象的拦截处理(csdn)————程序.pdf

    Spring AOP 是一种面向切面编程的技术,它允许我们在不修改源代码的情况下,对应用程序的特定部分(如方法调用)进行增强。在 Spring 中,AOP 的实现主要依赖于代理模式,有两种代理方式:JDK 动态代理和 CGLIB 动态...

    Spring_AOP_学习小结 Spring_AOP_学习小结 Spring_AOP_学习小结

    Spring AOP,即面向切面编程,是Spring框架的核心组件之一,它允许程序员在不修改原有业务代码的情况下,对程序进行功能增强。本篇文章将详细阐述Spring AOP的基本概念、种类、代理原理、通知类型以及切入点,帮助你...

    Spring实现AOP的4种方式

    本篇文章将详细探讨Spring实现AOP的四种主要方法:基于代理的方式、基于AspectJ的注解方式、基于XML的AOP配置以及基于Java的AOP配置。 1. 基于代理的实现 Spring的AOP支持两种代理类型:JDK动态代理和CGLIB代理。...

    spring-aop AND proxy

    标签“源码”意味着这篇博客可能深入解析了Spring AOP的内部工作机制,包括如何通过代理机制实现切面的织入,以及Spring AOP的相关核心类如`Advised`、`ProxyFactoryBean`、`DefaultAdvisorAdapterRegistry`等。...

    Spring之AOP注解之引入通知

    本篇文章将深入探讨Spring AOP中的注解引入通知,以及如何利用它们来增强代码的可维护性和模块化。 首先,我们要理解什么是“引入通知”(Introduction Advice)。引入通知是Spring AOP的一种特殊类型的通知,它...

    spring注解aop配置详解

    本篇将深入讲解如何通过注解来配置Spring AOP,以实现更加简洁、高效的代码编写。 首先,我们来看注解在Spring AOP中的应用。在传统的AOP配置中,我们需要定义切入点表达式和通知(advice)在XML配置文件中。然而,...

    使用Spring配置文件实现AOP

    这篇教程将详细讲解如何通过Spring的配置文件来实现AOP。 一、理解AOP概念 AOP的核心思想是将分散在各个模块中的交叉性代码(如日志、事务处理)抽取出来,形成独立的切面,以便于复用和维护。它提供了一种模块化的...

    以注解方式模拟Spring IoC AOP

    本篇将深入探讨如何通过注解方式来模拟Spring的这两种机制,帮助你理解其底层原理。 ### 1. 依赖注入(IoC) 依赖注入是Spring框架的核心特性之一,它通过反转对象创建和管理的控制权,使得应用程序组件之间的耦合...

    Spring-AOP需要的外部包

    本篇将详细解析标题“Spring-AOP需要的外部包”以及描述中提及的两个关键包:`aspectjweaver.jar`和`aspectjrt.jar`。 首先,我们需要了解AOP的基本概念。面向切面编程(Aspect Oriented Programming,AOP)是一种...

    Spring-AOP-JDK动态代理

    在Java编程领域,Spring框架是应用最广泛的轻量级开源框架之一,它提供了一系列强大的功能,包括依赖注入、面向切面编程(AOP)等。本篇将详细讲解Spring中的AOP实现,特别是JDK动态代理的应用。 首先,我们要了解...

    Spring之AOP在鉴权和日志记录中的应用

    本篇将深入探讨如何利用Spring AOP来实现鉴权和日志记录。 **二、AOP基础知识** 1. **切面(Aspect)**:AOP的核心概念,它封装了关注点,如日志记录或权限验证。一个切面通常包含一个或多个通知(advice)。 2. ...

    研究下Spring中AOP的实现?

    本篇文章将深入探讨Spring AOP的实现原理,并通过一个名为`myAOPExample`的示例来阐述其工作方式。 首先,理解AOP的基本概念至关重要。AOP的核心是切面(Aspect)、通知(Advice)、连接点(Join Point)、切点...

    Spring AOP依赖jar包

    本篇文章将详细介绍 Spring AOP 的核心概念、如何配置以及所依赖的 Jar 包,特别是 `AspectJ 1.6.12` 版本。 1. **AOP 概念** - **切面(Aspect)**:切面是关注点的模块化,如日志、事务管理等,它们横切多个对象...

    初探spring aop内部实现 java

    本篇文章将深入探讨Spring AOP的内部实现,以及如何通过源代码理解其DataSource实现和FactoryBean模式。 首先,让我们了解AOP的基本概念。AOP的核心思想是“切面”,它封装了特定的关注点,如日志记录、事务管理、...

    spring对AOP的支持(使用Spring的配置文件来演示)

    Spring框架是Java领域中极为重要的一个组件,它为开发者提供了许多便利,其中之一就是对面向切面编程(Aspect Oriented Programming,简称AOP)的支持。AOP允许我们分离关注点,将横切关注点(如日志、事务管理等)...

    Spring中的AOP不生效

    ### Spring中的AOP不生效的原因及解决方法 在Java开发中,面向切面编程(Aspect Oriented Programming,简称AOP)是一种重要的编程思想和技术手段,主要用于处理横切关注点问题,如日志记录、性能统计、安全控制、...

    Spring基础:AOP编程(4)

    在本篇博客“Spring基础:AOP编程(4)”中,我们将深入探讨Spring框架中的面向切面编程(Aspect-Oriented Programming,简称AOP),这是一个强大的功能,它允许我们在不修改原有业务代码的情况下,实现对系统中横切...

    Spring aop 性能监控器

    本篇文章将深入探讨如何使用Spring AOP实现性能监控器,并通过源码分析来理解其工作原理。 首先,我们要了解AOP的核心概念——切面(Aspect)、通知(Advice)、连接点(Join Point)、切入点(Pointcut)和织入...

    spring-aop.rar

    本篇将深入探讨Spring AOP的核心概念、工作原理以及实际应用。 一、Spring AOP基础 1. 切面(Aspect):切面是程序中的一个关注点,如日志、事务处理,它跨越多个对象。在Spring AOP中,切面由通知(Advice)和...

    Spring编程(AOP篇实例)

    在Spring框架中,AOP(面向切面编程)是一种强大的设计模式,它允许开发者将关注点从核心业务逻辑中分离出来,比如日志记录、事务管理、权限控制等。本实例将深入探讨如何在Spring中实现AOP,帮助你更好地理解和应用...

Global site tag (gtag.js) - Google Analytics