1.基本概念java
面向切面编程也叫Aop。面向对象的特色是继承、封装、多态。封装要求咱们将不一样的功能分散到不一样的类中去实现,每一个类有本身的职责,这样的好处是下降了代码的复杂度,使得类能够重用;可是在分散代码的同时,也会增长代码的复杂性,好比一些通用的功能,日志,权限等。在以前进行app后端开发的时候,为了跟踪问题,须要对每一个api的请求日志都记录下来,能够在每一个方法的入口处都加上log.info.....,可是这样不够灵活,若是之后对记录的日志的方式变化,那么改动可想而知,也许你会说封装起来,每一个类中进行调用,可是这样就耦合太严重了。这时候就可使用Aop,在运行时动态的插入代码;这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。web
通常而言,咱们将切入到指定类指定方法的代码片断称为切面,将切入到哪些类,或者哪些方法的称为切入点。使用Aop,将不一样类中重复的地方抽取出来,造成切面,在须要的地方插入到对象或方法上,从而动态改变原有的行为。spring
2.一个简单权限校验的例子编程
基于注解的切面,须要定义注解,定义切面后端
1>定义注解api
package com.example.springbootDemo.service.aspect; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.TYPE,ElementType.METHOD})//注解类型,级别 @Retention(RetentionPolicy.RUNTIME)//运行时注解 public @interface PermissionCheck { String value() default ""; }
2>根据注解定义一个切面springboot
package com.example.springbootDemo.service.aspect; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import java.lang.reflect.Method; @Aspect @Component public class PermissionAspect { /** * 选取切入点为自定义注解 */ @Pointcut("@annotation(com.example.springbootDemo.service.aspect.PermissionCheck)") public void permissionCheck(){} /** * 在切入点业务逻辑执行前进行操做 * @param joinPoint */ @Before(value = "permissionCheck();") public void before(JoinPoint joinPoint) throws Throwable { Signature signature = joinPoint.getSignature(); //获取链接点的方法签名对象 if (!(signature instanceof MethodSignature)){ throw new PermissionCheckException("user permission check fail,stop this request!"); } MethodSignature methodSignature = (MethodSignature) signature; Object target = joinPoint.getTarget(); Method method = target.getClass().getDeclaredMethod(methodSignature.getName(),methodSignature.getParameterTypes());//获取到当前执行的方法 PermissionCheck annotation = method.getAnnotation(PermissionCheck.class);//获取方法的注解 System.out.println(annotation); System.out.println(annotation.value()); System.out.println("我是在执行业务逻辑以前"); } /** * 在业务逻辑执行后进行的操做 * @param joinPoint */ @After(value = "permissionCheck();") public void after(JoinPoint joinPoint) throws NoSuchMethodException { System.out.println("我是在执行业务逻辑后"); } @Around(value = "permissionCheck();") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("around方法执行前1"); Object proceed = joinPoint.proceed(); System.out.println("around方法执行前2"); System.out.println("proceed:"+proceed.toString()); return proceed; } }
3>权限验证不知足的时候须要定义异常,定义一个PermissionCheckException来标识权限不知足的异常信息app
package com.example.springbootDemo.service.aspect; public class PermissionCheckException extends RuntimeException { public PermissionCheckException(String message) { super(message); } }
4>定义Advice处理该异常this
package com.example.springbootDemo.service.aspect; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; @ControllerAdvice public class PermissionAdvice { @ExceptionHandler(value = PermissionCheckException.class) @ResponseStatus(value = HttpStatus.OK) @ResponseBody public String dealWithPermissionCheckException(PermissionCheckException exception){ System.out.println(exception.getMessage()); return exception.getMessage(); } }
注:PermissionCheck 注解必须声明为@Retention(RetentionPolicy.RUNTIME),不然,默认的是@Retention(RetentionPolicy.CLASS) 注解会在class字节码文件中存在,但运行时没法得到;.net
3.@Before @Around @After 等 advice 的执行顺序
正常状况的执行顺序
异常状况
注:Around中
Object proceed = joinPoint.proceed();
表示
Proceed with the next advice or target method invocation
表示处理返回的结果对象,必须返回;