共计 8461 个字符,预计需要花费 22 分钟才能阅读完成。
将 Bean 交给 Spring 容器治理的几种形式
Spring 外围
Spring
外围是 IOC 和 AOP。
所谓 IoC
,对于spring
框架来说,就是由 spring
来负责管制对象的生命周期和对象间的关系。
至于更具体的阐明,或者去深刻了解 Spring
这两大外围,不是此篇文章的目标,暂不细说了。
咱们在 Spring
我的项目中,咱们须要将 Bean
交给 Spring
容器,也就是 IOC 治理,这样你才能够应用注解来进行依赖注入。
包扫描 + 组件注解
针对类是咱们本人编写的状况
这种形式是咱们日常开发中最罕用到的 spring
将扫描门路下带有 @Component
、@Controller
、@Service
、@Repository
注解的类增加到 spring IOC
容器中。
如果你应用过 MybatisPlus,那这个就和他的包扫描注入一样。
那咱们这个 ComponentScan
注解,又有三个配置。
配置项一
basePackages
用于定义扫描的包的门路。
@ComponentScan(basePackages = "com.timemail.bootmp")
比方这样,就是扫描 com.timemail.bootmp
整个包下的,带有以上指定注解的类,并放入IOC
。
我在别的文章上找了一个残缺的示例:
@Component
public class Person {
private String name;
public String getName() {return name;}
public void setName(String name) {this.name = name;}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
@ComponentScan(basePackages = "com.springboot.initbean.*")
public class Demo1 {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
}
}
// 后果
Person{name='null'}
这就阐明下面代码的 Person
类曾经被 IOC 容器所治理了。
配置项二
includeFilters
蕴含规定
Filter 注解 用 FilterType.CUSTOM 能够自定义扫描规定 须要实现 TypeFilter 接口实现 match 办法 其中参数 MetadataReader 以后类的信息(注解,类门路,类原信息…)MetadataReaderFactory MetadataReader 的工厂类。
配置项三
excludeFilters
移除规定
同蕴含规定。
这后两个配置项我没怎么用过,也不太熟悉,所以具体应用请自行查阅相干材料。
@Configuration
+ @Bean
@Configuration
+ @Bean
也是咱们罕用的一种放入容器的形式。
@Configuration
用于申明配置类
@Bean
用于申明一个Bean
@Configuration
public class Demo {
@Bean
public Person person() {Person person = new Person();
person.setAge(10);
return person;
}
}
就像这样。
那咱们指晓得,在 SSM
外面,通常咱们会在 xml 外面去配置bean
。
@Configuration
public class ConfigBean {}
那咱们这个 @Configuration
注解,就相当于一个 Bean
的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-4.3.xsd">
</beans>
Bean 注解中的属性
咱们 @Bean
注解还有许多属性能够配置。
咱们能够查看其源码:
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) //@1
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {@AliasFor("name")
String[] value() default {};
@AliasFor("value")
String[] name() default {};
@Deprecated
Autowire autowire() default Autowire.NO;
boolean autowireCandidate() default true;
String initMethod() default "";
String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;}
- value 和 name 是一样的,设置的时候,这 2 个参数只能选一个,起因是 @AliasFor 导致的
- value:字符串数组,第一个值作为 bean 的名称,其余值作为 bean 的别名
- autowire:这个参数下面标注了 @Deprecated,示意曾经过期了,不倡议应用了
- autowireCandidate:是否作为其余对象注入时候的候选 bean。
- initMethod:bean 初始化的办法,这个和生命周期无关,当前详解
- destroyMethod:bean 销毁的办法,也是和生命周期相干的,当前详解
扩大
被 @Configuration
润饰的类,spring
容器中会通过 cglib
给这个类创立一个代理,代理会拦挡所有被 @Bean
润饰的办法,默认状况(bean
为单例)下确保这些办法只被调用一次,从而确保这些 bean
是同一个bean
,即单例的。
@Import 注解导入
先看源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
* 用于导入一个 class 文件
* {@link Configuration @Configuration}, {@link ImportSelector},
* {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
*/
Class<?>[] value();
}
@Import
只能用于类注解。
这里我间接搬运公众号:小哈学 Java 的内容吧。
他的解说十分具体。
前两种形式,大家用的可能比拟多,也是平时开发中必须要晓得的,@Import 注解用的可能不是特地多了,然而也是十分重要的,在进行 Spring 扩大时常常会用到,它常常搭配自定义注解进行应用,而后往容器中导入一个配置文件。
对于 @Import 注解,我会多介绍一点,它有四种应用形式。这是 @Import 注解的源码,示意只能搁置在类上。
@Import 间接导入类
public class Person {
private String name;
public String getName() {return name;}
public void setName(String name) {this.name = name;}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
/**
* 间接应用 @Import 导入 person 类,而后尝试从 applicationContext 中取,胜利拿到
**/
@Import(Person.class)
public class Demo1 {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
}
}
上述代码间接应用 @Import
导入了一个类,而后主动的就被搁置在 IOC
容器中了。
留神:咱们的
Person
类上 就不须要任何的注解了,间接导入即可。
@Import + ImportSelector
其实在 @Import
注解的源码中,说的曾经很分明了,感兴趣的能够看下,咱们实现一个 ImportSelector
的接口,而后实现其中的办法,进行导入。
@Import(MyImportSelector.class)
public class Demo1 {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
}
}
class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {return new String[]{"com.springboot.pojo.Person"};
}
}
我自定义了一个 MyImportSelector
实现了 ImportSelector
接口,重写 selectImports
办法,而后将咱们要导入的类的全限定名写在外面即可,实现起来也是非常简单。
@Import + ImportBeanDefinitionRegistrar
这种形式也须要咱们实现 ImportBeanDefinitionRegistrar
接口中的办法,具体代码如下:
@Import(MyImportBeanDefinitionRegistrar.class)
public class Demo1 {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
}
}
class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 构建一个 beanDefinition, 对于 beanDefinition 我后续会介绍,能够简略了解为 bean 的定义.
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Person.class).getBeanDefinition();
// 将 beanDefinition 注册到 Ioc 容器中.
registry.registerBeanDefinition("person", beanDefinition);
}
}
上述实现其实和 Import
的第二种形式差不多,都须要去实现接口,而后进行导入。接触到了一个新的概念,BeanDefinition
,能够简略了解为 bean
的定义 (bean
的元数据),也是须要放在 IOC 容器中进行治理的,先有 bean
的元数据,applicationContext
再依据 bean
的元数据去创立Bean
。
@Import + DeferredImportSelector
这种形式也须要咱们进行实现接口,其实它和 @Import
的第二种形式差不多,DeferredImportSelector
它是 ImportSelector
的子接口,所以实现的办法和第二种无异。只是 Spring
的解决形式不同,它和 Spring Boot
中的主动导入配置文件 提早导入无关,十分重要。应用形式如下:
@Import(MyDeferredImportSelector.class)
public class Demo1 {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
}
}
class MyDeferredImportSelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 也是间接将 Person 的全限定名放进去
return new String[]{Person.class.getName()};
}
}
对于 @Import
注解的应用形式,大略就以上三种,当然它还能够搭配 @Configuration
注解应用,用于导入一个配置类。
FactoryBean 接口
说到 FactoryBean
,咱们很多入门的开发者很容易将他与BeanFactory
搞混。
BeanFactory
他是所有 Spring Bean 的容器根接口,给 Spring 的容器定义一套标准,给 IOC 容器提供了一套残缺的标准,比方咱们罕用到的 getBean 办法等。也就是咱们常说的
Bean
的工厂。
而咱们的 FactoryBean
,它实际上就是一个Bean
,Factory
是他的名字,顾名思义嘛。
@Configuration
public class Demo1 {
@Bean
public PersonFactoryBean personFactoryBean() {return new PersonFactoryBean();
}
public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
}
}
class PersonFactoryBean implements FactoryBean<Person> {
@Override
public Person getObject() throws Exception {return new Person();
}
@Override
public Class<?> getObjectType() {return Person.class;}
}
这里,咱们能够先看看 FactoryBean
中的办法:
他是一个接口类。
那咱们就须要有一个类来继承这个接口,并且重写办法。
这里,咱们将须要注册的 Bean
的类,放到 FactoryBean
的泛型中。
getObject
办法用于间接返回创立的对象。
getObjectType
间接返回类的class
。
而后实际上,还是要应用 @Bean
注解,将继承接口的类对象返回。
而后 Configuration
注解,将此类改为 springboot
的配置类,相当于 springmvc
中的 xml
文件。
咱们能够通过 AnnotationConfigApplicationContext
的getBean
办法,来看看是否被 IOC
治理。
运行后,能够看到,对象地址被输入了。
阐明胜利了。
应用 BeanDefinitionRegistryPostProcessor
写这篇文章时,我也是查阅了网上很多大佬的材料。
有一种我不相熟的办法。
于是 ……
开始原文照搬 ….
其实这种形式也是利用到了
BeanDefinitionRegistry
,在 Spring 容器启动的时候会执行BeanDefinitionRegistryPostProcessor
的postProcessBeanDefinitionRegistry ()
办法,大略意思就是等 beanDefinition 加载结束之后,对 beanDefinition 进行后置解决,能够在此进行调整 IOC 容器中的 beanDefinition,从而烦扰到前面进行初始化 bean。
public class Demo1 {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
MyBeanDefinitionRegistryPostProcessor beanDefinitionRegistryPostProcessor = new MyBeanDefinitionRegistryPostProcessor();
applicationContext.addBeanFactoryPostProcessor(beanDefinitionRegistryPostProcessor);
applicationContext.refresh();
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
}
}
class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Person.class).getBeanDefinition();
registry.registerBeanDefinition("person", beanDefinition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}}
上述代码中,咱们手动向 beanDefinitionRegistry 中注册了 person 的BeanDefinition
,最终胜利将 person 退出到 applicationContext 中。
讲完了,下次再见。