spring@Value注解原理梳理及⾃定义实现@MyValue注解实例
spring @Value注解原理梳理及⾃定义实现@MyValue注解
⽬录
@Value注解如何使⽤?
探索@Value注解的实现过程
⾃定义实现@MyValue注解
1. @Value注解如何使⽤?
在项⽬中我们经常需要⽤到使⽤读取配置⽂件,进⾏属性值的注⼊很⽅便;
很多时候我们不想⼿动的去初始化配置,在spring就提供了很强⼤的属性依赖配置注解@Value来实现,在实现bean创建后,⾃动实现属性的注⼊功能. 接下来我们将先 从⼀个简单的例⼦来描述,如何使⽤@Value注解,能达到什么效果,到最后我们⾃⼰动⼿实现⾃⼰的属性注解.
1.1 准备
创建maven项⽬,导⼊spring相关的包的此处省略…
配置⼀个注解类,能让spring 管理, AppConfig
package c平平安安造句
om.mp.aop.fig;
import t.annotation.ComponentScan;
import org.springframework.stereotype.Component;
@ComponentScan("com.mp.aop.annotation.day02.rvice")
public class MyConfig {
}
此处配置@ComponentScan 注解扫描指定的package及其⼦包;将其所有实现了@bean @Component 等注解凤尾竹的养殖方法和注意事项
的类统⼀交由sping IOC来管理
准备程序⼊⼝,访问整画报模板
体的spring bean
package com.mp.aop.annotation.day02;
import com.mp.aop.fig.MyConfig;
import t.annotation.AnnotationConfigApplicationContext;
import java.util.stream.Stream;
public class MainManager {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
// 已经完成了整个spring容器的初始化;这⾥先简单的获取下,我们容器负责管理的所有bean
Stream.BeanDefinitionNames()).forEach(s -> System.out.println(s));
}
}
执⾏主⼊⼝程序,可以获取整个spring管理系统,⾃定义获取bean及其操作。
t.annotation.internalConfigurationAnnotationProcessor
t.annotation.internalAutowiredAnnotationProcessor
t.annotation.internalCommonAnnotationProcessor
t.event.internalEventListenerProcessor
t.event.internalEventListenerFactory
myConfig
可以看到,刚开始程序会创建6个bean,其中5个为spring容器需要初始化(后⾯会详细介绍分析);另外⼀个为我们添加的配置类.
添加⼀个带@Value注解的类 Person
package com.mp.aop.annotation.day02.rvice;
import com.mp.aop.annotation.MyAnnotation;
import lombok.Data;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import t.annotation.Bean;
import org.springframework.stereotype.Component;
/**
* ClassName: Person
* Function: TODO
* Date: 2020-04-11 21:34
* author mp
* version V1.0
*/
@Component
@Data
public class Person {
static {
System.out.println("this is person Static");
}
@Value("mp")
String name;
@Value("10")
int age;
String addr;
public Person(String name, int age,String addr) {
this.name = name;
this.age = age;
this.addr = addr;
System.out.println("constuctor ...");
}
public void tName(String name) {
this.name = name;
System.out.println("person.tName。。。。"+name);
}
public void tAge(int age) {
this.age = age;
System.out.println("person.tAge: "+ age);
}
public void tAddr(String addr) {
this.addr = addr;
System.out.println("person.tAddr: "+addr);
}
public Person() {
System.out.println("constructor null");
}
public void p1(){
System.out.println("this name:"+this.name+" this age:"+this.age);
}
/*@Bean("p1")
public Person getPerson(){
System.out.println("⾃定义...");
return new Person("zhuge",100,"wuhan");
}*/
}
再次执⾏,main⼊⼝程序,我们将看到,spring初始化的bean会多了⼀个person
在main程序中,获取car这个bean,打印其信息
// 获取car这个bean对象,打印其对象信息
System.out.Bean(Person.class));
打印结果:
Person(name=mp, age=10, addr=null)
⾄此,结果显⽰我们在属性上的注解值成功的注⼊到了我们的bean对象
2. 探究@Value注解的原理
在上⾯的使⽤例⼦中,成功的注⼊了属性值; 那我们想⼀下,spring到底是怎么创建带有属性的对象,并进⾏管理的呢?
我们先⼤胆猜测下,有以下⼏种可能
1. @Value直接调⽤带有属性的构造函数,创建对象并交由spring来统⼀管理;
2. 分两步,先调⽤空构造器创建⼀个属性为空的对象,再通过t⽅法来设置属性值;
到底是哪⼀种,我们通过程序来验证…
在new AnnotationConfigApplicationContext(MyConfig.class) 打上断点,debug模式⾛起…
public AnnotationConfigApplicationContext(Class<?>... annotatedClass) {
this(); // 构造器,初始化AnnotatedBeanDefinitionReader和 ClassPathBeanDefinitionScanner并准备好5个sping系统需要的BeanDefinition.
register(annotatedClass); // 注册我们的配置类AppConfig 对应的BeanDefinition,此时我们的beanFactory中有6个beanDefinitionName了
refresh(); // 重点流程
}
这⾥发现,spring在管理bean的时候,不是直接就创建对象扔进对象池,⽽是先创建起对应的BeanDefinition,这个还不是真正的bean,还未创建对象.
接着我们进⼊refresh()
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for u in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclass.
postProcessBeanFactory(beanFact怎么用气息唱歌
ory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory); // @1 说明
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific conte蒿子粑粑
xt subclass.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory); // @2 说明
/
/ Last step: publish corresponding event.
finishRefresh(); //
}
@1说明:
1. 会创建好spring需要的5个bean
2. 根据@ComponentScan扫描的包路径,ClassPathBeanDefinitionScanner 去target根据⽂件系统找到对应的class⽂件,并根据是否有注解
来判断,决定是否要加⼊到beanDefinitionNames 列表中,为后⾯初始化做准备.
3. 所以,这⾥beanDefinitionNames 列表扫描会新增1个person -bean
@2 说明
1. 实例化通过包扫描的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 pvsToU = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); 会做属性注⼊,继续debug进去.
好,看到我们想要的代码了,postProcessProperties(PropertyValues 教师转正申请书
pvs, Object bean, String beanName) 注⼊属性值,实际调⽤⽅法. meta最大的坦克
data.inject(bean, beanName, pvs) ,继续debug深⼊.
获取InjectedElement 集合,可以检查到两个注解属性,name age, 遍历属性字段,根据Field类做属性注⼊.从注解value中获取值field.t(bean, value) ⾄此,完成了 属性的注⼊
debug信息查看person: Person(name=mp, age=10, addr=null) 好了,完成梳理.
后记: 本次我初步梳理流程,后⾯会完善各个细节.
2. ⾃定义实现@MyValue注解
2.1 创建@MyValue注解类
package com.mp.aop.fig;
import java.lang.annotation.*;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyValue {
String value() default "";
}
2.1 创建使⽤@MyValue注解的属性的类.