SpringAop实现公共字段填充
一、说明
项目中经常会有一些放在缓存中的公共字段需要进行填充,我们知道mybatis-plus很方便地可以实现公共字段填充。在这里我定义了一个字段填充的注解,当我们需要进行数据填充的时候只要在方法上打上注解,aop自动扫描该注解就可以实现字段填充的功能了。温故而知新,今天有空整理下最近学到的东西,在分享的时候顺带复习下最近学到的知识。
二、注解相关知识简介
1、注解的定义:在java中使用@interface表示注解。四种元注解分别是@Retention、@Target、@Document、@Inherited。
2、@Retention 表示注解的保留策略:
@Retention(RetentionPolicy.SOURCE) // 注解仅存在于源码中,会被编译器丢弃,不包含在class字节码文件中
@Retention(RetentionPolicy.CLASS) // 默认的保留策略,注解会被VM丢弃,在class字节码文件中存在,在运行时无法获得
@Retention(RetentionPolicy.RUNTIME) // VM将在运行期间保留注解,在class字节码文件中存在,在运行时可以通过机制读取注解的信息
3、@Target 表示注解的作用目标(枚举 public enum ElementType)
@Target(ElementType.TYPE) // 接口、类、枚举、注解
@Target(ElementType.FIELD) // 字段、枚举的常量
@Target(ElementType.METHOD) // 方法
@Target(ElementType.PARAMETER) // 方法参数
@Target(ElementType.CONSTRUCTOR) // 构造函数
@Target(ElementType.LOCAL_VARIABLE) // 局部变量
@Target(ElementType.ANNOTATION_TYPE) // 注解
@Target(ElementType.PACKAGE) // 包
/**
* Java 8对注解处理提供了两点改进:可重复的注解及可用于类型的注解
* 新增两种类型注解,可以用在任何使用到类型的地方
*/
@Target(ElementType.TYPE_PARAMETER) //@since 1.8 表示该注解能写在类型参数的声明语句中
@Target(ElementType.TYPE_USE) //@since 1.8 表示注解可以再任何用到类型的地方使用
4、@Documented 表示注解包含在javadoc文档中
5、@Inherited 表示注解可以被继承,默认注解并没有继承于任何子类
三、Spring Aop简介
AOP(Aspect Oriented Programming),即面向切面编程。
1、横切关注点:对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点。
2、切面(aspect):切面是一个关注点的模块化,这个关注点可能是横切多个对象。类是对物体特征的抽象,切面就是对横切关注点的抽象。
3、连接点(joinpoint):被拦截到的点,因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器。
4、切入点(pointcut):匹配连接点的断言,对连接点进行拦截的定义。
5、通知(advice):所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类。
-
- 前置通知(Before):在目标方法或者说连接点被调用前执行的通知;
- 后置通知(After):指在某个连接点完成后执行的通知;
- 返回通知(After-returning):指在某个连接点成功执行之后执行的通知;
- 异常通知(After-throwing):指在方法抛出异常后执行的通知;
- 环绕通知(Around):指包围一个连接点通知,在被通知的方法调用之前和之后执行自定义的方法
6、目标对象(Target):代理的目标对象。
7、织入(weave):将切面应用到目标对象并导致代理对象创建的过程。
8、引入(introduction):引入也被称为内部类型声明,声明额外的方法或者某个类型的字段。在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段。
四、代码
1、自定义注解 FillFields
package com.gongyu.annotation.FillFields;
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 字段填充注解 * @author gongyu * @date 20190920 */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface FillFields { }
2、Aop扫描注解填充字段
package com.gongyu.filedAop.aop;
import java.lang.reflect;
import java.lang.reflect.Method;
import java.time.LocalDateTime; import java.time.ZoneOffset; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; @Aspect @Component public class ServiceAop {
private final String POINT_CUT = "@annotation(com.gongyu.annotation.FillFields)";public ServiceAop() { @Pointcut(POINT_CUT)public void fillFiled() {
} @Around("fillFiled()") public Object doAround(ProceedingJoinPoint pjp) throws Throwable { long orgId = 111L; long createdBy = 333L; LocalDateTime createdTime = LocalDateTime.now();
long version = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli(); Object[] args = pjp.getArgs(); Object object = args[0]; if (object != null) { Class objectClass = object.getClass(); Field[] fs = objectClass.getDeclaredFields(); for(Field f : fs) { // 设置访问权限 f.setAccessible(true); /* * 填充字段 orgId 有则取,没有则塞 * Method method=null; */ if ("orgId".equals(f.getName())) { Method getOrgIdMethod=objectClass.getDeclaredMethod("getOrgId"); Object o=getOrgIdMethod.invoke(object); if(o==null) { Method setOrgIdMethod = objectClass.getDeclaredMethod("setOrgId", long.class); setOrgIdMethod.invoke(object, orgId); } } //填充字段 createdBy if(("createdBy").equals(f.getName())){ Method createdByMethod=objectClass.getDeclaredMethod("setCreatedBy",long.class); createdByMethod.invoke(object,createdBy); } //填充字段 createdTime if(("createdTime").equals(f.getName())){ Method createdTimeMethod=objectClass.getDeclaredMethod("setCreatedTime",LocalDateTime.class); createdTimeMethod.invoke(object,createdTime); } //填充字段 version if(("version").equals(f.getName())){ Method versionMethod=objectClass.getDeclaredMethod("setVersion",long.class); versionMethod.invoke(object,version); } } System.out.println("字段填充成功后=>" + object); } return pjp.proceed(); } }