五步手把手教你摆脱冗余业务代码,了解 AOP,符合代码设计原则

单一职责,开闭原则,依赖倒置等是代码设计和优化的原则。

** 拿个简单的例子来说:

  1. 用户表 sys_user ( 存放用户基本信息 ) , 2. 用户扩展信息表 tb_user_extend (存放用户扩展信息,关联用户id)
    假设 : 一个需求,需要用户的基本信息,接口写好了,代码开发完成了。
    假设 代码是这样的 :
@Override
public SysUser getUserOne(String mobile) {
    return sysUserMapper.selectByMoblie(mobile);
}

** 假定的需求描述,只为提供思路 **
根据手机查询单表用户基本信息。
很简单的一个查询,返回要个简单的用户对象
–> 需求变更了,需要返回更多的用户信息 ,扩展信息一并返回。怎么办?
一般会想到,直接改代码,增加输出类字段,改查询,关联查询扩展表不就有了吗。
这样是可以,如果改的话就动了就动了两个地方 ,sql 的关联查询,增加输出类的字段信息;
改用 aop 就不用动这么多地方,只需要增加输出字段 ,加上注解就ok 了 。
** 废话不多说,如下:**

一:增加 返回对象需要增加的字段 (假设 address 为 用户的 扩展字段,存放在 扩展信息表)

public class SysUser {

    private Long user_id;

    private String user_name;

    private String pass_word;

    private String mobile;

    private Integer amount;
    
    private String address;
}

二: 创建 增加字段的 通用注解

targetField 关联扩展表查询出来的需要字段,扩展表查询字段为 address,
beanClazz 需要调用的 Bean ,
methodName 调用 Bean 下执行的查询方法,
paramFile 执行方法所需的参数

@ExtendInfo(targetField = "address",beanClazz = UserExtendMapper.class,methodName = "queryByUserId",paramFile = "user_id")
private String address;

** 注解类 (不用多说, 注解在关联查询的字段上)**

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtendInfo {
    /**
     * 目标字段
     * @return
     */
    String targetField();

    /**
     * 参数
     * @return
     */
    String paramFile();

    /**
     * 需要调用的 bean
     * @return
     */
    Class beanClazz();

    /**
     * 需要调用的方法
     * @return
     */
    String methodName();
}

三 : 创建 aop 拦截的 注解 ,在需要更改的方法上做拦截,添加注解:

@InterceptorExtend
@Override
public SysUser getUserOne(String mobile) {
    return sysUserMapper.selectByMoblie(mobile);
}

就是在 这个方法 加另一个注解,表示,这个方法 更改了需求,需要执行 aop 里面的逻辑 来添加 扩展字段;
** 注解类 (这个注解啥也没有,仅做 拦截方法的标识 而已)**

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

四 :aop 拦截 逻辑处理, 创建 aop :

整个逻辑处理和代码注释都在下面

@Component
@Aspect
public class InterceptorExtendHanddle {

    @Resource
    private ApplicationContext applicationContext;

    @Pointcut("execution(* com.example.service..*(..))")

    public void wePointCut() {}

    @Around("wePointCut() && @annotation(interceptorExtend)")
    public Object requestLimit(final ProceedingJoinPoint joinPoint, InterceptorExtend interceptorExtend) {
        Object proceed = null;
        try {
            proceed = joinPoint.proceed(); // 反射拿到方法返回结果
            MethodSignature signature = (MethodSignature)joinPoint.getSignature();
            Class returnType = signature.getReturnType(); // 返回结果类型
            boolean assignableFrom = returnType.isAssignableFrom(List.class); // 判断是否为集合,有的方法为单个对象,有的为集合
            if (assignableFrom) {
                // 返回是 list
                List proceed1 = (List) proceed;
                for (int i = 0; i < proceed1.size(); i++) {
                    Object o = proceed1.get(i);
                    fillValue2Field(o); // 填充扩展字段值
                }
                return proceed;
            }
            // 非集合,单个对象
            fillValue2Field(proceed);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return proceed;
    }

    /**
     * 填充扩展字段值
     * @param proceed
     * @throws Exception
     */
    private void fillValue2Field(Object proceed) throws Exception {
        Field[] declaredFields = proceed.getClass().getDeclaredFields();//拿到返回对象字段
        for (Field declaredField : declaredFields) {//遍历字段
            declaredField.setAccessible(true);
            System.out.println("字段名" + declaredField.getName()+",值:"+declaredField.get(proceed)); // 输出一下,看看是否拿到属性和值
            ExtendInfo extendInfo = declaredField.getAnnotation(ExtendInfo.class); //拿到属性字段是否有扩展注解,往下判断是否需要关联查询
            if (ObjectUtils.isEmpty(extendInfo)) {
                continue; // 没有扩展注解直接跳过
            }
            String targetFieldStr = extendInfo.targetField(); // 关联扩展表查询出来的需要字段
            String paramFieldStr = extendInfo.paramFile();  // 执行方法所需的参数
            String methodName = extendInfo.methodName(); //调用 Bean 下执行的查询方法
            Class beanClazz = extendInfo.beanClazz();  // 需要调用的 Bean
            System.out.println("目标字段"+targetFieldStr);
            System.out.println("参数字段"+paramFieldStr);
            System.out.println("方法"+methodName);
            Object beanResultObj = invokeBeanMethod(beanClazz, methodName, paramFieldStr, proceed); // spring 加载bean ,执行bean 方法,拿到关联查询的结果
            changeValue(declaredField,targetFieldStr,beanResultObj,proceed); // 将 查询的结果,和需要填充的扩展字段 传入该方法,来改变扩展字段的值
        }
        //System.out.println(JSONObject.toJSONString(proceed));
    }

    /**
     * 加载bean ,执行 bean 方法,拿到关联查询结果
     * @param beanClazz  所需加载的 bean
     * @param methodName  注解里 的 bean 方法
     * @param paramFieldStr  注解里 参数字段
     * @param proceed  aop 拦截的结果对象
     * @return Object
     * @throws Exception
     */
    private Object invokeBeanMethod(Class<Object> beanClazz,String methodName,String paramFieldStr,Object proceed) throws Exception{
        Field paramField = proceed.getClass().getDeclaredField(paramFieldStr); // 参数字段
        paramField.setAccessible(true);  // 私有字段,设置为true
        Object bean = applicationContext.getBean(beanClazz); // 拿到bean
        applicationContext.getAutowireCapableBeanFactory().autowireBean(bean); // 装载 bean
        Method declaredMethod = beanClazz.getDeclaredMethod(methodName, paramField.getType()); //反射代理方法。getDeclaredMethod 方法 ,第一个参数是 方法名,第个参数是参数类型
        declaredMethod.setAccessible(true);
        return  declaredMethod.invoke(bean, paramField.get(proceed));  // 执行bean 方法,并返回查询的结果
    }

    /**
     * 添加需要扩展字段的值
     * @param currentField  当前有注解,需要扩展信息的属性字段
     * @param targetFieldStr  关联扩展表查询出来的需要字段
     * @param beanResultObj  注解bean 执行的 查询 结果
     * @param aopProceed   aop 拦截的结果对象,应为是要在 aop拦截的结果对象里面 重新给扩展字段赋值, 这里需要传入
     * @throws Exception
     */
    private void changeValue(Field currentField,String targetFieldStr,Object beanResultObj,Object aopProceed) throws Exception {
        Class<?> beanResultObjClazz = beanResultObj.getClass();
        Field targetField = beanResultObjClazz.getDeclaredField(targetFieldStr); // 获取 bean 查询的 目标字段
        targetField.setAccessible(true);
        Object o = targetField.get(beanResultObj);  // 获取 bean 查询的目标字段值
        currentField.setAccessible(true);
        currentField.set(aopProceed,o);  // 赋值给 当前有注解需要扩展字段 
    }
}

五 :总结:

通过上述的处理,在不改变原有方法的情况下,增加的需求,这是可以通用的,再增加查询字段 ,只需在输出的对象里面 添加 注解就 OK 了,
以上代码 还要可以优化的地方,只仅仅只是一个思路。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值