精华内容
下载资源
问答
  • NULL 博文链接:https://free9277.iteye.com/blog/1935492
  • 我们项目中一般需要去监控controller 层, 校验参数、加密、日志打印等等。 一、首先是写个切面,定位所有controller 层的方法 @Aspect @Component ...public class MutmsAspect { ... @Value("${spring.application....

    我们项目中一般需要去监控 controller 层, 校验参数、加密、日志打印等等。

    一、首先是写个切面,定位所有controller 层的方法

    @Aspect
    @Component
    @Slf4j
    public class MutmsAspect {
    
    	@Value("${spring.application.name}")
    	private String  appName;
    
        @Autowired
        private ExeService exeService;
    
    	@Around("execution(public * com.ceair.mutms..controller.*.*(..))")
    	public Object exeMethod(ProceedingJoinPoint joinPoint)throws Throwable{

    二、校验参数

    // 版本验证,低版本报错
    MutmsVersion.update(methodName,req);
    // token 验证
    MutmsToken.verify(appName, permittedServer, joinPoint,req);
    // session校验
    MutmsSession.verify(appName,permittedServer, joinPoint);

    三、打印入参,执行方法,拿到反参

    // 因为阿里云日志只支持xxxx* 这种方式模糊查找不支持*xxxx这种方式所以给关键日志加一个前缀
    log.info("{}{}方法:{},入参: {}", AliyunLogConsts.MUTMS_REQ_PREFIX, methodName, joinPoint.getSignature().getName(), JsonUtil.toString(req,""));
    res=joinPoint.proceed(joinPoint.getArgs());

    四、打印反参,方法执行时间,保存访问记录等

    // 因为阿里云日志只支持xxxx* 这种方式模糊查找不支持*xxxx这种方式所以给关键日志加一个前缀
    log.info("{}{}返回: {}", AliyunLogConsts.MUTMS_RES_PREFIX, methodName, JsonUtil.toString(res,""));
    log.info("方法: {}, 接口耗时:{}毫秒", methodName, (System.currentTimeMillis()-starttime));
    // 异步保存接口访问记录
    MUTMSAccessLogQueueHelper.execute(() -> {
    MUTMSAccessLog accessLog = new MUTMSAccessLog();
    accessLog.setPassportId(passportId);
    accessLog.setTransactionId(transactionId);  // 明文transactionId
    accessLog.setSessionId(sessionId);
    accessLog.setOs(req.getOs());
    accessLog.setLocalIp(IpHelper.getLocalIp());
    accessLog.setClientRealIp(clentRealIp);
    accessLog.setLanguage(req.getLanguage());
    accessLog.setRegion(req.getRegion());
    accessLog.setAppVersion(req.getAppVersion());
    accessLog.setChannelNo(req.getChannelNo());
    accessLog.setSecondChannelNo(req.getSecondChannelNo());
    accessLog.setThirdChannelNo(req.getThirdChannelNo());
    accessLog.setSalesChannel(salesChannel);

     完整代码

    package com.ceair.mutms.common.aop;
    
    import com.ceair.mutms.common.aliyunlog.AliyunLogConsts;
    import com.ceair.mutms.common.aliyunlog.MUTMSAccessLogItemKeyConsts;
    import com.ceair.mutms.common.base.BaseReq;
    import com.ceair.mutms.common.base.CommonRes;
    import com.ceair.mutms.common.entity.MUTMSAccessLog;
    import com.ceair.mutms.common.exception.*;
    import com.ceair.mutms.common.filter.ContextUtil;
    import com.ceair.mutms.common.aliyunlog.AliyunLogHelper;
    import com.ceair.mutms.common.aliyunlog.AliyunLogItemKeyConsts;
    import com.ceair.mutms.common.log.MDCBean;
    import com.ceair.mutms.common.log.MDCHelper;
    import com.ceair.mutms.common.sec.aes.BangBang;
    import com.ceair.mutms.common.service.ExeService;
    import com.ceair.mutms.common.utils.*;
    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.springframework.beans.BeanUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    import org.springframework.web.multipart.support.StandardMultipartHttpServletRequest;
    
    import java.util.Map;
    /**
     * 接口控制
     * @author DELL
     *
     */
    @Order(-1)
    @Aspect
    @Component
    @Slf4j
    public class MutmsAspect {
    	@Value("${spring.application.name}")
    	private String  appName;
    
        @Autowired
        private ExeService exeService;
    
    
    	@Around("execution(public * com.ceair.mutms..controller.*.*(..))")
    	public Object exeMethod(ProceedingJoinPoint joinPoint)throws Throwable{
    		long starttime = System.currentTimeMillis();
    
    		String permittedServer=CacheUtil.getOtherCode(CacheKey.PERMITTED_SERVER);
    		BaseReq req=getParam(joinPoint.getArgs());
    
    		// 加密 token
    		String tokenRsa = req.getCeairToken();
    		Long passportId = req.getPassportId();
    		String clentRealIp = ContextUtil.getContext().getString(CacheKey.CLIENT_REAL_IP_KEY);
    		String sessionId = ContextUtil.getContext().getString(CacheKey.SESSION_ID_KEY);
    		String salesChannel = SalesChannelHelper.salesChannel(req.getLanguage(), req.getRegion());
    		//当前请求所执行的controller类的方法
    		final String methodName=joinPoint.getSignature().getName();
    		String className = joinPoint.getSignature().getDeclaringTypeName();
    		final String interfaceKey = className + "." + methodName;
    
            Object res=null;
    
            final String transactionId = Assert.isNotEmpty(req.getTransactionId()) ? req.getTransactionId() : BaseReq.generateTransactionId(passportId);
    
            MDCBean bean = new MDCBean();
    
            bean.setTransactionId(transactionId);
            bean.setPassportId(Assert.nullToEmpty(passportId));
            bean.setSessionId(sessionId);
    
            AliyunLogHelper.addItemIfAbsent(req.getAliyunLogItems(), AliyunLogItemKeyConsts.TRANSACTION_ID, transactionId);
            AliyunLogHelper.addItemIfAbsent(req.getAliyunLogItems(), AliyunLogItemKeyConsts.SESSION_ID, sessionId);
            AliyunLogHelper.addItemIfAbsent(req.getAliyunLogItems(), AliyunLogItemKeyConsts.PASSPORT_ID, passportId);
            AliyunLogHelper.addItemIfAbsent(req.getAliyunLogItems(), AliyunLogItemKeyConsts.INTERFACE_NAME, interfaceKey);
            AliyunLogHelper.addLogTime(req.getAliyunLogItems());
    
    		try {
                MDCHelper.insertIntoMDC(bean);
    
    			// 当前请求所使用的语言类型
    			ContextUtil.getContext().put(CacheKey.LANGUAGE_CACHE_KEY, Assert.isEmpty(req.getLanguage())?"zh":req.getLanguage());
    			// 版本验证,低版本报错
    			MutmsVersion.update(methodName,req);
    			// token 验证
    			MutmsToken.verify(appName, permittedServer, joinPoint,req);
    
    			req.setSalesChannel(salesChannel);
    			// 当前线程执行子任务的唯一标识
    //			ContextUtil.getContext().put("logbean.uuid", UUID.randomUUID().toString());
    			// 地区
    			ContextUtil.getContext().put(CacheKey.REGION_CACHE_KEY, Assert.isEmpty(req.getRegion())?"CN":req.getRegion());
    			// 渠道
    			ContextUtil.getContext().put(CacheKey.SALES_CHANNEL_CACHE_KEY, salesChannel);
    			//系统支持的支付方式
    			if(req.getSupportPay()!=null && req.getSupportPay()) {
    				ContextUtil.getContext().put(CacheKey.OS_PAY_METHOD_CACHE_KEY, req.getOsPayMethod());
    			}
    			// 每次请求唯一ID,明文tansactionId
    			ContextUtil.getContext().put(CacheKey.TRANSACTION_ID_CACHE_KEY, transactionId);
    			// 当前请求的用户ID
    			ContextUtil.getContext().put(CacheKey.PASSPORTID_CACHE_KEY, passportId);
    			// 当前请求的客户端平台系统:AD|iOS
    			ContextUtil.getContext().put(CacheKey.APP_OS_CACHE_KEY, req.getOs());
    			// 当前请求的客户端版本号
    			ContextUtil.getContext().put(CacheKey.APP_VERSION_CACHE_KEY, req.getAppVersion());
    			// 当前请求的设备ID
    			ContextUtil.getContext().put(CacheKey.IEMI_CACHE_KEY,req.getImei());
    			//阿里token
    			ContextUtil.getContext().put(CacheKey.APDIDTOKEN_CACHE_KEY,req.getApdidToken());
    
    			// session校验
    			MutmsSession.verify(appName,permittedServer, joinPoint);
    
    			String codeEnity=null;
    
    			if(permittedServer==null||!permittedServer.contains(appName)) {
    				//接口调用开关
    				codeEnity=CacheUtil.getMethod(methodName);
    			}
    			//*表示关闭当前所有接口
    			if(codeEnity !=null || (codeEnity=CacheUtil.getMethod("*"))!=null) {
    				log.info("方法:{},被禁用:{}", joinPoint.getSignature().getName(), codeEnity);
    				res = new CommonRes<Object>(CommonRes.FAIL,CacheUtil.getI18nVal(codeEnity, codeEnity));
    			}else {
    			    // 因为阿里云日志只支持xxxx* 这种方式模糊查找不支持*xxxx这种方式所以给关键日志加一个前缀
    				log.info("{}{}方法:{},入参: {}", AliyunLogConsts.MUTMS_REQ_PREFIX, methodName, joinPoint.getSignature().getName(), JsonUtil.toString(req,""));
    				res=joinPoint.proceed(joinPoint.getArgs());
    
    			}
    
    		}catch (MUTMSFirstExceptionWithData tx){
                res= ExceptionHandler.doException(tx);
            }catch (MUTMSFirstException tx){
    			res= ExceptionHandler.doException(tx);
    		}catch (MUTMSRuntimeException tx){
    			res= ExceptionHandler.doException(tx);
    		}catch(MUTMSSecondExceptionWithData e){
                res= ExceptionHandler.doException(e);
            }catch(MUTMSSecondException e){
    			res= ExceptionHandler.doException(e);
    		}catch(MUTMSThirdException e){
    			res= ExceptionHandler.doException(e);
    		}catch(MUTMSFourthException e){
                res= ExceptionHandler.doException(e);
            }catch(Exception e) {
    		    if(e instanceof NullPointerException)
                {
                    log.info("接口调用出现异常, ex: {}:", Assert.callStackOneLine(e));
                }
    
    			res= ExceptionHandler.doException(e);
    			log.error("接口调用出现异常:", e);
    		}finally {
    			// 把请求参数存入redis缓存
    			exeService.exec(()-> {
    				try {
    					MutmsToken.setToken(tokenRsa,req);
    				} catch (Exception e) {
    					log.error("异步记录ceair token 出现异常:", e);
    				}
    			});
    
    			final String resultCode;
    			final String resultMsg;
    			if(res instanceof CommonRes){
    				((CommonRes<?>) res).setTransactionId(transactionId);
    				resultCode = ((CommonRes<?>) res).getResultCode();
    				resultMsg = ((CommonRes<?>) res).getResultMsg();
    				// 构建统一弹框
                    DialogHelper.buildDialog((CommonRes<?>)res, interfaceKey, req.getDialogParams());
    
    			}else {
    				resultCode = "";
    				resultMsg = "";
    			}
    
    //			log.info("isNeedToAliyunLog: {}, interfaceKey: {}", AliyunLogHelper.isNeedToAliyunLog(interfaceKey), interfaceKey);
    			if(AliyunLogHelper.isNeedToAliyunLog(interfaceKey))
                {
                    AliyunLogHelper.addTopic(req.getAliyunLogItems(), interfaceKey);
    
                    AliyunLogHelper.execute(() -> {
                        AliyunLogHelper.putLogs(req.getAliyunLogItems());
                    });
                }
    
                Long costTimeMillisecond = System.currentTimeMillis() - starttime;
    			// 异步保存接口访问记录
    			MUTMSAccessLogQueueHelper.execute(() -> {
    				MUTMSAccessLog accessLog = new MUTMSAccessLog();
    				accessLog.setPassportId(passportId);
    				accessLog.setTransactionId(transactionId);  // 明文transactionId
    				accessLog.setSessionId(sessionId);
    				accessLog.setOs(req.getOs());
    				accessLog.setLocalIp(IpHelper.getLocalIp());
    				accessLog.setClientRealIp(clentRealIp);
    				accessLog.setLanguage(req.getLanguage());
    				accessLog.setRegion(req.getRegion());
    				accessLog.setAppVersion(req.getAppVersion());
    				accessLog.setChannelNo(req.getChannelNo());
    				accessLog.setSecondChannelNo(req.getSecondChannelNo());
    				accessLog.setThirdChannelNo(req.getThirdChannelNo());
    				accessLog.setSalesChannel(salesChannel);
    				accessLog.setCrtDt(TimeUtils.nowLong());
    				accessLog.setClassName(className);
    				accessLog.setMethodName(methodName);
    				accessLog.setImei(req.getImei());
    				accessLog.setResultCode(resultCode);
    				accessLog.setResultMsg(resultMsg);
                    accessLog.setRegistrationID(req.getRegistrationID());
    				accessLog.setCostTimeMillisecond(costTimeMillisecond);
    
                    Map<String, String> accessLogItemsMap =  MUTMSAccessLogItemKeyConsts.buildAliyunLogItems(accessLog, req.getAliyunLogItems(), req.getAliyunLogItemsExt());
    
                    AliyunLogHelper.putAccessLogs(accessLogItemsMap);
    
    //                MongoTemplate mongoTemplate = AppContext.getBean(MongoTemplate.class);
    //				mongoTemplate.insert(accessLog);
    			});
    
    			//非生产环境打印接口返回值
    			if(CacheUtil.PRINT_RES) {
    			    // 因为阿里云日志只支持xxxx* 这种方式模糊查找不支持*xxxx这种方式所以给关键日志加一个前缀
    				log.info("{}{}返回: {}", AliyunLogConsts.MUTMS_RES_PREFIX, methodName, JsonUtil.toString(res,""));
    			}
     
                log.info("方法: {}, 接口耗时:{}毫秒", methodName, (System.currentTimeMillis()-starttime));
    		}
    		return res;
    	}
    
    
    	private BaseReq getParam(Object args[]) {
    
    	    String appVersion = null;
    	    Long passportId = null;
    		// 转BaseReq 对象并返回
    	    if(args !=null) {
    			for (Object object : args) {
    				if(object instanceof BaseReq) {
    					return (BaseReq)object;
    				// 参数写在 form-data中处理
    				}else if(object instanceof StandardMultipartHttpServletRequest){
                        try {
                            String pid = ((StandardMultipartHttpServletRequest) object).getParameter("passportId");
                            passportId = Assert.isEmpty(pid)?null:Long.valueOf(pid);
                            appVersion = ((StandardMultipartHttpServletRequest) object).getParameter("appVersion");
                        }catch (Exception e){
                            Assert.callStack(e);
                        }
                    }
    			}
    		}
    
    
    		BaseReq req =new BaseReq();
    		req.setLanguage("zh");
    		req.setRegion("CN");
            req.setAppVersion(appVersion);
            req.setPassportId(passportId);
    		return req;
    	}
    
    }
    

     

    展开全文
  • NULL 博文链接:https://yuhuiblog695685688425687986842568269.iteye.com/blog/2409674
  • NULL 博文链接:https://z18022893621.iteye.com/blog/1956146
  • Spring返回通知、异常通知和环绕通知

     Spring返回通知、异常通知和环绕通知

     一、说在前面

     加入的jar包,配置的aop命名空间,以及基于注解的相关配置和上文中前置通知和后置通知完全一样,在这里就不在赘述。

     二、实现代码如下:

    1、返回通知

    	//返回通知:在代码正常执行之后返回的代码
    	//返回通知是可以访问到方法的返回值的。
    	@AfterReturning(value="execution(* com.at.aop.ArithmeticCalculator.*(..))",returning="result")
    	public void afterReturning(JoinPoint joinPoint,Object result){
    		String methodName = joinPoint.getSignature().getName();
    		System.out.println("返回通知方法 "+methodName+" 结果为 "+result);
    	}

    2、异常通知

            //异常通知
    	@AfterThrowing(value="execution(* com.at.aop.ArithmeticCalculator.*(..))",throwing="ex")
    	public void afterThrowing(JoinPoint joinPoint,Exception ex){
    		String methodName = joinPoint.getSignature().getName();
    		System.out.println("异常通知方法 "+methodName+" 发生的异常为 "+ex);
    	}

    3、环绕通知

    	/*环绕通知需要携带 ProceedingJoinPoint 类型的参数
    	 * 环绕通知类似于动态代理的全过程: ProceedingJoinPoint 类型的参数可以决定是够执行目标方法
    	 * 并且环绕通知必须有返回值,返回值就是目标方法的返回值。
    	 */
    	@Around("execution(* com.at.aop.ArithmeticCalculator.*(..))")
    	public Object aroundMethod(ProceedingJoinPoint pjp){
    		
    		Object result = null;
    		String methodName = pjp.getSignature().getName();
    		
    		try {
    			//前置通知
    			System.out.println("环绕通知中的前置通知方法 "+methodName+" 开始于参数 "+Arrays.asList(pjp.getArgs()));
    			//执行目标
    			result = pjp.proceed();
    			//返回通知
    			System.out.println("环绕通知中的返回通知方法 "+methodName+" 结果为 "+result);
    		} catch (Throwable e) {
    			//异常通知
    			System.out.println("环绕通知中的异常通知方法 "+methodName+" 结果为 "+e);
    			throw new RuntimeException(e);
    		}
    		//后置通知
    		System.out.println("环绕通知中的后置通知方法 "+methodName+" 结束了 ");
    		
    		return result;
    	}

    4、applicationContext.xml配置文件
    <?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"
    	xmlns:context="http://www.springframework.org/schema/context"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
    		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    	
    	<!-- 配置自动扫描的包 -->
    	<context:component-scan base-package="com.at.aop"></context:component-scan>
    	
    	<!-- 使AspjectJ 注解起作用:自动为匹配的类生产代理对象 -->
    	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    </beans>
    

    5、测试函数
    package com.at.aop;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestAspect {
    
    	public static void main(String[] args) {
    		
    		//1、创建spring的ioc容器
    		ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    		
    		//2、从ioc容器中获取bean的事例
    		ArithmeticCalculator ac = (ArithmeticCalculator) ctx.getBean("arithmeticCalculator");
    		
    		//3、使用bean
    		int result = ac.add(3, 6);
    		System.out.println("result "+result);
    		
    		int result2 = ac.div(12, 0);
    		System.out.println("result "+result2);
    	}
    }
    

    6、测试结果
    信息: Loading XML bean definitions from class path resource [applicationContext.xml]
    环绕通知中的前置通知方法 add 开始于参数 [3, 6]
    前置通知方法 add 开始 [3, 6]
    环绕通知中的返回通知方法 add 结果为 9
    环绕通知中的后置通知方法 add 结束了 
    后置通知方法 add 结束 
    返回通知方法 add 结果为 9
    result 9
    环绕通知中的前置通知方法 div 开始于参数 [12, 0]
    前置通知方法 div 开始 [12, 0]
    环绕通知中的异常通知方法 div 结果为 java.lang.ArithmeticException: / by zero
    后置通知方法 div 结束 
    异常通知方法 div 发生的异常为 java.lang.RuntimeException: java.lang.ArithmeticException: / by zero
    Exception in thread "main" java.lang.RuntimeException: java.lang.ArithmeticException: / by zero
    	at com.at.aop.LoggingAspect.aroundMethod(LoggingAspect.java:71)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:606)
    	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621)
    	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610)
    	at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:68)
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    	at org.springframework.aop.aspectj.AspectJAfterAdvice.invoke(AspectJAfterAdvice.java:43)
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    	at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:52)
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    	at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:58)
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
    	at com.sun.proxy.$Proxy11.div(Unknown Source)
    	at com.at.aop.TestAspect.main(TestAspect.java:20)
    Caused by: java.lang.ArithmeticException: / by zero
    	at com.at.aop.ArithmeticCalculatorImpl.div(ArithmeticCalculatorImpl.java:28)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:606)
    	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    	at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:52)
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    	at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85)
    	at com.at.aop.LoggingAspect.aroundMethod(LoggingAspect.java:65)
    	... 19 more
    


    By luoyepiaoxue2014
    微博地址:http://weibo.com/luoyepiaoxue2014  点击打开链接

    展开全文
  • ①AOP的前置通知方法 、后置通知方法 、环绕通知方法 首先导入相关的包: (1)定义一个java文件来使用AOP技术。(LogAdvice.java(日志通知切面)) LogAdvice.java package com . advice ; import ...

    AOP(Aspect Oriented Programming)也就是面向切面编程的技术。AOP基于IoC基础,是对OOP的有益补充。AOP是代码之间解耦的一 种实现。可以这样理解,面向对象编程是从静态角度考虑程序结构,面向切面编程是从动态角度考虑程序运行过程。AOP将应用系统分为两部分:
    (1)核心业务逻辑(Core Business Concerns)。
    (2)横向的通用逻辑,也就是所谓的切面(Crosscutting Enterprise Concerns)。
    例如,所有大中型应用都要涉及的持久化管理(Persistent)、事务管理(Transaction Management)、安全管理(Security)、日志管理(Logging)和调试管理(Debugging)等。

    AOP的底层实现原理实际是Java语言的动态代理机制。AOP 代理是由AOP框架动态生成一个对象,该对象可作为目标对象使用。AOP代理包含了目标对象的全部方法,但代理中的方法与目标对象的方法存在差异: AOP方法在特定切入点添加了增强处理,并回调了目标对象的方法。Spring 的AOP通常和IoC配合使用,需要程序员参与的有3个部分:
    (1)定义普通业务组件。
    (2)定义切入点。一个切入点可以横切多个业务组件。
    (3)定义增强处理。增强处理就是在AOP框架为普通业务组件织入的处理动作。
    Spring有如下两种方式来定义切入点和增强处理:
    (1)annotation 配置方式。使用@Aspect、 @Pointcut 等Annotation标注切入点和增强处理。
    (2)xml 配置方式。使用xml配置文件定义切入点和增强处理。


    我使用的是上一个学习crud操作的代码(MVC),稍作修改完成这个知识点的学习。
    使用Spring实现mysql数据库的CRUD操作

    ①AOP的前置通知方法 、后置通知方法 、环绕通知方法

    首先导入相关的包:
    在这里插入图片描述

    (1)定义一个java文件来使用AOP技术。(LogAdvice.java(日志通知切面))

    LogAdvice.java

    package com.advice;
    
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.ProceedingJoinPoint;
    
    
    public class LogAdvice {
    	//前置通知方法
    	public void myBeforeAdvice(JoinPoint joinPoint){
    		String methodname = joinPoint.getSignature().getName();
            System.out.println("Before前置通知——执行"+methodname+"方法前");
        }
    	
    	//后置通知方法
        public void myAfterAdvice(JoinPoint joinPoint){
        	String methodname = joinPoint.getSignature().getName();
            System.out.println("执行"+methodname+"方法后——After后置通知");
        }
        
        //环绕通知方法
        public void myAroundAdvice(ProceedingJoinPoint point) throws Throwable{
        	String methodname = point.getSignature().getName();
        	System.out.println("环绕通知,执行"+methodname+"方法前");
            //选择执行
            point.proceed();
            System.out.println("环绕通知,执行"+methodname+"方法后");
        }
    }
    

    (2)在applicationContext.xml文件做相关bean配置(部分代码)

    <bean id="userDao" class="com.dao.UserDao"/>
        <bean id="userService" class="com.service.UserService">
        	<property name="userDao" ref="service"></property>
        </bean>
        <bean id="logAdvice" class="com.advice.LogAdvice"/>
        
        <aop:config>
           <!-- 配置一个切面 -->
           <aop:aspect id="logaop" ref="logAdvice" order="1">
               <!-- 定义切入点,表示对service的所有方法都进行拦截 -->
               <!-- 
               expression:用来匹配执行方法的连接点
               	第一个*表示匹配任意的方法的返回值
               	第一个..表示service包及其子包
               	第二个*表示所有类
               	第三个*表示所有方法
               	第二个..表示方法的任意参数个数
                -->
               <aop:pointcut expression="execution(* com.service.*.*(..))" id="testpointcut"/>
               <!-- 定义前置通知 -->
               <aop:before method="myBeforeAdvice" pointcut-ref="testpointcut"/>
               <!-- 定义后置通知 -->
               <aop:after method="myAfterAdvice" pointcut-ref="testpointcut"/>
               <!-- 定义环绕通知 -->           
               <aop:around method="myAroundAdvice" pointcut-ref="testpointcut"/>
           </aop:aspect>
        </aop:config>
    </beans>
    

    再写个测试类跑起来就可以了。


    ②通过拦截器判断用户是否可以执行目标代码的方法

    (1)AuthorityInterceptor.java(用户权限拦截器)
    功能:
    admin用户可以执行add()和update()方法
    register用户可以执行add()方法,但不可执行update()方法
    other用户都不可以执行add()和update()方法

    package com.interceptor;
    
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    
    import com.bean.User;
    
    public class AuthorityInterceptor implements MethodInterceptor {
    
    	private User user;
    	public User getUser() {
    		return user;
    	}
    	public void setUser(User user) {
    		this.user = user;
    	}
    	
    	public Object invoke(MethodInvocation arg0) throws Throwable {
    		// TODO Auto-generated method stub
    		System.out.println("拦截器==权限验证开始");
    		String username=this.user.getUsername();
    		String methodName = arg0.getMethod().getName();
    		if(!username.equals("admin")&&!username.equals("register")) {
    			System.out.println("没有任何执行权限");
    			return null;
    		}
    		else if(username.equals("register")&&methodName.equals("update")) {
    			System.out.println("register用户没有修改权限");
    			return null;
    		}
    		else {
    			Object object = arg0.proceed();
    			System.out.println("拦截器==权限验证结束");
    			return object;
    		}
    	}
    
    }
    

    (2)LogInterceptor.java(用户操作日志拦截器)

    package com.interceptor;
    
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    
    public class LogInterceptor implements MethodInterceptor {
    
    	public Object invoke(MethodInvocation arg0) throws Throwable {
    		// TODO Auto-generated method stub
    		String methodName = arg0.getMethod().getName();
    		Object object = arg0.proceed();
    		System.out.println("拦截器==日志记录:尝试执行"+methodName+"方法");
    		return object;
    	}
    }
    

    (3)在applicationContext.xml文件做相关bean配置(部分代码)

       <bean id="admin" class="com.bean.User">
      		<property name="username" value="admin"></property>
      		<property name="password" value="123456"></property>
      	</bean>
      	
      	<bean id="register" class="com.bean.User">
      		<property name="username" value="register"></property>
      		<property name="password" value="654321"></property>
      	</bean>
      	
      	<bean id="other" class="com.bean.User">
      		<property name="username" value="other"></property>
      		<property name="password" value="666666"></property>
      	</bean>
      	<!--  -->
      	<bean id="logInterceptor" class="com.interceptor.LogInterceptor"/>
      	
      	<bean id="authorityInterceptor" class="com.interceptor.AuthorityInterceptor">
      		<!-- admin/register/other -->
      		<property name="user" ref="admin"></property>
      	</bean>
      	
      	<bean id="service" class="org.springframework.aop.framework.ProxyFactoryBean">
      		<property name="target" ref="userDao"></property>
      		<property name="interceptorNames">
      			<list>
      				<value>authorityInterceptor</value>
      				<value>logInterceptor</value>
      			</list>
      		</property>
      	</bean>
    

    激动人心的时刻到了,我们把成品跑起来!

    测试类:

    package com.test;
    
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import com.service.UserService;
    
    public class Test {
    
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    
    		ClassPathXmlApplicationContext l = new ClassPathXmlApplicationContext("applicationContext.xml");
    		
    		UserService service = l.getBean("userService",UserService.class);
    		
    		
    		service.add();
    		l.close();//关闭流
    		service.update();
    		l.close();
    	}
    
    }
    

    applicationContext.xml(完整代码)

    <?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:tx="http://www.springframework.org/schema/tx"
           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/context http://www.springframework.org/schema/context/spring-context.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
        
        <bean id="admin" class="com.bean.User">
      		<property name="username" value="admin"></property>
      		<property name="password" value="123"></property>
      	</bean>
      	
      	<bean id="register" class="com.bean.User">
      		<property name="username" value="register"></property>
      		<property name="password" value="123"></property>
      	</bean>
      	
      	<bean id="other" class="com.bean.User">
      		<property name="username" value="other"></property>
      		<property name="password" value="123"></property>
      	</bean>
      	<!--  -->
      	<bean id="logInterceptor" class="com.interceptor.LogInterceptor"/>
      	
      	<bean id="authorityInterceptor" class="com.interceptor.AuthorityInterceptor">
      		<!-- admin/register/other -->
      		<property name="user" ref="admin"></property>
      	</bean>
      	
      	<bean id="service" class="org.springframework.aop.framework.ProxyFactoryBean">
      		<property name="target" ref="userDao"></property>
      		<property name="interceptorNames">
      			<list>
      				<value>authorityInterceptor</value>
      				<value>logInterceptor</value>
      			</list>
      		</property>
      	</bean>
        
        <bean id="userDao" class="com.dao.UserDao"/>
        <bean id="userService" class="com.service.UserService">
        	<property name="userDao" ref="service"></property>
        </bean>
        <bean id="logAdvice" class="com.advice.LogAdvice"/>
        
        <aop:config>
           <!-- 配置一个切面 -->
           <aop:aspect id="logaop" ref="logAdvice" order="1">
               <!-- 定义切入点,表示对service的所有方法都进行拦截 -->
               <!-- 
               expression:用来匹配执行方法的连接点
               	第一个*表示匹配任意的方法的返回值
               	第一个..表示service包及其子包
               	第二个*表示所有类
               	第三个*表示所有方法
               	第二个..表示方法的任意参数个数
                -->
               <aop:pointcut expression="execution(* com.service.*.*(..))" id="testpointcut"/>
               <!-- 定义前置通知 -->
               <aop:before method="myBeforeAdvice" pointcut-ref="testpointcut"/>
               <!-- 定义后置通知 -->
               <aop:after method="myAfterAdvice" pointcut-ref="testpointcut"/>
               <!-- 定义环绕通知 -->           
               <aop:around method="myAroundAdvice" pointcut-ref="testpointcut"/>
           </aop:aspect>
        </aop:config>
    </beans>
    

    修改xml文件的联系用户id来显示不同用户下的打印结果。

      	<bean id="authorityInterceptor" class="com.interceptor.AuthorityInterceptor">
      		<!-- admin/register/other -->
      		<property name="user" ref="admin"></property>
      	</bean>
    

    (1)admin
    在这里插入图片描述
    (2)register
    在这里插入图片描述
    (3)other
    在这里插入图片描述

    展开全文
  • 环绕通知的特点: 必须传入ProceedingJoinPoint对象,调用其proceed()方法来手动执行切入点,否则什么也不执行 切入点: package com.dwb.service.impl; @Service public class UserServiceImpl implements ...

    环绕通知的特点:

    • 必须传入ProceedingJoinPoint对象,调用其proceed()方法来手动执行切入点,否则什么也不执行

    切入点:

    package com.dwb.service.impl;
    
    @Service
    public class UserServiceImpl implements IUserService {
    
        @Autowired
        private IUserDao userDao;
    
        @Override
        public User findUserById(int id) {
            System.out.println("方法被执行");
            return userDao.findUserById(id);
        }
    }
    
    

    切面类:

    package com.dwb.aop;
    
    @Component
    @Aspect
    public class LogAspect {
    
        @Around("execution(* com.dwb.service.impl.*.*(..))")
        public void around(ProceedingJoinPoint pjp) {
            try {
                pjp.proceed();
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
        }
    }
    

    运行结果:

    方法被执行
    
    • proceed()方法相对于是一个代理方法,如果写了其他四种通知,将会相应的执行
    package com.dwb.aop;
    
    @Component
    @Aspect
    public class LogAspect {
    
        @Before("execution(* com.dwb.service.impl.*.*(..))")
        public void before(){
            System.out.println("前置通知");
        }
    
        @After("execution(* com.dwb.service.impl.*.*(..))")
        public void after(){
            System.out.println("后置通知");
        }
    
        @AfterThrowing("execution(* com.dwb.service.impl.*.*(..))")
        public void throwing() {
            System.out.println("异常通知");
        }
    
        @AfterReturning("execution(* com.dwb.service.impl.*.*(..))")
        public void returning() {
            System.out.println("返回通知");
        }
    
        @Around("execution(* com.dwb.service.impl.*.*(..))")
        public void around(ProceedingJoinPoint pjp) {
            try {
                pjp.proceed();
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
        }
    }
    

    运行结果

    前置通知
    方法被执行
    返回通知
    后置通知
    

    建议:环绕通知不要和其他四种通知一起使用,而是把其他四种通知的代码都放在环绕通知的方法里面(和我们自己写的静态代理是一样的)

    @Around("execution(* com.dwb.service.impl.*.*(..))")
    public void around(ProceedingJoinPoint pjp) {
        // 这里写前置通知
        try {
            pjp.proceed();	// 这里执行被代理的方法(切入点)
            // 这里写返回通知
        } catch (Throwable throwable) {
            // 这里写异常通知
        } finally {
        	// 这里写后置通知
        }   
    }
    
    展开全文
  • Java中,实现动态代理一般需要以下几个步骤 写一个getProxy(final 被代理对象类名 参数名)方法 通过Proxy.newProxyInstance(loader, interfaces, h)创建一个proxy代理对象 loader:表示被代理对象的类加载器,...
  • AOP基于注解环绕通知

    千次阅读 2019-05-09 21:17:25
    * 要求 使用spring 基于注解的 aop 配置 使用环绕通知 */ @Aspect // 1. 让当前类变成切面类 @Component // 2. 让spring接管切面类的创建 public class LogAspect { @Autowired private ...
  • public void pointcut() { } /** * 通过环绕通知获取到注释上的信息 * * @param proceedingJoinPoint * @throws NoSuchMethodException */ @Around("pointcut()") public void around(ProceedingJoinPoint ...
  • SpringAOP环绕通知使用

    万次阅读 2017-06-07 16:23:55
    上一篇文章介绍了SpringAOP的概念以及简单使用:SpringAOP概念及其使用在springAOP中有五种通知,环绕通知是最为强大的通知。它能够让你编写的逻辑将被通知的目标方法完全包装起来。实际上就像在一个通知方法中同时...
  • aop环绕通知获取参数

    2021-06-10 16:41:30
    一、注解的参数获取: @Around("myPointCut() && @annotation(a)") public Object mylogger(ProceedingJoinPoint ...通过反射执行目标对象连接点处的方法,使用新的参数列表替换原来的: point.proceed(Object[] args);
  • * 该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用。 * * spring中的环绕通知: * 它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式...
  • } } 1、SpringBoot引入AOP <dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-aopartifactId> dependency> 2、编写环绕增强 package com.example.demo.controller;...
  • SpringAOP四种通知类型+环绕通知

    千次阅读 2020-06-18 20:17:25
    SpringAOP的四种通知类型:前置通知、异常通知、后置通知、异常通知 一、四种常见的通知类型 给出 账户的业务层接口 IAccountService.java, 为了便于演示这四种通知类型,我们就只留下了一个方法。 public ...
  • AOP的五种通知方式?代码搞起pom文件添加依赖包新建自定义注解类新建日志实体类新建操作类型类新建切面处理类将注解加到需要记录用户操作的方法体上最后 什么是AOP? AOP为Aspect Oriented Programming的缩写,是...
  • AccountServiceImpl.java package com.itheima.service.impl; import com.itheima.service.IAccountService; import org.springframework.stereotype.Service; /** * 账户的业务层实现类 */ @Service("account...
  • 文章目录一、切入点表达式二、环绕通知 一、切入点表达式 springConfig.xml 代码如下: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:...
  • Spring框架----Spring的环绕通知

    千次阅读 2020-08-13 12:45:12
    问题:当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了 /*环绕通知 * 问题:当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了 * */ public void aroundPrintLog(){ System.out....
  • Spring AOP 环绕通知

    千次阅读 2017-11-20 15:23:35
    Spring AOP的环绕通知和前置、后置通知有着很大的区别,主要有两个重要的区别: 1)目标方法的调用由环绕通知决定,即你可以决定是否调用目标方法,而前置和后置通知是不能决定的,它们只是在方法的调用前后执行...
  • 准备:aopliance.jar、aspect ...配置:将通知文件和方法关联起来 举例方法:addStudent StudentServiceImpl.java package com.dt.service.impl; import org.springframework.transaction.annotation.Propagati...
  • SpringAOP环绕通知注解形式

    千次阅读 2019-06-25 19:50:22
    package ... import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.A...
  • Spring中的环绕通知

    万次阅读 2018-10-28 15:45:33
    Spring中的环绕通知: 1.概述 问题: 当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了 分析: 通过对比动态代理的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用.而我们的没有 解决...
  • 1 AOP AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。...(1)@Before:前置通知,在方法执行之前返回 (2)@After:后
  • 13-SpringAOP最强大的通知-环绕通知

    千次阅读 2018-05-17 16:35:20
    为什么说环绕通知是SpringAOP最强大的通知,因为他可以将之前的几个通知融合到一起,所以功能相比其他通知,会更加强大和灵活。下面,我们通过代码来理解下:1,为了跟之前的切面类区分,我们创建一个新的切面类2,...
  • 项目中用户使用相关功能时需要判断是否登陆 二、自定义注解 约定切入点是标记了@LoginJudge的方法 //登陆判断 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface LoginJudge { } ...
  • 1、在注解+配置实现aop环绕通知基础上,直接删除配置文件。 2、新建配置类,在类上增加注释以代理配置文件中的两个配置:包扫描和自动开启代理 3、测试时,使用AnnotationConfigApplicationContext类加载配置类...
  • 但我加入了环绕通知后,发现全局异常失效了。 来看看我的全局异常 package com.yiban.suoai.exception; import com.yiban.suoai.exception.SAException; import org.aspectj.lang.annotation.Aspect...
  • 异常通知异常通知:只有当切点报异常才能触发异常通知 AspectJ方式实现 新建类,在类写任意名称的方法public class MyThrowAdvice{ public void myexception(Exception e1){ System.out.println("执行异常通知"+e1....
  • JAVA框架

    千次阅读 2019-10-15 10:42:43
    JAVA框架SpringSpringMVCSpringBootDubboMavenRedisMybatis Spring SpringMVC SpringBoot Dubbo Maven Redis Mybatis

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 23,842
精华内容 9,536
关键字:

java环绕通知的使用

java 订阅