自定义业务封装之annotation+reflection+aop初识

场景:获取订单信息,其中需要用户名,封装需准备如下:

  • 方法的注解:利用aop通过该方法注解进行切入目标方法增强目标方法的结果
  • 字段的注解:利用反射通过该字段注解获取是那个类执行了那个方法,其方法是那个入参,返回结果中取那个目标字段
需要:1、那个类;2、那个方法;3、那个入参;4、那个目标字段
  • aop切面类:动态代理增强
  • beanUtil工具类:增强的实现过程
  • 用户实体类:id、name。。。
  • 订单实体类:id、customId、customName(加上字段注解)
  • 用户mapper:findUserById方法,入参为customId
  • 订单mapper:findOrderList
  • 订单service:findOrderList,该方法加上方法注解,需要对目标方法的结果进行增强
  • 用户service:findUserById方法,测试该方法
  • @Test:注入service,进行测试
  • 入口类:添加@MapperScan注解

代码示例:

/**
 * 方法注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SetValue {
}
/**
 * 字段注解
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SetValueField {

    //@SetValueField(beanClass = UserMapper.class,param = "customId",method = "findUserById",targetField = "name")
    //method.invoke(bean,args)  通过传参的形式告诉

    //反射需要知道的类
    Class<?> beanClass();

    //反射执行所需的方法名
    String method();

    //反射方法的入参名
    String paramField();

    //指的是dao层的返回对象结果中的的所需字段名,可以通过该名字拿到具体的值
    String targetField();

}
/**
 * @description:自定义业务封装的aop切面
 */
@Component
@Aspect
public class SetFieldValueAspect {

    @Autowired
    private BeanUtil beanUtil;

    @Around("@annotation(com.ldc.annotation_reflection_aop.annotation.SetValue)")
    public Object doSetFieldValue(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        //前置增强
        System.out.println("前置增强操作");
        Object result = proceedingJoinPoint.proceed();
        //List = [{id:1,customId:1,customName:null @SetValueField}]
        //向customName赋值》获取目标对象的结果对象
        //后置增强
        System.out.println("后置增强操作");
        beanUtil.setFieldValue((Collection) result);
        return result;
    }
}
@Component
public class BeanUtil implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public void setFieldValue(Collection collection) throws Exception {
        //1获取结果集中的对象
        //2获取该对象的类元信息
        //3通过类元信息获取该对象的所有字段
        //4遍历所有字段
        //5有注解的字段则需要遍历结果集,将每个对象的该字段赋值,此时需要结果集中对象及目标对象的目标字段的值
        //6结果集中对象已有,目标对象需要通过下面获取

        //1通过注解获取目标对象的实例
        //2通过注解获取目标对象的目标方法名
        //3通过注解获取结果集中对象的参数字段名
        //4通过注解获取目标对象的目标字段名
        //5通过注解获取目标对象的目标方法
        //6目标对象的目标方法通过目标对象和参数值利用反射获取目标对象的结果对象
        //7目标字段通过目标对象的结果对象利用反射获取值

        //缓存
        HashMap<Object, Object> cache = new HashMap<>();
        //1获取结果集中的对象的类元信息
        Class<?> clazz = collection.iterator().next().getClass();//order类元信息
        //2通过类元信息获取该对象的所有字段
        Field[] declaredFields = clazz.getDeclaredFields();
        //3遍历所有字段
        for (Field declaredField : declaredFields) {
            //3获取每个字段上的注解
            SetValueField annotation = declaredField.getAnnotation(SetValueField.class);
            if (annotation == null) continue;
            //4设置该有注解的字段可见,为赋值做准备
            declaredField.setAccessible(true);

            //①通过注解获取目标对象实例
            Object bean = this.applicationContext.getBean(annotation.beanClass());
            //②通过注解获取目标对象的目标方法名
            String methodName = annotation.method();
            //③通过注解获取结果集中对象的参数字段名
            String paramFieldName = annotation.paramField();
            Field paramField = clazz.getDeclaredField(paramFieldName);
            paramField.setAccessible(true);
            //⑤通过注解获取目标对象的目标字段名
            String targetFieldName = annotation.targetField();
            Field targetField = null;
            //⑥通过注解获取目标对象的目标方法
            //Method method = annotation.beanClass().getMethod(methodName, clazz.getDeclaredField(paramName).getType());
            Method method = bean.getClass().getMethod(methodName, paramField.getType());
            //⑦目标对象的目标方法通过目标对象和参数值利用反射获取目标对象的结果对象

            Boolean flag = StringUtils.isEmpty(targetFieldName);
            String keyPrefix = annotation.beanClass()+"-"+annotation.method()+"-"+annotation.targetField()+"-";
            //5有注解的字段则需要遍历结果集,将每个对象的该字段赋值
            for (Object obj : collection) {
                Object paramValue = paramField.get(obj);
                if (paramValue == null) continue;
                //目标对象的结果对象及结果对象的目标字段值
                Object resultBean = null;
                Object targetFieldValue = null;
                String key = keyPrefix+paramValue;
                if (cache.containsKey(key)) {
                    targetFieldValue = cache.get(key);//缓存拿目标对象的结果对象
                } else {
                    resultBean = method.invoke(bean, paramValue);//user类元信息
                    if (!flag) {
                        if (resultBean != null) {
                            if (targetField == null) {
                                targetField = resultBean.getClass().getDeclaredField(targetFieldName);
                                targetField.setAccessible(true);
                            }
                            targetFieldValue = targetField.get(resultBean);
                        }
                    }
                    cache.put(key,targetFieldValue);
                }
                Object currentNeedVlaue = declaredField.get(obj);
                if (currentNeedVlaue == null){
                    declaredField.set(obj,targetFieldValue);
                }
            }
        }
    }
}

public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    private String id;

    private String name;

}
public class Order implements Serializable {

    public static final long serialVersionUID = 1L;

    private int id;

    private int customId;

    //beenClass:反射需要知道的类
    //param:反射方法的入参 (因为我们要拿到方法,拿到方法除了知道方法名字外还需要知道方法的入参数类型)
    //method:反射执行所需的方法名
    //targetFiled:指的是dao层的返回user对象结果中的的所需字段名字,可以通过该名字拿到具体的值
    @SetValueField(beanClass = UserMapper.class,paramField = "customId",method = "findUserById",targetField = "name")
    private String customName;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getCustomId() {
        return customId;
    }

    public void setCustomId(int customId) {
        this.customId = customId;
    }

    public String getCustomName() {
        return customName;
    }

    public void setCustomName(String customName) {
        this.customName = customName;
    }

}
@Mapper
public interface UserMapper {

    @Select("SELECT * FROM s_user WHERE id = #{customId}")
    User findUserById(@Param("customId") int customId);

}
@Mapper
public interface OrderMapper {

    @Select("SELECT * FROM b_order")
    @Results(id = "orderList",value = {
            @Result(property = "id",column = "id"),
            @Result(property = "customId",column = "custom_id"),
            @Result(property = "customName",column = "custom_name"),
    })
    List<Order> findOrderList();

}
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    public User findUserById(Integer id) {
        return userMapper.findUserById(id);
    }

}
@Service
public class OrderService {

    @Resource
    private OrderMapper orderMapper;

    @SetValue
    public List<Order> findOrderList(int pageNum,int pageSize){
        return orderMapper.findOrderList();
    }

}
@Test
    void contextLoads() {
        System.out.println(userService.findUserById(1).getName());
    }

    @Test
    void test() {
        List<Order> orderList = orderService.findOrderList(1, 10);
        System.out.println(orderList.get(1).getCustomName());
    }

结果:只打印了用户名字段
前置增强操作
2020-09-03 11:14:52.361 INFO 13504 — [ main] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} inited
后置增强操作
曹操(用户名)

小结:注解+反射+切面需多加练习,并理解其设计模式

Java中,自定义注解(Annotation)是一种元数据,用于在代码中提供附加信息,而这些信息通常不会直接影响程序的执行。自定义注解实体类是一种用户创建的注解类型,它包含一个或多个字段,这些字段可以存储额外的数据。 要创建一个自定义注解,你需要按照以下步骤操作: 1. 定义注解类:创建一个新的Java接口,添加你想要的属性(@Retention, @Target, 或者你自己定义的字段),例如: ```java import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) // 表示注解在运行时可用 @Target(ElementType.METHOD) // 指定注解可以应用在方法上 public @interface MyCustomAnnotation { String value(); int priority() default 0; } ``` 这里定义了一个名为`MyCustomAnnotation`的注解,有两个属性:`value`和`priority`。 2. 在目标类中使用注解:在需要应用注解的方法上添加注解,例如: ```java public class MyClass { @MyCustomAnnotation(value = "Important Method", priority = 1) public void myMethod() { // 方法体... } } ``` 3. AOP(面向切面编程)与自定义注解:Spring AOP或AspectJ等工具可以让你在运行时扫描并处理带有特定注解的方法。你可以编写一个切面(Aspect),使用`@Around`, `@Before`, 或 `@After` 等通知处理`@MyCustomAnnotation`注解的方法。例如,你可能想在方法执行前后执行一些额外的操作,或者根据`priority`属性决定是否执行某些操作。 ```java @Aspect @Component public class MyAspect { @Around("@annotation(myCustomAnnotation)") public Object advice(ProceedingJoinPoint joinPoint, MyCustomAnnotation myCustomAnnotation) throws Throwable { System.out.println("Before executing method with priority: " + myCustomAnnotation.priority); Object result = joinPoint.proceed(); System.out.println("After executing method with priority: " + myCustomAnnotation.priority); return result; } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值