Spring AOP 入门

引言

AOP是软件开发思想发展到一定阶段的产物,AOP的出现并不是为了代替OOP,仅作为OOP的有益补充,在下面的例子中这个概念将会得到印证。AOP的应用场合是受限制的,一般适用于那些具有横切逻辑的应用场合,例如性能监测,访问控制,事务管理,日志记录。在平常的应用开发中AOP很难被使用到,但是AOP是Spring的亮点之一,有必要一看。

一 AOP以及术语

AOP是Aspect Oriented Programing的简称,被译为面向切面编程。AOP希望将散落在业务逻辑函数中的相同代码抽取到一个独立的模块中。举个例子:

class A{
    public void run()
    {
            doSomething();
            doAthings();
            doOtherthing();
    }
}

class B{
    public void run()
    {
            doSomething();
            doBthings();
            doOtherthing();
    }
}

class C{
    public void run()
    {
            doSomething();
            doCthings();
            doOtherthing();
            doMorethings();
    }
}

例如上述三个类中的run方法,都有doSomething和doOtherthing代码,AOP就是要把这些代码抽取出来。我们知道,抽取代码很容易,但是如何将这些独立的逻辑代码再融合到业务类中完成和原来一样的业务操作,这才是AOP要解决的问题。

术语

Joinpoint连接点:程序执行的某个特定位置,例如类初始化前,类初始化后,方法执行前,方法执行后等,Spring只支持方法的连接点,即方法执行前,方法执行后,方法抛出异常时。

Pointcut切点:每个类中都拥有多个连接点,如拥有两个方法的类,这两个方法都是连接点,切点可以在连接点中定位一个或多个连接点。

Advice增强:增强是织入目标类连接点上的一段程序代码,即当程序到达一个执行点后会执行相对应的一段代码,Spring提供的Advice都带有接入点方位,例如BeforeAdvice,AftereturningAdvice,ThrowsAdvice等。只有结合切点和通知才能确定特定的连接点并执行对应代码。

Target目标对象:被嵌入代码的类。

Introductiony引介:可以为类添加属性和方法。

Weaving织入:AOP就像一台织布机,将增强代码嵌入到对应的类中。AOP有三种织入方式:

  • 编译期织入:这要求是用特殊的Java编译器
  • 类装载期织入:要求使用特殊的类装载器
  • 动态代理织入:在运行时期为目标对象生成代理对象的方式实现织入

Spring采用动态代理织入,AspectJ采用编译期织入和类装载织入。

Proxy代理:一个类被AOP织入后,就会产生一个代理对象,他融合了原有类代码和增强代码。

Aspect切面:由切点和增强组成,他包括了连接点定义和横切逻辑代码的定义,SpringAOP就是负责实施切面的框架。

二 Advice增强

Spring使用增强类定义横切逻辑,增强类还包括了在方法的哪一点加入代码的信息。

增强类型

  • 前置增强:在方法执行前实施增强
  • 后置增强:在方法返回后实施增强
  • 异常抛出增强:在目标方法抛出异常后实施增强
  • 环绕增强:在方法执行前和执行后实施增强
  • 引介增强:在目标类中加入新的方法和属性

接下来,我以简单的示例解释前四种增强

//Dog.java
//定义狗的接口
package test.aop;
/**
 * Created by gavin on 15-7-18.
 */
public interface Dog {
    public void shout(String name);
    public void sleep(String name);
}

//ChinaDog.java
//设计中华田园犬
package test.aop;
/**
 * Created by gavin on 15-7-18.
 */
public class ChinaDog implements Dog {
    @Override
    public void shout(String name) {
        System.out.println("中华田园犬"+name+" 叫了一声");
    }
    @Override
    public void sleep(String name) {
        System.out.println("中华田园犬"+name+" 睡着了");
    }
    public void error() throws Exception {
        throw new Exception("shout exception");
    }
}

//BeforeAdvice.java
//前置增强类
package test.aop;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
/**
 * Created by gavin on 15-7-18.
 */
public class BeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        String name = (String)objects[0];    //参数获取
        System.out.println("中华田园犬"+name+" 前置增强");
    }
}

//AfterReturnAdvice.java
//后置增强类
package test.aop;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
/**
 * Created by gavin on 15-7-18.
 */
public class AfterReturnAdvice implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        String name = (String)objects[0];
        System.out.println("中华田园犬"+name+" 后置增强");
    }
}

//SurroundAdvice.java
//环绕增强类
package test.aop;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
 * Created by gavin on 15-7-18.
 */
public class SurroundAdvice implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        Object[] objects = methodInvocation.getArguments();
        String name = (String)objects[0];
        System.out.println("环绕增强----前"+name);

        Object object = methodInvocation.proceed();    //此处执行的是原有函数

        System.out.println("环绕增强----后"+name);

        return object;
    }
}

//ThrowAdvice.java
//异常抛出增强类
package test.aop;
import org.springframework.aop.ThrowsAdvice;
import java.lang.reflect.Method;
/**
 * Created by gavin on 15-7-18.
 */
public class ThrowAdvice implements ThrowsAdvice {
    @Override
    public void afterThrowing(Method method,Object[] args, Object obj ,Exception ex)throws Throwable
    {
        System.out.println("------------------------");
        System.out.println("------异常抛出增强");
        System.out.println("------method "+method.getName());
        System.out.println("------exception "+ex.getMessage());
        System.out.println("------------------------");
    }
}

//AopTest.java
//AOP测试类
package test.aop;
import org.aopalliance.aop.Advice;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
/**
 * Created by gavin on 15-7-18.
 */
public class AopTest {
    private Dog dog;
    private Advice advice;
    private ProxyFactory pf;

    @BeforeTest
    public void init()
    {
        dog = new ChinaDog();
        pf = new ProxyFactory();
        pf.setTarget(dog);
    }

    @Test
    public void beforeAdvice()
    {
        advice = new BeforeAdvice();
        pf.addAdvice(advice);
        Dog dog = (Dog)pf.getProxy();
        dog.shout("jim");
        dog.sleep("tom");
    }

    @Test
    public void afterReturndvice()
    {
        advice = new AfterReturnAdvice();
        pf.addAdvice(advice);
        Dog dog = (Dog)pf.getProxy();
        dog.shout("jim");
        dog.sleep("tom");
    }

    @Test
    public void arroundAdvice()
    {
        SurroundAdvice advice = new SurroundAdvice();
        pf.addAdvice(advice);
        Dog dog = (Dog)pf.getProxy();
        dog.shout("jim");
        dog.sleep("tom");
    }

    @Test
    public void throwAdvice()
    {
        advice = new ThrowAdvice();
        pf.addAdvice(advice);
        ChinaDog dog = (ChinaDog)pf.getProxy();
        try {
            dog.error();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

分别对四种增强方式进行测试,得到以下结果:

前置增强

后置增强

环绕增强

异常抛出增强

三 创建切面

看完上面的代码,我们可能会注意一个问题,那就是增强类被织入到了目标类的所有方法中,即shout和sleep方法都会被增强。这时就需要用切点来定位了。

Spring通过org.springframework.aop.Pointcut接口描述切点,通过这个接口的ClassFilter定位到特定的类,通过MethodMatcher定位到特定的方法。

通过静态正则表达式方法匹配切面

在applicationContext.xml中加入以下xml语句:

<!--aop-->
<!-- 被代理对象 -->
<bean class="test.aop.ChinaDog" id="chinaDog"/>

<!-- 前置通知 -->
<bean class="test.aop.BeforeAdvice" id="beforeAdvice"/>

<!-- 定义切面 -->
<bean id="beforeAdviceFilter" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
       <property name="advice" ref="beforeAdvice"/>
       <property name="patterns">
              <list>
                     <value>s.*</value>
              </list>
       </property>
</bean>
<!-- 代理对象 -->
<bean class="org.springframework.aop.framework.ProxyFactoryBean" id="proxyFactoryBean">
       <!-- 设置代理接口集合 -->
       <property name="proxyInterfaces">
              <list>
                     <!--接口要写全-->
                     <value>test.aop.Dog</value>
              </list>
       </property>
       <!-- 把增强织入代理对象 -->
       <property name="interceptorNames">
              <list>
                     <value>beforeAdviceFilter</value>
              </list>
       </property>
       <!-- 配置被代理对象 -->
       <property name="target" ref="chinaDog"/>
</bean>

在AopTest.java中调用applicationContext.xml:

public static void main(String[] args)
{
    //PropertyConfigurator.configure("web/WEB-INF/log4j.properties");
    ApplicationContext ac = new FileSystemXmlApplicationContext("web/WEB-INF/applicationContext.xml");
    Dog dog = (Dog)ac.getBean("proxyFactoryBean");
    dog.shout("jim");
    dog.sleep("tom");
}

结果同前置增强的结果。

四 基于@AspectJ配置切面

在第三节中我们使用Pointcut和Advice接口来描述切点和增强,并用Advisor整合描述切面,@AspectJ则使用注解来描述切点和增强,描述的内容完全相同,只不过方式不同。

我们先用@AspectJ定义一个切面:

package test.aop;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

/**
 * Created by gavin on 15-7-18.
 */
@Aspect
public class PreAspect {
    @Before("execution(* shout(..))")//意思是返回值任意 参数任意
    public void beforeShout()
    {
        System.out.println("准备叫");
    }
}

在applicationContext.xml中设置:

<!--AOP-->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">

<!--基于@AspectJ切面的驱动器-->
<aop:aspectj-autoproxy/>
<bean class="test.aop.PreAspect" />

在AopTest.java中进行测试,代码如下:

public static void main(String[] args)
{
    //PropertyConfigurator.configure("web/WEB-INF/log4j.properties");
    ApplicationContext ac = new FileSystemXmlApplicationContext("web/WEB-INF/applicationContext.xml");
    Dog dog = (Dog)ac.getBean("chinaDog");    //注意这里
    dog.shout("jim");
    dog.sleep("tom");
}

结果为:

只有shout方法前调用了beforeShout方法。

总结

AOP是OOP的有益补充,他为程序开发提供了一个新的思考角度,可以将重复性的横切逻辑抽取到统一的模块中。只有通过OOP的纵向抽象和AOP的横向抽取,程序才可以真正的解决重复性代码问题。

Spring采用JDK动态代理和CGLib动态代理技术在运行期间织入增强。所以用户不用装备特殊的编译器或类装载器。要使用JDK动态代理,目标对象必须实现接口,而CGLib不对目标类采取限制。

JDK创建代理对象效率比CGLib高,而CGLib创建的代理对象运行效率比JDK的要高。

时间: 2024-11-10 10:33:29

Spring AOP 入门的相关文章

Spring AOP入门——概念及注意点

AOP是什么? AOP从功能上来说就是在执行某些业务逻辑的前后,可以允许你动态地添加一些操作(比如记录日志.或者是判断是否有权限等),这些操作的添加,完全不耦合于原来的业务逻辑,从而对原有业务逻辑完全是透明. 也就是说,这段操作和业务逻辑是完全分开的,它可能在项目中需要横切多个模块,且其自身也是一个独立的模块,贯穿了整个项目.我们完全可以根据需要启用或者停用这个功能. AOP的典型应用就是事务管理和日志. AOP中的概念 下面这些术语并不是Spring定义的.由于AOP中的术语不是那么形象,所以

Spring -- AOP入门基础

动态代理 我们在日常开发过程中是否会遇到下图中的这种状况 红框中的是我们要输出的日志,你是否发现,日志中大部分信息都是相同的,并且如果我们要修改一个地方,所有的地方都需要改,而且代码看起来还比较冗余 下面我们就可以通过动态代理的方式解决这个问题 看下代码 public interface Calculation { public int add(int x, int y); public int sub(int x, int y); public int mul(int x, int y); p

Spring AOP入门基础-继承、装饰者,代理的选择

关于Spring AOP,底层是基于动态代理实现的,下面简单的学习下为什么选择动态代理,而不选择继承实现,装饰者模式实现,下面参考如下业务场景下理解. 业务场景 业务层如果有业务需求,需要在注册用户,升级用户,和删除用户方法前都进行一次权限验证,最原始的方法就是在业务层每个方法前都添加代码验证.这是最原始的方式,在实际业务中有很多的方法,那都需要重写修改,很显然这是不合理的,因此衍生如下几个解决方案: (1)使用继承类,在继承类中对继承的方法进行修改,参考DogDemo01 (2)使用装饰者模式

spring aop入门

一.何为AOP? spring 的两大核心思想无非是 IOC和AOP.那么Spring 的 aop 是神马意思呢?AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术.AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,提高代码的灵活性和可扩展性,AOP可以说也是这种目标的一种实现. 二.有何用途? 主要用于将日志记录,性能统计,安全控制

[Spring框架]Spring AOP基础入门总结二:Spring基于AspectJ的AOP的开发.

前言: 在上一篇中: [Spring框架]Spring AOP基础入门总结一. 中 我们已经知道了一个Spring AOP程序是如何开发的, 在这里呢我们将基于AspectJ来进行AOP 的总结和学习. 一, AspectJ的概述: AspectJ是一个面向切面的框架,它扩展了Java语言.AspectJ定义了AOP语法所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件. Spring为了简化自身的AOP的开发,将AspectJ拿过来作为Spring自身一个AOP的开发.

[Spring框架]Spring AOP基础入门总结一.

前言:前面已经有两篇文章讲了Spring IOC/DI 以及 使用xml和注解两种方法开发的案例, 下面就来梳理一下Spring的另一核心AOP. 一, 什么是AOP 在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型.利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务

Spring @AspectJ 实现AOP 入门例子(转)

AOP的作用这里就不再作说明了,下面开始讲解一个很简单的入门级例子. 引用一个猴子偷桃,守护者守护果园抓住猴子的小情节. 1.猴子偷桃类(普通类): Java代码   package com.samter.common; /** * 猴子 * @author Administrator * */ public class Monkey { public void stealPeaches(String name){ System.out.println("[猴子]"+name+&quo

使用Spring的注解方式实现AOP入门

首先在Eclipse中新建一个普通的Java Project,名称为springAOP.为了使用Spring的注解方式进行面向切面编程,需要在springAOP项目中加入与AOP相关的jar包,spring aop需要额外的jar包有: com.springsource.org.aopalliance-1.0.0.jar com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar spring-aop-4.2.5.RELEASE.jar sprin

Spring入门(二)— IOC注解、Spring测试AOP入门

一.Spring整合Servlet背后的细节 1. 为什么要在web.xml中配置listener <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> 配置listener主要是为了捕获项目发布 | 服务器启动的契机 ,为了解析xml , 创建工厂. 这个listener是spring官方提供