一、概述
1.1、官网
AOP的中文名称是面向切面编程或者面向方面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
1.2、通俗描述
不通过修改源代码的方式,在主干功能里面添加新功能。
1.3、案例说明
二、底层原理
AOP底层使用动态代理技术。有两种情况的动态代理:
- 有接口情况:JDK动态代理
- 无接口情况:CGLIB动态代理,基于类的继承,使用代理子类的形式,对父类的方法进行重写,完成方法的增强
2.1、JDK动态代理案例代码
2.1.1、UserDao
package org.star.dao;public interface UserDao {/*** 两数相加* @param num1* @param num2* @return*/int add(int num1,int num2);/*** 根据id修改* @param id* @return*/int update(long id);}
2.1.2、UserDaoImpl
package org.star.dao.impl;import org.star.dao.UserDao;public class UserDaoImpl implements UserDao {@Overridepublic int add(int num1, int num2) {return num1 + num2;}@Overridepublic int update(long id) {// do your business...return 1;}
}
2.1.3、MyInvocationHandler
package org.star.handler;import lombok.extern.slf4j.Slf4j;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;@Slf4j
public class MyInvocationHandler implements InvocationHandler {private Object obj;public MyInvocationHandler(Object obj) {this.obj = obj;}/*** 增强逻辑** @return* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 目标方法执行之前log.info("目标方法执行之前执行,目标方法名称:{},入参:{}", method.getName(), Arrays.toString(args));// 目标方法执行Object result = method.invoke(obj, args);// 目标方法执行之后log.info("目标方法执行之后执行,result:{},obj:{}",result,obj.getClass());return result;}
}
2.1.4、测试类
package org.star;import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.star.dao.UserDao;
import org.star.dao.impl.UserDaoImpl;
import org.star.handler.MyInvocationHandler;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;/*** Unit test for simple App.*/
@Slf4j
public class AppTest {@Testpublic void testJDKProxy1() {Class[] interfaces = {UserDao.class};UserDaoImpl userDaoImpl = new UserDaoImpl();UserDao userDao = (UserDao) Proxy.newProxyInstance(AppTest.class.getClassLoader(),interfaces,new MyInvocationHandler(userDaoImpl));int result = userDao.add(3,5);log.info("result:{}",result);}@Testpublic void testJDKProxy2() {Class[] interfaces = {UserDao.class};UserDao userDao = (UserDao) Proxy.newProxyInstance(AppTest.class.getClassLoader(), interfaces, new InvocationHandler() {UserDaoImpl userDaoImpl = new UserDaoImpl();@Overridepublic Object invoke(Object obj, Method method, Object[] args) throws Throwable {// 目标方法执行之前log.info("目标方法执行之前执行,目标方法名称:{},入参:{}", method.getName(), Arrays.toString(args));// 目标方法执行Object result = method.invoke(userDaoImpl, args);// 目标方法执行之后log.info("目标方法执行之后执行,obj:{}",userDaoImpl.getClass());return result;}});int result = userDao.add(5,5);log.info("result:{}",result);}}
2.2、Cglib动态代理案例代码
2.2.1、pom
<dependencies><dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.1</version></dependency><dependency><groupId>aopalliance</groupId><artifactId>aopalliance</artifactId><version>1.0</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.19</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.2.5.RELEASE</version></dependency><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.1.1</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.16</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>5.2.5.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.5.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>5.2.5.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-expression</artifactId><version>5.2.5.RELEASE</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version></dependency><!-- 普通maven项目中使用Sl4j注解 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.22</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.32</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.10</version></dependency></dependencies>
2.2.2、Panda
package org.star.cglib;import lombok.extern.slf4j.Slf4j;/*** @Author: * @Date: 2023/8/28 14:32* @Description:*/
@Slf4j
public class Panda {public void eat() {log.info("熊猫吃竹子");}}
2.2.3、App
package org.star;import lombok.extern.slf4j.Slf4j;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.star.cglib.Panda;import java.lang.reflect.Method;@Slf4j
public class App {public static void main( String[] args ) {Panda panda = new Panda();// 创建代理类Panda proxyPanda = (Panda) Enhancer.create(Panda.class, new MethodInterceptor() {@Overridepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {log.info("target method before...");Object result = method.invoke(panda, args);log.info("target method after...");return result;}});// 通过代理类调用目标方法实现增强proxyPanda.eat();}
}
三、AOP术语
3.1、连接点
类里面哪些方法可以被增强,这些被增强的方法就被称为连接点。
3.2、切入点
类里面实际真正被增强的方法,称为切入点。
3.3、通知(增强)
实际增强的逻辑部分称为通知(增强),通知按照作用于目标方法的位置不同,可以分为如下5种类型。
3.3.1、前置通知
前置通知因在目标方法之前执行,故名前置通知,注解为@Before。
3.3.2、后置通知
后置通知又叫正常结束通知,即目标方法正常结束后执行,常用于做一些善后的操作,注解为@AfterReturning。
注意事项:如果方法有返回值,可以在通知中获取到方法的返回值;
3.3.3、异常通知
异常通知故名思议是指当目标方法运行期间出现异常时才会执行,注解为@AfterThrowing。
3.3.4、最终通知
最终通知有点儿类似于try..catch...finally中的finally代码块,不管目标方法运行期间是否出现异常,都会执行,用于兜底。注解为@After
3.3.5、环绕通知
环绕通知是上述四个通知的集大成者,一个通知等价于上述四个通知。注解为@Around。
注意事项:
(1)需要通过代码调用方法,在方法执行的周围进行增强;
(2)使用代码调用连接点方法:proceedingJoinPoint.proceed();
(3)环绕通知需要有返回值,此返回值就是方法调用的结果;
3.4、切面
把通知应用到切入点的过程称为切面,是动作。
四、AOP案例
4.1、基于xml配置文件实现(maven)
4.1.1、pom
<dependencies><dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.1</version></dependency><dependency><groupId>aopalliance</groupId><artifactId>aopalliance</artifactId><version>1.0</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.19</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.2.5.RELEASE</version></dependency><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.1.1</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.16</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>5.2.5.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.5.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>5.2.5.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-expression</artifactId><version>5.2.5.RELEASE</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.76</version></dependency><!-- 普通maven项目中使用Sl4j注解 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.22</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.32</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.10</version></dependency></dependencies>
4.1.2、ATM
package org.star.component;import lombok.extern.slf4j.Slf4j;/*** 目标*/
@Slf4j
public class ATM {public int take(int money) {log.info("取钱方法正在执行...");if (money == 100) {throw new RuntimeException("自定义的异常");}return 10000;}}
4.1.3、LogAspect
package org.star.aspect;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;/*** 增强类*/
@Slf4j
public class LogAspect {/*** 前置通知:方法执行之前执行*/public void beforeLog() {log.info("前置通知执行...");}/*** 后置通知(正常结束通知):方法正常执行结束时才会通知,出现异常不会执行*/public void afterReturningLog(Object result) {log.info("后置通知(正常结束通知)执行...,返回值:{}",result);}/*** 异常通知*/public void afterThrowingLog(Exception ex) {log.info("异常通知(目标方法出现异常时执行)...,异常信息:{}",ex.getMessage());}/*** 最终通知:不管目标方法执行期间是否有异常,都会执行**/public void afterLog() {log.info("最终通知(不管目标方法执行期间是否有异常,都会执行)...");}/*** 环绕通知* @param joinPoint* @return*/public Object aroundLog(ProceedingJoinPoint joinPoint) {Object result = null;try {log.info("around before");result = joinPoint.proceed(); // 调用目标方法返回的值log.info("around afterReturning");} catch (Throwable e) {log.info("around afterThrowing");} finally {log.info("around after");}return result;}}
4.1.4、applicationContext
<?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"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"><!-- bean配置 --><bean id="atm" class="org.star.component.ATM"></bean><bean id="logAspect" class="org.star.aspect.LogAspect"></bean><!-- aop配置 --><aop:config><!--切入点说明:execution中的第一个参数代表返回值,*代表所有--><aop:pointcut id="p" expression="execution(* org.star.component.ATM.take(..))"/><!-- 切面 --><aop:aspect ref="logAspect"><!-- 增强作用在具体的方法上 --><!-- 前置通知 --><!--<aop:before method="beforeLog" pointcut-ref="p"></aop:before>--><!-- 后置通知(正常结束通知) --><!--<aop:after-returning method="afterReturningLog" pointcut-ref="p" returning="result"></aop:after-returning>--><!-- 异常通知 --><!--<aop:after-throwing method="afterThrowingLog" pointcut-ref="p" throwing="ex"></aop:after-throwing>--><!-- 最终通知 --><!--<aop:after method="afterLog" pointcut-ref="p"></aop:after>--><!-- 环绕通知 --><aop:around method="aroundLog" pointcut-ref="p"></aop:around></aop:aspect></aop:config></beans>
4.1.5、AppTest
package org.star;import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.star.component.ATM;/*** Unit test for simple App.*/
@Slf4j
public class AppTest {@Testpublic void aopTest() {ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");ATM atm = context.getBean(ATM.class);int take = atm.take(100);log.info("aopTest take:{}",take);}}
4.2、基于全注解方式实现(maven)
4.2.1、pom
<dependencies><dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.1</version></dependency><dependency><groupId>aopalliance</groupId><artifactId>aopalliance</artifactId><version>1.0</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.19</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.2.5.RELEASE</version></dependency><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.1.1</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.16</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>5.2.5.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.5.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>5.2.5.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-expression</artifactId><version>5.2.5.RELEASE</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.76</version></dependency><!-- 普通maven项目中使用Sl4j注解 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.22</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.32</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.10</version></dependency></dependencies>
4.2.2、MySpringConfig
package org.star.config;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration
@ComponentScan(basePackages = {"org.star"})
@EnableAspectJAutoProxy(proxyTargetClass = true) // 启用CGLB动态代理
public class MySpringConfig {}
4.2.3、ATM
package org.star.component;import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;@Slf4j
@Component
public class ATM {public int take(int money) {log.info("取钱方法正在执行...");if (money == 100) {throw new RuntimeException("自定义的异常");}return 10000;}}
4.2.4、LogAspect
package org.star.aspect;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Slf4j
@Component
@Aspect // 生成代理对象
public class LogAspect {/*** 相同的切入点进行抽取*/@Pointcut(value = "execution(* org.star.component.ATM.take(..))")public void commonPoint() {}/*** 前置通知:方法执行之前执行*/// @Before(value = "execution(* org.star.component.ATM.take(..))")// @Before(value = "commonPoint()")public void beforeLog() {log.info("前置通知执行...");}/*** 后置通知(正常结束通知):方法正常执行结束时才会通知,出现异常不会执行*/// @AfterReturning(value = "execution(* org.star.component.ATM.take(..))",returning = "result")// @AfterReturning(value = "commonPoint()",returning = "result")public void afterReturningLog(Object result) {log.info("后置通知(正常结束通知)执行...,返回值:{}",result);}// @AfterThrowing(value = "execution(* org.star.component.ATM.take(..))",throwing = "ex")// @AfterThrowing(value = "commonPoint()",throwing = "ex")public void afterThrowingLog(Exception ex) {log.info("异常通知(目标方法出现异常时执行)...,异常信息:{}",ex.getMessage());}/*** 最终通知:不管目标方法执行期间是否有异常,都会执行* 思考:目标方法正常执行结束,控制台打印的日志如下:* 07:55:40.702 [main] INFO org.star.aspect.LogAspect - 前置通知执行...* 07:55:40.717 [main] INFO org.star.component.ATM - 取钱方法正在执行...* 07:55:40.717 [main] INFO org.star.aspect.LogAspect - 最终通知(不管目标方法执行期间是否有异常,都会执行)...* 07:55:40.717 [main] INFO org.star.aspect.LogAspect - 后置通知(正常结束通知)执行...,返回值:10000* 为什么最终通知先于后置通知执行?* 答:后置通知是在方法执行return后执行的,是不可能修改方法的返回值的,而最终通知是在目标方法返回前执行的,即便目标方法出现抛出异常,最终通知* 也会执行,但当抛出异常时,后置通知将不会执行。*/// @After(value = "execution(* org.star.component.ATM.take(..))")// @After(value = "commonPoint()")public void afterLog() {log.info("最终通知(不管目标方法执行期间是否有异常,都会执行)...");}/*** 环绕通知* @param joinPoint* @return*/// @Around(value = "execution(* org.star.component.ATM.take(..))")@Around(value = "commonPoint()")public Object aroundLog(ProceedingJoinPoint joinPoint) {Object result = null;try {log.info("around before");result = joinPoint.proceed(); // 调用目标方法返回的值log.info("around afterReturning result:{}",result);} catch (Throwable e) {log.info("around afterThrowing");} finally {log.info("around after");}return result;}}
4.2.5、AppTest
package org.star;import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.star.component.ATM;
import org.star.config.MySpringConfig;/*** Unit test for simple App.*/
@Slf4j
public class AppTest {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);ATM atm = context.getBean(ATM.class);log.info("atm:{}",atm);if (atm != null) {atm.take(1000);}}}
4.3、基于自定义注解方式实现(springboot)
4.3.1、pom
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-collections4</artifactId><version>4.4</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>2.0.6</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.22</version></dependency></dependencies>
4.3.2、LogAnnotation
package org.star.annotation;import java.lang.annotation.*;/*** @Author: liuhaibo* @Date: 2023/8/28 10:16* @Description:*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogAnnotation {String value() default "";}
4.3.3、UserVO
package org.star.entity.vo;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;/*** @Author: liuhaibo* @Date: 2023/8/28 10:22* @Description:*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserVO implements Serializable {/*** 编号*/private Integer id;/*** 姓名*/private String name;/*** 年龄*/private Integer age;}
4.3.4、UserService
package org.star.service;import org.star.entity.vo.UserVO;import java.util.List;/*** @Author : 一叶浮萍归大海* @Date: 2023/8/28 7:55* @Description:*/
public interface UserService {/*** 查询所有用户* @return*/List<UserVO> listAllUser();/*** 根据id查询用户* @param id* @return*/UserVO getUserById(Integer id);/*** 添加用户* @param param* @return*/boolean saveUser(UserVO param);/*** 修改用户* @param param* @return*/boolean editUser(UserVO param);/*** 根据id删除用户* @param id* @return*/boolean delUserById(Integer id);
}
4.3.5、UserServiceImpl
package org.star.service.impl;import org.springframework.stereotype.Component;
import org.star.entity.vo.UserVO;
import org.star.service.UserService;import java.util.Arrays;
import java.util.List;/*** @Author : 一叶浮萍归大海* @Date: 2023/8/28 7:55* @Description:*/
@Component
public class UserServiceImpl implements UserService {@Overridepublic List<UserVO> listAllUser() {List<UserVO> users = Arrays.asList(new UserVO(1, "张三", 23),new UserVO(2, "李四", 24),new UserVO(3, "王五", 25));return users;}@Overridepublic UserVO getUserById(Integer id) {return new UserVO(1, "张三", 23);}@Overridepublic boolean saveUser(UserVO param) {return true;}@Overridepublic boolean editUser(UserVO param) {return true;}@Overridepublic boolean delUserById(Integer id) {return true;}
}
4.3.6、UserController
package org.star.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.star.annotation.LogAnnotation;
import org.star.entity.vo.UserVO;
import org.star.service.UserService;import java.util.List;/*** @Author: liuhaibo* @Date: 2023/8/28 10:19* @Description:*/
@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@LogAnnotation(value = "查询所有用户")@GetMapping("/listAllUser")public List<UserVO> listAllUser() {return userService.listAllUser();}@GetMapping("/getUserById/{id}")public UserVO getUserById(@PathVariable("id") Integer id) {return userService.getUserById(id);}@LogAnnotation(value = "添加用户")@PostMapping("/saveUser")public boolean saveUser(@RequestBody UserVO param) {return userService.saveUser(param);}@LogAnnotation(value = "修改用户")@PutMapping("/editUser")public boolean editUserById(@RequestBody UserVO param) {return userService.editUser(param);}@LogAnnotation(value = "根据ID删除用户")@DeleteMapping("/delUserById/{id}")public boolean delUserById(@PathVariable("id") Integer id) {return userService.delUserById(id);}}
4.3.7、测试
启动服务,在Postman中访问接口,观察控制台的日志输出。
五、切入点表达式
5.1、概述
5.2、语法
execution([权限修饰符] [返回值类型] [简单类名/全类名] [方法名]([参数列表]))
5.3、案例
表达式 | 含义 |
execution(* org.star.ArithmeticCalculator.*(..)) | (1)ArithmeticCalculator接口中声明的所有方法 (2)第一个*代表任意修饰符及任意返回值 (3)第二个*代表任意方法 (4)..代表匹配任意数量、任意类型的参数。若目标类、接口与该切面类在同一个包中可以省略包名。 |
execution(public * ArithmeticCalculator.*(..)) | ArithmeticCalculator接口的所有公有方法 |
execution(public double ArithmeticCalculator.*(..)) | ArithmeticCalculator接口中返回值类型为double的方法 |
execution(public double ArithmeticCalculator.*(double,..)) | ArithmeticCalculator接口中第一个参数为double类型的方法。第二个参数".."代表匹配任意数量、任意类型的参数 |
execution(public double ArithmeticCalculator.*(double, double)) | ArithmeticCalculator接口中参数类型为double,double类型的方法 |
5.4 、高级应用
在AspectJ中,切入点表达式还可以与逻辑运算符结合起来使用
# 任意类中第一个参数为int类型的add方法或sub方法
execution (* org.star.ArithmeticCalculator.add(int,..)) || execution(* org.star.ArithmeticCalculator.sub(int,..))# 匹配不是任意类中第一个参数为int类型的add方法
!execution (* org.star.ArithmeticCalculator.add(int,..))
六、切面的优先级
6.1、概述
如果系统定义了多个切面,如何让某些切面先运行?可以通过设置切面的优先级来改变切面的执行顺序,注解为@Order(数字),其中数字越小,优先级越高。
6.2、案例代码
6.2.1、pom
<dependencies><dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.1</version></dependency><dependency><groupId>aopalliance</groupId><artifactId>aopalliance</artifactId><version>1.0</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.19</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.2.5.RELEASE</version></dependency><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.1.1</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.2.16</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>5.2.5.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.5.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>5.2.5.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-expression</artifactId><version>5.2.5.RELEASE</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.76</version></dependency><!-- 普通maven项目中使用Sl4j注解 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.22</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.32</version></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.10</version></dependency></dependencies>
6.2.2、ATM
package org.star.component;import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;@Slf4j
@Component
public class ATM {public int take(int money) {log.info("取钱方法正在执行...");if (money == 100) {throw new RuntimeException("自定义的异常");}return 10000;}}
6.2.3、MySpringConfig
package org.star.config;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration
@ComponentScan(basePackages = {"org.star"})
@EnableAspectJAutoProxy(proxyTargetClass = true) // 启用CGLB动态代理
public class MySpringConfig {}
6.2.4、LogAspect
package org.star.aspect;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;/*** @Aspect : 生成代理类*/
@Slf4j
@Component
@Aspect
@Order(1)
public class LogAspect {/*** 相同的切入点进行抽取*/@Pointcut(value = "execution(* org.star.component.ATM.take(..))")public void commonPoint() {}/*** 前置通知:方法执行之前执行*/// @Before(value = "execution(* org.star.component.ATM.take(..))")@Before(value = "commonPoint()")public void beforeLog() {log.info("log前置通知执行...");}/*** 后置通知(正常结束通知):方法正常执行结束时才会通知,出现异常不会执行*/// @AfterReturning(value = "execution(* org.star.component.ATM.take(..))",returning = "result")@AfterReturning(value = "commonPoint()",returning = "result")public void afterReturningLog(Object result) {log.info("log后置通知(正常结束通知)执行...,返回值:{}",result);}}
6.2.5、ArgsAspect
package org.star.aspect;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;/*** @Aspect : 生成代理类*/
@Slf4j
@Component
@Aspect
@Order(2)
public class ArgsAspect {/*** 相同的切入点进行抽取*/@Pointcut(value = "execution(* org.star.component.ATM.take(..))")public void commonPoint() {}/*** 前置通知:方法执行之前执行*/// @Before(value = "execution(* org.star.component.ATM.take(..))")@Before(value = "commonPoint()")public void beforeLog() {log.info("args前置通知执行...");}/*** 后置通知(正常结束通知):方法正常执行结束时才会通知,出现异常不会执行*/// @AfterReturning(value = "execution(* org.star.component.ATM.take(..))",returning = "result")@AfterReturning(value = "commonPoint()",returning = "result")public void afterReturningLog(Object result) {log.info("args后置通知(正常结束通知)执行...,返回值:{}",result);}}
6.2.6、AppTest
package org.star;import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.star.component.ATM;
import org.star.config.MySpringConfig;/*** Unit test for simple App.*/
@Slf4j
public class AppTest {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);ATM atm = context.getBean(ATM.class);log.info("atm:{}",atm);if (atm != null) {atm.take(1000);}}}// 控制台打印结果
15:39:18.209 [main] INFO org.star.AppTest - atm:org.star.component.ATM@223aa2f7
15:39:18.213 [main] INFO org.star.aspect.LogAspect - log前置通知执行...
15:39:18.213 [main] INFO org.star.aspect.ArgsAspect - args前置通知执行...
15:39:18.224 [main] INFO org.star.component.ATM - 取钱方法正在执行...
15:39:18.224 [main] INFO org.star.aspect.ArgsAspect - args后置通知(正常结束通知)执行...,返回值:10000
15:39:18.224 [main] INFO org.star.aspect.LogAspect - log后置通知(正常结束通知)执行...,返回值:10000
七、使用AOP注意事项
(1)连接点方法不能是private,否则AOP不能进行增强;
(2)连接点在其他方法内部被调用时,不会被增强;