Spring Aop Demo

    最近做项目时,接入了四个极其重要的外部接口。为了核对接口的准确性,把这四个接口的所有入参和返回值都打了日志。完全是重复性的机械劳动。端午节的时候回想这件事,发现AOP不正是职业解决这种事情的吗!而且用AOP打日志,原有的业务代码省去了日志代码,更加精简。

    先写了个AOP的demo体验下功能。

导入aop相关的依赖

    虽然没有显式引入cglib.jar,但是spring-aop-3.2.jar已经集成了cglib的功能。所以我的服务类没有实现接口,但依然能实现AOP的功能。

<properties>
    <v.spring>3.2.0.RELEASE</v.spring>
    <v.fastjson>1.1.36</v.fastjson>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${v.spring}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>${v.spring}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${v.spring}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${v.spring}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>${v.spring}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>${v.spring}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>${v.spring}</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>${v.fastjson}</version>
    </dependency>
</dependencies>

需要被切入的服务类

package com.jd.ls;

import org.springframework.stereotype.Service;

/**
 * Created by Administrator on 2016/6/8.
 */
@Service
public class Hello {
	public String hello(String name) {

		String s = "hello, " + name;
		System.out.println(s);
		return s;
	}

	public void throwEx() {
		throw new NullPointerException();
	}
}

AOP切面类

package com.jd.ls;

import com.alibaba.fastjson.JSON;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * Created by Administrator on 2016/6/8.
 */

@Component
@Aspect
public class AopTest {

	//如果要设置多个切点可以使用 || 拼接
	@Pointcut("execution(* com.jd.ls.Hello.*(..))")
	private void anyMethod() {
	}

	@Before(value = "anyMethod()")
	public void doBefore(JoinPoint joinPoint) {

		System.out.println("前置通知");
		System.out.println("方法签名:" + joinPoint.getSignature().toLongString());
	}

	@AfterReturning(value = "anyMethod()", returning = "result")
	public void doAfter(JoinPoint jp, String result) {

		System.out.println("后置通知, result:\t" + result);
	}

	@After("anyMethod()")
	public void after() {

		System.out.println("最终通知");
	}

	@AfterThrowing(value = "anyMethod()", throwing = "e")
	public void doAfterThrow(JoinPoint joinPoint, Throwable e) {
		e.printStackTrace();
	}

	@Around("anyMethod()")
	public Object doBasicProfiling(ProceedingJoinPoint joinPoint) throws Throwable {
		System.out.println("进入环绕通知");
		System.out.println("目标类名称:" + joinPoint.getTarget().getClass().getName());
		System.out.println("方法签名:" + joinPoint.getSignature().toLongString());
		System.out.println("方法参数:");
		for (Object o : joinPoint.getArgs()) {
			System.out.println(o.getClass().getName() + ":\t" + o.toString());
		}
		System.out.println("target" + joinPoint.getTarget());
		System.out.println("staticPart:" + joinPoint.getStaticPart().toShortString());
		System.out.println("kind:" + joinPoint.getKind());
		System.out.println("sourceLocation:" + joinPoint.getSourceLocation());
		Object object = joinPoint.proceed();// 执行该方法
		System.out.println("执行结果" + JSON.toJSONString(object));
		System.out.println("退出方法");
		System.out.println(joinPoint.toLongString());
		return object;
	}

}

    @Pointcut的路径参数指定了哪些类的哪些方法会作为切入点进行横向编程。而@Before,@After等注解则指定在什么时机织入代码。

spring配置文件

<?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: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-3.0.xsd
				http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
				http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    
    <context:component-scan base-package="com.jd.ls"/>
    <!-- 打开aop 注解 -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>

</beans>

单元测试

package com.jd.ls;


import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring-config.xml"})
public class AppTest {

	@Autowired
	Hello hello;

	@Test
	public void testAop() {
		System.out.println("====================启动服务");
		hello.hello("world");
		System.out.println("====================终止服务");
	}

	@Test
	public void testAopEx() {
		System.out.println("====================启动服务");
		hello.throwEx();
		System.out.println("====================终止服务");
	}

}

测试结果

    方法1的运行结果
====================启动服务
进入环绕通知
目标类名称:com.jd.ls.Hello
方法签名:public java.lang.String com.jd.ls.Hello.hello(java.lang.String)
方法参数:
java.lang.String:	world
targetcom.jd.ls.Hello@4ac216
staticPart:execution(Hello.hello(..))
kind:method-execution
sourceLocation:org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint$SourceLocationImpl@1777b1
前置通知
方法签名:public java.lang.String com.jd.ls.Hello.hello(java.lang.String)
hello, world
执行结果"hello, world"
退出方法
execution(public java.lang.String com.jd.ls.Hello.hello(java.lang.String))
最终通知
后置通知, result:	hello, world
====================终止服务

    方法二的运行结果
====================启动服务
进入环绕通知
目标类名称:com.jd.ls.Hello
方法签名:public void com.jd.ls.Hello.throwEx()
方法参数:
targetcom.jd.ls.Hello@4ac216
staticPart:execution(Hello.throwEx())
kind:method-execution
sourceLocation:org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint$SourceLocationImpl@1777b1
前置通知
方法签名:public void com.jd.ls.Hello.throwEx()
最终通知
java.lang.NullPointerException
	at com.jd.ls.Hello.throwEx(Hello.java:18)
	at com.jd.ls.Hello$$FastClassByCGLIB$$3f78c6d2.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:698)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
	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.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:80)
	at com.jd.ls.AopTest.doBasicProfiling(AopTest.java:59)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610)
	at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:65)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
	at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:42)
	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.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:55)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:90)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631)
	at com.jd.ls.Hello$$EnhancerByCGLIB$$d4ecbb7c.throwEx(<generated>)
	at com.jd.ls.AppTest.testAopEx(AppTest.java:27)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
	at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
	at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
	at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:78)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

    方法二抛了异常,异常的堆栈在AOP的切面代码里被输出。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值