系列十一、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,一经查实,立即删除!

相关文章

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

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

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

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

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 关键字 一、多态的概念和虚…

webrtc的Sdp中的Plan-b和UnifiedPlan

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

android framework之Applicataion启动流程分析

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

ABeam×Startup | 德硕管理咨询(深圳)创新研究团队拜访微漾创客空间

近日&#xff0c;德硕管理咨询&#xff08;深圳&#xff09;&#xff08;以下简称&#xff1a;“ABeam-SZ”&#xff09;创新研究团队前往微漾创客空间&#xff08;以下简称&#xff1a;微漾&#xff09;拜访参观&#xff0c;并展开合作交流。会议上&#xff0c;双方相互介绍了…

每日一题 57. 插入区间

读研了&#xff0c;开始用python刷题 今天的题目是力扣 每日一题 57. 插入区间 难度&#xff1a;中等 思路&#xff1a; 处理新区间起点&#xff0c;要么在两个老区间之间&#xff0c;要么被一个老区间包含处理新区间中点&#xff0c;同起点一样 我的代码如下 class Solut…

解锁市场进入成功:GTM 策略和即用型示例

在最初的几年里&#xff0c;创办一家初创公司可能会充满挑战。根据美国小企业管理局的数据&#xff0c;大约三分之二的新成立企业存活了两年&#xff0c;几乎一半的企业存活了五年以上。导致创业失败的因素有市场需求缺失、资金短缺、团队不合适、成本问题等。由此&#xff0c;…

合宙Air724UG LuatOS-Air LVGL API控件--按钮 (Button)

按钮 (Button) 按钮控件&#xff0c;这个就不用多说了&#xff0c;界面的基础控件之一。 示例代码 – 按键回调函数 event_handler function(obj, event) if event lvgl.EVENT_CLICKED then print(“Clicked\n”) elseif event lvgl.EVENT_VALUE_CHANGED then print(“To…

服务器数据库中了locked勒索病毒怎么办,locked勒索病毒恢复工具

最近一段时间网络上的locked勒索病毒非常嚣张&#xff0c;自从6月份以来&#xff0c;很多企业的计算机服务器数据库遭到了locked勒索病毒的攻击&#xff0c;起初locked勒索病毒攻击用友畅捷通T用户&#xff0c;后来七月份开始攻击金蝶云星空客户&#xff0c;导致企业的财务系统…

揭秘视频号创收计划:松松一个月赚1300+

我是卢松松&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 这是卢松松一个月视频号的收益&#xff0c;1300元。自从视频号在五月份推出创作者分成计划以来&#xff0c;许许多多的视频号创作者开始获得了一些收益&#xff0c;这绝对是一项挺不错的进展。 目前…

R-Meta分析核心技术教程

详情点击链接&#xff1a;全流程R-Meta分析核心技术教程 一&#xff0c;Meta分析的选题与检索 1、Meta分析的选题与文献检索 1)什么是Meta分析 2)Meta分析的选题策略 3)精确检索策略&#xff0c;如何检索全、检索准 4)文献的管理与清洗&#xff0c;如何制定文献纳入排除标准 …

回归预测 | MATLAB实现TSO-ELM金枪鱼群优化算法优化极限学习机多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现TSO-ELM金枪鱼群优化算法优化极限学习机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现TSO-ELM金枪鱼群优化算法优化极限学习机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09;效…

【大数据】Linkis:打通上层应用与底层计算引擎的数据中间件

Linkis&#xff1a;打通上层应用与底层计算引擎的数据中间件 1.引言2.背景3.设计初衷4.技术架构5.业务架构6.处理流程7.如何支撑高并发8.用户级隔离度和调度时效性9.总结 Linkis 是微众银行开源的一款 数据中间件&#xff0c;用于解决前台各种工具、应用&#xff0c;和后台各种…