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"}]

 

 

 

 

 

 

 

 

 

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM