注意:@Import导入的bean的名称是全路径类名,其他注解只是类名(首字母小写)。
注意:AnnotationConfigApplicationContext对象才有注册bean的方法,其他的对象没有。
@Import(MyImportSelector.class)在SpringConfig6上面,所以SpringConfig6类是MyImportSelector类的元数据。
@Configuration
//@ComponentScan(basePackages = "com.itheima")
@Import(MyImportSelector.class)
public class SpringConfig6 {
}
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata metadata) {
System.out.println("================");
System.out.println("提示:"+metadata.getClassName());
System.out.println(metadata.hasAnnotation("org.springframework.context.annotation.Configuration"));
Map attributes = metadata.getAnnotationAttributes("org.springframework.context.annotation.ComponentScan");
System.out.println(attributes);
System.out.println("================");
//各种条件的判定,判定完毕后,决定是否装在指定的bean
boolean flag = metadata.hasAnnotation("org.springframework.context.annotation.Configuration");
if(flag){
return new String[]{"com.itheima.bean.Dog"};
}
return new String[]{"com.itheima.bean.Cat"};
}
}
@Import({BookServiceImpl1.class, MyPostProcessor.class, MyRegistrar2.class, MyRegistrar.class})
public class SpringConfig8 {
}
理解: 等BeanDefinitionRegistry注册完所有bean之后,BeanDefinitionRegistryPostProcessor才会开始执行,如果遇到同名的则会覆盖之前的。
@Import({BookServiceImpl1.class, MyPostProcessor.class, MyRegistrar2.class, MyRegistrar.class})
public class SpringConfig8 {
}
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig3.class);
提示:若创建上下文对象的时候不指定该类(即需要被组件扫描发现是配置类),则需加上@Configuration注解声明该类为配置类。
@ComponentScan({"com.itheima.bean","com.itheima.config"})
public class SpringConfig3 {
@Bean
public DogBean dog(){
return new DogBean();
}
}
public class App3 {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig3.class);
String[] names = ctx.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
System.out.println(ctx.getBean("dog"));
}
}
注意:虽然返回是DogFactoryBean类型,但实际上返回的是泛型的类型。(原理暂不清楚)
public class DogFactoryBean implements FactoryBean {
@Override
public Dog getObject() throws Exception {
Dog d = new Dog();
//.........
return d;
}
@Override
public Class getObjectType() {
return Dog.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
@ComponentScan({"com.itheima.bean","com.itheima.config"})
public class SpringConfig3 {
//该方法与下面的方法只能存在一个。(因为两个方法返回值都是Dog类型)
@Bean
public DogFactoryBean dog(){
return new DogFactoryBean();
}
//这个返回的是Dog类型(Dog是接口),该方法与下面的方法只能存在一个。(因为两个方法返回值都是Dog类型)
// @Bean
// public Dog dog(){
// return new Dog();
// }
}
提示:传递的泛型是Book,即@Bean出来的实际是Book对象。
提示:个人觉得,跟配置文件中注解扫描该配置类,然后上下文对象中指定配置文件是一样的。
ApplicationContext ctx = new ClassPathXmlApplicationContext("SpringConfig2.xml");
@Configuration默认创建的是代理对象,在获取代理对象后,调用该对象里面的方法(有@Bean标记的方法)的时候先去IOC容器中获取返回(即得到的都是同一个对象)。
注意:proxyBeanMethods属性默认是ture,如果是false,则不是代理对象。
注意:@Import导入的bean的名称是全路径类名,其他注解只是类名(首字母小写)。
提示:6、7、8这上使用@Import导入实现接口的类,实际该类不会被注册到容器里面,只会注册接口方法中定义的需要被注册到容器的bean。
提示:能够对bean的加载进行控制的基本都是编程式的。
@Import(MyImportSelector.class)
public class SpringConfig {
}
@ConditionalOnClass(Mouse.class):检测是否存在这个类,其实使用类型没意义,需要使用字符串才不报错。(这是关于项目有没有该类)
@ConditionalOnBean(name = "com.itheima.bean.Mouse"):bean名为mouse(使用@Component注解标记),这也满足。(这是在IOC容器中有没有该对象)
@ConditionalOnBean(name = "jerry"):则名称必须要相同(使用@Component(“jerry”)标记)。
注意:注解需要导入springboot基本依赖。
org.springframework.boot
spring-boot-starter
2.5.4
注意:还可以在类上就行判断是否需要加载为bean:
@Component("tom")
@ConditionalOnBean(name = "jerry")
//@ConditionalOnWebApplication
@ConditionalOnNotWebApplication
public class Cat {
}
总结起来就是:如果配置文件中有值则使用,如果没有则使用默认值。
@EnableConfigurationProperties(CartoonProperties.class)
注解的作用是将 CartoonProperties
类注册为一个 Spring Bean。
@Import(AutoConfigurationPackages.Registrar.class):设置启动类所在的包作为扫描包,后续要针对当前的包进行扫描,如果其他starter也是该包名开头,可能也会扫描。(将启动类所在的包进行添加到xxx对象中,xxx对象被注册为bean) @Import(AutoConfigurationImportSelector.class) :将所有的自动配置类(满足条件的自动配置类)注册为bean
原理:将文件中的自动配置类的全路径名全部加载出来,然后进行判断,如果满足则加载(比如依赖中是否有redis的某个类,如果有则加载redis技术相关配置),不满足则不加载。
该文件中定义了很多技术的类(自动配置类),其实就是下面说的技术集A。
使用自定义自动配置后,不需要使用@Import(CartoonCatAndMouse.class)就能自动配置。
自定义starter:做一个独立项目,有自动配置类,最后在META-INF/spring.factories文件中写上自动配置类,然后执行maven的clean,再执行install(将starter添加到本地仓库)。
下面的模拟调用是导入了该自定义starter的项目,模拟调用上面的都是自定义starter里面的内容,编写好自定义starter后,需要调用maven中的生命周期执行clean,再执行install(安装到本地仓库中,方便其他需要使用该自定义starter的项目在pom中导入该自定义starter,每次修改自定义starter之后都要重写clean+install,确保仓库中是最新版)。
注意:如果主项目基础包是com.itheima,starter的也是com.itheima,那么会出现bean不唯一的问题。(自动配置类-第1次(自动配置类导入了该类),扫描的时候-第2次(扫描com.itheima包的时候扫到了该类)) 。
@Component("ipProperties")
@ConfigurationProperties(prefix = "tools.ip")
@EnableConfigurationProperties(IpProperties.class)
注意:这三个不能一起用,否则@Component("ipProperties")起名字就不起作用,如果使用前面两个注解,那么第三个注解可以换成@Import(IpProperties.class)
自定义starter的spring-configuration-metadata.json文件展示:
{
"groups": [
{
"name": "tools.ip",
"type": "cn.itcast.properties.IpProperties",
"sourceType": "cn.itcast.properties.IpProperties"
}
],
"properties": [
{
"name": "tools.ip.cycle",
"type": "java.lang.Long",
"description": "日志的显示周期",
"sourceType": "cn.itcast.properties.IpProperties",
"defaultValue": 5
},
{
"name": "tools.ip.cycle-reset",
"type": "java.lang.Boolean",
"description": "是否周期内重置数据",
"sourceType": "cn.itcast.properties.IpProperties",
"defaultValue": false
},
{
"name": "tools.ip.model",
"type": "java.lang.String",
"description": "日志输出模式 detail:详细模式 simple:极简模式",
"sourceType": "cn.itcast.properties.IpProperties"
}
],
"hints": [
{
"name": "tools.ip.model",
"value": [
{
"value": "detail",
"description": "详细模式"
},
{
"value": "simple",
"description": "极简模式"
}
]
}
]
}
导入这个坐标之后,执行maven的clean,然后compile编译,然后可以找到target/classer/META-INF/spring-configuration-metadata.json文件,将该文件复制到自定义starter中的resources/META-INF目录下。然后就可以将pom中刚加进入的坐标删掉(否则有重复的提示,因为有两个该json文件)。
为tools.ip.mode属性设置提示信息: