3.1.5 springAOP实现步骤

  • AspectJ:Java 社区里最完整最流行的 AOP 框架.

  • 在 Spring2.0 以上版本中, 可以使用基于 AspectJ 注解或基于 XML 配置的 AOP

使用步骤:

1、加入jar包,使用maven 引入依赖即可

     aopalliance.jar、aspectj.weaver.jar 和 spring-aspects.jar

2.spring-mvc.xml配置文件中添加:

  1)xmlns:aop="http://www.springframework.org/schema/aop"                    http://www.springframework.org/schema/aop
                  http://www.springframework.org/schema/aop/spring-aop.xsd

  2) <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

注意了!!!刚开始加在spring.xml中切面没有生效,后来改在spring-mvc.xml中

<beans xmlns="http://www.springframework.org/schema/beans"	
	xmlns:context="http://www.springframework.org/schema/context"
    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.1.xsd
            http://www.springframework.org/schema/context
			http://www.springframework.org/schema/context/spring-context-4.1.xsd
	   		http://www.springframework.org/schema/aop
			http://www.springframework.org/schema/aop/spring-aop.xsd">

	<!--开启Aspect生成代理对象	-->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

切入点表达式

// 语法结构
execution([权限修饰符][返回类型][类全路径][方法名称]([参数列表]))
//权限修饰符--可以不写
//返回类型--* 表示返回值的类型任意
// 指定单类中某个方法
@Pointcut("execution(* com.hsnn.medstgmini.suppur.controller.SuppurController.exportInfo(..))")
// 指定到impl包下面所有方法
 execution(* cn.li.service.impl.*.*(String,..)) 

在Spring中的AOP有6种增强方式,分别是:

1、@Before 前置增强

2、@After 后置增强,使用方法与前置通知相同。注意:后置通知即使方法异常也会成功执行,但是后置通知无法拿到目标方法的返回结果。需要返回通知。

3、@Around 环绕增强

4、@AfterReturning 最终增强,在方法正常之后之后执行的通知,可以拿到目标方法的返回结果。使用返回通知需要注意的是:指定returnning="result",afterReturnningAdvice(JoinPoint joinpoint,Object result)与方法入参位置的对象名称一致,否则会产生异常。

5、@AfterThrowing 异常增强,方法产生异常的时候,可以拿到异常信息。同样需要注意的是:指定throwing="e",与afterThrowingAdvice(JoinPoint joinpoint,Exception e)方法入参位置的异常对象名称一致。

6、@DeclareParents 引入增强

1.基于注解的方式实现springAOP:

1.创建切面类(LoggingAspect)。

@Aspect:声明当前类为切面类。

@Component:交给springIOC容器进行管理。

@Order(number):@Order 表示 配置多个切面之间的优先级问题 。 谁的值越小谁的优先级越高 。

具体代码实现如下:》》》》》
//当前类就是一个切面类
//想要一个类成为切面类,1.添加@Component 注释标注 当前类被springIOC容器所管理
//2.@Aspect表示当前类为一个切面类
//@Order 表示 配置多个切面之间的优先级问题  。 谁的值越小谁的优先级越高 。
//@Order(2)
//@Aspect
//@Component
public class LoggingAspect {
	// @Before表示前置通知。指的是在特定位置之前,去执行该方法
	// 通知 其实是切面类中一个具体的方法
	// JoinPoint 表示连接点
	/**
	 * 可以使用AspectJ表达式,对目标方法进行抽象概括。
	 * 
	 * execution(* cn.li.service.impl.*.*(String,..))
	 * 
	 * 第一个* 表示匹配所有访问修饰符 以及所有返回值类型的方法 第二个* 表示当前包下所有的类 第三个* 表示所有的方法名称 ..
	 * 表示匹配任意多个参数。 (String,.. )表示匹配第一个参数为String类型的方法,..表示匹配任意数量任意类型的参数。
	 * String,String 表示匹配参数为两个字符串的方法。
	 */
	//@Pointcut("execution(* cn.li.service.impl.*.*(..))")
	public void declareRepeatJoinPointExpression(){
		
	}

	//@Before("execution(* cn.li.service.impl.*.*(..))")
	public void beforeLog(JoinPoint joinpoint) {
		// 通过连接点对象可以获得调用目标方法的名称和参数
		// 获得方法名称。能拿到你要调用方法的名称
		String method = joinpoint.getSignature().getName();
		// 获得调用方法时传递的参数
		List arguments = Arrays.asList(joinpoint.getArgs());
		System.out.println("前置日志调用了方法" + method + "方法,参数是" + arguments);
	}

	// 注意:后置通知即使方法异常也会成功执行,但是后置通知无法拿到目标方法的返回结果。 需要使用返回通知。。。
	//@After("execution(* cn.li.service.impl.*.*(..))")
	public void afterLog(JoinPoint joinpoint) {
		String method = joinpoint.getSignature().getName();
		List arguments = Arrays.asList(joinpoint.getArgs());
		System.out.println("后置日志 。");
	}

	// 返回通知
	// 注意:返回通知 ,其实跟后置通知一样 。都是在目标方法执行完之后 才会被执行 。
	// returning="result" 名字 要跟参数列表中 Object 对象的名称一致 ,不然产生异常。
	//@AfterReturning(value = "execution(* cn.li.service.impl.*.*(..))", returning = "result")
	public void testAfterReturning(JoinPoint joinpoint, Object result) {
		String method = joinpoint.getSignature().getName();
		System.out.println("我是返回通知  。 我在目标方法核心业务执行完才会执行 。" + result);
	}
	//@AfterThrowing(value="execution(* cn.li.service.impl.*.count(..))",throwing="e")
	public void testAfterThrowing(JoinPoint joinpoint,Exception e){
		System.out.println("我是异常通知 ,我是在方法产生异常后执行的。"+e);
	}
	//环绕通知 。   跟动态代理的代码很像。
	//@Around("declareRepeatJoinPointExpression()")
//	public void around(ProceedingJoinPoint pjp){
//		//声明一个Object 对象 用来表示 目标方法的返回值 。
//		Object result=null;
//		String method=pjp.getSignature().getName();
//		try {
//			System.out.println("我是前置日志 。。。"+method);
//			result = pjp.proceed();//调用proceed() 表示执行被代理类的目标方法。
//			System.out.println("我是返回通知"+method+result);
//		} catch (Throwable e) {
//			//Throwable 所有异常类跟错误类的父类 。Exception  Error ...
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//			System.out.println("异常通知,产生异常的时候 会执行catch 里面的代码 。");
//		}
//		System.out.println("我是后置通知 。。。"+result+method);	
//	}
}
//@Order(1)
//@Aspect
//@Component
public class CheckAspect {
	
	//@Before("execution(* cn.li.service.impl.*.*(..))")
	public void checkBeforeLog(JoinPoint joinpoint){
		System.out.println("我是验证切面的前置通知 。");
	}
}

2.基于XML文件的方式实现springAOP:

<!-- 
		@Aspect  注解生效 。
		让注解生效,切面中的注解生效。
	当调用目标方法,跟Aspect中声明的方法相匹配的时候,
	AOP框架会自动的为目标方法所在的类创建代理对象。 
	
	 作用是让注解生效 ,当调用的方法,跟通知中声明的方法一致的时候。AOP框架会自动的为那个方法所在的类生成代理对象,然后在调用目标方法(之前或者之后)把通知中的方法加进去。
	-->
	<!-- <aop:aspectj-autoproxy></aop:aspectj-autoproxy> -->
	<!-- 配置切面 的bean -->
	<bean id="checkAspect" class="cn.li.aspect.CheckAspect"></bean>
	<bean id="loggingAspect" class="cn.li.aspect.LoggingAspect"></bean>
	<!-- 配置aop -->
	<aop:config>
		<!-- 配置切点表达式 -->
		<aop:pointcut expression="execution(* cn.li.service.impl.*.*(..))" id="pointcut"/>
		<aop:aspect ref="checkAspect" order="1">
			<aop:before method="checkBeforeLog" pointcut-ref="pointcut"/>
		</aop:aspect>
		<aop:aspect ref="loggingAspect" order="2">
			<aop:before method="beforeLog" pointcut-ref="pointcut"/>
			<aop:after method="afterLog" pointcut-ref="pointcut"/>
			<aop:after-returning method="testAfterReturning" returning="result" pointcut-ref="pointcut"/>
			<aop:after-throwing method="testAfterThrowing" throwing="e" pointcut-ref="pointcut"/>
			<!-- <aop:around method="around" pointcut-ref="pointcut"/> -->
		</aop:aspect>
	</aop:config>

案例1:记录controller 层请求请求地址+请求参数+处理时间

package com.hsnn.medstgmini.aop;

import com.baomidou.mybatisplus.core.toolkit.ArrayUtils;
import com.hsnn.medstgmini.base.sys.model.SysUser;
import com.hsnn.medstgmini.log.constant.LogCons;
import com.hsnn.medstgmini.log.model.TableLog;
import com.hsnn.medstgmini.log.service.ITableLogService;
import com.hsnn.medstgmini.util.SessionUtil;
import org.apache.commons.collections4.MapUtils;
import org.aspectj.lang.JoinPoint;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * 用户动作记录日志  controller 层记录
 *
 * @author :chengang
 * @date :Created in 2022-12-9 16:38
 */
@Aspect
@Component
public class ActionAspect {

//    @Autowired
//    private SqlSessionFactory sqlSessionFactory;

    @Autowired
    private ITableLogService tableLogService;

    /**
     * 切点设置
     */
//    @Pointcut("execution(public * com.hsnn.medstgmini.suppur.controller.SuppurOrderdetailRecentController.exportSuppurOrderdetailRecentDataInfo(..))")
//    public void exportSuppurOrderdetailRecentDataInfo() {
//    }
    @Pointcut("execution(public * com.hsnn.medstgmini.suppur.controller.*.*(..))")
    public void all() {
    }

    @Pointcut("execution(public * com.hsnn.medstgmini.pay.controller.*.*(..))")
    public void payAll() {
    }
    //切点路径
    @Pointcut("execution(public * com.hsnn.medstgmini.project.controller.*.*(..))")
    public void projectAll() {
    }

    /**
     * 环绕
     *
     * @param joinpoint
     * @return void
     * @author 
     * @date 2022-12-9 16:59
     **/
    @Around("payAll()|| all()|| projectAll()")
    public Object afterLog(ProceedingJoinPoint joinpoint) throws Throwable {
        // 获取登陆信息
        SysUser sysUser = SessionUtil.getSysUser();
        TableLog log = new TableLog();

        //2.调用目标方法
        // 参数
        Map<String, Object> paramMap = new HashMap<>();
        String comment = "";
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (!Objects.isNull(sra) && !Objects.isNull(sra.getRequest())) {
            HttpServletRequest request = sra.getRequest();
            //
            Map<String, Object> map = request.getParameterMap();
            if (MapUtils.isNotEmpty(map)) {
                paramMap.putAll(map);
            }

            comment = request.getRequestURI();
        }

        Map<String, Object> map = this.getMethodParams(joinpoint);
        if (MapUtils.isNotEmpty(map)) {
            paramMap.putAll(map);
        }
        // 开始时间
        Long start = System.currentTimeMillis();
        Object proceed = joinpoint.proceed();
        //执行方法后

        log.setUserId(sysUser.getUserId());
        log.setUserName(sysUser.getUserName());
        log.setType(LogCons.TypeEnums.SCENE_OPERATION.getKey());
        log.setComment(comment);
        // 参数
        Map<String, Object> reMap = new HashMap<>();
        for (String key : paramMap.keySet()) {
            if (paramMap.get(key) instanceof String[]) {
                String[] cid = (String[]) paramMap.get(key);
                if (cid.length > 0) {
                    reMap.put(key, cid[0]);
                }
            } else {
                reMap.put(key, paramMap.get(key));
            }
        }

        // 方法参数
        log.setContextBefore(reMap.toString());
        // 执行时间 毫秒
        log.setContextAfter(Long.toString(System.currentTimeMillis() - start));
        log.setAddTime(new Date());
        // 记录日志表
        tableLogService.save(log);

        return proceed;

    }

    /**
     * 获取请求方法的参数
     *
     * @param joinPoint
     */
    public Map getMethodParams(JoinPoint joinPoint) {
        String[] names = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
        Map params = new HashMap();
        if (ArrayUtils.isEmpty(names)) return params;
        Object[] values = joinPoint.getArgs();
        for (int i = 0; i < names.length; i++) {
            params.put(names[i], values[i]);
        }
        return params;
    }


}

 aop不生效原因,需要public方法才能生效。

部分参考:3.1.5 springAOP实现步骤 · java框架 · 看云(感谢大佬的分享)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值