8000 Spring Boot1.5.4 AOP实例 · Issue #12 · x113773/testall · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Spring Boot1.5.4 AOP实例 #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
x113773 opened this issue Jun 30, 2017 · 2 comments
Open

Spring Boot1.5.4 AOP实例 #12

x113773 opened this issue Jun 30, 2017 · 2 comments
Labels

Comments

@x113773
Copy link
Owner
x113773 commented Jun 30, 2017
  1. 还是首先添加依赖(使用当前springboot的默认版本)
<dependency>  
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-aop</artifactId>  
</dependency>  

  • 参考下面官方文档的部分配置说明,可见aop是默认开启的,自动添加了@EnableAspectJAutoProxy注解
# AOP
spring.aop.auto=true # Add @EnableAspectJAutoProxy.
spring.aop.proxy-target-class=false # Whether subclass-based (CGLIB) proxies are to be created (true) as opposed to standard Java interface-based proxies (false).
  1. 编写一个切面类,AspectAdviceConfig.java,里面定义了一个切点指示器和各种通知(Advice,也译作 增强)
package com.ansel.testall.aop;

import java.lang.reflect.Method;
import java.util.Arrays;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.DeclareParents;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Aspect
@Component
public class AspectAdviceConfig {
	private Logger logger = LoggerFactory.getLogger(this.getClass());

	/**
	 * 定义切点指示器:com.ansel.testall.aop包下,带@RestController注解的类。
	 */
	@Pointcut("execution(* com.ansel.testall.aop..*(..)) and @annotation(org.springframework.web.bind.annotation.RestController)")
	public void myPointcut() {
	}

	/**
	 * 前置通知,在目标方法完成之后调用通知,此时不会关 心方法的输出是什么
	 */
	@Before("myPointcut()")
	public void beforeAdvice() {
		System.out.println("Before--通知方法会在目标方法调用之前执行");
	}

	/**
	 * 后置通知,在目标方法完成之后调用通知,此时不会关 心方法的输出是什么
	 */
	@After("myPointcut()")
	public void afterAdvice() {
		System.out.println("After--通知方法会在目标方法返回或抛出异常后调用");
	}

	/**
	 * 返回通知,在目标方法成功执行之后调用,可以获得目标方法的返回值,但不能修改(修改也不影响方法的返回值)
	 * 
	 * @param jp
	 *            JoinPoint接口,可以获得连接点的一些信息
	 * 
	 * @param retVal
	 *            目标方法返回值,和jp一样会由spring自动传入
	 */
	@AfterReturning(returning = "retVal", pointcut = "myPointcut()")
	public void afterReturningAdvice(JoinPoint jp, Object retVal) {
		retVal = retVal + " (@AfterReturning can read the return value, but it can't change the value!)";
		System.out.println("AfterReturning--通知方法会在目标方法返回后调用; retVal = " + retVal);
		System.out.println(jp.toLongString());
	}

	/**
	 * 异常通知,在目标方法抛出异常后调用通知
	 */

	@AfterThrowing("myPointcut()")
	public void afterThrowingAdvice() {
		System.out.println("AfterThrowing--通知方法会在目标方法抛出异常后调用");
	}

	/**
	 * 环绕通知,可以在目标方法调用前后,自定义执行内容。可以修改目标方法的返回值
	 * 
	 * @param pjp
	 */
	@Around("myPointcut()")
	public Object aroundAdvice(ProceedingJoinPoint pjp) {
		Object retVal = null;
		try {
			System.out.println("Around--目标方法调用之前执行");

			ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
			HttpServletRequest request = attributes.getRequest();

			MethodSignature signature = (MethodSignature) pjp.getSignature();
			Method method = signature.getMethod(); // 获取被拦截的方法
			String methodName = method.getName(); // 获取被拦截的方法名

			logger.info("requset method name is: " + methodName);
			logger.info("request URL is: " + request.getRequestURL().toString());
			logger.info("request http method: " + request.getMethod());
			logger.info("request arguments are: " + Arrays.toString(pjp.getArgs()));

			retVal = pjp.proceed();
			retVal = retVal + " (@Around can change the return value!)";

			System.out.println("Around--目标方法返回后调用");
		} catch (Throwable e) {
			System.out.println("Around--目标方法抛出异常后调用");
		}
		return retVal;
	}
}

  1. 通过Advice可以某些方法增加一些功能,若要为某个对象增加新的方法,则要用到Introduction,编写另外一个切面AspectIntroductionConfig.java
package com.ansel.testall.aop;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class AspectIntroductionConfig {

	/**
	 * DeclareParents注解所标注的静态属性指明了要引入的接口;
	 * value属性指定了哪种类型的bean要引入该接口;
	 * defaultImpl属性指定了为引入功能提供实现的类。
	 */
	@DeclareParents(value = "com.ansel.testall.aop.AopService+", defaultImpl = IntroductionServiceImpl.class)
	public static IntroductionService introductionService;
}

  1. 编写两个接口AopService、IntroductionService,和2个实现AopServiceImpl、IntroductionServiceImpl
package com.ansel.testall.aop;

public interface AopService {
	String myOwnMethod();
}
/***********************************/
package com.ansel.testall.aop;

public interface IntroductionService {
	String IntroductionMethod();
}
/***********************************/
package com.ansel.testall.aop;

import org.springframework.stereotype.Service;

@Service
public class AopServiceImpl implements AopService {

	@Override
	public String myOwnMethod() {
		return "this method is from AopService";
	}

}
/***********************************/
package com.ansel.testall.aop;

import org.springframework.stereotype.Service;

@Service
public class IntroductionServiceImpl implements IntroductionService {

	@Override
	public String IntroductionMethod() {
		return "this method from Introduction.";
	}

}
  1. 编写一个切点实例AopController.java (其实就是个普通的controller)
package com.ansel.testall.aop;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AopController {

	/**
	 * 只注入AopService
	 */
	@Autowired
	AopService aopService;

	/**
	 * 测试AOP通知(Advice,也译作 增强)
	 * 
	 * @return
	 */
	@RequestMapping(value = "/aop", method = RequestMethod.GET)
	public String testAop() {
		return "this is a AOP Advice test.";
	}

	/**
	 * 测试AOP引入(Introduction)
	 * 
	 * @return
	 */
	@RequestMapping(value = "/aop/introdution", method = RequestMethod.GET)
	public String testAopIntroduction() {
		System.out.println(aopService.myOwnMethod());
		//接口类型转换
		IntroductionService introductionService = (IntroductionService) aopService;
		System.out.println(introductionService.IntroductionMethod());
		return "this is a AOP Introduction test.";
	}
}

  1. 启动项目,触发 localhost:8080/aop 请求,控制台输出结果如下:
Around--目标方法调用之前执行
2017-07-03 17:33:15.494  INFO 8548 --- [nio-8443-exec-7] tConfig$$EnhancerBySpringCGLIB$$5f4562fb : requset method name is: testAOP
2017-07-03 17:33:15.495  INFO 8548 --- [nio-8443-exec-7] tConfig$$EnhancerBySpringCGLIB$$5f4562fb : request URL is: https://localhost:8443/aop
2017-07-03 17:33:15.495  INFO 8548 --- [nio-8443-exec-7] tConfig$$EnhancerBySpringCGLIB$$5f4562fb : request http method: GET
2017-07-03 17:33:15.495  INFO 8548 --- [nio-8443-exec-7] tConfig$$EnhancerBySpringCGLIB$$5f4562fb : request arguments are: []
Before--通知方法会在目标方法调用之前执行
Around--目标方法返回后调用
After--通知方法会在目标方法返回或抛出异常后调用
AfterReturning--通知方法会在目标方法返回后调用; retVal = this is a AOP test. (@Around can change the return value!) (@AfterReturning can read the return value, but it can't change the value!)
execution(public java.lang.String com.ansel.testall.aop.AOPController.testAOP())

页面返回结果如下:
this is a AOP test. (@Around can change the return value!)

触发 localhost:8080/aop/introdution 请求,控制台输出结果如下:

qq 20170704124306


注:

@Pointcut("execution(* com.ansel.testall.aop..*(..)) and @annotation(org.springframework.web.bind.annotation.RestController)")

当我使用and操作符连接上面两个切点指示器,没有任何问题(把注解RestController换成RequestMapping也没问题)
然而,当我使用&&操作符连接上面两个切点指示器时,完全没有触发AspectJ,切面没有织入任何连接点(把注解RestController换成RequestMapping就没问题)

   /**
    * OK
    */
   @Pointcut("execution(* com.ansel.testall.aop..*(..)) and @annotation(org.springframework.web.bind.annotation.RestController)")

   /**
    * not OK
    */
   @Pointcut("execution(* com.ansel.testall.aop..*(..)) && @annotation(org.springframework.web.bind.annotation.RestController)")

   /**
    * OK
    */
   @Pointcut("execution(* com.ansel.testall.aop..*(..)) and @annotation(org.springframework.web.bind.annotation.RequestMapping)")

   /**
    * OK
    */
   @Pointcut("execution(* com.ansel.testall.aop..*(..)) && @annotation(org.springframework.web.bind.annotation.RequestMapping)")
@x113773
Copy link
Owner Author
x113773 commented Jul 3, 2017
  • JoinPoint:提供访问当前被通知方法的目标对象、代理对象、方法参数等数据
package org.aspectj.lang;  
import org.aspectj.lang.reflect.SourceLocation;  
public interface JoinPoint {  
    String toString();         //连接点所在位置的相关信息  
    String toShortString();     //连接点所在位置的简短相关信息  
    String toLongString();     //连接点所在位置的全部相关信息  
    Object getThis();         //返回AOP代理对象  
    Object getTarget();       //返回目标对象  
    Object[] getArgs();       //返回被通知方法参数列表  
    Signature getSignature();  //返回当前连接点签名  
    SourceLocation getSourceLocation();//返回连接点方法所在类文件中的位置  
    String getKind();        //连接点类型  
    StaticPart getStaticPart(); //返回连接点静态部分  
} 
  • ProceedingJoinPoint:用于环绕通知,使用proceed()方法来执行目标方法:
public interface ProceedingJoinPoint extends JoinPoint {  
    public Object proceed() throws Throwable;  
    public Object proceed(Object[] args) throws Throwable;  
}  

参考:http://blog.csdn.net/zhengchao1991/article/details/53391244

@x113773
Copy link
Owner Author
x113773 commented Jul 4, 2017

Spring AOP所支持的AspectJ切点指示器
qq 20170704095028

来源:Spring实战(第四版)

@x113773 x113773 added doc and removed doc labels Sep 28, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant
0