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(转)的更多相关文章

  1. Spring基于AOP的事务管理

                                  Spring基于AOP的事务管理 事务 事务是一系列动作,这一系列动作综合在一起组成一个完整的工作单元,如果有任何一个动作执行失败,那么事务 ...

  2. Spring实现AOP的4种方式

    了解AOP的相关术语:1.通知(Advice):通知定义了切面是什么以及何时使用.描述了切面要完成的工作和何时需要执行这个工作.2.连接点(Joinpoint):程序能够应用通知的一个“时机”,这些“ ...

  3. spring的AOP

    最近公司项目中需要添加一个日志记录功能,就是可以清楚的看到谁在什么时间做了什么事情,因为项目已经运行很长时间,这个最初没有开来进来,所以就用spring的面向切面编程来实现这个功能.在做的时候对spr ...

  4. Spring(五)AOP简述

    一.AOP简述 AOP全称是:aspect-oriented programming,它是面向切面编号的思想核心, AOP和OOP既面向对象的编程语言,不相冲突,它们是两个相辅相成的设计模式型 AOP ...

  5. Spring中AOP原理,源码学习笔记

    一.AOP(面向切面编程):通过预编译和运行期动态代理的方式在不改变代码的情况下给程序动态的添加一些功能.利用AOP可以对应用程序的各个部分进行隔离,在Spring中AOP主要用来分离业务逻辑和系统级 ...

  6. Spring之AOP面向切片

       一.理论基础: AOP(Aspectoriented programming)面向切片/服务的编程,在Spring中使用最多的是对事物的处理.而AOP这种思想在程序中很多地方可以使用的,比如说, ...

  7. 利用CGLib实现动态代理实现Spring的AOP

    当我们用Proxy 实现Spring的AOP的时候, 我们的代理类必须实现了委托类的接口才能实现. 而如果代理类没有实现委托类的接口怎么办? 那么我们就可以通过CGLib来实现 package cn. ...

  8. spring之aop概念和配置

    面向切面的一些概念: 简单说: 连接点就一些方法,在这些方法基础上需要额外的一些业务需求处理. 切入点就是方法所代表的功能点组合起来的功能需求. 通知就是那些额外的操作. 织入就是使用代理实现整个切入 ...

  9. Spring的AOP与代理

    spring 支持两种注入方式: setter/constructor 支持多种配置方式: xml/java5注解/java类配置 支持两种事务管理: 声明性/编程性 实际上上述方式只有一个就能保证系 ...

  10. Spring 实践 -AOP

    Spring 实践 标签: Java与设计模式 AOP引介 AOP(Aspect Oriented Programing)面向切面编程采用横向抽取机制,以取代传统的纵向继承体系的重复性代码(如性能监控 ...

随机推荐

  1. iOS开发—— Couldn't add the Keychain Item

    报错:*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Couldn ...

  2. CF718C Sasha and Array(线段树维护矩阵)

    题解 (不会矩阵加速的先去学矩阵加速) 反正我想不到线段树维护矩阵.我太菜了. 我们在线段树上维护一个区间的斐波那契的列矩阵的和. 然后询问时提取每个符合题意列矩阵的答案项(不是列矩阵存了两项吗,一个 ...

  3. 紫书 例题 10-20 UVa 10900(连续概率)

    分两类,当前第i题答或不答 如果不回答的话最大期望奖金为2的i次方 如果回答的话等于p* 下一道题的最大期望奖金 那么显然我们要取最大值 所以就要分类讨论 我们设答对i题后的最大期望奖金为d[i] 显 ...

  4. 【Educational Codeforces Round 37 B】 Tea Queue

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 用一个队列来模拟排队就好. 队列放三元组(x,y,z) x表示人的下标,y和z分别表示进入和退出时间. 然后枚举时间从1到5000 ...

  5. HDU3265 Examining the Rooms【stirling数】

    题目链接: http://acm.hdu.edu.cn/showproblem.php? pid=3625 题目大意: 有N个房间,每一个房间的要是随机放在某个房间内,概率同样.有K次炸门的机会. 求 ...

  6. 如何在VMware中创建虚拟机

    今天给大家分享如何在VMware中创建虚拟机,具体的教程如下.在这里小编提前下载了Ubuntu14.04桌面系统,为后面在虚拟机中安装Ubuntu14.04桌面系统做准备. 1.从官网上或者直接百度上 ...

  7. Svn备份与Bandizip压缩批处理程序

    目的:为了定时备份多个svn仓库数据,使用批处理程序进行备份并Bandizip进行压缩保存到指定位置,操作完成后弹出成功提示. 为了完成以上目标,需要了解以下几个方面: 批处理命令 Svn命令 Ban ...

  8. 学习参考《TensorFlow深度学习》高清中文版PDF+英文版PDF+源代码

    我们知道,TensorFlow是比较流行的深度学习框架,除了看手册文档外,推荐大家看看<Tensorflow深度学习>,共分5方面内容:基础知识.关键模块.算法模型.内核揭秘.生态发展.前 ...

  9. yum配置中driver-class-name: com.mysql.jdbc.Driver报错

    错误: 原因: 解决方法:把方框中的<scope>runtime</scope>删掉

  10. CSUOJ 1603 Scheduling the final examination

    1603: Scheduling the final examination Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 49  Solved: 1 ...