spring实现简单AOP

简介: spring实现简单AOP

Sometimes we may think about how to do something before the method runs

有时我们会想,如何在一些方法执行前或者执行后做一些操作

比如日志的记录、权限的鉴定等等

昨天,我们搭建了个简单的spring项目并写了几个接口

其中一个是这样的

这里返回给前端的map中的data,我是手动加的前缀“服务器对你说”

我们现在来用AOP实现

先修改我们接口中的代码,去掉前缀

然后配置AOP

<?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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
  http://www.springframework.org/schema/mvc
  http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
  http://www.springframework.org/schema/context
  http://www.springframework.org/schema/context/spring-context-4.2.xsd">
    <!-- 配置包扫描器,扫描@Controller注解的类 -->
    <context:component-scan base-package="com.ruben.controller"/>
    <!-- 配置注解驱动 -->
    <mvc:annotation-driven/>
    <mvc:default-servlet-handler/>
    <!-- 视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
    <!-- 配置资源映射 -->
    <mvc:resources location="/static/" mapping="/static/**"/>
    <!-- 激活组件扫描功能,在包"com.example.aop及其子包下面自动扫描通过注解配置的组件-->
    <context:component-scan base-package="com.ruben.aop"/>
    <!-- 启动AspectJ支持   只对扫描过的bean有效-->
    <aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>

然后创建我们的aop

aop包创建一个类,并加上注解

定义我们的切入点,也就是需要被织入的位置

/**
 * 指定say方法
 */
@Pointcut("execution(* com.ruben.controller.UserController.say(..))")
private void pointcut() {
}

然后编写我们的切面

/**
     * 返回前对参数进行处理,加上前缀
     * @param point
     * @param returnValue
     */
    @AfterReturning(pointcut = "pointcut()", returning = "returnValue")
    public void addPrefix(JoinPoint point, Object returnValue) {
        if (returnValue instanceof HashMap) {
            Map<String, Object> result = (Map<String, Object>) returnValue;
            result.put("data", "achao对你说:" + result.get("data"));
        }
    }

完整代码

package com.ruben.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
 * @ClassName: SayWordAop
 * @Description: 说话Aop
 * @Date: 2020/8/7 19:36
 * *
 * @author: achao<achao1441470436 @ gmail.com>
 * @version: 1.0
 * @since: JDK 1.8
 */
@Aspect
@Component
public class SayWordAop {
    /**
     * 指定say方法
     */
    @Pointcut("execution(* com.ruben.controller.UserController.say(..))")
    private void pointcut() {
    }
    /**
     * 返回前对参数进行处理,加上前缀
     * @param point
     * @param returnValue
     */
    @AfterReturning(pointcut = "pointcut()", returning = "returnValue")
    public void addPrefix(JoinPoint point, Object returnValue) {
        if (returnValue instanceof HashMap) {
            Map<String, Object> result = (Map<String, Object>) returnValue;
            result.put("data", "achao对你说:" + result.get("data"));
        }
    }
}

然后启动我们的项目,发现切面生效了

发送的参数为登录成功!

但是返回的结果是

说明我们的切面生效了,我们顺便把其他几种也加上吧

比如加个校验

/**
 * 登录参数校验
 *
 * @param point
 * @return
 * @throws Throwable
 */
@Around("execution(* com.ruben.controller.UserController.login(..))")
public Object permissionCheck(ProceedingJoinPoint point) throws Throwable {
    List<Object> paramList = Arrays.asList(point.getArgs());
    AtomicBoolean passValidate = new AtomicBoolean(true);
    String pattern = "^[A-Za-z0-9]{4,40}$";
    AtomicReference<String> errorMsg = new AtomicReference<>("您输入的参数格式有误");
    paramList.forEach(param -> {
        User user = (User) param;
        if (!Pattern.matches(pattern, user.getUsername())) {
            errorMsg.set("用户名格式错误");
            passValidate.set(false);
        } else if (!Pattern.matches(pattern, user.getPassword())) {
            errorMsg.set("密码格式错误");
            passValidate.set(false);
        }
    });
    if (!passValidate.get()) {
        Map<String, Object> map = new HashMap<>(1 << 2);
        map.put("success", false);
        map.put("code", -629);
        map.put("msg", errorMsg.get());
        return map;
    }
    return point.proceed();
}

还比如在方法执行前后记录日志

先加上注解

然后修改配置文件

<!-- 配置log4j日志 -->
<context-param>
    <param-name>log4jConfigLocation</param-name>
    <param-value>/WEB-INF/config/log4j.properties</param-value>
</context-param>
<!--log4j日志监听  -->
<listener>
    <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>

添加配置文件

image-20200807215610818.png

#log4j properties file
#log4j.rootLogger = [level],appenderName,appenderName2,...
log4j.rootLogger=INFO,console,file
#console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%-22d{yyyy/MM/dd HH:mm:ss}%m%n
#file
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=./logs/project.log
log4j.appender.file.Append=true
log4j.appender.file.Encoding=UTF-8
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%-5p][%-22d{yyyy/MM/dd HH:mm:ssS}][%l]%n%m%n

就可以用log.info()打印日志啦!

/**
     * 在com.ruben包下任意方法执行前打印日志
     *
     * @param point
     */
    @Before(value = "execution(* com.ruben.*.*.*(..))")
    public void logRecode(JoinPoint point) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        log.info("==>url:" + request.getRequestURI());
        log.info("==>参数:" + Arrays.toString(point.getArgs()));
    }
    /**
     * 在com.ruben.controller包下任意方法执行后打印
     *
     * @param point
     */
    @After("execution(* com.ruben.controller.*.*(..))")
    public void recodeWord(JoinPoint point) {
        log.info("<==" + point.getSignature().getDeclaringTypeName()
                + "." + point.getSignature().getName() + "方法执行结束");
    }


2020/08/07 22:00:11   ==>url:/user/login
2020/08/07 22:00:11   ==>参数:[User(username=achao, password=ruben)]
2020/08/07 22:00:11   <==com.ruben.controller.UserController.login方法执行结束
2020/08/07 22:00:11   ==>url:/user/say
2020/08/07 22:00:11   ==>参数:[登录成功!]
2020/08/07 22:00:11   <==com.ruben.controller.UserController.say方法执行结束

完整代码

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
7天前
|
XML Java API
Spring AOP切点和通知机制的深度解析
Spring AOP切点和通知机制的深度解析
20 4
|
12天前
|
Java Maven 数据安全/隐私保护
详解 Java AOP:面向方面编程的核心概念与 Spring 实现
详解 Java AOP:面向方面编程的核心概念与 Spring 实现
21 1
|
9天前
|
XML 安全 Java
Spring高手之路19——Spring AOP注解指南
在本文中,我们将深入探索Spring AOP(面向切面编程)的核心概念及其在现代Spring应用中的实际应用。从基本的注解使用到复杂的切面配置,本文将一步步指导你如何利用Spring AOP提升代码的模块化,帮助你在Spring开发路上更进一步。
21 3
Spring高手之路19——Spring AOP注解指南
|
6天前
|
缓存 监控 安全
在 Spring Boot 中使用 AOP(Aspect-Oriented Programming)实现日志记录功能
在 Spring Boot 中使用 AOP(Aspect-Oriented Programming)实现日志记录功能
17 1
|
9天前
|
设计模式 网络安全 开发工具
|
12天前
|
缓存 Java uml
Spring压轴题:当循环依赖遇上Spring AOP
Spring压轴题:当循环依赖遇上Spring AOP
17 1
|
13天前
|
XML 监控 Java
Java一分钟之-Spring AOP:基于Spring的AOP
【6月更文挑战第13天】Spring框架集成AOP支持,便于实现如日志、监控和事务管理等关注点的集中管理。本文探讨Spring AOP的核心概念(切面、切入点、通知和代理),常见问题(代理对象理解不清、切入点表达式错误、通知类型混淆和事务管理配置不当)及其对策,并提供注解式日志记录代码示例。通过学习和实践,开发者能更好地运用Spring AOP提升代码质量。
21 2
|
22天前
|
数据采集 Java 程序员
【JavaEE进阶】 Spring AOP快速上手
【JavaEE进阶】 Spring AOP快速上手
|
22小时前
|
XML 监控 Java
Java中的AOP编程:AspectJ与Spring AOP的应用
Java中的AOP编程:AspectJ与Spring AOP的应用
|
22天前
|
Java Spring
【JavaEE进阶】 Spring AOP源码简单剖析
【JavaEE进阶】 Spring AOP源码简单剖析