文章目录
- 一、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
@Bean
public 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:可以获取到其他任何类信息的工厂
*/
@Override
public 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注解的注释信息
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 1. 获取IOC容器使用的BeanFactory
ConfigurableListableBeanFactory 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容器中是不是包含有某一个bean
boolean definition = registry.containsBeanDefinition("person");
// 动态获取环境变量的值:Windows 10
String 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注解里面的信息,还能获取到其他注解的信息
@Override
public 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手动注册进来
*/
@Override
public 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 { // 主配置 - 程序员编写的
@Bean
public MyBean myBean() {
return new Bean1();
}
}
@Configuration
static class OtherConfig { // 从属配置 - 自动配置
@Bean
public 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 { // 主配置 - 程序员编写的
@Bean
public MyBean myBean() {
return new Bean1();
}
}
public class MySelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{OtherConfig.class.getName()};
}
}
@Configuration
public class OtherConfig { // 从属配置 - 自动配置
@Bean
@ConditionalOnMissingBean
public 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 {
@Bean
public ColorFactoryBean colorFactoryBean() {
return new ColorFactoryBean();
}
}
// 自定义实现FactoryBean接口的类:创建一个bean,并注册到容器中
public class ColorFactoryBean implements FactoryBean<Color> {
@Override
public Color getObject() throws Exception {
System.out.println("ColorFactoryBean...getObject...");
return new Color();
}
@Override
public Class<?> getObjectType() {
// bean的类型
return Color.class;
}
// 是单例?
// true:这个bean是单实例,在容器中保存一份
// false:多实例,每次获取都会创建一个新的bean;
@Override
public boolean isSingleton() {
return true;
}
}