Aop AfterReturning增强方法返回值

需求:查询订单要返回用户名

  为了解耦,查询订单中不查询用户,使用aop自动注入用户名

    注意:订单列表中的用户缓存到了内存,遍历查询很快,如果直接查数据库,则效率相对低

 

思路:对返回值加强(aop对返回值增强,向订单表中注入userName)

  1.注解

/**
 * 设置属性非空的开关
 * 只有方法上加上此注解,才会对Field上加上 FieldNotNull 的属性赋值
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SetFieldSwitch {
}

  

/**
 * 字段非空注解
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)          //作用在字段上(告诉aop去哪个object调用哪个method,需要传什么param参数,查询的结果需要取哪一个targetField)
public @interface FieldNotNull {

    Class beanClass();  // 需要去哪个class中调用 (userName的属性从)

    String method();    // 需要调用class中的哪个方法

    String param();     // 调用方法的参数 

    String targetField();   //调用方法后需要哪个值(为了set到添加该注解的属性上)
    
}

  2:订单+用户对象

 

@Data
public class UserOrder /*extends Order*/ {
    private Integer id;

    private Integer goodsId;

    private Integer userId;

    @FieldNotNull(beanClass = UserCache.class, method = "get", param = "userId", targetField = "realName")
    private String userName;    //用户名

    private String goodName;    //物品名称

    public UserOrder(Integer id, Integer userId, Integer goodId, String userName, String goodName) {
        this.userName = userName;
        this.goodName = goodName;
        this.setId(id);
        this.setUserId(userId);
        this.setGoodsId(goodId);
    }

}

 

3:查询订单方法

 

@Service
public class OrderService {
    @Autowired
    private OrderDao orderDao;

    @SetFieldSwitch  // 开启aop增强(如果不开启,则FileNotNull注解不会起作用),将控制与实现分开)
public List<UserOrder> listOrder() {
        return orderDao.listOrder();
    }
}

 

 

4:查询用户的方法

 

/**
 * 模拟用户的缓存
 */
@Component
public class UserCache {
    @Autowired
    private UserDao userDao ;
    private static Map<Integer, User> userCache = new HashMap<>();

    public void put(Integer userId, User user) {
        userCache.put(userId, user);
    }

    public User get(Integer userId) {
        User user = userCache.get(userId);
        if (user == null) {
            user = userDao.getById(userId);
            if (user==null)  return null;
            userCache.put(userId,user);
        }
        return user;
    }

    public boolean contain(Integer userId) {
        return userCache.containsKey(userId);
    }
}

 

5:aop切面

    使用AfterReturning

    /**
     *
     * @param point 切点
     * @param obj   返回值
     * @return
     * @throws Throwable
     */
    @AfterReturning(value = "setFieldValuePoint()", returning = "obj")
    public Object setValue(JoinPoint point, Object obj) throws Throwable {

        this.setFieldValueForCollection((Collection) obj);
        return obj;
    }

 

 6:使用反射+注解  赋值

 /**
     * 查询并赋值操作
     *
     * @param collection
     */
    private void setFieldValueForCollection(Collection collection) throws Exception {
        if (collection == null || collection.size() == 0) return;
        Iterator iterator = collection.iterator();
        Object next = iterator.next();

        /*collection中userOrder元素对应的class*/
        Class clazz = next.getClass();      // class com.draymond.aop.query.UserOrder

        /*获取userOrder中所有的属性*/
        Field[] declaredFields = clazz.getDeclaredFields();

        for (Field field : declaredFields) {
            /* ----------- 获取 哪个class 调用哪个 method ,需要什么paras -----------*/

            /*获取userOrder属性上的FieldNotNull注解(其他 Field 也可以有其他注解)*/
            FieldNotNull annotation = field.getAnnotation(FieldNotNull.class);
            if (annotation == null) continue;
            field.setAccessible(true);                       // 暴力拆解
            Class beanClass = annotation.beanClass();
            String method = annotation.method();         // get(注解上的值)
            String param = annotation.param();          // userId
            String targetField = annotation.targetField();  // realName

            Field paraField = clazz.getDeclaredField(param);    // 获取方法需要的参数值(paraField.get(user)中获取)
            paraField.setAccessible(true);
            Method methodExecute = beanClass.getDeclaredMethod(method, paraField.getType());

            /*从spring上下文获取beanClass对应的bean*/
            Object userCache = applicationContext.getBean(beanClass);

            // for循环,对list中每个user的带FieldNotNull注解的属性赋值
            for (Object userOrder : collection) {    //list上的每条user对象

                Object user = methodExecute.invoke(userCache, paraField.get(userOrder));   // 执行 UserCache 中的 get 方法,需要的参数值 paraField
                if (user == null) continue;
                Field targetFieldValue = user.getClass().getDeclaredField(targetField);    // 获取user对象中的 targetField 的值
                if (targetFieldValue == null) continue;
                targetFieldValue.setAccessible(true);
                field.set(userOrder, targetFieldValue.get(user));                           // 将 user中的targetField(realName) 的值,set到userOrder对象中的 userName
            }

        }

    }

 

其他

/**
 * 模拟从数据库中查询订单
 */
@Component
public class OrderDao {

    public List<UserOrder> listOrder() {

        UserOrder userOrder1 = new UserOrder(1, 1, 1, "zsc", "电冰箱");
        UserOrder userOrder2 = new UserOrder(2, 1, 2, null, "洗衣机");
        UserOrder userOrder3 = new UserOrder(3, 1, 3, null, "java Thread");
        List list = new ArrayList();
        list.add(userOrder1);
        list.add(userOrder2);
        list.add(userOrder3);
        return list;
    }

    public UserOrder getById(Integer id) {
        return listOrder().stream().filter(userOrder -> userOrder.getId() == id).findFirst().orElse(null);
    }

}

 

方法上使用注解开启增强

@Service
public class OrderService {
    @Autowired
    private OrderDao orderDao;

    @SetFieldSwitch
    public List<UserOrder> listOrder() {
        return orderDao.listOrder();
    }

    public UserOrder getOrderById(Integer id) {
        return orderDao.getById(id);
    }
}

 

 

 

 

未增强效果

[{"id":1,"goodsId":1,"userId":1,"userName":"zsc","goodName":"电冰箱"},    // 模拟数据的时候就赋值了
{"id":2,"goodsId":2,"userId":2,"userName":null,"goodName":"洗衣机"},
{"id":3,"goodsId":3,"userId":1,"userName":null,"goodName":"java Thread"}]

 

增强效果

 

[{"id":1,"goodsId":1,"userId":1,"userName":"zsc","goodName":"电冰箱"},
{"id":2,"goodsId":2,"userId":2,"userName":"draymond","goodName":"洗衣机"},
{"id":3,"goodsId":3,"userId":1,"userName":"zsc","goodName":"java Thread"}]

 

 

 

 

 

 

 

 

 

 

  

posted @ 2020-04-09 22:50  Draymond  阅读(5088)  评论(0编辑  收藏  举报