Java Spring AOP代码3分钟快速入手
AOP
Spring入门(十):Spring AOP使用讲解 - 掘金
maven的依赖:
<dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId>
</dependency>
<!--aspectj支持-->
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId>
</dependency>
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId>
</dependency>
demo1
基于注解实现
AopAnnotation:
package com.example.learn.aop;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface AopAnnotation {
}
TestAspect:
package com.example.learn.aop;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;@Aspect
@Component
@Slf4j
public class TestAspect {@Pointcut("@annotation(com.example.learn.aop.AopAnnotation)")private void cut() {}@Around("cut()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {log.info("====环绕通知start");// 注解所切的方法所在类的全类名String typeName = joinPoint.getTarget().getClass().getName();log.info("目标对象:[{}]", typeName);// 注解所切的方法名String methodName = joinPoint.getSignature().getName();log.info("所切方法名:[{}]", methodName);StringBuilder sb = new StringBuilder();// 获取参数Object[] arguments = joinPoint.getArgs();for (Object argument : arguments) {sb.append(argument.toString());}log.info("所切方法入参:[{}]", sb.toString());// 统计方法执行时间long start = System.currentTimeMillis();//执行目标方法,并获得对应方法的返回值Object result = joinPoint.proceed();log.info("返回结果:[{}]", result);long end = System.currentTimeMillis();log.info("====执行方法共用时:[{}]", (end - start));log.info("====环绕通知之结束");return result;}}
实现一个被织入的类,注意这个类得是Bean,一般aop都是针对容器的,针对普通类的很少且不常用:
AopClass:
package com.example.learn.aop;import org.springframework.stereotype.Component;@Component
public class AopClass {@AopAnnotationpublic void testAop() {System.out.println("func invoked!");}
}
测试:
正是因为AOP是针对Bean的,因此测试的时候不要像下面这样去测试,因为new出来的这个对象没有被Spring管理:
@SpringBootTest
class LearnApplicationTests {@Testvoid contextLoads() {AopClass aopClass = new AopClass();aopClass.testAop();}}
憨憨行为。应该注入容器来测试:
@SpringBootTest
class LearnApplicationTests {@Autowiredprivate AopClass aopClass;@Testvoid contextLoads() {aopClass.testAop();}}
打印结果:
. ____ _ __ _ _/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/ ___)| |_)| | | | | || (_| | ) ) ) )' |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot :: (v2.5.5)2023-10-27 10:56:13.600 INFO 16048 --- [ main] com.example.learn.LearnApplicationTests : Starting LearnApplicationTests using Java 1.8.0_381 on DESKTOP-NFN5QCN with PID 16048 (started by DELL in F:\OneDrive\Java学习\yunfei\learn)
2023-10-27 10:56:13.604 INFO 16048 --- [ main] com.example.learn.LearnApplicationTests : No active profile set, falling back to default profiles: default
2023-10-27 10:56:15.202 INFO 16048 --- [ main] com.example.learn.LearnApplicationTests : Started LearnApplicationTests in 1.916 seconds (JVM running for 2.886)
2023-10-27 10:56:15.408 INFO 16048 --- [ main] com.example.learn.aop.TestAspect : ====环绕通知start
2023-10-27 10:56:15.408 INFO 16048 --- [ main] com.example.learn.aop.TestAspect : 目标对象:[com.example.learn.aop.AopClass]
2023-10-27 10:56:15.410 INFO 16048 --- [ main] com.example.learn.aop.TestAspect : 所切方法名:[testAop]
2023-10-27 10:56:15.410 INFO 16048 --- [ main] com.example.learn.aop.TestAspect : 所切方法入参:[]
func invoked!
2023-10-27 10:56:15.420 INFO 16048 --- [ main] com.example.learn.aop.TestAspect : 返回结果:[null]
2023-10-27 10:56:15.420 INFO 16048 --- [ main] com.example.learn.aop.TestAspect : ====执行方法共用时:[10]
2023-10-27 10:56:15.420 INFO 16048 --- [ main] com.example.learn.aop.TestAspect : ====环绕通知之结束
demo2
写一个切面类:
package com.example.base.aop;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;@Aspect
public class Detector {@Pointcut("execution(* com.example.base.AopController.hello(..))")public void helloAspect() {}@Before("helloAspect()")public void beforeAction() {System.out.println("before hello~");}@After("helloAspect()")public void afterAction() {System.out.println("after hello~");}@AfterReturning("helloAspect()")public void afterReturningAction() {System.out.println("after return~");}@Around("helloAspect()")public Object aroundAction(ProceedingJoinPoint pjp) throws Throwable {System.out.println("around~");pjp.proceed();System.out.println("around~");return null;}
}
切点为Controller的一个方法:
package com.example.base;import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class AopController {@GetMapping("/hello")public String hello(@RequestParam("id") int userId) {return null;}
}
写切面的配置类,自动加载切面:
package com.example.base.aop;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration
@EnableAspectJAutoProxy
@ComponentScan
public class Config {@Beanpublic Detector detector() {return new Detector();}
}
启动项目,浏览器http://localhost:8080/hello?id=1
触发打印:
2023-09-06 15:49:32.974 INFO 198380 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2023-09-06 15:49:32.974 INFO 198380 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2023-09-06 15:49:32.975 INFO 198380 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
around~
before hello~
after return~
after hello~
around~
Disconnected from the target VM, address: '127.0.0.1:61026', transport: 'socket'