Java SpringBoot

1、@Value注解如何使用?

在项目中经常需要用到使用读取配置文件,进行属性值的注入很方便;
很多时候不想手动的去初始化配置,在Spring就提供了很强大的属性依赖配置注解@Value来实现,在实现bean创建后,自动实现属性的注入功能。接下来先从一个简单的例子来描述,如何使用@Value注解,能达到什么效果,到最后自己动手实现自己的属性注解。

1.1 准备

创建Maven项目,导入Spring相关的包。

  • 配置一个注解类,能让Spring管理,AppConfig

    1. import org.springframework.context.annotation.ComponentScan;
    2. import org.springframework.stereotype.Component;
    3. @ComponentScan("com.mp.aop.annotation.day02.service")
    4. public class MyConfig {
    5. }
  • 此处配置@ComponentScan 注解扫描指定的package及其子包;将其所有实现了@bean @Component 等注解的类统一交由Spring IOC来管理

  • 准备程序入口,访问整体的spring bean ```java import com.mp.aop.annotation.day02.config.MyConfig; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import java.util.stream.Stream;

public class MainManager { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);

  1. // 已经完成了整个spring容器的初始化;这里先简单的获取下,我们容器负责管理的所有bean
  2. Stream.of(context.getBeanDefinitionNames()).forEach(s -> System.out.println(s));
  3. }

}

  1. - 执行主入口程序,可以获取整个Spring管理系统,自定义获取bean及其操作。
  2. ```java
  3. org.springframework.context.annotation.internalConfigurationAnnotationProcessor
  4. org.springframework.context.annotation.internalAutowiredAnnotationProcessor
  5. org.springframework.context.annotation.internalCommonAnnotationProcessor
  6. org.springframework.context.event.internalEventListenerProcessor
  7. org.springframework.context.event.internalEventListenerFactory
  8. myConfig

可以看到,刚开始程序会创建6个bean,其中5个为Spring容器需要初始化(后面会详细介绍分析);另外一个为自己添加的配置类。

  • 添加一个带@Value注解的类 Person ```java import com.mp.aop.annotation.MyAnnotation; import lombok.Data; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component;

/**

  • ClassName: Person
  • version V1.0 */ @Component @Data public class Person { static {

    1. System.out.println("this is person Static");

    }

    @Value(“mp”) String name;

    @Value(“10”) int age;

    String addr;

  1. public Person(String name, int age,String addr) {
  2. this.name = name;
  3. this.age = age;
  4. this.addr = addr;
  5. System.out.println("constuctor ...");
  6. }
  7. public void setName(String name) {
  8. this.name = name;
  9. System.out.println("person.setName。。。。"+name);
  10. }
  11. public void setAge(int age) {
  12. this.age = age;
  13. System.out.println("person.setAge: "+ age);
  14. }
  15. public void setAddr(String addr) {
  16. this.addr = addr;
  17. System.out.println("person.setAddr: "+addr);
  18. }
  19. public Person() {
  20. System.out.println("constructor null");
  21. }
  22. public void p1(){
  23. System.out.println("this name:"+this.name+" this age:"+this.age);
  24. }
  25. /*@Bean("p1")
  26. public Person getPerson(){
  27. System.out.println("自定义...");
  28. return new Person("zhuge",100,"wuhan");
  29. }*/

}

  1. - 再次执行,main入口程序,可以看到,Spring初始化的bean会多了一个person
  2. - main程序中,获取car这个bean,打印其信息
  3. ```java
  4. // 获取car这个bean对象,打印其对象信息
  5. System.out.println(context.getBean(Person.class));

打印结果:

  1. Person(name=mp, age=10, addr=null)

至此,结果显示在属性上的注解值成功的注入到bean对象

2、探究@Value注解的原理

在上面的使用例子中,成功的注入了属性值;那想一下,Spring到底是怎么创建带有属性的对象,并进行管理的呢?
先大胆猜测下,有以下几种可能

  • @Value直接调用带有属性的构造函数,创建对象并交由Spring来统一管理;
  • 分两步,先调用空构造器创建一个属性为空的对象,再通过set方法来设置属性值;

到底是哪一种,通过程序来验证…
new AnnotationConfigApplicationContext(MyConfig.class)打上断点,debug模式走起…

  1. public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
  2. this(); // 构造器,初始化AnnotatedBeanDefinitionReader和 ClassPathBeanDefinitionScanner并准备好5个sping系统需要的BeanDefinition.
  3. register(annotatedClasses); // 注册我们的配置类AppConfig 对应的BeanDefinition,此时我们的beanFactory中有6个beanDefinitionName了
  4. refresh(); // 重点流程
  5. }

这里发现,Spring在管理bean的时候,不是直接就创建对象扔进对象池,而是先创建起对应的BeanDefinition,这个还不是真正的bean,还未创建对象。
接着进入refresh()

  1. synchronized (this.startupShutdownMonitor) {
  2. // Prepare this context for refreshing.
  3. prepareRefresh();
  4. // Tell the subclass to refresh the internal bean factory.
  5. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
  6. // Prepare the bean factory for use in this context.
  7. prepareBeanFactory(beanFactory);
  8. try {
  9. // Allows post-processing of the bean factory in context subclasses.
  10. postProcessBeanFactory(beanFactory);
  11. // Invoke factory processors registered as beans in the context.
  12. invokeBeanFactoryPostProcessors(beanFactory); // @1 说明
  13. // Register bean processors that intercept bean creation.
  14. registerBeanPostProcessors(beanFactory);
  15. // Initialize message source for this context.
  16. initMessageSource();
  17. // Initialize event multicaster for this context.
  18. initApplicationEventMulticaster();
  19. // Initialize other special beans in specific context subclasses.
  20. onRefresh();
  21. // Check for listener beans and register them.
  22. registerListeners();
  23. // Instantiate all remaining (non-lazy-init) singletons.
  24. finishBeanFactoryInitialization(beanFactory); // @2 说明
  25. // Last step: publish corresponding event.
  26. finishRefresh(); //
  27. }

@1说明:

  • 会创建好Spring需要的5个bean
  • 根据@ComponentScan扫描的包路径,ClassPathBeanDefinitionScanner 去target根据文件系统找到对应的class文件,并根据是否有注解来判断,决定是否要加入到beanDefinitionNames 列表中,为后面初始化做准备。
  • 所以,这里beanDefinitionNames 列表扫描会新增1个person -bean

@2 说明
实例化通过包扫描的bean, 实例化person对象,并完成属性注入,重点来了…

  • 继续断点,finishBeanFactoryInitialization(beanFactory);
  • -> 经过一系列判断后,调用beanFactory.preInstantiateSingletons();进行实例化。
  • -> 遍历BeanDefinitionNames来判断是否要创建bean,针对上面已经创建过的5个bean,见到单例池中存在,则不再创建,这里所有的bean都是单例。
  • 增加断点条件beanName.equals("person")
  • -> getBean(beanName); 继续调用

Object sharedInstance = getSingleton(beanName); 这里是否单例池缓存中取对象,由于之前没有创建,所以这里为null,流程继续往下走
mbd.isSingleton() 为默认单例,开始创建实例

  • 关键的步骤:RootBeanDefinition
  • resolveBeanClass(mbd, beanName)根据beanName获取class
  • instanceWrapper = createBeanInstance(beanName, mbd, args);这里调用空构造器,创建一个空对象

接下来,哪里实现了属性的注入呢?来,继续往下走…

  • populateBean(beanName, mbd, instanceWrapper);根据 instanceof InstantiationAwareBeanPostProcessor 判断结果,dp为AutowiredAnnotationBeanPostProcessor 类, —debug发现这个postProcessor的服务会处理@Autowired@Value来个注解
  • PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);会做属性注入,继续debug进去。
  • 可以看到想要的代码了,postProcessProperties(PropertyValues pvs, Object bean, String beanName)注入属性值,实际调用方法。metadata.inject(bean, beanName, pvs) ,继续debug深入。
  • 获取InjectedElement 集合,可以检查到两个注解属性,name age, 遍历属性字段,根据Field类做属性注入。从注解value中获取值
  • field.set(bean, value) 至此,完成了 属性的注入
  • debug信息查看person:Person(name=mp, age=10, addr=null)好了,完成梳理。

    3、自定义实现@MyValue注解

    3.1 创建@MyValue注解类

    ```java import java.lang.annotation.*;

@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MyValue { String value() default “”; }

  1. <a name="gvbaw"></a>
  2. #### 3.2 创建使用`@MyValue`注解的属性的类
  3. ```java
  4. import com.mp.aop.annotation.day02.config.MyValue;
  5. import lombok.Data;
  6. import org.springframework.stereotype.Component;
  7. /**
  8. * ClassName: Car
  9. * version V1.0
  10. */
  11. @Component
  12. @Data
  13. public class Car {
  14. @MyValue(value = "布加迪")
  15. String name;
  16. @MyValue(value = "6666w")
  17. String price;
  18. static {
  19. System.out.println("static car...");
  20. }
  21. }

3.3 自定义后置处理器 MyPostProcessor

这边采用的策略是,在完成spring bean构建之后,通过beanFactory获取对应的bean,做字段的依赖注入

  1. import com.mp.aop.annotation.day02.config.MyValue;
  2. import org.springframework.beans.BeansException;
  3. import org.springframework.beans.factory.BeanFactory;
  4. import org.springframework.beans.factory.SmartInitializingSingleton;
  5. import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
  6. import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
  7. import java.util.stream.Stream;
  8. /**
  9. * ClassName: ServicePostProcess
  10. * Function: TODO
  11. * version V1.0
  12. */
  13. @Component
  14. public class MyPostProcess implements SmartInitializingSingleton,BeanFactoryPostProcessor {
  15. BeanFactory beanFactory;
  16. @Override
  17. public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
  18. System.out.println("this is factory...");
  19. this.beanFactory = beanFactory;
  20. }
  21. @Override
  22. public void afterSingletonsInstantiated() {
  23. System.out.println("process afterSingletons...");
  24. // 处理@MyValue注解
  25. Car car = beanFactory.getBean(Car.class);
  26. Stream.of(Car.class.getDeclaredFields())
  27. .filter(field -> field.getAnnotation(MyValue.class) != null)
  28. .forEach(field -> {
  29. MyValue myValue = field.getAnnotation(MyValue.class);
  30. try {
  31. field.set(car,myValue.value()); // 属性注入.
  32. } catch (IllegalAccessException e) {
  33. e.printStackTrace();
  34. }
  35. // 设置属性
  36. // System.out.println(field.getName());
  37. //
  38. //1. 方法2,反射找到对应的set方法做属性的设置
  39. /*String fieldName = field.getName();
  40. Class<?> type = field.getType();
  41. String setMethodName = "set".concat(fieldName.substring(0,1).toUpperCase()).concat(fieldName.substring(1));
  42. try {
  43. Method method = Car.class.getMethod(setMethodName, type);
  44. method.invoke(car,myValue.value());
  45. } catch (Exception e) {
  46. e.printStackTrace();
  47. }*/
  48. });
  49. System.out.println("car: "+ car);
  50. }
  51. }

打印结果:

  1. Car(name=布加迪, price=6666w)

完成自定义属性注解。
这里需要重点说如一下,自定义后置处理器,实现SmartInitializingSingletonBeanFactoryPostProcessor 接口,需要重写postProcessBeanFactory()afterSingletonsInstantiated() 方法

  • postProcessBeanFactory()方法块,在Spring初始化bean之前就会调用执行;
  • afterSingletonsInstantiated()方法会在完成bean的创建及管理之后执行。