【Java】Java 面向切面编程 @Aspect

切面(Aspect)概念:

定义:
切面是一种模块化的方式,用于将横切关注点(cross-cutting concerns)从业务逻辑中分离出来
横切关注点包括日志记录、性能统计、安全性等,它们通常涉及多个对象和方法

主要组成:
1.切入点(Join Point): 在应用程序执行期间的某个特定点,如方法调用、异常抛出等。
2.通知(Advice): 定义在切入点上执行的操作,包括前置、后置、环绕、异常等不同类型的通知。
3.切入点表达式(Pointcut): 用于匹配切入点的表达式,决定在哪些切入点上执行通知。

主要作用:
将横切关注点集中管理,避免散布在各处的重复代码
提高代码的模块化和可维护性

使用例子:

 

import java.util.Arrays;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import com.alibaba.fastjson.JSONObject;

import cn.ctg.common.response.ResponseCode;
import cn.ctg.common.response.ResponseData;
import cn.ctg.common.util.UserTokenUtils;
import cn.ctg.common.util.XssUtils;
import cn.hutool.json.JSONUtil;

 
@Aspect
@Component
public class SecurityAspect {

    @Value("${keys.aeskey:-1}")
    private String AES_KEY;

    @Value("${keys.jwtkey:-1}")
    private String JWT_KEY;

    @Value("${xss.url:-1}")
    private String xxsUrl;

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    /**切面*/
    @Pointcut("@annotation(cn.ctg.common.util.security.CtgDecrypt) || @annotation(cn.ctg.common.util.security.CtgEncrypt)")
    public void pointCut(){ }

    /**
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("execution(* cn.ctg.*.controller.*.*(..))")
    public Object doAroundHtml(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();
        HttpServletRequest httpServletRequest = UserTokenUtils.getHttpServletRequest();
        String requestURI = httpServletRequest.getRequestURI();
        String[] split = xxsUrl.split("\\|");
        if(split==null){
            return joinPoint.proceed(args);
        }
        if(pathMatcher(Arrays.asList(split),requestURI)) {
            for (int i = 0; i < args.length; i++) {
                Object arg = args[i];
                Map<String, Object> map = JSONUtil.parseObj(JSONObject.toJSONString(arg));
                for (Map.Entry<String, Object> entry : map.entrySet()) {
                    if (XssUtils.isStripXSS(entry.getValue().toString())) {
                        ResponseData<Object> responseData = ResponseData.error(ResponseCode.XSS_CODE_ERROR);
                        return responseData;
                    }
                }
            }
        }
        return joinPoint.proceed(args);
    }

    /** 返回参数加密*/
    @Around("pointCut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {

        //执行方法,获取返回值
        Object result = joinPoint.proceed();
        String data = JSONUtil.toJsonStr(((ResponseData<?>) result).getData());
        if(data.equals("{}")){
            data = String.valueOf(((ResponseData<?>) result).getData());
        }
 
        /** 可以根据注解选择 加密方法 防止统一*/
        ((ResponseData<?>) result).setEncrypt(true);
        return result;
    }

 

}
 

import java.util.Arrays;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import com.alibaba.fastjson.JSONObject;

import cn.ctg.common.response.ResponseCode;
import cn.ctg.common.response.ResponseData;
import cn.ctg.common.util.UserTokenUtils;
import cn.ctg.common.util.XssUtils;
import cn.hutool.json.JSONUtil;

 
@Aspect
@Component
public class SecurityAspect {

    @Value("${keys.aeskey:-1}")
    private String AES_KEY;

    @Value("${keys.jwtkey:-1}")
    private String JWT_KEY;

    @Value("${xss.url:-1}")
    private String xxsUrl;

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    /**切面*/
    @Pointcut("@annotation(cn.ctg.common.util.security.CtgDecrypt) || @annotation(cn.ctg.common.util.security.CtgEncrypt)")
    public void pointCut(){ }

    /**
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("execution(* cn.ctg.*.controller.*.*(..))")
    public Object doAroundHtml(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();
        HttpServletRequest httpServletRequest = UserTokenUtils.getHttpServletRequest();
        String requestURI = httpServletRequest.getRequestURI();
        String[] split = xxsUrl.split("\\|");
        if(split==null){
            return joinPoint.proceed(args);
        }
        if(pathMatcher(Arrays.asList(split),requestURI)) {
            for (int i = 0; i < args.length; i++) {
                Object arg = args[i];
                Map<String, Object> map = JSONUtil.parseObj(JSONObject.toJSONString(arg));
                for (Map.Entry<String, Object> entry : map.entrySet()) {
                    if (XssUtils.isStripXSS(entry.getValue().toString())) {
                        ResponseData<Object> responseData = ResponseData.error(ResponseCode.XSS_CODE_ERROR);
                        return responseData;
                    }
                }
            }
        }
        return joinPoint.proceed(args);
    }

    /** 返回参数加密*/
    @Around("pointCut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {

        //执行方法,获取返回值
        Object result = joinPoint.proceed();
        String data = JSONUtil.toJsonStr(((ResponseData<?>) result).getData());
        if(data.equals("{}")){
            data = String.valueOf(((ResponseData<?>) result).getData());
        }
 
        /** 可以根据注解选择 加密方法 防止统一*/
        ((ResponseData<?>) result).setEncrypt(true);
        return result;
    }

 

}

 

<think>好的,用户想了解Java面向切面编程(AOP)的@Aspect注解的详细用法和原理。首先,我需要回忆AOP的基本概念,确保自己理解正确。AOP是面向切面编程,用于将横切关注点(如日志、事务管理)与业务逻辑分离,提高模块化。@Aspect注解在Spring AOP中用来定义切面,对吧? 接下来,用户需要详细的使用方法。我应该分步骤说明如何定义切面类,使用@Aspect注解,并配置Spring支持。然后,切入点表达式是关键部分,需要解释不同的指示符,比如execution、within等,并举例说明。例如,execution(* com.example.service.*.*(..))匹配service包下所有方法。 然后是关于通知类型,用户可能想知道@Before、@After等注解的具体用法。要分别解释每个通知的作用,并给出代码示例,比如在方法执行前记录日志。同时,要提到环绕通知@Around,因为它可以控制方法的执行,需要特别注意ProceedingJoinPoint的使用。 原理部分,需要解释Spring AOP是如何通过动态代理实现的。如果是接口,使用JDK动态代理;如果是类,使用CGLIB。这点很重要,用户可能需要了解性能差异或配置方式。此外,要说明@Aspect注解是如何被处理的,可能是通过自动代理创建器,比如AnnotationAwareAspectJAutoProxyCreator。 用户提供的引用中有提到@Aspect的匹配规则,比如返回void、方法名chop、参数类型。我需要将这些信息整合到切入点表达式的示例中,确保引用正确。例如,引用[1]中的方法匹配,可以作为一个例子展示如何精确匹配方法。 另外,用户可能对AOP的应用场景和优势感兴趣,虽然问题主要集中在用法和原理,但简要提及AOP如何降低耦合度、提高重用性也是有帮助的,如引用[2]所述。需要确认这些引用是否准确,并正确标注来源。 还要考虑用户可能的疑问,比如如何启用@Aspect支持,是否需要XML配置或注解驱动。应该提到在配置类中使用@EnableAspectJAutoProxy,或者XML中的<aop:aspectj-autoproxy/>。 最后,生成相关问题时要围绕AOP和@Aspect的扩展,比如与其他注解的区别、性能影响、解决的实际问题等,确保问题有针对性,帮助用户深入理解。 检查是否有遗漏的点,比如AOP的局限性,比如方法内部调用切面不生效的情况,或者动态代理的一些限制。这些可能不是用户直接问的,但相关问题中可以提到。 确保语言口语化,不使用专业术语过多,但保持准确性。分步骤解释,结构清晰,代码示例正确,并正确引用提供的资料。</think>### Java AOP @Aspect 注解使用详解及原理介绍 #### 一、@Aspect 注解基础 @Aspect 是 Spring AOP 中定义**切面(Aspect)**的核心注解,用于将横切关注点(如日志、事务、权限控制)模块化。需配合 Spring 的自动代理机制使用,以下是具体用法: 1. **定义切面类** 在类上添加 `@Aspect` 注解,并通过 `@Component` 注册为 Bean: ```java @Aspect @Component public class LoggingAspect { // 定义切入点与通知 } ``` 2. **配置 Spring 支持** 在配置类中添加 `@EnableAspectJAutoProxy` 启用 AOP 自动代理: ```java @Configuration @EnableAspectJAutoProxy public class AppConfig {} ``` --- #### 二、切入点表达式与通知类型 1. **切入点表达式(Pointcut)** 使用 `@Pointcut` 定义可重用的匹配规则,支持多种指示符: - `execution()`:匹配方法执行(最常用) - `@annotation()`:匹配带有特定注解的方法 - `within()`:匹配类或包路径 **示例** ```java @Pointcut("execution(* com.example.service.*.*(..))") // 匹配 service 包下所有方法 public void serviceMethods() {} @Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)") // 匹配带 @Transactional 的方法 public void transactionalMethod() {} ``` **引用示例** 匹配目标类中任意修饰符、返回 `void`、方法名为 `chop`、参数为 `String` 和 `int` 的方法[^1]: ```java @Pointcut("execution(void chop(String, int))") ``` 2. **通知类型** | 注解 | 执行时机 | 代码示例 | |------------|-------------------------|-----------------------------------| | `@Before` | 目标方法执行前 | 记录方法调用日志 | | `@After` | 目标方法执行后 | 资源清理 | | `@AfterReturning` | 方法正常返回后 | 处理返回值 | | `@AfterThrowing` | 方法抛出异常时 | 异常监控与报警 | | `@Around` | 包裹目标方法执行 | 性能统计、事务控制 | **环绕通知示例** ```java @Around("serviceMethods()") public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable { long start = System.currentTimeMillis(); Object result = joinPoint.proceed(); // 执行目标方法 long duration = System.currentTimeMillis() - start; System.out.println("方法执行耗时:" + duration + "ms"); return result; } ``` --- #### 三、实现原理 1. **动态代理机制** Spring AOP 基于动态代理实现,具体分为两种模式: - **JDK 动态代理**:针对实现了接口的类,通过 `Proxy` 类生成代理对象。 - **CGLIB 代理**:针对无接口的类,通过字节码增强生成子类代理。 $$ \text{代理对象} = \begin{cases} \text{JDK Proxy} & \text{if 目标类实现接口} \\ \text{CGLIB} & \text{otherwise} \end{cases} $$ 2. **注解处理流程** - **步骤 1**:Spring 容器启动时扫描带有 `@Aspect` 的类。 - **步骤 2**:通过 `AnnotationAwareAspectJAutoProxyCreator` 自动创建代理对象。 - **步骤 3**:调用代理对象时,按优先级执行关联的 Advice 链。 --- #### 四、核心优势 1. **解耦性**:将横切逻辑与业务代码分离,降低耦合度[^2]。 2. **可维护性**:统一管理日志、事务等重复代码。 3. **灵活性**:通过切入点表达式动态控制织入位置。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奋力向前123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值