`
ganglong99
  • 浏览: 161353 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类
最新评论

Spring AOP的应用

阅读更多

在实际的应用程序开发中,经常需要在一个服务流程中插入一些与业务逻辑无关的系统服务逻辑(最常见的就是记录日志,权限检查等),如果把所有这些与业务逻辑无关的服务与业务逻辑编织在一起,就会使业务逻辑对象的负担加重,因为它不但要具有业务逻辑的功能,还带有例如记录日志等其他功能,这样就容易产生对象的职责混淆。
为了避免对象职责的混淆,我们在设计中就需要将与业务逻辑无关的服务逻辑从业务逻辑中剥离出来,独立设计为一个模块或对象,而在希望需要使用这些对象的时候插入进来,不希望使用的时候去掉即可,这种设计模式就称为AOP。
Spring AOP是实现AOP的一种技术。Spring AOP最常用的就是采用XML配置文件的方式。Spring AOP只支持在目标对象的某个方法的前后调用服务代码。
下面是使用Spring AOP的几个简单的例子:


1.在目标对象的方法执行之前调用(Before Adivce)
首先需要定义目标对象必须实现的接口:

package com.test.spring.aop;

public interface IHello {

 public void sayHello(String name);
}

 
接着定义一个IHello的实现类:

package com.test.spring.aop;

public class HelloSpeaker implements IHello {

 public void sayHello(String name) {
  System.out.println("Hello," + name);
 }
}

 
现在我们想要在调用对象HelloSpeaker的sayHello()方法前打印一下方法开始执行的日志,一般情况下我们会直接在sayHello()的方法中直接加上日志的代码,这样就把日志和业务逻辑的代码融在了一起,对于要记录大量日志的程序来说,无疑会加重目标对象的负担。
我们把打印日志的代码独立出来成为一个专门用于日志记录的对象,这里需要实现Spring的MethodBeforeAdvice接口:

package com.test.spring.aop;

import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.springframework.aop.MethodBeforeAdvice;

public class LogBeforeAdvice implements MethodBeforeAdvice {

 private Logger logger = Logger.getLogger(this.getClass().getName());

 public void before(Method method, Object[] args, Object target) throws Throwable {
  logger.log(Level.INFO, "method start..." + method);
  System.out.println("LogBeforeAdvice: method start... " + method);
 }
}

 
在before()方法的实现中,加入了一些记录日志的代码,LogBeforeAdive被设计成一个独立的服务,可以提供给需要这个服务的对象,在下面的配置中,我们把这个服务提供给了前面的HelloSpeaker对象:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans.xsd">
 
 <bean id="logBeforeAdvice" class="com.test.spring.aop.LogBeforeAdvice"></bean>
 
 <bean id="helloSpeaker" class="com.test.spring.aop.HelloSpeaker"></bean>
 
 <bean id="helloProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
  <property name="proxyInterfaces" value="com.test.spring.aop.IHello"></property>
  <property name="target" ref="helloSpeaker"></property>
  <property name="interceptorNames">
   <list>
    <value>logBeforeAdvice</value>
   </list>
  </property>
 </bean>
</beans>

 
在上面的配置文件中,属性target指定了要被服务的目标对象为helloSpeaker,属性interceptorNames指定了使用的服务对象是logBeforeAdvice。这样就建立了目标对象与服务对象之间的联系,下面是测试代码:

package com.test.spring.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BeforeAdviceTest {

 public static void main(String[] args) {
  ApplicationContext context = new ClassPathXmlApplicationContext(
    "com/test/spring/aop/beforeAfterAdvice.xml");

  IHello helloProxy = (IHello) context.getBean("helloProxy");
  helloProxy.sayHello("world");
 }
}

 
可以看到,测试代码中,取的对象是helloProxy代理对象,而不是helloSpeaker目标对象,取到的代理对象必须转换为IHello接口,这样当调用代理对象的sayHello()方法时,就可以在调用目标对象的sayHello()方法体中的代码前打印日志,实际上流程是先进入LogBeforeAdvice对象执行before()方法,执行完毕后再进入helloSpeaker的sayHello()方法,这一切的流程控制都是有Spring完成的,而我们仅仅需要做的工作就是定义目标对象和服务对象,并编写XML配置文件建立服务对象与目标对象的联系。动手试一下吧,看看执行结果再说。


2.在目标对象的方法执行之后调用(After Advice)
这里就不在定义目标对象的接口及实现类了,依然使用前面定义好的目标对象helloSpeaker。
要在目标对象的方法执行之后进行一些操作,这里依然是打印日志,Spring AOP要求我们实现AfterReturningAdvice接口:

package com.test.spring.aop;

import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.springframework.aop.AfterReturningAdvice;

public class LogAfterAdvice implements AfterReturningAdvice {

 private Logger logger = Logger.getLogger(this.getClass().getName());

 public void afterReturning(Object object, Method method, Object[] args, Object target)
   throws Throwable {
  logger.log(Level.INFO, "method end... " + method);
  System.out.println("method end... " + method);
 }
}

 
可以看到,这个服务对象的目的是在执行目标对象的方法之后执行afterReturning()方法打印方法执行完毕的日志。
下一步就是配置XML文件了,把这个服务提供给前面helloSpeaker目标对象:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans.xsd">
 
 <bean id="logAfterAdvice" class="com.test.spring.aop.LogAfterAdvice"></bean>
 
 <bean id="helloSpeaker" class="com.test.spring.aop.HelloSpeaker"></bean>
 
 <bean id="helloProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
  <property name="proxyInterfaces" value="com.test.spring.aop.IHello"></property>
  <property name="target" ref="helloSpeaker"></property>
  <property name="interceptorNames">
   <list>
    <value>logAfterAdvice</value>
   </list>
  </property>
 </bean>
</beans>

 
可以看到,与前面的配置Before Advice非常相似。
测试代码与Before Advice的完全一样,这里不再重复列出。动手试一下,看看是否在执行目标对象的sayHello()方法之后调用了打印结束日志。
当然,还可以把Before Advice和After Advice一起提供给目标对象,这样,就可以在执行目标对象的方法的前、后都打印出日志了,配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans.xsd">

 <bean id="logBeforeAdvice" class="com.test.spring.aop.LogBeforeAdvice"></bean>
 
 <bean id="logAfterAdvice" class="com.test.spring.aop.LogAfterAdvice"></bean>
 
 <bean id="helloSpeaker" class="com.test.spring.aop.HelloSpeaker"></bean>
 
 <bean id="helloProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
  <property name="proxyInterfaces" value="com.test.spring.aop.IHello"></property>
  <property name="target" ref="helloSpeaker"></property>
  <property name="interceptorNames">
   <list>
    <value>logBeforeAdvice</value>
    <value>logAfterAdvice</value>
   </list>
  </property>
 </bean>
</beans>

 
可以看到,在interceptorNames属性中,把logBeforeAdvice和logAfterAdvice两个服务对象都提供给了目标对象,实际上我们可以配置任意多个。
测试代码依然与前面的一样,不再列出,执行一下,看看结果是否是在执行sayHello()方法之前和之后分别打印了日志。


3.在目标对象的方法执行前后调用(Around Advice)
在前面的两种模式中,有一个缺陷,就是无论如何,目标对象的方法总是会被执行,我们不能控制目标对象的方法是否执行,且执行的时机。
有时候我们需要自行决定是否要执行目标对象的方法,有时需要在不同的时机才执行目标对象的方法,比如权限检查,当权限检查通过时,才继续执行目标对象的方法,如果权限检查不通过,则进入别的流程中。前面的两种模式是做不到这一点的。
在Around Advice这种模式中,我们就可以自行决定是否执行目标对象的方法及其执行的时机了。
这种模式需要实现MethodInterceptor接口:

package com.test.spring.aop;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class LogInterceptor implements MethodInterceptor {

 public Object invoke(MethodInvocation methodInvocation) throws Throwable {
  System.out.println("Log start...");
  Object result = null;
  result = methodInvocation.proceed();
  System.out.println("Log end...");
  return result;
 }
}

 
这个服务实现的方法是invoke()方法,在这个方法中,调用了proceed()方法,当调用了这个方法时,就表示需要执行目标对象的方法,下面是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"
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans.xsd">

 <bean id="logInterceptor" class="com.test.spring.aop.LogInterceptor"></bean>
 
 <bean id="helloSpeaker" class="com.test.spring.aop.HelloSpeaker"></bean>
 
 <bean id="helloProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
  <property name="proxyInterfaces" value="com.test.spring.aop.IHello"></property>
  <property name="target" ref="helloSpeaker"></property>
  <property name="interceptorNames">
   <list>
    <value>logInterceptor</value>
   </list>
  </property>
 </bean>
</beans>

 
如上例中,我们在调用proceed()方法的前后打印了日志,则表示在执行helloSpeaker目标对象的sayHello()方法的前后都打印日志。
可以看到,关键就在于proceed()方法的调用,因此,在这种模式中,我们可以在调用proceed()方法的前、后的任何地方插入任何处理逻辑,当然,我们也可以不调用proceed()方法,这样,目标对象的方法就不能执行了。
测试代码与前面的完全一样,这里不再列出。
对于前面的三种方式,我们可以看出,这几种方式都需要在使用时建立代理对象,然后获取代理对象并把类型转换为目标对象的接口,这需要我们手动创建代理对象,即每一个目标对象都要建立一个相应的代理对象,对于有很多目标对象的应用程序,配置XML文件无疑会是一件痛苦的事。
Spring AOP框架中提供了一种自动创建代理的工具。


4.自动代理(BeanNameAutoProxyCreator)
要使用Spring AOP的自动代理,需要使用org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator类来完成。
这里,目标对象依然使用前面的helloSpeaker,服务对象依然使用前面的LogInterceptor,代码不再列出,下面是配置自动代理的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"
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans.xsd">
 
 <bean id="logInterceptor" class="com.test.spring.aop.LogInterceptor"></bean>

 <bean id="helloSpeaker" class="com.test.spring.aop.HelloSpeaker"></bean>
 
 <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
  <property name="beanNames">
   <list>
    <value>helloSpeaker</value>
   </list>
  </property>
  <property name="interceptorNames" value="logInterceptor"></property>
 </bean>
</beans>

 
在上面的配置中,指定了org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator类的两个属性,beanNames和interceptorNames,beanNames指定了要进行自动代理的目标对象,interceptorNames与前面的几种模式中的这个属性含义相同,指定了提供服务的对象。
测试代码为:

package com.test.spring.aop;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class BeforeAdviceTest {

 public static void main(String[] args) {
  ApplicationContext context = new ClassPathXmlApplicationContext(
    "com/test/spring/aop/beforeAfterAdvice.xml");
  IHello helloSpeaker = (IHello) context.getBean("helloSpeaker");
  helloSpeaker.sayHello("longyg");
 }
}

 
看,这次我们不再是获取的代理对象,而是直接获得helloSpeaker目标对象了。由于我们在XML配置文件中指定了为helloSpeaker目标对象进行自动代理,因此当我们获取目标对象时,Spring会自动为其创建代理对象,并根据配置的服务对象提供相应的服务(logInterceptor)。
基于这种方式,我们可以把一个服务对象用于多个目标对象,下面我另外定义了一个helloSpeaker2目标对象,它也实现了IHello接口(当然我们也可以实现任务 其他接口):

package com.test.spring.aop;

public class HelloSpeaker2 implements IHello {

 public void sayHello(String name) {
  System.out.println("Welcome to world," + name);
 }
}

 
下面在XML文件中增加对helloSpeaker2目标对象提供服务:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans.xsd">
 
 <bean id="logInterceptor" class="com.test.spring.aop.LogInterceptor"></bean>

 <bean id="helloSpeaker" class="com.test.spring.aop.HelloSpeaker"></bean>

 <bean id="helloSpeaker2" class="com.test.spring.aop.HelloSpeaker2"></bean>
 
 <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
  <property name="beanNames">
   <list>
    <value>helloSpeaker</value>
    <value>helloSpeaker2</value>
   </list>
  </property>
  <property name="interceptorNames" value="logInterceptor"></property>
 </bean>
</beans>

 
在beanNames属性中增加了helloSpeaker2目标对象,因此logInterceptor也对它提供服务。我们还可以增加任意多的目标对象。
同样,我们也可以为目标对象提供多个服务对象,下面我又定义了一个agumentInterceptor服务对象:

package com.test.spring.aop;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class AgumentInterceptor implements MethodInterceptor {

 public Object invoke(MethodInvocation methodInvocation) throws Throwable {
  String name = null;
  Object[] args = methodInvocation.getArguments();
  for (int i = 0; i < args.length; i++) {
   if (args[i] instanceof String) {
    name = (String) args[i];
   }
  }
  System.out.println("the argument is : " + name);  
  Object result = methodInvocation.proceed(); 
  System.out.println("the argument is proceed"); 
  return result;
 }
}

 

下面在XML文件中增加agumentInterceptor服务对象:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans.xsd">
 
 <bean id="logInterceptor" class="com.test.spring.aop.LogInterceptor"></bean>

 <bean id="agumentInterceptor" class="com.test.spring.aop.AgumentInterceptor"></bean>

 <bean id="helloSpeaker" class="com.test.spring.aop.HelloSpeaker"></bean>

 <bean id="helloSpeaker2" class="com.test.spring.aop.HelloSpeaker2"></bean>
 
 <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
  <property name="beanNames">
   <list>
    <value>helloSpeaker</value>
    <value>helloSpeaker2</value>
   </list>
  </property>
  <property name="interceptorNames">
   <list>
    <value>logInterceptor</value>
    <value>agumentInterceptor</value>
   </list>
  </property>
 </bean>
</beans>

 

 

在interceptorNames属性中增加了agumentInterceptor服务对象,这样,agumentInterceptor服务就可以在目标对象上被应用了。
5.结束语
以上就是Spring AOP提供的几种比较简单且常用的模式,当然,Spring还提供了很多其他的方式,这里不再一一列出。一般情况下,自动代理的模式是我们经常使用的,它的配置很灵活,我认为初学只要能掌握并使用这种模式就可以了。

0
0
分享到:
评论

相关推荐

    Spring AOP应用Demo

    **Spring AOP应用Demo** Spring AOP(Aspect Oriented Programming,面向切面编程)是Spring框架中的一个重要模块,它提供了一种在不修改源代码的情况下,对程序进行功能增强的技术。这个Demo是针对Spring AOP的...

    Spring AOP应用

    **Spring AOP 应用详解** Spring AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架的重要组成部分,它提供了一种在程序运行时动态插入代码的能力,以实现跨切面的关注点,如日志、事务管理、权限控制...

    Java进阶之SpringAOP应用共16页.pdf.zi

    本资料"Java进阶之SpringAOP应用共16页.pdf"深入探讨了Spring AOP在实际开发中的应用和实现原理,旨在提升Java开发者的技能水平。 首先,理解AOP的基本概念至关重要。AOP是一种编程范式,它将关注点分离为不同的...

    spring 应用aop 实例

    通过上述步骤,你可以创建一个简单的Spring AOP应用,实现对特定方法的调用进行日志记录。当然,AOP的潜力远不止于此,你可以根据实际场景扩展通知类型,或者创建更复杂的切入点表达式,以实现更细粒度的控制。 在...

    spring aop jar 包

    在实际开发中,Spring AOP广泛应用于事务管理。例如,我们可以定义一个切面来处理所有数据库操作的事务,这样无需在每个业务方法中显式调用开始和提交事务,只需在切面中配置事务规则即可。 `aop-jar`这个压缩包...

    springaop.zip

    在本示例中,"springaop.zip" 包含了一个使用XML配置的Spring AOP应用实例,可以直接运行,配合相关的博客文章学习效果更佳。 在Spring AOP中,我们首先需要了解几个核心概念: 1. **切面(Aspect)**:切面是关注...

    spring aop实例

    另一个文件"testaop"是一个MyEclipse项目,包含了实际的Spring AOP应用实例。这个项目可以帮助你更好地理解如何在实际项目中集成和使用Spring AOP。你可以直接导入此项目到MyEclipse中,运行并观察AOP如何工作,例如...

    Spring AOP应用开源架构源码2021.pdf

    Spring框架是目前Java开发中使用最广泛的应用程序框架之一,它为开发企业级应用提供了全面的基础支持。其中,AOP(面向切面编程)是Spring框架的一个核心特性,它允许开发者将横切关注点(cross-cutting concerns)...

    简单spring aop 例子

    本示例将简要介绍如何在Spring应用中实现AOP,通过实际的代码示例帮助理解其工作原理。 首先,我们要理解AOP的核心概念。AOP是一种编程范式,它允许开发者定义“切面”(Aspects),这些切面封装了特定的关注点,如...

    Spring AOP 16道面试题及答案.docx

    Spring AOP,全称为Aspect Oriented Programming,是面向切面编程的一种编程范式,它是对传统的面向对象编程(OOP)的一种补充。在OOP中,核心是对象,而在AOP中,核心则是切面。切面是关注点的模块化,即程序中的...

    Spring基础:Spring AOP简单使用

    创建一个简单的Spring AOP应用,首先定义一个切面类,包含切点和通知,然后在Spring配置文件中启用AOP并注册切面,最后编写测试类验证AOP功能是否正常工作。 8. **最佳实践** - 适度使用AOP,过多的切面可能导致...

    Spring AOP完整例子

    AOP的核心是切点(Pointcut),它定义了关注点在何处应用。在Spring中,我们通常使用表达式或者注解来定义切点。例如,我们可以使用`@Before`、`@After`、`@Around`、`@AfterReturning`和`@AfterThrowing`等注解来...

    spring aop依赖jar包

    现在,我们回到主题——"springaop依赖的jar包"。在Spring 2.5.6版本中,使用Spring AOP通常需要以下核心jar包: - `spring-aop.jar`:这是Spring AOP的核心库,包含了AOP相关的类和接口。 - `spring-beans.jar`:...

    spring-aop.rar_java aop_spring aop

    这个压缩包可能包含了一个简单的Spring AOP应用示例,同时也涵盖了Java的一些核心概念,如过滤器、监听器、JDBC等。 【描述】"SPRING AOP自主实现,还包括一些java基础,可以很快自主实现切面编程" 提示我们,此...

    springAop-demo.zip

    在这个"springAop-demo.zip"压缩包中,我们很可能会看到一个实际的Spring AOP应用示例,让我们来详细探讨一下Spring AOP的核心概念和使用方法。 1. **切面(Aspect)**:切面是Spring AOP的核心概念,它封装了多个...

    Spring AOP实现机制

    **Spring AOP 实现机制详解** Spring AOP(面向切面编程)是Spring框架的核心特性之一,它允许程序员在不修改源代码的...通过深入理解Spring AOP的实现机制,我们可以更好地利用这一强大的工具,优化我们的应用程序。

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

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

    Spring AOP面向方面编程原理:AOP概念

    ### Spring AOP面向方面编程原理:AOP概念详解 #### 一、引言 随着软件系统的日益复杂,传统的面向对象编程(OOP)逐渐暴露出难以应对某些横切关注点(cross-cutting concerns)的问题。为了解决这一挑战,面向方面编程...

    @AspectJ配置Spring AOP,demo

    `springAOP2`可能是一个包含具体示例代码的目录。`基于@AspectJ配置Spring AOP之一 - 飞扬部落编程仓库-专注编程,网站,专业技术.htm`和其关联的`_files`目录可能包含了一个详细的教程或演示如何配置和运行@AspectJ的...

Global site tag (gtag.js) - Google Analytics