Java自定义注解+动态代理实现字段注入,方法拦截

前言

前面写过自定义注解+切面的方式去做一些事情,坏处是切面注解只能用在方法上,字段注入,参数注入都无法实现,并且拓展性差,借鉴@Autowire依赖注入在bean初始化的时候做一些操作,就可以显现字段的注入,也能检测哪些bean有自定义注解;同时借鉴使用动态代理就能实现注解的多功能。

了解

bean生命周期中有很多地方都能参与到bean的初始化过程中,此次实现BeanPostProcessor接口,BeanPostProcessor接口中有两个方法

public interface BeanPostProcessor {
    //bean初始化方法调用前被调用
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    //bean初始化方法调用后被调用
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

这两个方法均参与bean初始化的历程中,bean类似于流水从参数流入,返回值流出,如果在方法中代理传入的bean,并返回代理对象就可以实现动态bean代理


/**
 * @author jiang
 */
@Component
public class AnnotationsImpl implements BeanPostProcessor, ApplicationContextAware, ApplicationRunner {

    private ApplicationContext applicationContext;

    private Set<Object> annotationBeans = new HashSet<>();

    @Bean("injectBeans")
    public Map<String, Object> injectBeans(){
        return new ConcurrentHashMap<>();
    }

    @Resource(name = "injectBeans")
    private Map<String, Object> injectBeans;


    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class<?> targetCls = bean.getClass();
        Set<AnnotationProcessor> annotationProcessors = new HashSet<>();
        //获取自定义注解实现类,自定义注解需实现AnnotationProcessor接口
        Map<String, AnnotationProcessor> res = applicationContext.getBeansOfType(AnnotationProcessor.class);
        //将所有的自定义注解实现类记录,用于自定义注解实现类区分注解
        annotationBeans.addAll(res.values());
        Method[] declaredMethods = targetCls.getDeclaredMethods();
        //每个bean检测自定义注解
        for (AnnotationProcessor value : res.values()) {
            Class annotationClass = value.targetAnnotation();
            Annotation clazzAnnotation = AnnotationUtils.findAnnotation(targetCls, annotationClass);
            if (clazzAnnotation != null){
                //如果bean检测到类注解,执行自定义方法
                value.classInit(value.targetAnnotation(), bean);
            }
            //检测bean字段是否有自定义注解
            Field[] allFields = FieldUtils.getAllFields(targetCls);
            for (Field allField : allFields) {
                Annotation fieldAnnotation = AnnotationUtils.findAnnotation(allField, annotationClass);
                if (fieldAnnotation != null){
                    try {
                        Map<String, Object> map = new HashMap<>();
                        map.put("Bean", bean);
                        map.put("Field", allField);
                        map.put("AnnotationProcessor", value);
                        //如果字段有自定义注解,则记录下来,为啥要记录呢?卖个关子
                        injectBeans.put(bean.getClass().getName()+allField.getName(), map);
                    } catch (Exception e) {
                        e.printStackTrace();
           
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Java 中,注解是在编译时处理的,因此要在编译时修改属性上的注解内容,需要使用 Java 的 annotation processing 工具(APT)。 APT 是一种在编译时扫描和处理 Java 注解的工具,它可以在编译期间生成新的 Java 代码,并将其编译成可执行的程序。APT 工具提供了许多 API,可以用来读取和修改源代码上的注解信息。 下面是一个简单的例子,演示如何在编译时修改属性上的注解内容: ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface MyAnnotation { String value(); } public class MyClass { @MyAnnotation("old value") private String myField; } public class MyProcessor extends AbstractProcessor { @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { for (Element element : roundEnv.getElementsAnnotatedWith(MyAnnotation.class)) { if (element.getKind() == ElementKind.FIELD) { VariableElement variableElement = (VariableElement) element; MyAnnotation annotation = variableElement.getAnnotation(MyAnnotation.class); String newValue = "new value"; MyAnnotation newAnnotation = new MyAnnotation() { @Override public String value() { return newValue; } @Override public Class<? extends Annotation> annotationType() { return MyAnnotation.class; } }; AnnotatedTypeMirror.AnnotatedDeclaredType fieldType = (AnnotatedTypeMirror.AnnotatedDeclaredType) variableElement.asType(); AnnotatedTypeMirror.AnnotatedDeclaredType annotatedFieldType = fieldType.replaceAnnotation(newAnnotation); variableElement.getEnclosingElement().getEnclosedElements().remove(variableElement); FieldSpec.Builder fieldBuilder = FieldSpec.builder(TypeName.get(annotatedFieldType.getUnderlyingType()), variableElement.getSimpleName().toString()); variableElement.getModifiers().forEach(modifier -> fieldBuilder.addModifiers(modifier)); fieldBuilder.addAnnotation(annotatedFieldType.getAnnotations().stream().map(AnnotationSpec::get).collect(Collectors.toList())); fieldBuilder.initializer("null"); FieldSpec fieldSpec = fieldBuilder.build(); JavaFile.builder(annotatedFieldType.getUnderlyingType().toString().replace('.', '/').replace('$', '/') + "Generated", fieldSpec) .build() .writeTo(processingEnv.getFiler()); } } return true; } } ``` 在这个例子中,`MyAnnotation` 是一个自定义的注解,`MyClass` 是一个包含注解属性的。`MyProcessor` 是一个 APT 处理器,它会在编译时扫描和处理带有 `@MyAnnotation` 注解的元素。在 `process` 方法中,我们使用 `roundEnv.getElementsAnnotatedWith` 方法获取所有带有 `@MyAnnotation` 注解的元素,然后判断元素型是否为字段。如果是字段,我们就可以使用 `variableElement.getAnnotation` 方法获取字段上的注解对象,然后构造一个新的注解对象并使用 `replaceAnnotation` 方法替换原有的注解对象。最后,我们可以使用 `JavaFile.builder` 方法创建一个新的 Java 文件,并将修改后的字段写入该文件中。 需要注意的是,APT 处理器需要在编译时自动运行,可以通过在 `build.gradle` 文件中配置以下内容来实现: ```groovy dependencies { compileOnly 'com.google.auto.service:auto-service:1.0-rc7' annotationProcessor 'your.package.MyProcessor' } sourceSets { main { java { srcDirs 'src/main/java', 'build/generated/source/apt/main' } } } compileJava.options.annotationProcessorPath = configurations.compileClasspath ``` 这个例子只是一个简单的示例,实际使用中可能需要更复杂的处理逻辑。但是,这个例子可以帮助你了解在编译时修改注解内容的基本原理。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值