菜鸟学习-尝试用aop给参数赋值

菜鸟学习-尝试用aop给参数赋值

项目结构

springboot+mybatis+mysql

功能需求

最近领导派了个新活,要做个增删改查。
这个活我拢了拢,有个特点,表单特别多,就是有大量定义好的常量,类似以下这种:

public static Map<Integer,String> RESEARCH_FIELD = new HashMap<Integer,String>(){{
        put(0,"经济学");
        put(1,"政治学");
        put(2,"历史学");
        put(3,"社会学");
        put(4,"人类学");
        put(5,"管理学");
        put(6,"公共管理");
        put(7,"法学");
        put(8,"艺术");
    }};
public static Map<Integer,String> EDUCATION = new HashMap<Integer, String>(){{
        put(0,"本科在读");
        put(1,"学士");
        put(2,"硕士在读");
        put(3,"硕士");
        put(4,"博士在读");
        put(5,"博士");
    }};

这些常量一般用作前端显示。

以往我对这些常量都是做表存到数据库,需要的时候取出来即可。
但谁让我闲呢,于是试着用aop使用这些常量,顺便也学习一下aop的用法。

代码结构

接口:http://localhost:8080/page

    @PostMapping("/page")
    @WireDataIdentityAt
    public Object page(@RequestParam(required = false, defaultValue = "1") int currentPage,
                       @RequestParam(required = false, defaultValue = "10") int pageSize) {
        try {
            List<Paper> papers = paperDao.selectPage(currentPage,pageSize);
            return papers;
        } catch (Exception e) {
            return errorObject(e);
        }
    }

@WireDataIdentityAt 自定义注解,try catch 方便处理异常

实体类:Paper

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class Paper{
    private String id;
    private String title;
    @WireData(AutoConfig.ISO.RESEARCH_FIELD)
    private String topicArea;
}

@WireData 自定义注解
AutoConfig.ISO.TOPIC_AREA 自定义枚举类型

自定义注解:@WireDataIdentityAt

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface WireDataIdentityAt {
}

@Target 作用目标,METHOD 表示注解可以写到方法上。
@Retention 元注解必须有,详细作用可百度(我就是看百度的)

自定义注解:@WireData

import java.lang.annotation.*;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface WireData {
    AutoConfig.ISO value() default AutoConfig.ISO.NONE;
}

@Target 作用目标,FIELD 表示注解可以写到字段上。
@Retention 元注解必须有,详细作用可百度(我就是看百度的)
value 类型为自定义枚举,存在默认值。

自定义枚举类型和常量类:AutoConfig

public class AutoConfig {
    public enum ISO {
        NONE, RESEARCH_FIELD 
    }
    
    public static Map<Integer,String> RESEARCH_FIELD = new HashMap<Integer,String>(){{
        put(0,"经济学");
        put(1,"政治学");
        put(2,"历史学");
        put(3,"社会学");
        put(4,"人类学");
        put(5,"管理学");
        put(6,"公共管理");
        put(7,"法学");
        put(8,"艺术");
    }};
}

一个枚举,一个常量。

aop代码:WireDataAspect

//作用是把当前类标识为一个切面供容器读取
@Aspect
@Component
public class WireDataAspect {
    //这个方法在接口执行完返回给前端数据的时候生效。
    //@annotation,代表aop拦截WireDataIdentityAt自定义注解所标注的方法。
    //returning 代表接口返回的数据。
    @AfterReturning(value = "@annotation(com.my.addons.common.annotation.WireDataIdentityAt)", returning = "val")
    public void restoreDataAt(Object obj) throws IllegalAccessException {
        if (obj != null){
            if (obj instanceof ArrayList<?>){
                ArrayList<?> list = (ArrayList<?>) obj;
                for (Object o : list) {
                    initDate(o);
                }
            }else{
                initDate(obj);
            }
        }
    }
    
    public void initDate(Object obj) throws IllegalAccessException {
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
        	//判断字段上是否有自定义注解WireData
            boolean fieldHasAnno = field.isAnnotationPresent(WireData.class);
            //设置私有读写权限,必须设置为true
            field.setAccessible(true);
            //如果包含自定义注解WireData
            if (fieldHasAnno && field.get(obj) != null) {
                WireData annotation = field.getAnnotation(WireData.class);
                AutoConfig.ISO iso = annotation.value();
                switch (iso) {
//--------------------------------------------------------- paper ---------------------------------------------------------//
                    case RESEARCH_FIELD:
                        field.set(obj, AutoConfig.RESEARCH_FIELD.get(Integer.parseInt((String) field.get(obj))));
                        break;
                    //
                }
            }
        }
    }
}

aop实现赋值主要代码在这个方法里,方法用到了反射获取返回数据,然后对数据进行处理

拓展

在使用aop的时候,我发现有时会出现不进aop方法的情况,例如:
接口代码:

@RequestMapping("/addons-bk-user")
public class UserController{
	@PostMapping("/export")
    public void export(HttpServletResponse response){
            List<User> users = mtUserDao.selectList();
 		    doWrite(excelWriter,users,userSheet);
 		    //
 	}
 	@WireDataIdentityBf
    public void doWrite(ExcelWriter excelWriter, List<?> obj, WriteSheet sheet){
        	excelWriter.write(obj, sheet);
    }
}

@WireDataIdentityBf 自定义注解

aop代码:

@Aspect
@Component
public class WireDataAspect {

    @Before(value = "@annotation(com.my.addons.common.annotation.WireDataIdentityBf)")
    public void restoreDataBf(JoinPoint joinPoint) throws IllegalAccessException {
        Object[] objects = joinPoint.getArgs();
        //
    }
}

@Before 方法执行前生效

但是代码运行时并没有进入aop方法。
后通过了解得知,aop是对当前类进行了二次包装来实现功能的,所以直接在本类里调用方法不会进入子类aop,可以把doWrite单独放到一个新的类里面,或者干脆自己装配自己也能实现,如下:

@RequestMapping("/addons-bk-user")
public class UserController{
	@Autowired
	private MyComponent myComponent;
	@PostMapping("/export")
    public void export(HttpServletResponse response){
            List<User> users = mtUserDao.selectList();
 		    myComponent.doWrite(excelWriter,users,userSheet);
 		    //
 	}
}

@Component
public class MyComponent{
	@WireDataIdentityBf
    public void doWrite(ExcelWriter excelWriter, List<?> obj, WriteSheet sheet){
        	excelWriter.write(obj, sheet);
    }
}

结尾

主要流程
请求接口 -> 数据库查询数据 -> aop拦截包含自定义注解WireDataIdentityAt的方法 -> 循环字段判断字段上是否包含自定义注解WireData -> 进case对字段重新赋值 -> aop完成返回前端

总结
aop可以做到不动旧代码的情况下做一些特殊的定制修改。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
纯手工打造Emit实现AOP private static void OverrideMethods(TypeBuilder tb, MethodInfo method) { if (!method.IsPublic|| !method.IsVirtual || IsObjectMethod(method)) return; Type[] paramTypes = GetParameterTypes(method); MethodAttributes attr = MethodAttributes.Public | MethodAttributes.Family | MethodAttributes.HideBySig | MethodAttributes.Virtual; MethodBuilder mb = tb.DefineMethod(method.Name, attr, method.ReturnType, paramTypes); LocalBuilder result = null; ILGenerator il = mb.GetILGenerator(); bool is_void = method.ReturnType != typeof(void); if (is_void == false) result = il.DeclareLocal(method.ReturnType); object[] attrs = method.GetCustomAttributes(typeof(AspectAttribute), false); if (attrs != null) { //初始化所有当前方法用到的参数object[] CreateLocalParameterArr(il, paramTypes); //初始化AspectContext Type ctxType = typeof(AspectContext); ConstructorInfo info = ctxType.GetConstructor(Type.EmptyTypes); var ctx = il.DeclareLocal(ctxType); il.Emit(OpCodes.Newobj, info); il.Emit(OpCodes.Stloc, ctx); //给AspectContext的参数值属性ParameterArgs赋值 var propMethod = ctxType.GetMethod("set_ParameterArgs"); il.Emit(OpCodes.Ldloc, ctx); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Call, propMethod); int m = attrs.Length; LocalBuilder[] lbs = new LocalBuilder[m]; MethodInfo[] endInvokeMethods = new MethodInfo[m]; //初始化标记的横切对象,并调用横切对象的BeforeInvoke方法 for (int i = 0; i < m; i++) { var tmpType = attrs[i].GetType(); var aspect = il.DeclareLocal(tmpType); ConstructorInfo tmpInfo = tmpType.GetCon

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值