@Configuration
用来声明一个配置类,然后使用 @Bean
注解,用于声明一个bean,将其加入到Spring容器中。这种方式是我们最常用的一种
@Configuration
public class MyConfiguration {
@Bean
public Person person() {
Person person = new Person();
person.setName("spring");
return person;
}
}
@Componet
中文译为组件,放在类名上面,然后@ComponentScan
放置在我们的配置类上,然后可以指定一个路径,进行扫描带有@Componet
注解的bean,然后加至容器中。这种方式也较为常用,spring扫描包路径就是使用这种方式,这样可以一下子扫描很多个bean到容器
// 该类在com.shawn.*包下面
@Component
public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
//*代表该包下匹配的所有包和类
@ComponentScan(basePackages = "com.shawn.*")
public class Demo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo.class);
Person bean = applicationContext.getBean(Person.class);
//结果输出Person{name='null'}
System.out.println(bean);
}
}
@Import
注解用到的并不是很多,但是非常重要,在进行Spring扩展时经常会用到。它通过搭配自定义注解进行使用,然后往容器中导入一个配置文件。它有四种使用方式。
@Import
注解的源码,表示只能放置在类上
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
* 用于导入一个class文件
* {@link Configuration @Configuration}, {@link ImportSelector},
* {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
*/
Class<?>[] value();
}
直接使用@Import
导入了一个类,然后自动的就被放置在IOC容器中了。注意我们的Person类上 就不需要任何的注解了,直接导入即可
public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
/**
* 直接使用@Import导入person类,然后尝试从applicationContext中取,成功拿到
**/
@Import(Person.class)
public class Demo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
}
}
自定义了一个MyImportSelector
实现了 ImportSelector
接口,重写selectImports
方法,然后将我们要导入的类的全限定名写在里面即可导入
@Import(MyImportSelector.class)
public class Demo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
}
}
class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//这里需要具体到类名
return new String[]{"com.shawn.Person"};
}
}
这种方式需要实现 ImportBeanDefinitionRegistrar
接口中的方法。BeanDefinition可以简单理解为bean的定义(bean的元数据),也是需要放在IOC容器中进行管理的,先有bean的元数据,applicationContext再根据bean的元数据去创建Bean。
@Import(MyImportBeanDefinitionRegistrar.class)
public class Demo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
}
}
class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 构建一个beanDefinition
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Person.class).getBeanDefinition();
// 将beanDefinition注册到Ioc容器中
registry.registerBeanDefinition("person", beanDefinition);
}
}
这种方式也需要我们进行实现接口,其实它和@Import
的第二种方式差不多,DeferredImportSelector
它是 ImportSelector
的子接口,所以实现的方法和第二种无异。只是Spring的处理方式不同,它和Spring Boot中的自动导入配置文件延迟导入有关,非常重要
@Import(MyDeferredImportSelector.class)
public class Demo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
}
}
class MyDeferredImportSelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 也是直接将Person的全限定名放进去
return new String[]{Person.class.getName()};
}
}
上述三类还可以搭配@Configuration注解使用,用于导入一个配置类
FactoryBean接口和BeanFactory不一样,BeanFactory
顾名思义 bean工厂,它是IOC容器的顶级接口。
下述代码通过@Configuration + @Bean
的方式将 PersonFactoryBean
加入到容器中,注意,我没有向容器中注入 Person
, 而是直接注入的 PersonFactoryBean
然后从容器中拿Person
这个类型的bean
,成功运行。
@Configuration
public class Demo {
@Bean
public PersonFactoryBean personFactoryBean() {
return new PersonFactoryBean();
}
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
}
}
class PersonFactoryBean implements FactoryBean<Person> {
/**
* 直接new出来Person进行返回.
*/
@Override
public Person getObject() throws Exception {
return new Person();
}
/**
* 指定返回bean的类型.
*/
@Override
public Class<?> getObjectType() {
return Person.class;
}
}
这种方式也是利用到了 BeanDefinitionRegistry
,在Spring容器启动的时候会执行 BeanDefinitionRegistryPostProcessor
的 postProcessBeanDefinitionRegistry
方法,大概意思就是等beanDefinition
加载完毕之后,对beanDefinition
进行后置处理,可以在此进行调整IOC容器中的beanDefinition
,从而干扰到后面进行初始化bean。
下述代码中我们手动向beanDefinitionRegistry中注册了person的BeanDefinition,最终成功将person加入到applicationContext中
public class Demo {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
MyBeanDefinitionRegistryPostProcessor beanDefinitionRegistryPostProcessor = new MyBeanDefinitionRegistryPostProcessor();
applicationContext.addBeanFactoryPostProcessor(beanDefinitionRegistryPostProcessor);
applicationContext.refresh();
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
}
}
class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Person.class).getBeanDefinition();
registry.registerBeanDefinition("person", beanDefinition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
## 自定义属性
myProperty:
name: shawn
introduce: 这是我自定义属性介绍
licences: shawn,helen
infos: "{'phone':'36xx102','address':'xx省xx市'}"
获取从yml定义的数据
// 注意读取的话要加入Spring容器才行
@RestController
@RequestMapping("/source")
public class SourceAction {
@Value("${myProperty.name}")
private String name;
@Value("${myProperty.introduce}:匿名用户")
private String introduce;
@Value("${myProperty.licences}")
private List<String> licences;
@Value("#{${myProperty.infos}}")
private Map<String, String> infos;
@RequestMapping("/show")
public Object show() {
Map<String, Object> map = new LinkedHashMap();
map.put("name", name);
map.put("introduce", introduce);
map.put("licences", licences);
map.put("infos", infos);
return map;
}
}
${} 与 #{}的区别
// 注入String
@Value("${populate.string2:}") // 默认值是空字符串""
private String stringV;
@Value("${populate.string:null}") // 默认值是null
private String stringV2;
@Value("${populate.string:defaultValue}") // 默认值是"defaultValue"
private String stringV3;
//注入Array
@Value("${populate.array:}") // 默认值是[]
private String[] array;
//注入List
@Value("${populate.list:}") // 默认值是空List,[]
private List<String> list0;
@Value("#{'${populate.list:}'.split(',')}") // 默认值是包含一个空字符串的List [""]
private List<String> list1;
@Value("${populate.list:l1,l2,l3}") // 默认值是[l1,l2,l3]
private List<String> list2;
@Value("#{'${populate.list:l1,l2,l3}'.split(',')}") // 默认值是[l1,l2,l3]
private List<String> list3;
@Value("#{'${populate.list:,}'.split(',')}") // 默认值是空List,[]
private List<String> list4;
//注入Map
@Value("#{${populate.map:{}}}") // 默认值是null
private Map<String,String> map;
@Value("#{${populate.map:null}}}") // 默认值是null
private Map<String, String> map2;
@Value("#{${populate.map:{k1:'v1',k2:'v2'}}}") // 默认值是{"k1":"v1","k2":"v2"}
private Map<String, String> map3;
@Value("#{${populate.mapList:{}}}") // 值为{"key1":["v11","v12"],"key2":["v21","v22"],"key3":["v31","v32"]}
private Map<String,List<String>> mapList;
yml配置文件,注意@Value注解获取会报错
## 自定义属性
my-property:
name: shawn
introduce: 我的自定义属性
age:
- 18
- 19
shopInfo:
phone: 36xx102
address: xx省xx市
licences: 上市许可证
pets:
- pet:
name: 金毛
price: 3365.21
- pet:
name: 巴哥
price: 2136.10
Spring进行获取
@Data
public class Pet {
private String name;
private double price;
}
@Data
public class PetShopInfo {
private String phone;
private String address;
private List<String> licences;
}
/**
* ConfigurationProperties不支持驼峰
* 通知配置文件的属性命名和bean要一致
*/
@Data
@Component
@ConfigurationProperties(prefix = "my-property")
public class PetShop {
private String name;
private String introduce;
private List<Integer> age;
private PetShopInfo shopInfo;
private List<Pet> pets;
}
@RestController
@RequestMapping("/source")
public class SourceAction {
@Autowired
private PetShop petShop;
@RequestMapping("/show")
public Object show() {
return petShop;
}
}
在resource目录下新建petshop/petshop.properties
文件,将application.yml中的属性转换为properties中的key-value格式,也可以是xxx.ini等
@Data
@Component
@PropertySource(value = "classpath:petshop/petshop.properties", encoding = "UTF-8")
@ConfigurationProperties(prefix = "my-property")
public class PetShop {
private String name;
private String introduce;
private List<Integer> age;
private PetShopInfo shopInfo;
private List<Pet> pets;
}
参考文章
https://blog.csdn.net/weixin_43741092/article/details/120176466
https://mp.weixin.qq.com/s/NpTNVGqU4dQwwgec3X4ibg
https://blog.csdn.net/qmqm011/article/details/118678495