package com.zdj.springboot_aop;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps; // guava 24.1-jar
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
//import com.google.common.collect.Maps;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
/**
* Created by Administrator on 2018/3/28.
*/
/**
* 1.先创建一个Aspect切面类
*/
@Component
@Aspect
public class WebControllerAop {
/**
* 2. 指定切点
* 匹配com.zdj.springboot_aop.Controller包及其子包下面的所有类的所有方法
*/
@Pointcut("execution(* com.zdj.springboot_aop.Controller..*.*(..))")
public void executeService(){
}
/**
* 01 . 前置通知:方法调用前被调用
*/
@Before("executeService()")
public void doBeforeAdvice(JoinPoint joinPoint){// 通过JoinPoint 获取通知的签名信息,如目标方法名,目标方法参数信息等
System.out.println("我是前置通知");
Object[] obj=joinPoint.getArgs();//获取目标方法的参数信息
joinPoint.getThis(); // AOP代理类信息
joinPoint.getTarget(); // 代理的目标对象
Signature signature=joinPoint.getSignature(); // 用的最多,通知的签名
System.out.println("代理的方法是 : "+signature.getName()); // 打印 代理的是哪一个方法
// AOP 代理的名字
System.out.println("AOP 代理的名字 : "+signature.getDeclaringTypeName());
signature.getDeclaringType();// AOP代理类的类(class)信息
/*
通过RequestContextHolder获取请求信息,如session 信息 ;
*/
// 获取RequestAttributes
RequestAttributes requestAttributes= RequestContextHolder.getRequestAttributes();
// 从requestAttributes中获取HttpServletRequest信息
HttpServletRequest request=(HttpServletRequest)requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
// 获取session信息
HttpSession session=(HttpSession)requestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION);
System.out.println("请求 : "+request+" , HttpSession : "+session);
Enumeration<String> enumerations=request.getParameterNames();
// Map<String,String> parameterMaps=new HashMap<>();
Map<String,String> parameterMaps= Maps.newHashMap();
while(enumerations.hasMoreElements()){
String parameter=enumerations.nextElement();
parameterMaps.put(parameter,request.getParameter(parameter));
}
// String str=JSON.toJSONString(parameterMaps);
String str= JSON.toJSONString(parameterMaps);// alibaba.fastjson
if(obj.length>0){
System.out.println("请求参数信息为 : "+ str );
}
}
/**
* 02 .后置返回通知
* 需要注意:
* 如果第一个参数是JoinPoint,则第二个参数是返回值的信息
* 如果参数中的第一个不是JoinPoint,则第一个参数是returning中对应的参数,
* returning 限定了只有目标方法返回值与通知方法相应参数类型时才能
* 执行后置返回通知,否则不执行;
* 对于returning对应的通知方法参数为Object类型将匹配任何目标返回值
* @param joinPoint
* @param keys
*/
@AfterReturning(value="execution(* com.zdj.springboot_aop.Controller..*.*(..))",returning = "keys")
public void doAfterReturningAdvice1(JoinPoint joinPoint,Object keys){
System.out.println("后置通知执行了!!");
System.out.println("第一个后置返回通知的返回值是 :"+keys);
}
@AfterReturning(value="execution(* com.zdj.springboot_aop.Controller..*.*(..))",returning = "keys",argNames="keys")
public void doAfterReturningAdvice2(String keys){ // 通知方法形影参数的类型是String
System.out.println("第二个后置返回通知的返回值是 :"+keys);
}
/**
* 03 . 后置异常通知
* 定义一个名字,该名字用于匹配通知实现方法的一个参数名,当目标方法抛出异常返回后,将把目标方法抛出的异常传给通知方法;
* throwing 限定了只有目标方法抛出的异常与通知方法相应参数异常类型时才能执行后置异常通知,否则不执行,
* 对于throwing对应的通知方法参数为Throwable类型将匹配任何异常。
*/
@AfterThrowing(value="executeService()",throwing = "exception")
public void doAfterThrowingAdvice(JoinPoint joinPoint,Throwable exception){
// 目标方法名
System.out.println(joinPoint.getSignature().getName());
if(exception instanceof NullPointerException){
System.out.println("发生了空指针异常");
}
}
/**
* 04 . 后置最终通知(目标方法只要执行完了就会执行后置通知方法)
*/
@After("executeService()")
public void doAfterService(JoinPoint joinPoint){
System.out.println("后置最终通知执行了!");
}
/**
* 环绕通知:
* 环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。
* 环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型
*/
@Around("execution(* com.zdj.springboot_aop.Controller..*.testAround*(..))")
public Object doAroundService(ProceedingJoinPoint proceedingJoinPoint){
System.out.println("环绕通知的目标方法名为 : "+proceedingJoinPoint.getSignature().getName());
try {
Object object=proceedingJoinPoint.proceed();
return object;
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return null;
}
}
package com.zdj.springboot_aop.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 创建AOP测试Controller.Created by Administrator on 2018/3/28.
*/
@RestController
@RequestMapping("/aop")
public class AopTestController {
@RequestMapping("/testBeforeService.do")
public String testBeforeService(String key ,String value){
return "key : "+key+ ", value : "+value;
/*
url: http://localhost:8080/aop/testBeforeService.do?key=zdj&value=123
我是前置通知
代理的方法是 : testBeforeService
AOP 代理的名字 : com.zdj.springboot_aop.Controller.AopTestController
请求 : org.apache.catalina.connector.RequestFacade@4f8cf74e , HttpSession : org.apache.catalina.session.StandardSessionFacade@7b37a638
请求参数信息为 : {"value":"123","key":"zdj"}
*/
}
@RequestMapping("/testAfterReturning1.do")
public String testAfterReturning1(String key){
return "key = "+key;
/*
url : http://localhost:8080/aop/testAfterReturning1.do?key=zdj&value=123
后置通知执行了!!
第一个后置返回通知的返回值是 :key = zdj
第二个后置返回通知的返回值是 :key = zdj
*/
}
@RequestMapping("/testAfterReturning2.do")
public Integer testAfterReturning2(Integer key){
return key;
/*
url : http://localhost:8080/aop/testAfterReturning2.do?key=111222&value=123
后置通知执行了!!
第一个后置返回通知的返回值是 :111222
注 : 因第二个后置通知首参不是JoinPoint,并且相应参数类型是String,而该目标方法的返回值类型是Integer,所以第二个后置通知方法不执行
*/
}
@RequestMapping("/testAfterThrowing.do")
public String testAfterThrowing(String key){
throw new NullPointerException();
/*
url : http://localhost:8080/aop/testAfterThrowing.do?key=zdk&value=123
我是前置通知
代理的方法是 : testAfterThrowing
AOP 代理的名字 : com.zdj.springboot_aop.Controller.AopTestController
请求 : org.apache.catalina.connector.RequestFacade@41b8dcce , HttpSession : org.apache.catalina.session.StandardSessionFacade@33c33c37
请求参数信息为 : {"value":"123","key":"zdk"}
testAfterThrowing
发生了空指针异常
*/
}
@RequestMapping("/testAfter1.do")
public String testAfter1(String key){
throw new NullPointerException();
/*
url: http://localhost:8080/aop/testAfter1.do?key=zdj&value=123
后置最终通知执行了!
*/
}
@RequestMapping("/testAfter2.do")
public String testAfter2(String key){
return key;
/*
url: http://localhost:8080/aop/testAfter2.do?key=zdj&value=123
后置最终通知执行了!
*/
}
@RequestMapping("/testAroundService.do")
public String testAroundService(String key){
return "环绕通知 : " + key;
/*
url : http://localhost:8080/aop/testAroundService.do?key=1122
环绕通知的目标方法名为 : testAroundService
当访问 http://localhost:8080/aop/testAfter2.do?key=1122&value=sjshhjdh,不符合环绕通知的切入规则,所以环绕通知不会执行;
*/
}
}
package com.zdj.springboot_aop;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Created by Dylan on 2018/6/22.
*/
@SpringBootApplication
public class StartApplication {
public static void main(String[] args) {
SpringApplication.run(StartApplication.class, args);
}
}