自动配置
bean 的加载方式
-
spring 的 xml 配置文件模板
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> ... </beans>
1. xml + <bean/>
-
使用方法
方法一:我们可以在 xml 配置文件中通过
<bean id="beanId" class="classPath" />来将classPath路径的类放入 spring 容器当中。方法二:我们也可以通过
<bean class="classPath"/>将类放入 spring 容器当中。这种做法会将小驼峰写法的类名作为 id。方法三:我们可以通过方法一将第三方类放入 spring 容器当中,例如 druid、tomcat。
<!--xml方式创建 bean 方式一 -->
<bean id="beanID" class="classPath"/>
<!--自动生成id-->
<bean class="classPath"/>
<!--xml方式声明第三方的类-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"/>
2. xml:context + 注解
-
使用方法
添加对应的约束
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 http://www.springframework.org/schema/context/spring-context.xsd",然后通过<context:component-scan base-package="packageName"/>指定开启包路径下的注解。对应路径下的类就可以通过@Component注解注入 spring 容器当中。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--使用组件扫描指定加载 bean 所在的包路径-->
<context:component-scan base-package="packageName"/>
</beans>
@Component("beanID")
public class Test {
}
3. 配置类 + 扫描 + 注解
在我们使用 @Configuration的时候,会把当前的配置类放入 spring 容器当中。当我们注册配置类的时候,默认将配置类放入 spring 容器当中。
-
通过
@Configuration将指定的类放入 spring 容器当中。通过
@Configuration将当前类设置为配置类并放入 spring 容器里;使用@Bean创建要放入 spring 容器中的类的方法,返回类型是要放入的类。这个方法可以代替 xml 配置文件。也可以用来放入第三方的 bean。
@Configuration public class ConfigurationClass { @Bean public DruidDataSource dataSource() { DruidDataSource ds = new DruidDataSource(); return ds; } } -
通过
@ImportResource将其他配置类或 xml 配置文件导入到当前配置类统一管理当我们接手的项目是别人的 jar 包或原代码无法修改的情况下,我们如何通过配置类将原代码的某些类放入 spring 容器当中去,通过 @ImportResource 将其他的配置类或配置文件导入到当前配置类里面去。这样子我们就可以通过当前的配置类去管理其他配置类,从而达到整合的目的了。
@ImportResource({"OtherConfigurationClass.class","applicationContext.xml"}) public class ConfigurationClass { /* * 当我们接手的项目是别人的 jar 包或原代码无法修改的情况下,我们如何通过配置类将原代码的某些类放入 spring 容器当中去 * 我们通过 @ImportResource 将其他的配置类或配置文件导入到当前配置类里面去。 * 我们就可以通过当前的配置类去管理其他配置类,从而达到整合的目的了。 * */ } -
通过
@Configuration(proxyBeanMethods = false)将配置类设置为非代理模式(不使用单列模式)当
proxyBeanMethods = false的时候 ,通过注册配置类获取其中的 spring 容器的 bean 每一个都是新建的。@Configuration(proxyBeanMethods = false) public class ConfigurationClass { @Bean public Cat cat() { return new Cat(); } }
4. @Import 导入 bean 的类
当我们在面对一些不需要过多的配置,仅仅只是为了能够通过自动转配获取的类的时候,可以通过 @Import将对应的类放入 spring 容器中。
@Configuration
@Import(OterClass.class)
public class ConfigurationClass {
}
5. AnnotationCofigApplicationContext 调用 registerBean方法
当上下文容器已经初始化完毕,我们可以通过手动将 bean 注入 spring 容器中,指定 id、class 和使用有参构造方法来完成。但是我们必须是使用AnnotationConfigApplicationContext来完成。注意的是,如果要在 bean 里添加参数请使用有参构造的方法。
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConfigurationClass.class);
// 上下文容器已经初始化完毕,手工加载 bean,指定 id,class 和使用有参构造方法来完成。
context.registerBean("beanID", bean.class, Object... constructorArgs);
6. @Import 导入 ImportSelector 接口
我们通过实现 ImportSelector 接口来创建 bean。这个实现类是一个选择器,通过对配置文件的条件决定装载的bean
package com.hyz.bean.pojo;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import java.util.Map;
/**
* @author workplace
* @date 2022/5/12 15:10
*/
public class MyImportSelector implements ImportSelector {
/*
* 实现了 ImportSelector 接口的实现类,可以通过各种条件的判定,判定完毕后,决定是否装载指定的bean
* 可以实现动态加载 bean
* */
@Override
public String[] selectImports(AnnotationMetadata metadata) {
// 下面的数组填入要加载到 spring 容器中的类的去路径
System.out.println("---------------------------");
// 查看当前作用类的类名
System.out.println("当前选择器作用的类名:" + metadata.getClassName());
// 查看当前作用类是否有使用 annotationName 的注解
System.out.println("当前作用类是否用使用 @Configuration 注解:" + metadata.hasAnnotation("org.springframework.context.annotation.Configuration"));
// 获取注解的属性
Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes("org.springframework.context.annotation.ComponentScan");
System.out.println(annotationAttributes);
System.out.println("---------------------------");
// 如果被装载的 bean 有 @Configuration 注解
boolean b = metadata.hasAnnotation("org.springframework.context.annotation.Configuration");
if (b) {
return new String[]{"com.hyz.bean.pojo.Dog"};
}
return new String[]{"com.hyz.bean.pojo.Cat"};
// return new String[]{"com.hyz.bean.pojo.Dog"};
}
}
当我们完成了实现类之后,可以使用 @Import 来将实现类放入 spring 容器中
@Configuration
@Import({MyImportSelector.class})
public class SpringConfig6 {
}
这样就将实现类里的 bean 放入 spring 容器当中。
这个实现类的最大的作用就是能够通过条件判断选择创建什么 bean。
7. @Import 导入 ImportBeanDefinitionRegistrar 接口
我们通过实现 ImportBeanDefinitionRegistrar 接口来现有的 bean 覆盖,进而达成不修改源代码的情况下更换实现的效果。
public class MyRegistrar implements ImportBeanDefinitionRegistrar {
/*
* 导入实现了 ImportBeanDefinitionRegistrar 接口的类,通过 BeanDefinition 的注册器注册使命 bean
* 实现对容器中 bean 的裁定,例如对现有 bean 的覆盖,进而达成不修改源代码的情况下更换实现的效果。
* */
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 可以使用使用元数据去做判定,和 MyImportSelector 一样
// 使用 bean定义 来选择什么类放入 bean 中。
BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Dog.class).getBeanDefinition();
// 设置 beanID 为 “狗”。
registry.registerBeanDefinition("狗", beanDefinition);
}
}
和 ImportSelect 的步骤一样,在实现类完成之后,通过在配置类上使用@Imorp来将其注入 spring 容器当中。
@Configuration
@Import({MyRegistrar.class})
public class SpringConfig7 {
}
这个实现类的最大作用就是重写,而且也能够实现选择对应的 bean 注入到 spring 容器中的效果。可以说覆盖了一点 ImportSelect 的功能。
8. @Import 导入 BeanDefinitionRegistryPostProcessor 接口
public class MyPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
// 通过 rootBeanDefinition 来选择最终要注入的 bean 的类
BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl5.class).getBeanDefinition();
// 通过 registerBeanDefinition 来选择注入 bean 的 beanID 是什么
beanDefinitionRegistry.registerBeanDefinition("bookService", beanDefinition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
在实现类完成之后,通过在配置类上使用@Imorp来将其注入 spring 容器当中。当 @Import 中的 spring 容器里有的 bean 有冲突,全部按照实现类的 bean 来注入。
@Import({BookServiceImpl1.class, MyRegistrar2.class, MyRegistrar3.class, MyPostProcessor.class})
public class SpringConfig8 {
/*
* 通过 Import 将类放入 spring 容器中
* 相同级别的按照顺序覆盖:前被后覆盖。
*
* */
}
这个实现方法最大的作用就是对容器中的 bean 的最终裁定。无论如何都是根据 eanDefinitionRegistryPostProcessor 的实现结果来完成。就是说,他也包含了一部分的 ImportBeanDefinitionRegistrar 的功能。
总结
- xml 里使用
<bean id = "beanID" calss = "classPath"来将对应的类放入 spring 容器当中去。 - xml 里添加约束,开启对应包路径下的注解功能
<context:component-scan base-package="packageName"/>,在对应的类上添加@Component("beanId")``@Service("beanId")``@Controller("beanId")``@Repository("beanId")将其添加到 spring 容器当中。 - 通过配置类将类注入到 spring 容器当中。在配置类上添加
@Comfiguration,创建一个返回值为对应类的方法并添加@Bean注解。 - 通过在配置类上添加
@Import({"className.class","className.class"})将多个类变作 bean 注入到 spring 容器当中。 - 当容器已经初始话完毕之后,通过
AnnotationConfigApplicationContext的registerBean("beanID", bean.class, Object... constructorArgs)方法可以将 bean 注入到 spring 容器当中。 - 通过 ImportSelector 实现类来选择创建哪一个类的 bean。配置类通过
@Import导入 ImportSelector 接口实现类的 bean 放入 spring 容器当中。 - 通过
ImportBeanDefinitionRegistrar实现类的方法将已存在的 bean 覆盖,不存在的 bean 创建,然后通过配置类将 bean 放入 spring 容器当中。 - 通过
BeanDefinitionRegistryPostProcessor实现类的方法对 bean 进行裁定,它是目前位置我知道的最高权限的!!然后通过配置类将实现方法修改过/创建的 bean 放入 spring 容器当中。 - 当我们通过
new AnnotationConfigApplicationContext(configuration.class);来将配置类获取上下文的时候,默认将配置类放入 spring 容器当中。所以当我们所有的操作都集中在一个配置类上的时候,那个配置类可以不添加任何的注解也能正常使用。