《框架的艺术》系列|写了两年代码之后再来看看Spring中的Bean
(一)什么是Bean Spring中的Bean简单来讲就是一个个被Spring容器管理的Java对象,我们写了一个类之后,这个类只是一个单纯的Java类,可以通过new的方式去创建它。当我们把这个类添加到Spring的容器里之后,这个类就变成了Bean,由Spring容器管理,可以通过自动注入的方式去使用。
(二)如何往Spring容器中添加Bean 这里列出四种常用的添加Bean的方式。
1、@Bean: 写一个普通的类时最常用的添加Bean的方式
【《框架的艺术》系列|写了两年代码之后再来看看Spring中的Bean】2、@ComponentScan + @Controller @Service @Component @Repository:SpringBoot写多了之后一定会很熟悉这些。
3、@Import:通过导入的方式注入Bean
4、@ImportBeanDefinitionRegister:和Import类似,可以指定Bean的名称
(三)Bean的作用域 首先介绍最基本的@Bean注解,@Bean注解声明这个类是一个Bean,在Spring5之前,大部分的声明都会放到配置文件里,Spring5之后通过两个注解就可以完成。以Teacher类为例
public class Teacher {}@Configuration
public class MainConfig {@Bean
public Teacher teacher(){return new Teacher();
}
}
在不指定@Scope的情况下,所有bean的实例都是单实例的bean,并且是饿汉式加载(容器启动时就创建好了)。可以通过注解@Lazy实现懒加载(在调用时被加载)。
@Bean
//@Lazy
public User user(){return new User();
}
指定@Scope为prototype表示为多实例,并且是懒汉式加载(使用时才会创建)
@Bean
@Scope(value = "https://www.it610.com/article/prototype")
public User user(){return new User();
}
列出其他的几种Bean作用域:
singleton单例(默认)
prototype多实例
request同一次请求
session同一个会话级别
(四)Bean的常用注解 有几个注解经常会和@Bean一起使用
4.1 Conditional Conditional注解的意思是条件,即满足条件的情况下才会生效
比如我在Bean中配置了Conditional:
@Bean
@Conditional(value = https://www.it610.com/article/TeacherCondition.class)
public Teacher teacher(){return new Teacher();
}
TeacherCondition 代码如下:如果Spring的Bean中有名字为student的,则返回true,否则返回false
public class TeacherCondition implements Condition {public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {if (conditionContext.getBeanFactory().containsBean("student")){return true;
}
return false;
}
}
最后的结果就是,如果TeacherCondition返回的是true,则teacher这个bean会被注册到容器中,否则就不会注册到容器中。
4.2 ComponentScan 这个注解会和Controller、Service等同时出现,给一个类添加Controller、Service等注解后,需要在配置类中增加ComponentScan,ComponentScan扫描到的包下的Controller、Service等注解才会生效:
@Configuration
//最基本的扫描路径方式
//@ComponentScan(basePackages = {"com.javayz.testcompentscan"})
//增加了Filter的方式
@ComponentScan(basePackages = {
"com.javayz.testcompentscan"},includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = https://www.it610.com/article/{
Controller.class, Service.class}),
@ComponentScan.Filter(type = FilterType.CUSTOM,value = {
TestFilterType.class})
},useDefaultFilters = false)
public class MainConfig {@Bean
@Scope(value ="https://www.it610.com/article/prototype")
public User user(){return new User();
}
}
Filter是在扫描时的过滤器,比如设置FilterType.ANNOTATION表示只有这里设置的注解才会被扫描到,FilterType.CUSTOM是自定义过滤器,TestFilterType 类进行了一层判断:包名为dao下的类会被注册到Bean容器中
public class TestFilterType implements TypeFilter {public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {//获取当前类的class源信息
ClassMetadata classMetadata = https://www.it610.com/article/metadataReader.getClassMetadata();
if (classMetadata.getClassName().contains("dao")){return true;
}
return false;
}
}
4.3 @Import @Import可以用来往容器中导入第三方的组件,也可以起到和@Bean一样的作用:
@Configuration
//@Import(value = https://www.it610.com/article/{Teacher.class, Student.class})
//@Import(value = {MyImportSelector.class})
@Import(value = {
MyBeanDefinitionRegister.class})
public class MainConfig {}
第一种方式直接导入对应的类,这里和直接写@Bean效果一致
@Import(value = https://www.it610.com/article/{
Teacher.class, Student.class})
第二种方式导入ImportSelector对象,通过selectImports方法返回要导入Bean的全限定名:
public class MyImportSelector implements ImportSelector {public String[] selectImports(AnnotationMetadata annotationMetadata) {return new String[]{
"com.javayz.testimport.compent.Teacher"};
}
}
第三种方式通过BeanDefinitionRegister注入Bean(可以指定Bean的名称)
public class MyBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Student.class);
registry.registerBeanDefinition("student",rootBeanDefinition);
}
}
Import注解最常用的场景就是SpringBoot自动注入,在SpringBoot自动注入源码中导出可以看到@Import注解的身影。
(五)Bean的初始化和销毁 当由容器管理Bean的生命周期时,我们可以通过自己指定Bean方法的初始化方法和销毁方法,使得一个Bean在初始化和销毁时能执行自己的方法。
1、自定义初始化方法和销毁方法
public class Teacher {public Teacher(){System.out.println("Teacher 构造方法");
}
public void init(){System.out.println("Teacher 初始化方法");
}
public void destory(){System.out.println("Teacher 销毁方法");
}
}@Configuration
public class MainConfig {@Bean(initMethod = "init",destroyMethod = "destory")
public Teacher teacher(){return new Teacher();
}
}
对于单例bean(singleton)容器启动的时候,bean对象就创建了,在容器销毁的时候,就会去调用Bean的销毁方法。
对于多实例的bean,容器启动的时候bean还未被创建,在获取Bean的时候才会被创建,并且bean的销毁不受IOC容器的管理。
2、通过 InitializingBean, DisposableBean 接口实现 Spring的这两个接口也可以实现初始化和销毁的功能。
public class Student implements InitializingBean, DisposableBean {public Student(){System.out.println("Student 构造方法");
}
@Override
public void destroy() throws Exception {System.out.println("Student销毁");
}
@Override
public void afterPropertiesSet() throws Exception {System.out.println("Student初始化");
}
}
@Configuration
public class MainConfig {@Bean
public Student student(){return new Student();
}
}
3、BeanPostProcessor BeanPostProcessor在所有Bean的初始化前和初始化后都会被调用
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("Bean初始化前");
return bean;
}@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("Bean初始化后");
return bean;
}
}@ComponentScan
@Configuration
public class MainConfig {@Bean(initMethod = "init",destroyMethod = "destory")
public Teacher teacher(){return new Teacher();
}
@Bean
public Student student(){return new Student();
}
}
(六)总结 别看Bean这个概念听起来简单,里面的内容还真不少。Spring的核心之一IOC也就是对Bean进行管理。我是鱼仔,我们下期再见!
推荐阅读
- 慢慢的美丽
- 《真与假的困惑》???|《真与假的困惑》??? ——致良知是一种伟大的力量
- 《跨界歌手》:亲情永远比爱情更有泪点
- 诗歌:|诗歌: 《让我们举起世界杯,干了!》
- 期刊|期刊 | 国内核心期刊之(北大核心)
- 《魔法科高中的劣等生》第26卷(Invasion篇)发售
- 人间词话的智慧
- 《一代诗人》37期,生活,江南j,拨动心潭的一泓秋水
- 广角叙述|广角叙述 展众生群像——试析鲁迅《示众》的展示艺术
- 书评——《小行星》