AOP 面向切面编程
适用场景:日志记录、执行前后拦截等
步骤
0. 引入相关依赖
1. 编写实际业务方法
2. 编写切面类
2.1 定义连接点
2.2 使用通知
3. 调用业务方法
0. 依赖
<dependencies> <!-- test --> <dependency> <groupid>junit</groupid> <artifactid>junit</artifactid> <scope>test</scope> </dependency> <dependency> <groupid>org.springframework</groupid> <artifactid>spring-test</artifactid> <scope>test</scope> </dependency> <dependency> <groupid>ch.qos.logback</groupid> <artifactid>logback-classic</artifactid> </dependency> <dependency> <groupid>org.slf4j</groupid> <artifactid>jcl-over-slf4j</artifactid> </dependency> <dependency> <groupid>org.springframework</groupid> <artifactid>spring-core</artifactid> <exclusions> <exclusion> <groupid>commons-logging</groupid> <artifactid>commons-logging</artifactid> </exclusion> </exclusions> </dependency> <dependency> <groupid>org.springframework</groupid> <artifactid>spring-context</artifactid> </dependency> <dependency> <groupid>org.aspectj</groupid> <artifactid>aspectjrt</artifactid> </dependency> <dependency> <groupid>org.aspectj</groupid> <artifactid>aspectjweaver</artifactid> </dependency> </dependencies>
版本自行去找最新版
配置:
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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" 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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component -scan base-package="com.youthlin.demo"></context:component> <!-- aop --> <aop:aspectj -autoproxy></aop:aspectj> </beans>
Logback:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <!-- https://logback.qos.ch/manual/layouts.html#coloring --> <pattern> %d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) [%15.15t]%X{sessionId} %cyan(%-40.40logger{39}) : %m%n </pattern> </encoder> </appender> <logger name="org" level="warn"></logger> <root level="${log.level:-debug}"> <appender -ref ref="STDOUT"></appender> </root> </configuration>
1. 业务方法
在这个类写实际的业务方法。
package com.youthlin.demo.service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; /** * 创建者: youthlin.chen 日期: 17-3-26. */ @Service public class HelloService { private static final Logger LOGGER = LoggerFactory.getLogger(HelloService.class); @SuppressWarnings("SameParameterValue") public String sayHello(String name) { LOGGER.debug("in say hello method"); return "Hello, " + name; } public void voidFun() { LOGGER.debug("void method."); throw new RuntimeException(""); } }
2. 切面通知
在这个类定义要拦截的方法及拦截的操作。
package com.youthlin.demo.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.util.StopWatch; import java.util.Arrays; /** * 创建者: youthlin.chen 日期: 17-3-26. */ @Aspect//声明切面 @Component//让 Spring 扫描到 public class LogAop { private static final Logger LOGGER = LoggerFactory.getLogger(LogAop.class); @SuppressWarnings("unused") @Pointcut(value = "execution(* com.youthlin.demo.service.*.*(..))")//定义连接点 private void pointcut() { } @Around("pointcut()")//环绕通知 public Object processTx(ProceedingJoinPoint pjp) throws Throwable { LOGGER.debug("环绕通知 之前"); StopWatch stopWatch = new StopWatch(); stopWatch.start(); Object proceed = pjp.proceed(); stopWatch.stop(); LOGGER.debug("环绕通知 之后. {}ms", stopWatch.getTotalTimeMillis()); return proceed; } @Before("pointcut()")//前置通知 public void before(JoinPoint jp) { Object[] args = jp.getArgs(); LOGGER.debug("前置通知 {} args = {}", jp.getSignature(), Arrays.toString(args)); } @AfterReturning(pointcut = "pointcut()", returning = "result")//后置返回通知 public void afterReturning(Object result) { LOGGER.debug("后置返回通知 result = {}", result); } @AfterThrowing(pointcut = "pointcut()", throwing = "throwable")//后置异常通知 public void afterThrowing(Throwable throwable) { LOGGER.debug("后置异常通知", throwable); } @After("pointcut()")//后置通知 public void after() { LOGGER.debug("后置通知"); } }
3. 测试
package com.youthlin.demo.test; import com.youthlin.demo.service.HelloService; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import javax.annotation.Resource; /** * 创建者: youthlin.chen 日期: 17-3-26. */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:/app.xml"}) public class AopTest { private static final Logger LOGGER = LoggerFactory.getLogger(AopTest.class); @Resource private HelloService service; @Test public void logTest() { LOGGER.debug("{}", service.sayHello("Lin")); } @Test(expected = RuntimeException.class) public void aVoid() { service.voidFun(); } }
测试结果:
2017-03-26 20:40:56.063 DEBUG [ main] com.youthlin.demo.aop.LogAop : 环绕通知 之前 2017-03-26 20:40:56.073 DEBUG [ main] com.youthlin.demo.aop.LogAop : 前置通知 void com.youthlin.demo.service.HelloService.voidFun() args = [] 2017-03-26 20:40:56.134 DEBUG [ main] com.youthlin.demo.service.HelloService : void method. 2017-03-26 20:40:56.135 DEBUG [ main] com.youthlin.demo.aop.LogAop : 后置通知 2017-03-26 20:40:56.138 DEBUG [ main] com.youthlin.demo.aop.LogAop : 后置异常通知 java.lang.RuntimeException: at com.youthlin.demo.service.HelloService.voidFun(HelloService.java:22) at com.youthlin.demo.service.HelloService$$FastClassBySpringCGLIB$$8a47ffe4.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:721) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:52) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85) at com.youthlin.demo.aop.LogAop.processTx(LogAop.java:37) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:629) at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:618) at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:47) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:52) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:62) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:656) at com.youthlin.demo.service.HelloService$$EnhancerBySpringCGLIB$$340752f6.voidFun(<generated>) at com.youthlin.demo.test.AopTest.aVoid(AopTest.java:30) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.internal.runners.statements.ExpectException.evaluate(ExpectException.java:19) at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75) at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86) at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:237) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70) 2017-03-26 20:40:56.151 DEBUG [ main] com.youthlin.demo.aop.LogAop : 环绕通知 之前 2017-03-26 20:40:56.151 DEBUG [ main] com.youthlin.demo.aop.LogAop : 前置通知 String com.youthlin.demo.service.HelloService.sayHello(String) args = [Lin] 2017-03-26 20:40:56.151 DEBUG [ main] com.youthlin.demo.service.HelloService : in say hello method 2017-03-26 20:40:56.158 DEBUG [ main] com.youthlin.demo.aop.LogAop : 环绕通知 之后. 0ms 2017-03-26 20:40:56.158 DEBUG [ main] com.youthlin.demo.aop.LogAop : 后置通知 2017-03-26 20:40:56.158 DEBUG [ main] com.youthlin.demo.aop.LogAop : 后置返回通知 result = Hello, Lin 2017-03-26 20:40:56.158 DEBUG [ main] com.youthlin.demo.test.AopTest : Hello, Lin
完整代码:https://github.com/YouthLin/examples/tree/master/example-aop
声明
- 本作品采用署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。除非特别注明, 霖博客文章均为原创。
- 转载请保留本文(《Spring AOP 小例子》)链接地址: https://youthlin.com/?p=1431
- 订阅本站:https://youthlin.com/feed/