我们都知道Spring的核心IOC和AOP,今天说的就是经常接触的AOP。
AOP,面向切面编程,百度一下都知道,但是没啥luan用,啥是切面?啥叫面向切面编程?怎么使用都不知道,那你还说你知道AOP?
不过不知道不要紧,看完我的文章就懂了。我会用很简洁的语言来描述这个东西,并且加上简单的代码实现辅助理解。
我们首先想象这么一个场景,我做一个网页,Controller里面有10个接口需要用户登录才能请求,怎么实现?
1.普通人的做法:就是在10个接口里面分别取到用户信息,然后判断用户是否登录,否则直接返回或者抛异常等等,代码很多,很低级。
2.聪明人的做法:我们把校验用户的代码封装成一个方法,或者一个Util,然后在controller里面再调用方法校验,有一点那个味了,但是还是麻烦。
3.大佬的做法:采用AOP。
我们知道,懒惰才是人们科技进步的第一动力,AOP符合我们懒惰的情况下,还能够将我们的业务和校验(不光是校验)拆分开来,两者互不影响,简化我们的业务代码。
SpringBoot实现AOP,我从自定义注解开始说起。我们知道SpringBoot有很多注解,这很方便我们的开发,那么怎么自定义一个注解呢,很简单,加一个@interface就好了。
(1)我们首先创建一个AnnotationTest的注解
@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationTest {
String value() default "";
String type() default "1";
}
@Target注解,表示你这个注解标注在哪里,我这里用的ElementType.METHOD,表示我是标注在方法上的,加在类上就会报错,其他的自行百度。
@Retention(RetentionPolicy.RUNTIME),注解的生命周期,RetentionPolicy.RUNTIME表示的是在程序运行时。
我还写了两个属性,一个value和type,指定默认值,这个是我们在打注解的时候传参用的。
(2)来试试我们的注解呢,新建一个Controller,将我们的注解打在接口上。
并且将type改成2,在这个方法里,我往Redis里面插入了一条数据,然后从数据库查询了用户的记录再返回。
@RestController
@RequestMapping("/test")
public class UserInfoController{
@Resource
UserInfoService userInfoService;
@RequestMapping(value = "/testOne")
@AnnotationTest(type = "2")
public ResultMsg test(){
Double aDouble = RedisCacheUtil.inst().zincrementScore("TEST", 1, "test");
System.out.println(aDouble);
List<UserInfo> list = userInfoService.list();
return ResultMsg.success(list);
}
}
(3)我们光定义了注解和加上注解是没有用的,关键的是我们切面里面作什么处理。接下来一步是关键,我们新建一个TestAopAnnoAspect的类,注意两个很关键的注解@Aspect和@Component,@Aspect表示该类是一个切面,@Component就不说了嘛。
@Aspect
@Component
public class TestAopAnnoAspect {
@Pointcut("execution(* com.example.demo.controller..*(..))")
public void PointCut(){
}
@Before("execution(* com.example.demo.controller..*(..))")
public void Before() {
System.out.println("请求之前");
}
@Around("execution(* com.example.demo.controller..*(..))")
public Object Around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕");
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
AnnotationTest annotationTest = method.getAnnotation(AnnotationTest.class);
String type = annotationTest.type();
System.out.println("type 为:" + type);
return joinPoint.proceed();
}
@After("execution(* com.example.demo.controller..*(..))")
public void After() {
System.out.println("在请求之后");
}
@AfterReturning("execution(* com.example.demo.controller..*(..))")
public void AfterReturning() {
System.out.println("在返回之后");
}
}
说明:在切面里,我们可以作几种处理,通过注解,常用的我已经列出来了,
@PointCut:切点,不能有方法体。
@Before:程序执行之前
@After:程序执行之后
@Around:环绕整个程序执行
@AfterReturning:返回之前
这些注解的作用,我做一个比喻,比如我们程序的执行是一根筷子,从筷子头到筷子尾。那么这些注解就表示了,我们要在这根筷子的什么部位做什么事情。
最最最最最最重要的:注解里面execution表达式怎么写?下面简单列了一下结构
/**
* execution:表达式主体
* 第一部分 代表方法返回值类型 *表示任何类型
* 第二部分 com.example.demo.controller 表示要监控的包名
* 第三部分 .. 代表当前包名以及子包下的所有方法和类
* 第四部分 * 代表类名,*代表所有的类
* 第五部分 *(..) *代表类中的所有方法(..)代表方法里的任何参数
*/
然后我们在浏览器请求一下数据看看效果:
再看看控制台的打印:
通过打印我们可以知道什么呢?
1.AOP几个注解的执行顺序,注意第四行打印5.0,这是我们controller给Redis增加值改变的。
2.我们定义的注解里面type属性,默认是1的,但是我们Controller使用的时候设置type为2,这里也相应修改了。
上面就是AOP的一个简单示例了,很简单,但是也是五脏俱全。示例业务比这个复杂但也还好,简单举个例子,上面@Around注解里面有一个参数是ProceedingJoinPoint joinPoint,ProceedingJoinPoint里面又携带了很多信息。
比如:
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();//拿到方法
Object[] allArgs = joinPoint.getArgs();//拿到参数
CodeSignature codeSignature = (CodeSignature) joinPoint.getSignature();//连接点捕获的代码块.它可以是方法,构造函数,初始化程序(静态或非静态)或建议
String[] parameterNames = codeSignature.getParameterNames();//拿到参数
如果你有收获别忘了点赞哦,有不对的地方欢迎指出。