关于AOP做操作日志的实现案例

一、概念

AOP(Aspect Orient Programming)就是面向切面编程,图解:
在这里插入图片描述
四段代码执行,定义了某个点为切点,图示红色,蓝色,绿色均为定义的切点并不是每一段代码执行相同代码进行切割,而是执行到定义的切点时才切割。
一旦切割,将执行定义的切面上所有的代码
简单解释:相当于你写了一份代码,是切面的代码。而定义的切点处,只要执行到了切点,就会执行你写的切面代码。

二、简单使用

1.首先引入aop坐标

以boot项目为例:

 		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

2.定义切面类

以“后置通知”为案例,其他通知方式自行百度:

@Aspect
@Component
public class AspectLog {
	//所谓后置通知,就是指定义的切点执行完毕以后执行此类
    //常见日志打印
    //import org.slf4j.Logger;
	//import org.slf4j.LoggerFactory;
    private final static Logger logger =LoggerFactory.getLogger(AspectLog.class);
    /**
     * 签名:定义切入点
     * 第一种方式:使用切点函数指定包下任意类的任意方法。
     * 每个通知都支持切点函数,但统一定义pointCut(),可以统一管理切点。
     */
    @Pointcut("execution(* com.example.service..*(..))")
    public void pointCut() {}
    
    /**
     * 后置通知—1
     */
    @After("pointCut()")
    public void after1() {
        System.out.println("后置通知1");
    }
    
    /**
     * 签名:定义切入点
     * 第二种方式:使用注解方式。
     * 包含 "自定义的@DataLog注解" 的方法都会被作为切入点
     */
    @Pointcut("@annotation(com.example.utils.aop.DataLog)")
    public void annotationPoinCut(){}
    
    /**
     * 后置通知—2
     */
    @After("annotationPoinCut()")
    public void after2() {
        System.out.println("后置通知2");
    }

其中关于切点函数签名方式的案例:

 @Pointcut("execution(* com.example.service..*(..))")参数的各种写法举例说明(网上复制粘贴的,未详细整理):
 * “execution(* add())”匹配所有的不带参数的add()方法。
 * “execution(public * com.example..*.add*(..))”匹配所有com.example包及其子包下所有类的以add开头的所有public方法。
 * “execution(* *(..) throws Exception)”匹配所有抛出Exception的方法。
 * execution(public void com.example.Test.add(int, int)):只有add方法加入了4个通知,
 * execution(public void com.example.Test.*(int, int)):任意方法,参数为int,int
 * execution(public void com.example.Test.*(..)):Test中的任意方法,任意参数列表
 * execution(public * com.example.Test.*(..)):Test中的任意方法,任意参数列表,任意返回值
 * execution( * com.example.Test*(..)):Test中的任意方法,任意参数列表,任意返回值,任意访问修饰符

3.要使用的方法

两种方式:
1.由于切入点为
@Pointcut(“execution(* com.example.service…*(…))”)
public void pointCut() {}
也就是响应包下所有的方法:

package com.example.service.imp;

import com.example.dao.UserMapper;
import com.example.domain.User;
import com.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class UserServiceImp implements UserService {
  	@Autowired
    private UserMapper userMapper;
	@Override
    public List<User> getMapper1(String age) {
        List<User> listForTest = new ArrayList<User>();
        System.out.println("方法切入后置通知");
        return listForTest;
    }
}

2.由于切入点为
@Pointcut("@annotation(com.example.utils.aop.DataLog)")
public void annotationPoinCut(){}
也就是标记注解的方法:

    @Override
    @DataLog
    public List<User> getMapper2(String age) {
        List<User> listForTest = new ArrayList<User>();
        System.out.println("注解切入,后置通知");
        return listForTest;
    }

三、其他使用

1.有返回值的通知

	/**
     *
     * @param joinPoint 切入点
     * @param keys      返回结果
     */
    @AfterReturning(value = "operLogPoinCut()", returning = "keys")
    public void saveOperLog(JoinPoint joinPoint, Object keys) {
        // joinPoint的用途,比如获取请求的类名
        String className = joinPoint.getTarget().getClass().getName();
        // keys的用途
        if(keys.equals("abcd")){
            System.out.println("获得的返回值是"+keys+"符合要求");
            System.out.println(joinPoint.getSignature().getName());
        }else{
            System.out.println("获得的返回值不等于abcd,不符合要求");
        }
    }

四、传统配置文件方式

1.引入包

非SpringBoot的传统Spring项目要导入一些maven依赖。自行百度

2.自定义AOP增强类

如自定义一个MyAOP

public class MyAOP{
   public void before1() {
        System.out.println("前置增强........");
    }
    
    public void after1() {
        System.out.println("后置增强.......");
    }
    
    // 环绕通知
    public void around1(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // 方法之前
        System.out.println("方法之前.........");
        
        // 执行被增强的方法
        proceedingJoinPoint.proceed();
        
        // 方法之后
        System.out.println("方法之后.........");
    }
}

3.配置applicationContext.xml

<!-- 1.配置对象 -->
<bean id="myBook" class="com.test.aop.Test"></bean>
<bean id="myBook" class="com.test.aop.MyAOP"></bean>

<!-- 2.配置AOP操作 -->
<aop:config>
    <!-- 2.1配置切入点 -->
    <aop:pointcut expression="execution(* com.test.aop.Test.*(..))" id="pointcut1"/>
    
    <!-- 2.2配置切面
        把增强用到方法上面
     -->
    <aop:aspect ref="myBook">
        <!-- 配置增强类型 
            method:增强类中使用哪个方法作为前置
        -->
        <aop:before method="before1" pointcut-ref="pointcut1"/>    
        
        <aop:after method="after1" pointcut-ref="pointcut1"/>
        
        <aop:around method="around1" pointcut-ref="pointcut1"/>
    </aop:aspect>
</aop:config>

其中Test类为被增强的类,并没有给出Test的代码。切入点配置的是扫描Test类所有方法。

五、AOP实现日志

  • 定义的字段主要如下

    • 操作日志id
    • 操作人
    • 操作人工号
    • 执行模块
    • 执行方法
    • 参数名称
    • 参数值
    • 操作内容
    • 请求路径
    • IP地址
    • 操作时间
    • 执行描述(1:执行成功、2:执行失败)
    • 结果信息
    • …等等
  • 核心代码

@Aspect
@Component
public class TestAspect{
	private final Logger logger = LoggerFactory.getLogger(TestAspect.class);
	@Pointcut("@annotation(com.example.utils.aop.DataLog)")
	public void annotationPoinCut(){
	}

	@AfterReturning(value = "annotationPoinCut()", returning = "keys")
	public void saveOperLog(JoinPoint joinPoint, Object keys) {
	
		try {
		
		} catch (Exception e) {
			logger.error(e.toString()
		}
	}
}
  • 其他代码
    获取IP代码:
{
// 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes
                .resolveReference(RequestAttributes.REFERENCE_REQUEST);
 		String ip = getIpAddr(HttpServletRequest request);
}


private String getIpAddr(HttpServletRequest request) {
		String[] HEADERS_TO_TRY = {
            "X-Forwarded-For",
            "Proxy-Client-IP",
            "WL-Proxy-Client-IP",
            "HTTP_X_FORWARDED_FOR",
            "HTTP_X_FORWARDED",
            "HTTP_X_CLUSTER_CLIENT_IP",
            "HTTP_CLIENT_IP",
            "HTTP_FORWARDED_FOR",
            "HTTP_FORWARDED",
            "HTTP_VIA",
            "REMOTE_ADDR",
            "X-Real-IP"};
        for (String header : HEADERS_TO_TRY) {
            String ip = request.getHeader(header);
            if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
                return ip;
            }
        }
        return request.getRemoteAddr();
    }

获取方法各种信息代码:

{
// 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes
                .resolveReference(RequestAttributes.REFERENCE_REQUEST);
 		String ip = getIpAddr(HttpServletRequest request);
}


private String getIpAddr(HttpServletRequest request) {
		 // 从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取切入点所在的方法
        Method method = signature.getMethod();
        // 获取请求的类名
        String className = joinPoint.getTarget().getClass().getName();
        // 请求的参数设置
        String[] parameterNames = signature.getParameterNames();
        Object[] args = joinPoint.getArgs();
        //判定方法上方是否有自定义注解DataLog
        DataLogopLog = method.getAnnotation(DataLog.class);
        // 获取请求的方法名
        String methodName = method.getName();
    }
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值