组件注册
一、@Configuration和@Bean注解:
- @Configuration:标注该类是一个Spring的配置类
- @Bean:将方法的返回值注入spring容器中。
代码演示:
1、创建一个person类,该类只有名称和年龄两个简单的属性。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private String userName;
private int age;
}
2、因为是纯注解开发,所以舍弃了spring的配置文件。没有配置文件,如何向容器中注入实例呢?
用 @Configuration注解标注在类上,这类将成为spring的配置类,可以取代于配置文件。原本用 <bean 标签去配置一个实例到容器中,现在可以用@bean注解标注在方法上。
@Configuration
public class MainConfig {
@Bean
public Person person(){
return new Person("张三",18);
}
}
测试:
private ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
@Test
public void test1(){
Person person = context.getBean("person", Person.class);
//按照类型查找容器中bean的名称
String[] names = context.getBeanNamesForType(Person.class);
for (String name : names){
System.out.println(name);
}
System.out.println(person);
}
测试结果:
person
Person(userName=张三, age=18)
说明:默认情况下,使用@bean标注的方法,在容器中唯一标识为方法名,可以利用value或name属性来指定名称。
@Bean(value = "peron01")
public Person person(){
return new Person("张三",18);
}
二、@ComponentScan
@ComponentScan :自动扫描组件。
<!--包扫描-->
<context:component-scan base-package="com.wjh">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
这是配置文件中配置spring扫描规则。
可以用@ComponentScan来代替,把@ComponentScan注解标注在配置类中
@Configuration
@ComponentScan(value = "com.wjh")
public class MainConfig {
@Bean(value = "peron01")
public Person person(){
return new Person("张三",18);
}
}
新建dao、srevice、controller类,并用注解将其加入容器中。
@Repository
public class BookDao {
}
@Service
public class BookService {
}
@Controller
public class BookController {
}
测试:
@Test
public void test2(){
String[] names = context.getBeanDefinitionNames();
for (String name : names){
System.out.println(name);
}
}
测试结果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
bookController
bookDao
bookService
peron01
因为配置了扫描规则,所有容器中有对应的实例。
@ComponentScan的属性:
- String[] value() default {} :指定要扫描的包
- Filter[] includeFilters() default {} :指定要扫描的时候要按照扫描规则去排除哪些组件
- Filter[] excludeFilters() default {} :指定要扫描的时候要按照扫描规则去包含哪些组件
@Configuration
@ComponentScan(value = "com.wjh",excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)
})
public class MainConfig {
@Bean(value = "peron01")
public Person person(){
return new Person("张三",18);
}
}
结果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
bookDao
bookService
peron01
因为配置了排除controller注解,所以标注了Controller注解的类没有加入到容器中。
@Configuration
@ComponentScan(value = "com.wjh",includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)
},useDefaultFilters = false)
public class MainConfig {
@Bean(value = "peron01")
public Person person(){
return new Person("张三",18);
}
}
结果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
bookController
peron01
说明:useDefaultFilters = false,如果不设置,扫描规则为扫描全部。
扩展:
- FilterType.ASSIGNABLE_TYPE :按照给定的类型
@Configuration
@ComponentScan(value = "com.wjh",includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = BookService.class)
},useDefaultFilters = true)
结果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
bookController
bookDao
bookService
peron01
说明:BookService类的实例也都会加载进容器中。
- FilterType.ASPECTJ : 按照ASPECTJ表达式
- FilterType.REGEX : 按照正则表达式
- FilterType.CUSTOM : 自定义规则【重点】
实现方式:
创建一个自定义类实现TypeFilter接口,并在自定义扫描规则。
public class MyTypeFilter implements TypeFilter {
/**
*
* @param metadataReader :读取到当前正在扫描的类的信息
* @param metadataReaderFactory :可以获取其他任何其他类的信息
* @return
* @throws IOException
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
//获取当前类注解的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//获取正在扫描的类的类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
String className = classMetadata.getClassName();
System.out.println("--->"+className);
//获取当前类的资源信息(类的路径)
Resource resource = metadataReader.getResource();
//扫描到的类只有包含Dao的才加入到容器中
if (className.contains("Dao")){
return true;
}
return false;
}
}
主配置:
@Configuration
@ComponentScan(value = "com.wjh",includeFilters = {
// @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class),
// @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = BookService.class)
@ComponentScan.Filter(type = FilterType.CUSTOM,classes = MyTypeFilter.class)
},useDefaultFilters = false)
//FilterType.ASSIGNABLE_TYPE:按照给定的类型
//FilterType.ASPECTJ : 按照ASPECTJ表达式
//FilterType.REGEX : 按照正则表达式
public class MainConfig {
@Bean(value = "peron01")
public Person person(){
return new Person("张三",18);
}
}
结果:
--->com.wjh.PersonTest
--->com.wjh.bean.Person
--->com.wjh.config.MyTypeFilter
--->com.wjh.controller.BookController
--->com.wjh.dao.BookDao
--->com.wjh.service.BookService
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
bookDao
peron01
三、@Scope和@Lazy
如果从容器中获取同一个bean实例,是否为同一个实例?
Person person = context.getBean("person", Person.class);
Person person2 = context.getBean("person", Person.class);
System.out.println(person == person2);
结果为
true
@Scope:bean的作用范围。
- singleton:单实例的(默认)。ioc容器启动会调用方法创建对象放到容器中,以后每次获取就是直接从容器中(map.get())取。
@Configuration
public class MainConfig2 {
@Bean
@Scope(value = "singleton")
public Person person(){
System.out.println("调用方法把person实例放到容器中");
return new Person("张三",18);
}
}
测试:
@Test
public void test3() {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
System.out.println("容器创建完成。。。");
Person person = context.getBean("person", Person.class);
Person person2 = context.getBean("person", Person.class);
System.out.println(person == person2);
}
结果:
调用方法把person实例放到容器中
容器创建完成。。。
true
- prototype:多实例的。ioc容器启动的不会调用方法创建对象放到容器中,而是获取的时候才会调用方法创建放到容器中。
@Bean
@Scope(value = "prototype")
public Person person(){
System.out.println("调用方法把person实例放到容器中");
return new Person("张三",18);
}
测试:
@Test
public void test3() {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
System.out.println("容器创建完成。。。");
Person person = context.getBean("person", Person.class);
Person person2 = context.getBean("person", Person.class);
System.out.println(person == person2);
}
测试结果:
容器创建完成。。。
调用方法把person实例放到容器中
调用方法把person实例放到容器中
false
@Lazy:懒加载
- 单实例bean:会在容器启动的时候创建对象。
- 懒加载:容器启动不创建对象,在第一次使用的时候创建实例,并初始化。
@Configuration
public class MainConfig2 {
@Bean
@Scope(value = "singleton")
@Lazy
public Person person(){
System.out.println("调用方法把person实例放到容器中");
return new Person("张三",18);
}
}
测试:
@Test
public void test3() {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
System.out.println("容器创建完成。。。");
Person person = context.getBean("person", Person.class);
Person person2 = context.getBean("person", Person.class);
System.out.println(person == person2);
}
测试结果:
容器创建完成。。。
调用方法把person实例放到容器中
true
说明:标注**@Lazy**注解后,只有在获取(使用)实例的才会创建加入到容器中。但还是只创建一次。
四、@Conditional按照条件注册bean
场景:如果当前系统环境为windows,注入bill,如果为linux,注入linus。
@Configuration
public class MainConfig2 {
@Bean
@Scope(value = "singleton")
@Lazy
public Person person(){
// System.out.println("调用方法把person实例放到容器中");
return new Person("张三",18);
}
@Bean(value = "bill")
@Conditional(value = {WindowCondition.class})
public Person person01(){
return new Person("Bill Gates",68);
}
@Bean(value = "linus")
@Conditional(value = {LinuxCondition.class})
public Person person02(){
return new Person("Linus",48);
}
}
说明:@Conditional的属性值为实现了Condition接口的类,在该类中过滤条件。
WindowCondition
/判断是否为windows系统环境来注入bean
public class WindowCondition implements Condition {
/**
*
* @param context :判断能使用的上下文(环境)
* @param metadata :注释信息
* @return
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//能获取到ioc使用到的beanfactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//能获取到bean定义的注册类
BeanDefinitionRegistry registry = context.getRegistry();
//能获取到类加载器
ClassLoader classLoader = context.getClassLoader();
//能获取到当前环境
Environment environment = context.getEnvironment();
//判断是否为Windows环境,并且容器中有person实例才通过
String name = environment.getProperty("os.name");
if (name.contains("Windows") && registry.containsBeanDefinition("person")){
return true;
}
return false;
}
}
LinuxCondition
//判断是否为Linux系统环境来注入bean
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//能获取到ioc使用到的beanfactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//能获取到bean定义的注册类
BeanDefinitionRegistry registry = context.getRegistry();
//能获取到类加载器
ClassLoader classLoader = context.getClassLoader();
//能获取到当前环境
Environment environment = context.getEnvironment();
//判断是否为Windows环境,并且容器中有person实例才通过
String name = environment.getProperty("os.name");
if (name.contains("Linux") && registry.containsBeanDefinition("perosn")){
return true;
}
return false;
}
}
测试:
@Test
public void test4() {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
Environment environment = context.getEnvironment();
System.out.println(environment.getProperty("os.name"));
Map<String, Person> beans = context.getBeansOfType(Person.class);
System.out.println(beans);
}
测试结果:
Windows 10
调用方法把person实例放到容器中
{person=Person(userName=张三, age=18), bill=Person(userName=Bill Gates, age=68)}
在虚拟机参数上带上linux环境参数来测试
结果:
linux
{person=Person(userName=张三, age=18), linus=Person(userName=Linus, age=48)}
五、@Import导入组件
总结一下给容器导入组件的方法:
- 包扫描+组件标注解(@Controller、@Service、@Repository、@Compent)【自己定义的组件】
- @Bean【导入的第三方包里面的组件】
- @Import【快速给容器导入一个组件】
1)、@Import(要导入的组件):容器就会自动注册这个组件,id默认为全限定类名。
2)、ImportSelector:返回需要导入的组件的全限定类名数组。
3)、ImportBeanDefinitionRegistrar:手动注册bean到容器中 - 使用spring提供的FactoryBean(工厂bean)。
@Import
案例:用@Import注解给容器中注入Color和Red类。
@Configuration
@Import({Red.class, Color.class})
public class MainConfig2 {
@Bean
@Scope(value = "singleton")
@Lazy
public Person person(){
// System.out.println("调用方法把person实例放到容器中");
return new Person("张三",18);
}
@Bean(value = "bill")
@Conditional(value = {WindowCondition.class})
public Person person01(){
return new Person("Bill Gates",68);
}
@Bean(value = "linus")
@Conditional(value = {LinuxCondition.class})
public Person person02(){
return new Person("Linus",48);
}
}
public class Color {
}
public class Red {
}
测试:
@Test
public void test5() {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
String[] names = context.getBeanDefinitionNames();
for (String name : names){
System.out.println(name);
}
}
测试结果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig2
com.wjh.bean.Red
com.wjh.bean.Color
person
bill
说明:用 @Import注册的组件的bean名称为全限定类名
ImportSelector
实现:自定义一个实现了ImportSelector接口的类,定义需要注入到容器的组件
MyImportSelector
//自定义逻辑返回需要导入的组件
public class MyImportSelector implements ImportSelector {
//返回值就是要导入到容器的组件的类名
//AnnotationMetadata : 当前标注@Import注解的类的所有注解信息
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.wjh.bean.Blue","com.wjh.bean.Yellow"};
}
}
@Configuration
@Import({Red.class, Color.class, MyImportSelector.class})
public class MainConfig2 {
测试结果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig2
com.wjh.bean.Red
com.wjh.bean.Color
com.wjh.bean.Blue
com.wjh.bean.Yellow
person
bill
FactoryBean
实现:自定义一个类,并实现FactoryBean接口,在实现的getObject()中定义要加入到容器的组件。
public class ColorFactoryBean implements FactoryBean<Color> {
@Override
public Color getObject() throws Exception {
return new Color();
}
@Override
public Class<?> getObjectType() {
return Color.class;
}
}
把factoryBean加入到容器中:
@Bean
public ColorFactoryBean factoryBean(){
return new ColorFactoryBean();
}
测试:
@Test
public void test6() {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
String[] names = context.getBeanDefinitionNames();
for (String name : names){
System.out.println(name);
}
Object factoryBean = context.getBean("factoryBean");
System.out.println("bean的类型:"+factoryBean);
}
测试结果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig2
com.wjh.bean.Red
com.wjh.bean.Color
com.wjh.bean.Blue
com.wjh.bean.Yellow
person
bill
factoryBean
rainBow
bean的类型:com.wjh.bean.Color@4b0b0854
说明:容器中会把自定义的factory类和color类加入到组件中,并且自定义的factory类的实例类型为com.wjh.bean.Color@4b0b0854 。如果想要得到真实的factoryBean,需要在根据名称获取bean的时候在名称前加上"&"。
@Test
public void test6() {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig2.class);
String[] names = context.getBeanDefinitionNames();
for (String name : names){
System.out.println(name);
}
Object factoryBean = context.getBean("&factoryBean");
System.out.println("bean的类型:"+factoryBean);
}
结果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig2
com.wjh.bean.Red
com.wjh.bean.Color
com.wjh.bean.Blue
com.wjh.bean.Yellow
person
bill
factoryBean
rainBow
bean的类型:com.wjh.bean.ColorFactoryBean@4b0b0854