要了解** AOP(Aspect Oriented Programming)面向方面编程 ,我们往往将其和另一个名词即 OOP(Object Oriented Programming)面向对象编程**放一起来比较和理解。
在OOP的模式下,当多个不具有继承关系的对象需引用同一个公共行为时,就会产生大量重复冗余代码。最好理解就是我们的日志模块或者安全监测等。要在函数的调用处打出日志,我们则需要引入日志对象来调用,然而这个日志对象并非业务类所需要的。
AOP所关注的方向是横向的,是OOP的有益补充。为了能让业务类更专注于具体的物业开发,而其他模块功能的增加不修改到目标函数,可通过配置切面来实现达到功能解耦。
了解了大致概念之后,我们直接动手看究竟怎么来执行一个切面。
业务类代码TestController。
@RestController @RequestMapping("/test") public class TestController { public void testFunc(String userName) { //do something System.out.println("Hello " + userName); } } 复制代码
@Aspect @Component public class MyAspect { @Pointcut("execution(public * com.test.TestController.testFunc(..))") public void pointCut() {} @Before("pointCut()") public void before() { log.info("MyAspect before ..."); } @After("pointCut()") public void after() { log.info("MyAspect after ..."); } @AfterReturning("pointCut()") public void afterReturning() { log.info("MyAspect after returning ..."); } @AfterThrowing("pointCut()") public void afterThrowing() { log.info("MyAspect after throwing ..."); } @Around("pointCut()") public void around(ProceedingJoinPoint joinPoint) throws Throwable { log.info("MyAspect around before ..."); joinPoint.proceed(); log.info("MyAspect around after ..."); } } 复制代码
上面的例子就是对业务方法testFunc进行一个简单的切面定义实现。到这里,一个最基本的切面类已经实现了。下面我们继续了解AOP的更多内容。
Spring AOP提供使用org.aspectj.lang.JoinPoint类型获取连接点数据,任何通知方法的第一个参数都可以是JoinPoint(环绕通知是ProceedingJoinPoint,JoinPoint子类)。
如参数类型是JoinPoint、ProceedingJoinPoint类型,可以从“argNames”属性省略掉该参数名(可选,写上也对),这些类型对象会自动传入的,但必须作为第一个参数。
@Aspect @Component public class MyAspect { @Pointcut("execution(public * com.test.TestController.testFunc(..))") public void pointCut() {} @Before("pointCut()") public void before(JoinPoint joinPoint) { String method = joinPoint.getSignature().getName(); log.info("MyAspect before Method:{}::{}", joinPoint.getSignature().getDeclaringTypeName(), method); ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); log.info("ClientIP:{}", request.getRemoteAddr()); } @After("pointCut()") public void after(JoinPoint joinPoint) { String method = joinPoint.getSignature().getName(); log.info("MyAspect after Method:{}::{}", joinPoint.getSignature().getDeclaringTypeName(), method); } @AfterReturning("pointCut()") public void afterReturning(JoinPoint joinPoint) { String method = joinPoint.getSignature().getName(); log.info("MyAspect after returning Method:{}::{}", joinPoint.getSignature().getDeclaringTypeName(), method); } @AfterThrowing("pointCut()") public void afterThrowing(JoinPoint joinPoint) { log.info("MyAspect after throwing ..."); } @Around("pointCut()") public void around(ProceedingJoinPoint joinPoint) throws Throwable { log.info("MyAspect around before ..."); joinPoint.proceed(); log.info("MyAspect around after ..."); } } 复制代码
@Aspect @Component @Order(-1) public class MyAspect2 { //和上面例子一样,省略 } 复制代码
@Component @Aspect public class MyAspect2 implements Ordered { @Override public int getOrder() { //do something to gen order. return 2; } } 复制代码
PointCut的定义包括两个部分:Pointcut表示式(expression)和Pointcut签名(signature)。
//Pointcut表示式 @Pointcut("execution(public * com.test.TestController.testFunc(..))") //Pointcut签名 public void pointCut() {} 复制代码
execution表示式的格式:
execution( modifier-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?) 复制代码
括号中各个pattern分别表示
现在来看看几个例子。
execution(* *(..)) execution(public * com.test.TestController.*(..)) execution(* com.test..*.*(..))
先介绍到这里,欢迎留言讨论分享~