SpringIoc自定义

第一种方式

​ 在写代码的时候,我经常纳闷Spring @Autowired到底是如何实现的,我是不是自己实现一个,完后就有这篇文章

编写自定义注解

/** @author sulwan */
@Target({
  ElementType.CONSTRUCTOR,
  ElementType.METHOD,
  ElementType.PARAMETER,
  ElementType.FIELD,
  ElementType.ANNOTATION_TYPE
})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Sulwan {
  boolean required() default true;
}

@Target:

   @Target说明了Annotation所修饰的对象范围:Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。

具体取值范围:

public enum ElementType {
    /**用于描述类、接口(包括注解类型) 或enum声明 Class, interface (including annotation type), or enum declaration */
    TYPE,
 
    /** 用于描述域 Field declaration (includes enum constants) */
    FIELD,
 
    /**用于描述方法 Method declaration */
    METHOD,
 
    /**用于描述参数 Formal parameter declaration */
    PARAMETER,
 
    /**用于描述构造器 Constructor declaration */
    CONSTRUCTOR,
 
    /**用于描述局部变量 Local variable declaration */
    LOCAL_VARIABLE,
 
    /** Annotation type declaration */
    ANNOTATION_TYPE,
 
    /**用于描述包 Package declaration */
    PACKAGE,
 
    /**
     * 用来标注类型参数 Type parameter declaration
     * @since 1.8
     */
    TYPE_PARAMETER,
 
    /**
     *能标注任何类型名称 Use of a type
     * @since 1.8
     */
    TYPE_USE

@Retention 注解的作用

注解@Retention可以用来修饰注解,是注解的注解,称为元注解。 Retention注解有一个属性value,是RetentionPolicy类型的,Enum RetentionPolicy是一个枚举类型, 这个枚举决定了Retention注解应该如何去保持,也可理解为Rentention 搭配 RententionPolicy使用。RetentionPolicy有3个值:CLASS RUNTIME SOURCE 按生命周期来划分可分为3类: 1、RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃; 2、RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期; 3、RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在; 这3个生命周期分别对应于:Java源文件(.java文件) —> .class文件 —> 内存中的字节码。 那怎么来选择合适的注解生命周期呢? 首先要明确生命周期长度 SOURCE < CLASS < RUNTIME ,所以前者能作用的地方后者一定也能作用。 一般如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解,比如@Deprecated使用RUNTIME注解 如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS注解; 如果只是做一些检查性的操作,比如 @Override 和 @SuppressWarnings,使用SOURCE 注解。

注解@Override用在方法上,当我们想重写一个方法时,在方法上加@Override,当我们方法的名字出错时,编译器就会报错 注解@Deprecated,用来表示某个类或属性或方法已经过时,不想别人再用时,在属性和方法上用@Deprecated修饰 注解@SuppressWarnings用来压制程序中出来的警告,比如在没有用泛型或是方法已经过时的时候

@Documented注解

Documented注解表明这个注释是由 javadoc记录的,在默认情况下也有类似的记录工具。 如果一个类型声明被注释了文档化,它的注释成为公共API的一部分。

创建Bean加载注解

/** @author sulwan */
@Component
public class Beans {
  @Bean
  public AutowiredAnnotationBeanPostProcessor getBean() {
    AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor =
        new AutowiredAnnotationBeanPostProcessor();
    autowiredAnnotationBeanPostProcessor.setAutowiredAnnotationType(Sulwan.class);
    return autowiredAnnotationBeanPostProcessor;
  }
}

第二种方式:

实现*BeanPostProcessor*接口,嵌入到Bean的初始化过程中,来完成自定义注入的

定义注解

package com.pv3.springboot_base.Annotation;

import java.lang.annotation.*;

/** @author sulwan */
@Target({
  ElementType.CONSTRUCTOR,
  ElementType.METHOD,
  ElementType.PARAMETER,
  ElementType.FIELD,
  ElementType.ANNOTATION_TYPE
})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SulwanTwo {
  boolean required() default true;
}

继承实现类

package com.pv3.springboot_base.Bean;

import com.pv3.springboot_base.Annotation.SulwanTwo;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;

@Component
public class SulwanBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {
  private ApplicationContext applicationContext;

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

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

  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName)
      throws BeansException {
    if (hasAnnotation(bean.getClass().getAnnotations(), SulwanTwo.class.getName())) {
      Class beanClass = bean.getClass();
      do {
        Field[] fields = beanClass.getDeclaredFields();
        for (Field field : fields) {
          setField(bean, field);
        }
      } while ((beanClass = beanClass.getSuperclass()) != null);
    } else {
      processMyInject(bean);
    }
    return bean;
  }

  private void processMyInject(Object bean) {
    Class beanClass = bean.getClass();
    do {
      Field[] fields = beanClass.getDeclaredFields();
      for (Field field : fields) {
        if (!hasAnnotation(field.getAnnotations(), SulwanTwo.class.getName())) {
          continue;
        }
        setField(bean, field);
      }
    } while ((beanClass = beanClass.getSuperclass()) != null);
  }

  private void setField(Object bean, Field field) {
    if (!field.isAccessible()) {
      field.setAccessible(true);
    }
    try {
      field.set(bean, applicationContext.getBean(field.getType()));
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  private boolean hasAnnotation(Annotation[] annotations, String annotationName) {
    if (annotations == null) {
      return false;
    }
    for (Annotation annotation : annotations) {
      if (annotation.annotationType().getName().equals(annotationName)) {
        return true;
      }
    }
    return false;
  }
}