文章目录
- 一、xml方式注册Bean
- 二、@Configuration+@Bean注册Bean
- 三、@ComponentScan注册Bean
- 1. 使用XML文件配置包扫描
- 2. 使用注解配置包扫描
- 3. ComponentScans源码
- 4. ComponentScan源码
- 5. @ComponentScan + value + includeFilters
- 6. @ComponentScan + value + excludeFilters
- 7. @ComponentScan + 自定义过滤规则
- 四、@Scope设置Bean的作用域
- 五、@Lazy懒加载Bean
- 六、@Conditional按条件注册Bean
- 七、@Import注册Bean
- 1. @Import快速地导入组件,bean的id默认是组件的全类名
- 2. 自定义实现ImportSelector接口的类:自定义逻辑,导入组件全类名方式注册bean
- 3. 自定义实现ImportBeanDefinitionRegistrar接口的类:自定义逻辑,手动注册bean
- 4. 主从配置类中有相同的bean(例如:myBean()),会先注入@Import(OtherConfig.class)中的myBean(),后注入本配置类中的myBean(),后注入的会覆盖先注入的,所以最终注入的是Bean1。
- 5. 实现DeferredImportSelector接口的类可以被延迟注入
- 八、实现FactoryBean接口注册Bean
一、xml方式注册Bean
<!-- 注册Bean -->
<bean id="person" class="com.meimeixia.bean.Person"><property name="age" value="18"></property><property name="name" value="lwk"></property>
</bean>
public static void main(String[] args) {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");Person person = (Person) applicationContext.getBean("person");System.out.println(person);
}
二、@Configuration+@Bean注册Bean
/*** 以前配置文件的方式被替换成了配置类,即配置类==配置文件**/
// 这个配置类也是一个组件
@Configuration // 告诉Spring这是一个配置类
public class MainConfig {// @Bean注解是给IOC容器中注册一个bean,类型自然就是返回值的类型,id默认是用方法名作为id@Beanpublic Person person() {return new Person("lwk", 20);}
}
public static void main(String[] args) {ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);Person person = applicationContext.getBean(Person.class);System.out.println(person);// Person这个类型的组件在IOC容器中的名字是什么呢?String[] namesForType = applicationContext.getBeanNamesForType(Person.class);for (String name : namesForType) {System.out.println(name);}
三、@ComponentScan注册Bean
1. 使用XML文件配置包扫描
<!-- 包扫描:只要指定的包及其子包中标注了@Controller、@Service、@Repository、@Component这四个注解中的任何一个的组件,它就会被自动扫描,并加入容器中 -->
<!--当使用includeFilters()方法来指定只包含哪些注解标注的类时,需要禁用掉默认的过滤规则。use-default-filters="false"-->
<context:component-scan base-package="com.lwk" use-default-filters="false"></context:component-scan>
2. 使用注解配置包扫描
/*** 以前配置文件的方式被替换成了配置类,即配置类==配置文件* @author liayun**/
// 这个配置类也是一个组件
@ComponentScan(value="com.lwk") // value指定要扫描的包
@Configuration // 告诉Spring这是一个配置类
public class MainConfig {// @Bean注解是给IOC容器中注册一个bean,类型自然就是返回值的类型,id默认是用方法名作为id@Bean("person")public Person person01() {return new Person("liayun", 20);}}
3. ComponentScans源码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface ComponentScans {// 在ComponentScans注解类的内部只声明了一个返回ComponentScan[]数组的value()方法ComponentScan[] value();
}
4. ComponentScan源码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class) // 这在Java 8中是一个重复注解,可以在一个类上重复使用@ComponentScan这个注解
public @interface ComponentScan {@AliasFor("value")String[] basePackages() default {};Filter[] includeFilters() default {};Filter[] excludeFilters() default {};@Retention(RetentionPolicy.RUNTIME)@Target({})@interface Filter {FilterType type() default FilterType.ANNOTATION;}
}FilterType有5种类型:
public enum FilterType {ANNOTATION, // 注解ASSIGNABLE_TYPE, // 类ASPECTJ, // 切面REGEX, // 正则表达式CUSTOM // 自定义规则
}
5. @ComponentScan + value + includeFilters
value指定要扫描的包;
当使用includeFilters()方法来指定只包含哪些注解标注的类时,需要禁用掉默认的过滤规则use-default-filters=false;
结果信息中会一同输出Spring内部的组件名称;
@ComponentScan(value="com.lwk", includeFilters={
// @Filter(type=FilterType.ANNOTATION, classes={Controller.class}),
// // 按照给定的类型,只包含BookService类(接口)或其子类(实现类或子接口)的组件
// @Filter(type=FilterType.ASSIGNABLE_TYPE, classes={BookService.class})// 自定义过滤规则:MyTypeFilter类返回true或者false来代表匹配还是没匹配@Filter(type=FilterType.CUSTOM, classes={MyTypeFilter.class})
}, useDefaultFilters=false)
@Configuration
public class MainConfig {@Bean("person1")public Person person() {return new Person("liayun", 20);}
}
// 使用includeFilters()方法时,打印结果信息中会一同输出Spring内部的组件名称
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig
person1
6. @ComponentScan + value + excludeFilters
@ComponentScan(value="com.lwk", excludeFilters={// 除了@Controller和@Service标注的组件之外,该包中剩下的组件都会注册@Filter(type= FilterType.ANNOTATION, classes={Controller.class, Service.class})
})
7. @ComponentScan + 自定义过滤规则
/*** FilterType.CUSTOM:按照自定义规则进行包含或者排除* 如果实现自定义规则进行过滤时,自定义规则的类必须是org.springframework.core.type.filter.TypeFilter接口的实现类。*/
public class MyTypeFilter implements TypeFilter {/*** 当实现TypeFilter接口时,需要实现该接口中的match()方法,match()方法的返回值为boolean类型。* 当返回true时,表示符合规则,会包含在Spring容器中;* 当返回false时,表示不符合规则,不会被包含在Spring容器中;* 参数:* metadataReader:读取到的当前正在扫描的类的信息* metadataReaderFactory:可以获取到其他任何类信息的工厂*/@Overridepublic boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {// 获取当前类注解的信息AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();// 获取当前正在扫描类的类信息(类的类型,类实现的接口。。。)ClassMetadata classMetadata = metadataReader.getClassMetadata();// 获取当前类的资源信息(类的路径。。。)Resource resource = metadataReader.getResource();// 获取当前正在扫描类的类名String className = classMetadata.getClassName();System.out.println("--->" + className);// 自定义规则if (className.contains("service")) {return true; // 匹配成功,就会被包含在容器中}return false; // 匹配不成功,所有的bean都会被排除(除了Spring内置的bean的名称之外)}
}
四、@Scope设置Bean的作用域
@Scope("prototype") // 通过@Scope注解来指定该bean的作用范围
@Bean("person2")
public Person person() {return new Person("lwk", 25);
}
singleton 默认是单例的,IOC容器起动后立即将bean加载到容器中,以后每次获取直接从容器中获取;容器关闭时被销毁。
prototype 延迟创建bean,不会加入到容器中,每次用到都会重新创建bean;销毁时需要手动调用BeanFactory子类中的destroy()销毁方法。
request 在同一请求中创建一个bean;
session 在同一session中创建一个bean;
五、@Lazy懒加载Bean
@Lazy // 懒加载注解是针对单例bean使用的,IOC容器启动时,不创建bean,延迟到第一次使用时再创建bean
@Bean("person2")
public Person person() {return new Person("lwk", 25);
}
六、@Conditional按条件注册Bean
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {Class<? extends Condition>[] value();
}
1.@Conditional可以标注在方法、类上。
2.可以传一个数组,参数是实现了Conditional接口的实现类,可以在实现类中定义一些规则,满足条件的bean才能注册到容器中。
@Configuration
@Conditional({WindowsCondition.class}) // 类中组件统一设置。满足当前条件,这个类中配置的所有bean注册才能生效
public class MainConfig2 {/*** @Conditional({Condition}) : 按照一定的条件进行判断,满足条件给容器中注册bean* * 如果系统是windows,给容器中注册("bill")* 如果是linux系统,给容器中注册("linus")*/@Bean("bill")public Person person01(){return new Person("Bill Gates",62);}@Conditional(LinuxCondition.class)@Bean("linus")public Person person02(){return new Person("linus", 48);}
}
自定义实现Condition接口的条件类
public class LinuxCondition implements Condition {/*** ConditionContext:判断条件能使用的上下文(环境)* AnnotatedTypeMetadata:当前标注了@Conditional注解的注释信息*/@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {// 1. 获取IOC容器使用的BeanFactoryConfigurableListableBeanFactory beanFactory = context.getBeanFactory();System.out.println("beanFactory:" + beanFactory);// 2. 获取类加载器ClassLoader classLoader = context.getClassLoader();System.out.println("classLoader:" + classLoader);// 3. 获取当前环境信息Environment environment = context.getEnvironment();// 4. 获取到bean定义的注册类/*** Spring容器中所有的bean都可以通过BeanDefinitionRegistry对象来进行注册,* 因此可以通过它来查看Spring容器中注册了哪些bean。* BeanDefinitionRegistry接口中声明的各个方法:* registerBeanDefinition(注册bean)、removeBeanDefinition(移除bean)、getBeanDefinition(获取bean定义信息)、containsBeanDefinition(是否包含某个bean)。。。*/BeanDefinitionRegistry registry = context.getRegistry();System.out.println("registry:" + registry);// 在这还可以做更多的判断,比如判断一下Spring容器中是不是包含有某一个beanboolean definition = registry.containsBeanDefinition("person");// 动态获取环境变量的值:Windows 10String property = environment.getProperty("os.name");if(property.contains("linux")){return true;}return false;}
}
七、@Import注册Bean
@Import注解的用法:
1.@Import({参数:本项目中的类Color.class,或者 其他配置类otherConfig.class}) bean的id默认是组件的全类名,例如:Color.class bean的id是 com.lwk.bean.Color
2.@Import({参数:自定义实现ImportSelector接口的类:MyImportSelector.class}) 导入组件全类名方式注册bean
3.@Import({参数:自定义实现ImportBeanDefinitionRegistrar接口的类:MyImportBeanDefinitionRegistrar.class}) 手动注册bean
MyImportSelector、MyImportBeanDefinitionRegistrar接口自身不会注册到容器中
4.实现DeferredImportSelector接口的类可以被延迟注入(DeferredImportSelector是ImportSelector的子接口,也可以被@Import导入)
1. @Import快速地导入组件,bean的id默认是组件的全类名
@Configuration
@Import({Color.class, Red.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
public class ImportConfig {}
2. 自定义实现ImportSelector接口的类:自定义逻辑,导入组件全类名方式注册bean
public class MyImportSelector implements ImportSelector {// AnnotationMetadata:当前标注@Import注解类的所有注解信息,也就是说不仅能获取到@Import注解里面的信息,还能获取到其他注解的信息@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {// MyImportSelector类的selectImports()方法里面就不能返回一个null值,否则会报空指针异常
// return null;return new String[]{"com.lwk.bean.Blue", "com.lwk.bean.Yellow"};}
}
3. 自定义实现ImportBeanDefinitionRegistrar接口的类:自定义逻辑,手动注册bean
/*** Spring官方在动态注册bean时,大部分套路其实是使用ImportBeanDefinitionRegistrar接口。* 所有实现了该接口的类都会被ConfigurationClassPostProcessor处理,ConfigurationClassPostProcessor实现了BeanFactoryPostProcessor接口。*/
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {/*** AnnotationMetadata:当前标注了@Import注解所在类的所有注解信息* BeanDefinitionRegistry:BeanDefinition注册类* 把所有需要添加到容器中的bean;调用BeanDefinitionRegistry.registerBeanDefinition手动注册进来*/@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {boolean definition = registry.containsBeanDefinition("com.lwk.bean.Red");if (definition) {// 自定义逻辑:可以指定bean的定义信息,包括bean的类型、作用域等等// RootBeanDefinition是BeanDefinition接口的一个实现类RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class); // 手动注册一个bean,并且自定义beanName为 "rainBow"registry.registerBeanDefinition("rainBow", beanDefinition);}}
}
4. 主从配置类中有相同的bean(例如:myBean()),会先注入@Import(OtherConfig.class)中的myBean(),后注入本配置类中的myBean(),后注入的会覆盖先注入的,所以最终注入的是Bean1。
@Configuration
@Import(OtherConfig.class)
public class MyConfig { // 主配置 - 程序员编写的@Beanpublic MyBean myBean() {return new Bean1();}
}@Configuration
static class OtherConfig { // 从属配置 - 自动配置@Beanpublic MyBean myBean() {return new Bean2();}
}
5. 实现DeferredImportSelector接口的类可以被延迟注入
MySelector实现了DeferredImportSelector接口,所以MySelector引用的OtherConfig.class类中的myBean()会延迟注入,也就是会先注入主配置类MyConfig中的Bean1(),
后注入OtherConfig中的Bean2()时,会通过@ConditionalOnMissingBean注解判断是否没有注入过myBean(),由于注入过myBean(),所以此时Bean2()不再注入。@Configuration
@Import(MySelector.class)
public class MyConfig { // 主配置 - 程序员编写的@Beanpublic MyBean myBean() {return new Bean1();}
}
public class MySelector implements DeferredImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {return new String[]{OtherConfig.class.getName()};}
}
@Configuration
public class OtherConfig { // 从属配置 - 自动配置@Bean@ConditionalOnMissingBeanpublic MyBean myBean() {return new Bean2();}
}
八、实现FactoryBean接口注册Bean
1.从容器中获取FactoryBean类型的bean时,默认获取到的是 自定义实现FactoryBean接口的类中,getObject()创建的bean。Object bean1 = applicationContext.getBean("colorFactoryBean");System.out.println("bean的类型:" + bean1.getClass());bean的类型:class com.lwk.bean.Color
2.要获取自定义实现FactoryBean接口的类本身,需要通过 & + bean的id 获取。Object bean2 = applicationContext.getBean("&colorFactoryBean");System.out.println("bean的类型:" + bean2.getClass());bean的类型:class com.lwk.bean.ColorFactoryBean@Configuration
public class ImportConfig {@Beanpublic ColorFactoryBean colorFactoryBean() {return new ColorFactoryBean();}
}// 自定义实现FactoryBean接口的类:创建一个bean,并注册到容器中
public class ColorFactoryBean implements FactoryBean<Color> {@Overridepublic Color getObject() throws Exception {System.out.println("ColorFactoryBean...getObject...");return new Color();}@Overridepublic Class<?> getObjectType() {// bean的类型return Color.class; }// 是单例?// true:这个bean是单实例,在容器中保存一份// false:多实例,每次获取都会创建一个新的bean;@Overridepublic boolean isSingleton() {return true;}
}