Spring(三)--AOP【面向切面编程】、通知类型及使用、切入点表达式

1.概念:Aspect Oriented Programming 面向切面编程

在方法的前后添加方法

2.作用:本质上来说是一种简化代码的方式

继承机制

封装方法

动态代理

……

3.情景举例

  ①数学计算器接口[MathCalculator]

int add(int i,int j);

int sub(int i,int j);

int mul(int i, int j);

int div(int i,int j);

因为后面的通知方法需要返回值,所以在这里类型声明为 int 类型

public interface MathCaculator {
      public int add(int i,int j);
      public int sub(int i,int j);
      public int mul(int i,int j);
      public int div(int i,int j);
}

   ②提供简单实现[EasyImpl]

     一定要在类上写注解,否则在 IOC容器中找不到

@Component
public class CacultorEasyImpl implements MathCaculator{
      @Override
      public void add(int i, int j) {
            System.out.println("[日志],【参数:】"+i+","+j);
            int result = i + j;
            System.out.println("[日志],【参数:】"+i+","+j+"--"+result);
      }

      @Override
      public void sub(int i, int j) {
            System.out.println("[日志],【参数:】"+i+","+j);
            int result = i - j;
            System.out.println("[日志],【参数:】"+i+","+j+"--"+result);
      }

      @Override
      public void mul(int i, int j) {
            System.out.println("[日志],【参数:】"+i+","+j);
            int result = i * j;
            System.out.println("[日志],【参数:】"+i+","+j+"--"+result);
      }

      @Override
      public void div(int i, int j) {
            System.out.println("[日志],【参数:】"+i+","+j);
            int result = i / j;
            System.out.println("[日志],【参数:】"+i+","+j+"--"+result);
      }
}
<context:component-scan base-package="com.neuedu.aop"></context:component-scan>

  ③在简单实现的基础上让每一个计算方法都能够打印日志[LoginImpl]

private ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
@Test
public void test() {
      CacultorEasyImpl bean = ioc.getBean(CacultorEasyImpl.class);
      bean.add(10, 2);
      bean.sub(10, 2);
      bean.mul(10, 2);
      bean.div(10, 2);
}

  ④缺陷

[1]手动添加日志繁琐,重复

[2]统一修改不便

[3]对目标方法本来要实现的核心功能有干扰,使程序代码很臃肿,不易于开发维护

⑤使用动态代理实现

[1]创建一个类,让这个类能够提供一个目标对象的代理对象

[2]在代理对象中打印日志

4.AOP术语![参见图例和doc文档]

AOP概述

●AOP(Aspect-Oriented Programming,面向切面编程):是一种新的方法论

是对传统 OOP(Object-Oriented Programming,面向对象编程)的补充。

●参见图例和doc文档解释AOP的各个术语!

●Spring的AOP既可以使用xml配置的方式实现,也可以使用注解的方式来实现!

5.在Spring中使用AOP实现日志功能

  ①Spring中可以使用注解或XML文件配置的方式实现AOP。

  ②导入jar包

com.springsource.net.sf.cglib -2.2.0.jar

com.springsource.org.aopalliance-1.0.0 .jar

com.springsource.org.aspectj.weaver-1.6.8 .RELEASE.jar

commons-logging-1.1.3. jar

spring-aop-4.0.0.RELEASE.jar

spring-aspects-4.0.0.RELEASE.jar

spring-beans-4.0.0.RELEASE.jar

spring-context-4.0.0.RELEASE.jar

spring-core-4.0.0.RELEASE.jar

spring-expression-4.0.0.RELEASE. jar

  ③开启基于注解的AOP功能 < aop:aspectj-autoproxy />

<context:component-scan base-package="com.neuedu.aop"></context:component-scan>
<aop:aspectj-autoproxy/>

  ④声明一个切面类,并把这个切面类加入到 IOC容器中

   在类上加以下两个注解

@Aspect  :表示这是一个切面类

@Component  :加入IOC容器

  ⑤在切面类中声明通知方法

[1] 前置通知:@Before

[2] 返回通知:@AfterReturning

[3] 异常通知:@AfterThrowing

[4] 后置通知:@After

[5] 环绕通知:@Around :环绕通知是前面四个通知的集合体!

@Component
@Aspect
public class CaculatorAspect {

      @Before(value="execution(public void com.neuedu.aop.RawCaculatorImpl.add(int, int))")
      public void showBeginLog(){
            System.out.println("日志开始");
      }

      @After(value="execution(public void com.neuedu.aop.RawCaculatorImpl.add(int, int))")
      public void showReturnLog(){
            System.out.println("日志正常返回");
      }

      @AfterThrowing(value="execution(public void com.neuedu.aop.RawCaculatorImpl.add(int, int))")
      public void showExceptionLog(){
            System.out.println("日志有错");
      }

      @AfterReturning(value="execution(public void com.neuedu.aop.RawCaculatorImpl.add(int, int))")
      public void showAfterLog(){
            System.out.println("日志最终结束");
      }
}

  ⑥被代理的对象也需要加入IOC容器

@Component
public class RawCaculatorImpl implements MathCaculator{

      @Override
      public void add(int i, int j) {
            int result = i + j;
            System.out.println(i+"+"+j+"="+result);
      }

      @Override
      public void sub(int i, int j) {
            int result = i - j;
            System.out.println(i+"-"+j+"="+result);
      }

      @Override
      public void mul(int i, int j) {
            int result = i * j;
            System.out.println(i+"*"+j+"="+result);
      }

      @Override
      public void div(int i, int j) {
            int result = i / j;
            System.out.println(i+"/"+j+"="+result);
      }
}

  Test 中 用 id 查找,通过强转,调用加减乘除四个方法

private ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
@Test
public void test() {
      MathCaculator bean = (MathCaculator) ioc.getBean("rawCaculatorImpl");
      bean.add(10, 2);
      bean.sub(10, 2);
      bean.mul(10, 2);
      bean.div(10, 2);
}

6.切入点表达式:

(1)上述案例通过junit测试,会发现,我们调用目标类的四个方法只有add方法被加入了4个通知

如果想所有的方法都加上这些通知,可以在切入点表达式处:

将execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int)) 换成: execution(public int com.neuedu.aop.target.MathCalculatorImpl.*(int, int))

这样只要是有两个参数,且参数类型为int的方法在执行的时候都会执行其相应的通知方法!

  

@Component
@Aspect
public class CaculatorAspect {

      @Before(value="execution(public void com.neuedu.aop.RawCaculatorImpl.*(int, int))")
      public void showBeginLog(){
            System.out.println("日志开始");
      }

      @After(value="execution(public void com.neuedu.aop.RawCaculatorImpl.*(int, int))")
      public void showReturnLog(){
            System.out.println("日志正常返回");
      }

      @AfterThrowing(value="execution(public void com.neuedu.aop.RawCaculatorImpl.*(int, int))")
      public void showExceptionLog(){
            System.out.println("日志有错");
      }

      @AfterReturning(value="execution(public void com.neuedu.aop.RawCaculatorImpl.*(int, int))")
      public void showAfterLog(){
            System.out.println("日志最终结束");
      }

}

  如果方法中的参数类型不一致,可以用 (..) 代替 (int,int)

@Component
@Aspect
public class CaculatorAspect {

     @Pointcut(value="execution(* com.neuedu.aop.RawCaculatorImpl.*(..))")
     public void showLog(){}

      @Before(value="showLog()")
      public void showBeginLog(JoinPoint point){
            Object[] args = point.getArgs();//获取参数
            List<Object> asList = Arrays.asList(args);//转为list类型
            Signature signature = point.getSignature();//获取签名
            String name = signature.getName();//获取方法名字
            System.out.println("【前置通知】目标方法名:"+name+",参数为:"+asList);
      }

      @After(value="showLog()")
      public void showReturnLog(){
            System.out.println("【后置通知】日志最终返回");
      }

      @AfterThrowing(value="showLog()",throwing="ex")
      public void showExceptionLog(JoinPoint point,Exception ex){
            System.out.println("【异常通知】异常信息为:"+ex.getMessage());
      }

      @AfterReturning(value="showLog()",returning="result")
      public void showAfterLog(JoinPoint point,Object result){
            System.out.println("【返回通知】方法的返回值:"+result);
            System.out.println();
      }
}

(2)①切入点表达式的语法格式[参见第5章AOP细节]

execution([权限修饰符] [返回值类型] [简单类名/全类名] [方法名]([参数列表]))

参见第5章AOP细节:演示验证

1.任意参数,任意类型

2.任意返回值

3.用@PointCut注解统一声明,然后在其它通知中引用该统一声明即可!

需要注意的是:权限是不支持写通配符的,当然你可以写一个*表示所有权限所有返回值!

最详细的切入点表达式:

execution(public int com.neuedu.aop.target.MathCalculatorImpl.add(int, int))

最模糊的切入点表达式:

execution (* *.*(..))

第一个 * 代表   权限和返回类型

但是不可以 无权限却有返回类型   例如: execution(* int com.neuedu.aop.target.MathCalculatorImpl.add(int, int))

7.统一声明切入点表达式

@Pointcut(value= "execution(public int com.atguigu.aop.target.EazyImpl.add(int,int))")

public void myPointCut(){}

8.通知方法的细节
 
 ①在通知中获取目标方法的方法名和参数列表

[1]在通知方法中声明一个JoinPoint类型的形参

JointPoint 是切入点,

[2]调用JoinPoint对象的getSignature()方法获取目标方法的签名

[3]调用JoinPoint对象的getArgs()方法获取目标方法的实际参数列表

@Before(value="showLog()")
public void showBeginLog(JoinPoint point){
      Object[] args = point.getArgs();//获取参数
      List<Object> asList = Arrays.asList(args);//转为list类型
      Signature signature = point.getSignature();//获取签名
      String name = signature.getName();//获取方法名字
      System.out.println("目标方法名:"+name+",参数为:"+asList);
      System.out.println("日志开始");
}

  

    ②在返回通知中获取方法的返回值

[1]在 @AfterReturning   注解中添加returning属性

[2]在返回通知的通知方法中声明一个形参,形参名和returning属性的值一致

不知道返回什么类型,所以用 Object 类型

@AfterReturning(value="showLog()",returning="result")
public void showAfterLog(JoinPoint point,Object result){
      System.out.println("方法的返回值:"+result);
      System.out.println("日志正常结束");
      System.out.println();
}

 

   ③在异常通知中获取异常对象

[1]在 @AfterThrowing 注解中添加 throwing 属性

[2]在异常通知的通知方法中声明一个形参,形参名和throwing属性值一致

@AfterThrowing(value="showLog()",throwing="ex")
public void showExceptionLog(JoinPoint point,Exception ex){
      System.out.println("异常信息为:"+ex.getMessage());
      System.out.println("日志有错");
}

9.根据接口类型获取target对象时,实际上真正放在IOC容器中的对象是代理对象,而并不是目标对象本身!

10.环绕通知:@Around

1.环绕通知需要在方法的参数中指定JoinPoint的子接口类型ProceedingJoinPoint为参数

@Around(value="pointCut()")

public void around(ProceedingJoinPoint joinPoint){

}

2.环绕通知会将其他4个通知能干的,自己都给干了!

注意:@Around修饰的方法一定要将方法的返回值返回!本身相当于代理!

但是不知道返回值的类型 ,所以用 Object

@Around(value="execution(public * com.neuedu.aop.RawCaculatorImpl.*(..))")
public Object showLog(ProceedingJoinPoint point){
      Object[] args = point.getArgs();
      List<Object> asList = Arrays.asList(args);
      Signature signature = point.getSignature();//获取签名
      String name = signature.getName();
      Object result=null;
      try {
            try{
                  System.out.println("【前置通知】目标方法名:"+name+",参数为:"+asList);
                  result = point.proceed(args);
             }finally{
                  System.out.println("【后置通知】日志最终返回");
             }
             System.out.println("【返回通知】方法的返回值:"+result);
       } catch (Throwable e) {
             System.out.println("【异常通知】异常信息为:"+e.getMessage());
       }
       System.out.println();
       return result;
}

11.切面的优先级

对于同一个代理对象,可以同时有多个切面共同对它进行代理。

可以在切面类上通过@Order (value=50)注解来进行设置,值越小优先级越高!

没有 @Order 注解默认最大

@Component
@Aspect
@Order(value=20)
public class BAspect {

      @Around(value="execution(* com.neuedu.aop.RawCaculatorImpl.*(..))")
      public Object showLog(ProceedingJoinPoint point){
            Object[] args = point.getArgs();
            List<Object> asList = Arrays.asList(args);
            Signature signature = point.getSignature();//获取签名
            String name = signature.getName();
            Object result=null;
            try {
                  try{
                        System.out.println("【前置】目标方法名:"+name+",参数为:"+asList);
                        result = point.proceed(args);
                  }finally{
                        System.out.println("【后置】日志最终返回");
                  }
                  System.out.println("【返回】方法的返回值:"+result);
            } catch (Throwable e) {
                  System.out.println("【异常】异常信息为:"+e.getMessage());
            }
            System.out.println();
            return result;
      }
}

12.注意:上面的AOP都是通过注解实现的,AOP实际上也可以通过xml配置的方式实现!

将注解全删掉

<!-- 将需要加载到IOC容器中的bean配置好 -->
<bean id="caculatorAspect" class="com.neuedu.aop.CaculatorAspect"></bean>
<bean id="bAspect" class="com.neuedu.aop.BAspect"></bean>
<bean id="rawCaculatorImpl" class="com.neuedu.aop.RawCaculatorImpl"></bean>

<!-- 配置AOP,需要导入AOP名称空间 -->
<aop:config>
      <!-- 声明切入点表达式 -->
      <aop:pointcut expression="execution(* com.neuedu.aop.RawCaculatorImpl.*(..))" id="myPointCut"/>
      <!-- 配置日志切面类,引用前面的类 ,通过order属性控制优先级-->
      <aop:aspect ref="caculatorAspect" order="20">
      <!-- 通过method属性指定切面类的切面方法,通过pointcut-ref指定切入点表达式 -->
             <aop:before method="showBeginLog" pointcut-ref="myPointCut"/>
             <aop:after method="showAfterLog" pointcut-ref="myPointCut"/>
             <aop:after-returning method="showReturnLog" pointcut-ref="myPointCut" returning="result"/>
             <aop:after-throwing method="showExceptionLog" pointcut-ref="myPointCut" throwing="ex"/>
      </aop:aspect>
      <!-- 配置事务切面类,引用前面的类 -->
      <aop:aspect ref="bAspect" order="25">
            <aop:around method="showLog" pointcut-ref="myPointCut"/>
      </aop:aspect>

</aop:config>

需要知道的是:事务的管理是和AOP是有很大关系的,即声明式事务的底层是用AOP实现的!

时间: 2024-08-14 21:41:11

Spring(三)--AOP【面向切面编程】、通知类型及使用、切入点表达式的相关文章

Spring(三)AOP面向切面编程

原文链接:http://www.orlion.ga/205/ 一.AOP简介 1.AOP概念 参考文章:http://www.orlion.ml/57 2.AOP的产生 对于如下方法:     public class UserDAOImpl implements UserDAO{           public void saveUser(User user){      doSaveUser();      }     } 想在saveUser方法中执行保存用户之前和之后记录当前时间以求出

spring入门-AOP 面向切面编程

AOP 面向切面编程 在日常开发中最后将业务逻辑定义在一个专门的service包下,而实现定义在service包下的impl包中, 服务接口以IXXXService形式,而服务实现就是XXXService,这就是规约设计 步骤: 1.E:\Users\WorkSpaces\KmwalletApp\spring\aop\test\HelloWorldService.java 继承(implements)xx接口 2.E:\Users\WorkSpaces\KmwalletApp\spring\ao

Spring:AOP, 面向切面编程

AOP概述 什么是AOP, 面向切面编程 AOP为Aspect Oriented Programming的缩写, 意为:面向切面编程, 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. AOP是OOP的延续, 是函数式编程的一种衍生范型. 利用AOP可以对业务逻辑的各个部分进行隔离, 从而使得业务逻辑各部分之间的耦合度降低, 提高程序的可重用性, 同时提高了开发的效率. - 传统开发模型: 纵向的编程.  面向切面编程: 纵横配合的编程. AOP的作用及优势 作用: 在程序运行期

spring框架学习(三)——AOP( 面向切面编程)

AOP 即 Aspect Oriented Program 面向切面编程 首先,在面向切面编程的思想里面,把功能分为核心业务功能,和周边功能. 所谓的核心业务,比如登陆,增加数据,删除数据都叫核心业务 所谓的周边功能,比如性能统计,日志,事务管理等等 周边功能在Spring的面向切面编程AOP思想里,即被定义为切面 在面向切面编程AOP的思想里面,核心业务功能和切面功能分别独立进行开发 然后把切面功能和核心业务功能 "编织" 在一起,这就叫AOP 原理图 1. 功能分两大类,辅助功能和

Spring的AOP面向切面编程

什么是AOP? 1.AOP概念介绍 所谓AOP,即Aspect orientied program,就是面向方面(切面)的编程. 功能: 让关注点代码与业务代码分离! 关注点: 重复代码就叫做关注点: 业务代码: 核心业务的代码 业务代码与关注点代码分离,好处? --> 关注点代码写一次即可: -->开发者只需要关注核心业务: -->运行时期,执行核心业务代码时候动态植入关注点代码: [代理] 如何分离? 过程式/对象式/代理模式分离 AOP的好处是可以动态地添加和删除在切面上的逻辑而不

Spring之AOP(面向切面编程)_入门Demo

软件152 刘安民 AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向切面编程.AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现. 实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行:二是采用静态植入的方式,引入特定的语法创建"方面",从而使得编译器可以在编译期间织入有关"方面"的代

java的动态代理的两种方式和spring的aop面向切面编程的对比

java动态代理的两种方式 使用动态代理的好处:可以进行类的功能的加强,同时减少耦合和代码的冗余,耦合的意思是不用吧加强的部分写到各个实现类里面,冗余的意思是如果对每个实现类加强的部分是一样的,通过一个代理类即可实现 基于jdk的动态代理 通过jdk中自带的Proxy类进行动态的代理,Proxy创建动态代理对象的时候要传入三个参数,第一个是classloader,第二个是interfaces,第三个是代理类实现的方法 要求:代理的是一个接口,必须至少有一个实现类 创建接口的代码: /** * a

spring:AOP面向切面编程(注解)03

使用注解写aop时最好使用环绕通知写 切面类: /** * 用于记录日志的工具类,它里面提供了公共的代码 */ @Component("logger") @Aspect //表示当前类是一个切面类 public class Logger { @Pointcut("execution(* cn.flypig666.service.impl.*.*(..))") private void pt1(){}; /** * 前置通知 */ @Before("pt1(

【spring源码学习】spring的AOP面向切面编程的实现解析

一:Advice(通知)(1)定义在连接点做什么,为切面增强提供织入接口.在spring aop中主要描述围绕方法调用而注入的切面行为.(2)spring定义了几个时刻织入增强行为的接口??=>org.springframework.aop.BeforeAdvice???org.springframework.aop.MethodBeforeAdvice??=>org.springframework.aop.AfterAdvice???org.springframework.aop.After

sprint.net(2) AOP面向切面编程,spring.net的环绕通知;Spring.net的AOP通知的四种类型

AOP 有点类似于我们MVC里面的Filter过滤器,例如在MVC里面,如果给一个Action上打一个标签,就可以在这个Action执行之前或者之后,额外的执行一个方法,这个就相当于是面向切面编程. 无侵入式的. (也就是在不改变原来的代码的情况下,来跳转到一个其他的方法,执行完毕后回到主方法..),但是spring.net的AOP更牛叉,只需要在xml里面配置,就可以了,不需要在方法上面打特性的标签,也不需要继承什么类(例如MVC的过滤器是继承了ActionFilterAttribute) 主