系列十一、AOP

一、概述

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)连接点在其他方法内部被调用时,不会被增强;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/56973.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[JDK8环境下的HashMap类应用及源码分析] capacity实验

🌹作者主页:青花锁 🌹简介:Java领域优质创作者🏆、Java微服务架构公号作者😄、CSDN博客专家 🌹简历模板、学习资料、面试题库、技术互助 🌹文末获取联系方式 📝 系列文章目录 [Java基础] StringBuffer 和 StringBuilder 类应用及源码分析 [Java基础] 数组应用…

横扫“盲区”、“看透”缺陷,维视智造推出短波红外相机

在可见光领域&#xff0c;工业相机的视觉应用已经十分成熟&#xff0c;但在日常的客户咨询中&#xff0c;我们也经常接到一些“超纲需求”——客户想要检测“白底上的白色缺陷”、“不透明包装内的透明物体有无”等&#xff0c;均属于可见光无法实现的检测&#xff0c;而市面上…

API接口开发管理平台--多领域企业数字化管理的解决方案

随着数字化时代的到来&#xff0c;企业需要进行数字化转型才能更好地适应市场需求和用户需求。而API接口则是数字化转型中的重要组成部分&#xff0c;可以帮助企业更好地管理信息&#xff0c;提高效率。本文将介绍一种解决方案--API接口开发管理平台&#xff0c;该平台开发出多…

无线 CBTC 与互联互通

1 无线 CBTC 的技术与经济优势 由于无线 CB TC 可采用移动闭塞的制式&#xff0c;列车能以较小的间隔运行&#xff0c;可使运 营商实现“ 高密度”的运营模式&#xff0c;这使系统可在同样满足客运需求的基 础上&#xff0c;缩短旅客的候车时间&#xff0c;缩小站台长度和候…

微信小程序开发教学系列(13)- 小程序支付与电商功能

13. 小程序支付与电商功能 小程序支付与电商功能是微信小程序中非常重要的一部分&#xff0c;通过支付功能可以实现用户购买商品、支付订单等功能。下面将介绍小程序支付的接入和配置以及实现电商功能和订单管理的方法。 13.1 小程序支付的接入和配置 13.1.1 获取微信支付商…

Pygame编程(1)初始化和退出模块

初始化和退出模块 pygame使用基础流程 初始化模块设置主屏窗口程序主循环&#xff08;处理键盘、鼠标、游戏杆、触摸屏等事件&#xff09;退出模块终止程序 import sys import pygame from pygame.locals import *# 1.初始化模块 pygame.init()# 2.设置主屏窗口 display pyg…

SocketTools.NET 11.0.2148.1554 Crack

添加新功能以简化使用 URL 建立 TCP 连接的过程。 2023 年 8 月 23 日 - 12:35新版本 特征 添加了“HttpGetTextEx”函数&#xff0c;该函数在返回字符串缓冲区中的文本内容时提供附加选项。添加了对“FileTransfer”.NET 类和 ActiveX 控件中的“GetText”和“PutText”方法的…

HDLBits-Verilog学习记录 | Verilog Language-Modules(1)

文章目录 20.Module21.Connecting ports by position | Moudle pos22.Connecting ports by name | Module name23.Three modules | Module shift24.Modules and vectors | Module shift8 20.Module practice:You may connect signals to the module by port name or port posi…

FFmpeg支持多线程编码并保存mp4文件示例

之前介绍的示例&#xff1a; (1).https://blog.csdn.net/fengbingchun/article/details/132129988 中对编码后数据保存成mp4 (2).https://blog.csdn.net/fengbingchun/article/details/132128885 中通过AVIOContext实现从内存读取数据 (3).https://blog.csdn.net/fengbingchun/…

几个nlp的小任务(机器翻译)

几个nlp的小任务(机器翻译) 安装依赖库数据集介绍与模型介绍加载数据集看一看数据集的样子评测测试数据预处理测试tokenizer处理目标特殊的token预处理函数对数据集的所有数据进行预处理微调预训练模型设置训练参数需要一个数据收集器,把处理好数据喂给模型设置评估方法参数…

美团面试拷打:ConcurrentHashMap 为何不能插入 null?HashMap 为何可以?

周末的时候,有一位小伙伴提了一些关于 ConcurrentHashMap 的问题,都是他最近面试遇到的。原提问如下(星球原贴地址:https://t.zsxq.com/11jcuezQs ): 整个提问看着非常复杂,其实归纳来说就是两个问题: ConcurrentHashMap 为什么 key 和 value 不能为 null?ConcurrentH…

【C++ 学习 ⑱】- 多态(上)

目录 一、多态的概念和虚函数 1.1 - 用基类指针指向派生类对象 1.2 - 虚函数和虚函数的重写 1.3 - 多态构成的条件 1.4 - 多态的应用场景 二、协变和如何析构派生类对象 2.1 - 协变 2.2 - 如何析构派生类对象 三、C11 的 override 和 final 关键字 一、多态的概念和虚…

mysql建表问题

问题 例如用户表,我们需要建一个字段是创建时间, 一个字段是更新时间. 解决办法可以是指定插入时间,也可以使用数据库的默认时间. 在mysql中如果设置两个默认CURRENT_TIMESTAMP,会出现这样的错误. Error Code: 1293. Incorrect table definition; there can be only one TIMES…

算法通关村第十一关——位运算实现加减乘除

在计算机中&#xff0c;位运算的效率比加减乘除效率更高。 1.位运算实现加法 力扣371题&#xff0c; 给你两个整数 a 和 b &#xff0c;不使用 运算符 和-&#xff0c;计算并返回两整数之和。 分析&#xff1a;不让用运算符&#xff0c;就只能使用位运算。先来看一下两位二进…

【面试经典150题】删除有序数组中的重复项Ⅱ JavaScript

题目来源。 给你一个有序数组 nums &#xff0c;请你** 原地** 删除重复出现的元素&#xff0c;使得出现次数超过两次的元素只出现两次 &#xff0c;返回删除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下…

程序自动分析——并查集+离散化

在实现程序自动分析的过程中&#xff0c;常常需要判定一些约束条件是否能被同时满足。考虑一个约束满足问题的简化版本&#xff1a;假设 x1,x2,x3,… 代表程序中出现的变量&#xff0c;给定 n 个形如 xixj 或 xi≠xj 的变量相等/不等的约束条件&#xff0c;请判定是否可以分别为…

Matlab 生成一定信噪比的信号

文章目录 【 1. 信噪比 】【 2. 功率归一化 】2.1 实信号+实噪声2.2 实信号+复噪声【 3. 能量归一化 】3.1 实信号+实噪声3.2 实信号+复噪声【 4. 小结 】【 1. 信噪比 】 信噪比公式 1 : S N R = 10 ∗ l o g 10 P s P n 信噪比公式1:SNR=10*log_{10}\frac{P_s}{P_n} 信噪比…

webrtc的Sdp中的Plan-b和UnifiedPlan

在一些类似于视频会议场景下&#xff0c;媒体会话参与者需要接收或者发送多个流&#xff0c;例如一个源端&#xff0c;同时发送多个左右音轨的音频&#xff0c;或者多个摄像头的视频流&#xff1b;在2013年&#xff0c;提出了2个不同的SDP IETF草案Plan B和Unified Plan&#x…

【uniapp 上传图片示例】

以下是 uniapp 上传图片的详细步骤示例&#xff1a; 定义一个方法&#xff0c;用于选择图片并上传&#xff1a; methods: {chooseImage() {uni.chooseImage({count: 1, // 最多选择的图片数量sizeType: [original, compressed], // 可以指定原图或压缩图sourceType: [album, …

android framework之Applicataion启动流程分析

Application启动流程分析 启动方式一&#xff1a;通过Launcher启动app 启动方式二&#xff1a;在某一个app里启动第二个app的Activity. 以上两种方式均可触发app进程的启动。但无论哪种方式&#xff0c;最终通过通过调用AMS的startActivity()来启动application的。 根据上图…