@Configuration @Configuration标注上表明是一个配置类。@Bean标注在方法上,给容器中添加组件,方法名为组件ID,返回类型就是组件类型。返回值就是组件在容器中的实例。
首先创建两个bean,Pet和User
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class Pet { private String name; @Override public String toString () { return "Pet{" + "name='" + name + '\'' + '}' ; } public String getName () { return name; } public void setName (String name) { this .name = name; } public Pet (String name) { this .name = name; } public Pet () { } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class User { private String name; private int age; public String getName () { return name; } public void setName (String name) { this .name = name; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } @Override public String toString () { return "User{" + "name='" + name + '\'' + ", age='" + age + '\'' + '}' ; } public User () { } public User (String name, int age) { this .name = name; this .age = age; } }
接着创建一个config包编写MyConfig类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Configuration public class MyConfig { @Bean public User user01 () { return new User("zs" , 18 ); } @Bean("tomcat") public Pet tomcatPet () { return new Pet("tomcat" ); } }
修改主程序,获取到IOC容器,打印注册的组件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @SpringBootApplication public class SpringbootApplication { public static void main (String[] args) { ConfigurableApplicationContext run = SpringApplication.run(SpringbootApplication.class, args); String[] names = run.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); } } }
这就是JavaConfig配置方式。https://www.kylin.show/3172093075.html
接着我们从IOC容器中获取两个tomcat组件对象,进行比较。
发现是为ture
的,也就是默认是单例的。
我们尝试在IOC容器中获取配置类组件,也是可以获取到的。说明配置类本身也是组件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @SpringBootApplication public class SpringbootApplication { public static void main (String[] args) { ConfigurableApplicationContext run = SpringApplication.run(SpringbootApplication.class, args); String[] names = run.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); } Pet tom01 = run.getBean("tomcat" , Pet.class); Pet tom02 = run.getBean("tomcat" , Pet.class); System.out.println("组件:" + (tom01 == tom02)); MyConfig myConfig = run.getBean(MyConfig.class); System.out.println(myConfig); } }
我们查看@Configuration的源码发现从SpringBoot2.0开始多了一个属性,proxyBeanMethods
默认为ture。
这个属性有什么用呢。我们来用我们获取到的MyConfig对象来调用方法,获取到两个User对象,进行比较就知道了。
也就是说如果@Configuration(proxyBeanMethods=true)
我们获取到的配置类对象是代理对象com.kylin.boot.springboot.config.MyConfig$$EnhancerBySpringCGLIB$$9ec0e404@49b07ee3
当代理对象调用方法。SpringBoot总会检查这个组件是否存在容器,从而保持单实例。
我们把@Configuration(proxyBeanMethods=false)
修改为false,查看效果。
获取到的MyConfig是MyConfig对象而不是代理对象com.kylin.boot.springboot.config.MyConfig@4dd94a58
,多次调用方法也不会在IOC容器检查是否存在。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 @SpringBootApplication public class SpringbootApplication { public static void main (String[] args) { ConfigurableApplicationContext run = SpringApplication.run(SpringbootApplication.class, args); String[] names = run.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); } Pet tom01 = run.getBean("tomcat" , Pet.class); Pet tom02 = run.getBean("tomcat" , Pet.class); System.out.println("组件:" + (tom01 == tom02)); MyConfig myConfig = run.getBean(MyConfig.class); System.out.println(myConfig); User user = myConfig.user01(); User user1 = myConfig.user01(); System.out.println(user == user1); } }
这两种情况又被称为Full,Lite模式。
Full (proxyBeanMethods = true)
Lite (proxyBeanMethods = false)
两种使用场景如下。修改User类代码,添加Pet属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 public class User { private String name; private int age; private Pet pet; public User (String name, int age, Pet pet) { this .name = name; this .age = age; this .pet = pet; } public Pet getPet () { return pet; } public void setPet (Pet pet) { this .pet = pet; } public String getName () { return name; } public void setName (String name) { this .name = name; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } @Override public String toString () { return "User{" + "name='" + name + '\'' + ", age=" + age + ", pet=" + pet + '}' ; } public User () { } public User (String name, int age) { this .name = name; this .age = age; } }
当前为Full模式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Configuration(proxyBeanMethods = true) public class MyConfig { @Bean public User user01 () { User user = new User("zs" , 18 ); user.setPet(tomcatPet()); return user; } @Bean("tomcat") public Pet tomcatPet () { return new Pet("tomcat" ); } }
此时用户的宠物组件,就是IOC容器中注册的宠物组件。
如果当我们修改为lite模式后,则此时用户的宠物组件,就不是IOC容器中注册的宠物组件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 @SpringBootApplication public class SpringbootApplication { public static void main (String[] args) { ConfigurableApplicationContext run = SpringApplication.run(SpringbootApplication.class, args); String[] names = run.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); } Pet tom01 = run.getBean("tomcat" , Pet.class); Pet tom02 = run.getBean("tomcat" , Pet.class); System.out.println("组件:" + (tom01 == tom02)); MyConfig myConfig = run.getBean(MyConfig.class); System.out.println(myConfig); User user = myConfig.user01(); User user1 = myConfig.user01(); System.out.println(user == user1); User user01 = run.getBean("user01" , User.class); Pet tomcat = run.getBean("tomcat" , Pet.class); System.out.println("用户的宠物:" +(user01.getPet()==tomcat)); } }
如果组件之间存在组件依赖则使用Full
模式
组件之间不存在组件依赖则使用Lite
模式,无需要在IOC容器中检查判断是否存在该组件,从而加速容器启动过程。
@Import 快速给容器中导入一个组件。默认组件的名字就是全类名,也可以导入带@Configuration的配置类(4.2 版本之前只可以导入配置类,4.2版本之后 也可以导入普通类)
这里并不是@Import必须要在加@Configuration类上才能使用,而是需要被Spring容器给扫描注册到才行。也就是下图这样也可以。
1 2 3 4 @Import({Pet.class}) @Component public class TestImport {}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @Import({User.class, StaticMarkerBinder.class}) @Configuration(proxyBeanMethods = true) public class MyConfig { @Bean public User user01 () { User user = new User("zs" , 18 ); user.setPet(tomcatPet()); return user; } @Bean("tomcat") public Pet tomcatPet () { return new Pet("tomcat" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 @SpringBootApplication public class SpringbootApplication { public static void main (String[] args) { ConfigurableApplicationContext run = SpringApplication.run(SpringbootApplication.class, args); String[] names = run.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); } Pet tom01 = run.getBean("tomcat" , Pet.class); Pet tom02 = run.getBean("tomcat" , Pet.class); System.out.println("组件:" + (tom01 == tom02)); MyConfig myConfig = run.getBean(MyConfig.class); System.out.println(myConfig); User user = myConfig.user01(); User user1 = myConfig.user01(); System.out.println(user == user1); User user01 = run.getBean("user01" , User.class); Pet tomcat = run.getBean("tomcat" , Pet.class); System.out.println("用户的宠物:" + (user01.getPet() == tomcat)); String[] beanNamesForType = run.getBeanNamesForType(User.class); System.out.println("-----" ); for (String s : beanNamesForType) { System.out.println(s); } String[] beanNamesForType1 = run.getBeanNamesForType(Pet.class); System.out.println("-----" ); for (String s : beanNamesForType1) { System.out.println(s); } System.out.println("-----" ); StaticMarkerBinder bean = run.getBean(StaticMarkerBinder.class); System.out.println(bean); } }
@Conditional 条件装配:满足Conditional指定的条件,则进行组件注入
@Conditional拥有许多派生注解,每个注解都有用不同的功能。
@Conditional扩展注解
作用(判断是否满足当前指定条件)
@ConditionalOnJava
系统的java版本是否符合要求
@ConditionalOnBean
容器中存在指定Bean
@ConditionalOnMissingBean
容器中不存在指定Bean
@ConditionalOnExpression
满足SpEl表达式指定
@ConditionalOnClass
系统中有指定的类
@ConditionalOnMissingClass
系统中没有指定的类
@ConditionalOnSingleCandidate
容器中只有一个指定的Bean,或者这个Bean式首选的Bean
@ConditionalOnProperty
系统中指定的属性是否有指定的值
@ConditionalOnResource
类路径下是否存在指定资源文件
@ConditionalOnWebApplication
当前是web环境
@ConditionalOnNotWebApplication
当前不是web环境
@ConditionalOnJndi
JNDI存在指定项
我们首先把Pet组件不注册到IOC容器中,准备测试环境。
此时容器中注册了user01组件,而没有注册tomcat组件。
因为容器中不存在tomcat组件,条件不成立,所以未注册user01组件。
我们也可以把该注解放在配置类上,就变成了只要该条件不成立,该配置类就不会生效。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 @Import({User.class, StaticMarkerBinder.class}) @Configuration(proxyBeanMethods = true) @ConditionalOnBean(name = "tomcat") public class MyConfig { @Bean public User user01 () { User user = new User("zs" , 18 ); user.setPet(tomcatPet()); return user; } @Bean("tomcat") public Pet tomcatPet () { return new Pet("tomcat" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @SpringBootApplication public class SpringbootApplication { public static void main (String[] args) { ConfigurableApplicationContext run = SpringApplication.run(SpringbootApplication.class, args); String[] names = run.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); } boolean tomcat = run.containsBean("tomcat" ); System.out.println("容器中tomcat组件:" + tomcat); boolean user01 = run.containsBean("user01" ); System.out.println("容器中user01组件:" + user01); } }
@ImportResource 引入原生配置文件,来配置容器。
例如上图就是我们以前使用配置文件导入两个ID为haha,hehe的组件到IOC容器中。但是我们可以知道这种方式在SpringBoot中是不支持的。
所以并没有注册成功。
但是我们可以使用@ImportResource注解来引入原生配置文件,来配置容器。
这样就方便了我们老项目向SpringBoot迁移了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd" > <bean id ="haha" class ="com.kylin.boot.springboot.bean.User" > <property name ="name" value ="zhangsan" > </property > <property name ="age" value ="18" > </property > </bean > <bean id ="hehe" class ="com.kylin.boot.springboot.bean.Pet" > <property name ="name" value ="tomcat" > </property > </bean > </beans >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 @Import({User.class, StaticMarkerBinder.class}) @Configuration(proxyBeanMethods = true) @ImportResource("classpath:bean.xml") public class MyConfig { @Bean public User user01 () { User user = new User("zs" , 18 ); user.setPet(tomcatPet()); return user; } @Bean("tomcat") public Pet tomcatPet () { return new Pet("tomcat" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @SpringBootApplication public class SpringbootApplication { public static void main (String[] args) { ConfigurableApplicationContext run = SpringApplication.run(SpringbootApplication.class, args); String[] names = run.getBeanDefinitionNames(); for (String name : names) { System.out.println(name); } boolean tomcat = run.containsBean("tomcat" ); System.out.println("容器中tomcat组件:" + tomcat); boolean user01 = run.containsBean("user01" ); System.out.println("容器中user01组件:" + user01); boolean haha = run.containsBean("haha" ); System.out.println("容器中haha组件:" + haha); boolean hehe = run.containsBean("hehe" ); System.out.println("容器中hehe组件:" + hehe); } }
@ConfigurationProperties 配置文件属性与Java Bean属性绑定,称为配置绑定。
编写一个Controller进行测试,查看是否绑定成功。
绑定成功。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @Component @ConfigurationProperties(prefix = "mycar") public class Car { private String brand; private Integer price; public Integer getPrice () { return price; } public void setPrice (Integer price) { this .price = price; } public String getBrand () { return brand; } public void setBrand (String brand) { this .brand = brand; } @Override public String toString () { return "Car{" + "brand='" + brand + '\'' + ", price=" + price + '}' ; } }
1 2 mycar.brand =BYD mycar.price =99999
1 2 3 4 5 6 7 8 9 10 11 @RestController public class HelloController { @Autowired Car car; @GetMapping("/car") public Car car () { return car; } }
如果想要编写自定类绑定的配置提示有提示可以导入
1 2 3 4 5 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-configuration-processor</artifactId > <optional > true</optional > </dependency >
我们也可以设置Maven打包时将这个jar包排除掉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <project > <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > <configuration > <excludes > <exclude > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-configuration-processor</artifactId > </exclude > </excludes > </configuration > </plugin > </plugins > </build > </project > S
@EnableConfigurationProperties 通过@EnableConfigurationProperties + @ConfigurationProperties来实现属性绑定。
开启配置绑定功能
把这个组件自动注册到容器中
绑定成功!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 @Import({User.class, StaticMarkerBinder.class}) @Configuration(proxyBeanMethods = true) @ImportResource("classpath:bean.xml") @EnableConfigurationProperties(Car.class) public class MyConfig { @Bean public User user01 () { User user = new User("zs" , 18 ); user.setPet(tomcatPet()); return user; } @Bean("tomcat") public Pet tomcatPet () { return new Pet("tomcat" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @ConfigurationProperties(prefix = "mycar") public class Car { private String brand; private Integer price; public Integer getPrice () { return price; } public void setPrice (Integer price) { this .price = price; } public String getBrand () { return brand; } public void setBrand (String brand) { this .brand = brand; } @Override public String toString () { return "Car{" + "brand='" + brand + '\'' + ", price=" + price + '}' ; } }
声明 以上除了@Configuration注解。其他的注解并不是必须要配合@Configuration注解。而是SpringBoot扫描包路径是能扫描的到该类(也就是注册该组件)这些注解才能生效,所以我们也可以使用@Componet
。
例如并不是@Configuration+@ImportResource,@ImportResource才会生效。