Spring AOP入门Demo分享

在阅读本文之前,大家可先行参阅简单理解Spring之IOC和AOP及代码示例一文,简单了解下ioc和aop的相关内容。下面进入正题。

本文将会一步一步创建一个最简单的例子,来使用Spring的AOP特性,算是一个Spring AOP的入门Demo。作为一个初学者,运行出这么简单的一个Demo也踩了很多的坑。

OOP的问题,AOP的补充

当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。

Spring中对AOP的支持

Spring中AOP代理由Spring的IoC容器负责生成、管理,其依赖关系也由IoC容器负责管理。因此,AOP代理可以直接使用容器中的其他Bean实例作为目标,这种关系可由IoC容器的依赖注入提供。Spring默认使用Java动态代理来创建AOP代理,这样就可以为任何接口实例创建代理了。当需要代理的类不是代理接口的时候,Spring自动会切换为使用CGLIB代理,也可强制使用CGLIB。

本例子的逻辑如下:有一个Car类(业务类),在Car类中的go方法运行之前和之后,都会有相应的日志记录,但Car类本身并不知道日志的任何逻辑。

创建Maven项目并添加依赖

首先,新建一个Maven项目,使用 maven‐archetype‐quickstart模板,然后打开pom.xml文件,加入Spring AOP运行需要的依赖包

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-core</artifactId>
  <version>4.0.5.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-beans</artifactId>
  <version>4.0.5.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>4.0.5.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
  <version>4.0.5.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.8.1</version>
</dependency>

编写业务代码

新增一个业务类Car,包含一个go()方法

package com.wowo.spring_aop_demo1;
public class Car {
  public void go(){
    System.out.println("go go go!");
  }
}

编写切面类

日志类会记录下系统的运行情况,但日志的逻辑不会在业务类中写的到处都是,而是作为一个切面类存在。

package com.wowo.spring_aop_demo1;
public class CarLogger {
  public void beforeRun(){
    System.out.println("car is going to run");
  }
  public void afterRun(){
    System.out.println("car is running");
  }
}

该切面类包含两个方法,他们分别是前置通知和后置通知。

通过bean来配置关联

新增一个配置文件,本例命名为bean.xml,在配置文件中来关联切面与通知

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:p="http://www.springframework.org/schema/p"
  xmlns:context="http://www.springframework.org/schema/context"
  xmlns:aop="http://www.springframework.org/schema/aop"
  xsi:schemaLocation="
      http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context-2.5.xsd
      http://www.springframework.org/schema/aop
      http://www.springframework.org/schema/aop/spring-aop.xsd"
  >
  <bean id="car" class="com.wowo.spring_aop_demo1.Car"/>
  <bean id="logger" class="com.wowo.spring_aop_demo1.CarLogger" />
  <aop:config>
    <aop:aspect ref="logger">
      <aop:pointcut expression="execution(* com.wowo.spring_aop_demo1.Car.go(..))" id="go"/>

      <aop:before pointcut-ref="go" method="beforeRun" />
      <aop:after pointcut-ref="go" method="afterRun" />
    </aop:aspect>
  </aop:config>
</beans>

注意:这个配置文件中,aop的命名空间,以及xsi:schemaLocation中包含的几个地址都是必须的。
execution(* com.wowo.spring_aop_demo1.Car.go(..))是一个AspectJ切点表达式,execution表示在执行时触发,后面的*表示任意类型的返回值,com.wowo.spring_aop_demo1.Car指的是切点所在的类,go(..)是方法名,..表示任意参数。

Spring切面可以应用5种类型的通知:

·Before——在方法被调用之前调用通知
·After——在方法完成之后调用通知,无论方法是否执行成功
·After-returning——在方法成功执行之后调用通知
·After-throwing——在方法抛出异常后调用通知
·Around——通知包裹了被通知的方法,在被通知的方法调用之前和调用之后都执行自定义的行为

运行业务代码

下面创建一个包含main()方法的类,来运行业务代码

package com.wowo.spring_aop_demo1;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App
{
  public static void main( String[] args )
  {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
    Car car=(Car) context.getBean("car");
    car.go();
  }
}

在上面的代码中,由Spring创建了一个car对象。Spring在创建该对象时,发现它的一个方法被配置成了切点(pointcut),所以,在实例化该对象时,会创建一个代理对象,当切点方法go()执行时,会被Spring创建的代理对象所拦截,运行go方法之前,会调用所对应的切面类CarLogger的前置方法beforeRun(),然后调用Car.go()方法,再然后就调用切面类CarLogger的后置方法afterRun()。

注意:必须使用Spring创建包含切点的对象,如果自己创建的话,Spring是监测不到的,它的运行也不会被应用任何通知。

项目输出结果为

car is going to run
go go go!
car is running

使用环绕通知

如果想使用环绕通知,我们需要修改切面类中的通知方法以及配置文件,业务类无需做任何修改,因为他们是完全解耦的。首先修改切面类CarLogger

import org.aspectj.lang.ProceedingJoinPoint;
public class CarLogger {

  public void aroundRun(ProceedingJoinPoint joinpoint){
    System.out.println("car is going to run");
    try {
      //调用被代理的对象的目标方法,本例中指向Car.go()方法
      joinpoint.proceed();
    } catch (Throwable e) {
      e.printStackTrace();
    }
    System.out.println("car is running");
  }
}

环绕通知的方法,需要接受ProceedingJoinPoint类型的参数,其proceed()方法将会调用被代理对象的目标方法,所以,正常情况下,这个方法一定要调用。我们也可以通过不调用该方法来组织被代理对象的运行。

接下来将配置文件的aop:config部分修改为如下所示

<aop:config>
    <aop:aspect ref="logger">
      <aop:pointcut expression="execution(* com.wowo.spring_aop_demo1.Car.go(..))" id="go"/>
      <aop:around method="aroundRun" pointcut-ref="go"/>
    </aop:aspect>
  </aop:config>

注意:环绕通知不能和前置/后置通知同时存在。运行代码后,输出结果不变。

总结

以上就是本文关于Spring AOP入门Demo分享的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

(0)

相关推荐

  • 使用Spring AOP实现MySQL数据库读写分离案例分析(附demo)

     一.前言 分布式环境下数据库的读写分离策略是解决数据库读写性能瓶颈的一个关键解决方案,更是最大限度了提高了应用中读取 (Read)数据的速度和并发量. 在进行数据库读写分离的时候,我们首先要进行数据库的主从配置,最简单的是一台Master和一台Slave(大型网站系统的话,当然会很复杂,这里只是分析了最简单的情况).通过主从配置主从数据库保持了相同的数据,我们在进行读操作的时候访问从数据库Slave,在进行写操作的时候访问主数据库Master.这样的话就减轻了一台服务器的压力. 在进行读写分离

  • Spring AOP入门Demo分享

    在阅读本文之前,大家可先行参阅<简单理解Spring之IOC和AOP及代码示例>一文,简单了解下ioc和aop的相关内容.下面进入正题. 本文将会一步一步创建一个最简单的例子,来使用Spring的AOP特性,算是一个Spring AOP的入门Demo.作为一个初学者,运行出这么简单的一个Demo也踩了很多的坑. OOP的问题,AOP的补充 当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力.也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系.例如日志功能.日志代

  • Spring Security入门demo案例

    目录 一.简介 二.入门案例 三.自定义认证逻辑 四.自定义授权逻辑 五.注销登录 六.记住我功能 七.会话管理 一.简介 Spring Security是一个高度自定义的安全框架.利用Spring IoC/DI和AOP功能,为系统提供了声明式安全访问控制功能,减少了为系统安全而编写大量重复代码的工作.主要包含如下几个重要的内容: 认证(Authentication),系统认为用户是否能登录. 授权(Authorization),系统判断用户是否有权限去做某些事情. 二.入门案例 首先引入必要的

  • Spring Aop常见注解与执行顺序详解

    目录 Spring Aop 的常用注解 常见问题 示例代码 配置文件 接口类 实现类 aop 拦截器 测试类 执行结论 多切面的情况 代理失效场景 总结 Spring 一开始最强大的就是 IOC / AOP 两大核心功能,我们今天一起来学习一下 Spring AOP 常见注解和执行顺序. Spring Aop 的常用注解 首先我们一起来回顾一下 Spring Aop 中常用的几个注解: @Before 前置通知:目标方法之前执行 @After 后置通知:目标方法之后执行(始终执行) @After

  • 使用spring aop 统一捕获异常和写日志的示例demo

    之前给大家介绍过Spring AOP的基础知识,需要的朋友点击了解下吧,这边我将给您介绍用spring AOP 实现的异常捕获和日志的小demo,我也会详细解释相关配置. 首先给大家看一下我的工程目录: 大家可以先用eclipse中新建一个maven工程,在工程中pom.xml按下面文件添加依赖: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XM

  • Spring Aop 源码增强获取分享

    目录 1 前言 2 spring 增强器 3 总结 1 前言 在前文中,已经讲述了 AOP的后置处理器使用和方法,在本文中继续分享增强信息相关的源码,这里才是 AOP 的核心代码. 2 spring 增强器 之前已经讲述了 spring AbstractApplicationContext.refresh 方法,在以下方法中都会处理会处理 BeanPostProcessor 接口. invokeBeanFactoryPostProcessors registerBeanPostProcessor

  • spring aop注解配置代码实例

    本文实例为大家分享了spring aop注解配置的具体代码,供大家参考,具体内容如下 Demo.java package cn.itcast.e_annotation; import javax.annotation.Resource; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.context.ApplicationContext; import org.springfra

  • Spring AOP定义Before增加实战案例详解

    本文实例讲述了Spring AOP定义Before增加.分享给大家供大家参考,具体如下: 一 配置 <?xml version="1.0" encoding="GBK"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:

  • spring AOP的After增强实现方法实例分析

    本文实例讲述了spring AOP的After增强实现方法.分享给大家供大家参考,具体如下: 一 配置 <?xml version="1.0" encoding="GBK"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmln

  • spring AOP定义AfterThrowing增加处理实例分析

    本文实例讲述了spring AOP定义AfterThrowing增加处理.分享给大家供大家参考,具体如下: 一 配置 <?xml version="1.0" encoding="GBK"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance&quo

随机推荐