返回顶部

收藏

Spring AOP入门实例

更多

Spring AOP框架可以方便的实现面向方面编程。面向方面编程通俗的将就是可以在不改变原方法代码的情况下,改变原方法的执行,给方法添加额外的执行,例如为方法添加日志,为方法添加事务等等。

在Spring AOP中有4中不同的advice,分别为:

  • Before advice – 在方法执行之前执行
  • After returning advice – 在方法顺利执行完毕时执行
  • After throwing advice – 在方法执行结束抛出异常时执行
  • Around advice – 在方法的执行前后,抛出异常时都可以注入方法,是上面三种切入点的综合

下面我们通过一个既简单的例子看spring aop是如何工作的:

首先我们需要创建一个maven项目,在pom.xml中添加如下依赖项:

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>3.0.0.RC2</version>
    </dependency>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>2.2.2</version>
    </dependency>

然后我们创建一个简单的类来作为spring aop切入的对象。

package cn.outofmemory.hellospring.aop;

public class BookService {

    public void returnBook() {
        System.out.println("return book");
    }

    public String getBook(String isbn) {
        return "getBook " + isbn;
    }

    public void clearBook() {
        throw new RuntimeException("throw exception method");
    }
}

然后在src/main/resources目录下新建spring.xml配置bean:

<?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-3.0.xsd">

    <bean id="bookService" class="cn.outofmemory.hellospring.aop.BookService">
    </bean>

</beans>

在上面的步骤中还没有涉及到AOP,我们准备了AOP切入的对象BookService类,下面正式进入AOP阶段:

我们先试试Before advice

Before advice会在方法执行之前执行,我们需要创建一个从MethodBeforeAdvice接口继承的类,并实现它的before方法:

package cn.outofmemory.hellospring.aop;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

public class BeforeAspect implements MethodBeforeAdvice {

    @Override
    public void before(Method arg0, Object[] arg1, Object arg2)
            throws Throwable {
        System.out.println("BeforeAspect working..");
    }

}

下面我们在spring.xml文件中配置BeforeAspect,并添加一个代理用来代理bookService。

<?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-3.0.xsd">

    <bean id="bookService" class="cn.outofmemory.hellospring.aop.BookService">
    </bean>

    <bean id="beforeAspect" class="cn.outofmemory.hellospring.aop.BeforeAspect" />

    <bean id="bookServiceProxy" 
                 class="org.springframework.aop.framework.ProxyFactoryBean"> 
        <property name="target" ref="bookService" /> 
        <property name="interceptorNames">
            <list>
                <value>beforeAspect</value>
            </list>
        </property>
    </bean>

</beans>

现在我们在spring的配置文件中配置了beforeAspect的实例,并且创建了一个bookService的代理bookServiceProxy。

下面我们在App.java中使用代理类看下输出:

package cn.outofmemory.hellospring.aop;

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

/**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args )
    {
        ApplicationContext appContext = new ClassPathXmlApplicationContext("spring.xml");

        BookService service = (BookService)appContext.getBean("bookServiceProxy");

        String book  = service.getBook("test isbn");
        System.out.println(String.format("getBook %s", book));

        service.returnBook();
        //service.clearBook();
    }
}

输出:

BeforeAspect working..
getBook getBook test isbn
BeforeAspect working..
return book

可以看到在方法执行之前,都执行了beforeAspect中定义的方法。

再看下after returning advice

顾名思义,after returning是在方法执行之后执行的,我们来实现一个AfterReturningAdvice。

如下代码:

package cn.outofmemory.hellospring.aop;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

public class AfterReturningAspect implements AfterReturningAdvice {

    @Override
    public void afterReturning(Object arg0, Method arg1, Object[] arg2,
            Object arg3) throws Throwable {
        String msg = String.format("%s complete", arg1.getName());
        System.out.println(msg);
    }

}

需要在spring.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-3.0.xsd">

    <bean id="bookService" class="cn.outofmemory.hellospring.aop.BookService">
    </bean>

    <bean id="beforeAspect" class="cn.outofmemory.hellospring.aop.BeforeAspect" />
    <bean id="afterAspect" class="cn.outofmemory.hellospring.aop.AfterReturningAspect" />

    <bean id="bookServiceProxy" 
                 class="org.springframework.aop.framework.ProxyFactoryBean"> 
        <property name="target" ref="bookService" /> 
        <property name="interceptorNames">
            <list>
                <value>beforeAspect</value>
                <value>afterAspect</value>
            </list>
        </property>
    </bean>

</beans>

然后再次执行项目会输入如下内容:

BeforeAspect working..
getBook complete
getBook getBook test isbn
BeforeAspect working..
return book
returnBook complete

可以看到在每个方法执行完之后打印出了xxx complete

After throwing advice

After throwing advice是在方法抛出异常时截获并执行一段代码,需要实现org.springframework.aop.ThrowsAdvice接口:

package cn.outofmemory.hellospring.aop;

import org.springframework.aop.ThrowsAdvice;

public class AfterThrowsAspect implements ThrowsAdvice {
    public void afterThrowing(RuntimeException e) throws Throwable {
        System.out.println("error occor : Throw exception!");
    }
}

需要修改下bean的配置文件:

<?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-3.0.xsd">

    <bean id="bookService" class="cn.outofmemory.hellospring.aop.BookService">
    </bean>

    <bean id="beforeAspect" class="cn.outofmemory.hellospring.aop.BeforeAspect" />
    <bean id="afterAspect" class="cn.outofmemory.hellospring.aop.AfterReturningAspect" />
    <bean id="afterThrowsAspect" class="cn.outofmemory.hellospring.aop.AfterThrowsAspect" />

    <bean id="bookServiceProxy" 
                 class="org.springframework.aop.framework.ProxyFactoryBean"> 
        <property name="target" ref="bookService" /> 
        <property name="interceptorNames">
            <list>
                <value>beforeAspect</value>
                <value>afterAspect</value>
                <value>afterThrowsAspect</value>
            </list>
        </property>
    </bean>

</beans>

然后在App.java中调用service.clearBook();方法,可以看到如下输入:

BeforeAspect working..
getBook complete
getBook getBook test isbn
BeforeAspect working..
return book
returnBook complete
BeforeAspect working..
error occor : Throw exception!
Exception in thread "main" java.lang.RuntimeException: throw exception method
    at cn.outofmemory.hellospring.aop.BookService.clearBook(BookService.java:16)
    at cn.outofmemory.hellospring.aop.BookService$$FastClassByCGLIB$$6cb0d166.invoke(<generated>)
    at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:692)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    at org.springframework.aop.framework.adapter.ThrowsAdviceInterceptor.invoke(ThrowsAdviceInterceptor.java:124)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:50)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:50)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:625)
    at cn.outofmemory.hellospring.aop.BookService$$EnhancerByCGLIB$$19ce7920.clearBook(<generated>)
    at cn.outofmemory.hellospring.aop.App.main(App.java:22)

以上输出说明在方法抛出异常时,aop截获了这个异常并输出了error occor : Throw exception!

最后我们看下Around advice

Around advice整合了所有其他的advice,可以任意控制是否要在方法的前后或者有异常发生时来截获方法的执行。他需要实现MethodInterceptor接口。

在around方法中需要通过调用methodInvocation.proceed();来调用被代理类的方法,否则被代理类的方法就不会执行,也就是说我们可以通过这种方法完全改变方法的执行。

package cn.outofmemory.hellospring.aop;

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

public class AroundAspect implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("AroundAspect method call start ..");
        try {
            // 执行被代理对象的方法
            Object result = methodInvocation.proceed();

            // 方法成功执行,和AfterReturningAdvice相同
            System.out.println("AroundAspect method call complete!");

            return result;
        } catch (RuntimeException ex) {
            // 方法执行出异常,
            System.out.println("AroundAspect : Throw exception!");
            throw ex;
        }
    }

}

spring.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-3.0.xsd">

    <bean id="bookService" class="cn.outofmemory.hellospring.aop.BookService">
    </bean>

    <bean id="beforeAspect" class="cn.outofmemory.hellospring.aop.BeforeAspect" />
    <bean id="afterAspect" class="cn.outofmemory.hellospring.aop.AfterReturningAspect" />
    <bean id="afterThrowsAspect" class="cn.outofmemory.hellospring.aop.AfterThrowsAspect" />
    <bean id="aroundAspect" class="cn.outofmemory.hellospring.aop.AroundAspect" />

    <bean id="bookServiceProxy" 
                 class="org.springframework.aop.framework.ProxyFactoryBean"> 
        <property name="target" ref="bookService" /> 
        <property name="interceptorNames">
            <list>
                <value>aroundAspect</value>             
            </list>
        </property>
    </bean>

</beans>

运行程序,得到如下输出:

AroundAspect method call start ..
AroundAspect method call complete!
getBook getBook test isbn
AroundAspect method call start ..
return book
AroundAspect method call complete!
AroundAspect method call start ..
AroundAspect : Throw exception!
Exception in thread "main" java.lang.RuntimeException: throw exception method
    at cn.outofmemory.hellospring.aop.BookService.clearBook(BookService.java:16)
    at cn.outofmemory.hellospring.aop.BookService$$FastClassByCGLIB$$6cb0d166.invoke(<generated>)
    at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:692)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    at cn.outofmemory.hellospring.aop.AroundAspect.invoke(AroundAspect.java:13)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:625)
    at cn.outofmemory.hellospring.aop.BookService$$EnhancerByCGLIB$$244315da.clearBook(<generated>)
    at cn.outofmemory.hellospring.aop.App.main(App.java:22)

总结: 本文只是spring aop的入门,我们在配置文件中为一个servic配置了代理,然后实验了4中不同的切入方式。

您可以下载本文相关的源码。

标签:spring,AOP,java

收藏

1人收藏

支持

2

反对

0

相关聚客文章
  1. 博主 发表 2015-06-18 08:04:10 Spring之思
  2. yeseng 发表 2017-09-01 14:13:03 Spring源码-AOP(三)Springboot与Aop
  3. 衔山 发表 2015-07-09 14:56:47 基于Redis的分布式锁的简易实现
  4. root 发表 2015-04-30 02:20:01 java框架篇—spring AOP 实现原理
  5. 刘莉莉 发表 2017-12-25 13:19:11 Spring核心技术原理-(2)-通过Web开发演进过程了解一下为什么要有Spring AOP?
  6. 博主 发表 2015-03-18 08:14:03 Spring之思
  7. sulong 发表 2008-02-21 02:18:14 AOP简介,理解AOP
  8. 漠然 发表 2015-07-18 05:47:11 Spring AOP笔记
  9. 加多 发表 2017-08-13 06:33:33 常用Web框架SpringMVC及WebX级联容器原理探究
  10. BYSocket 发表 2017-12-22 06:26:04 Spring Boot 2.0 自动配置原理浅析
  11. erenzone@gmail.com 发表 2015-07-07 03:42:59 Spring+Ehcache使用
  12. hellas 发表 2018-01-19 11:07:42 Spring Boot 工具 Spring Cloud Skipper 1.0 RC1 发布

发表评论