资讯专栏INFORMATION COLUMN

写了两年代码之后再来看看Spring中的Bean

PrototypeZ / 1019人阅读

摘要:一什么是中的简单来讲就是一个个被容器管理的对象,我们写了一个类之后,这个类只是一个单纯的类,可以通过的方式去创建它。以类为例在不指定的情况下,所有的实例都是单实例的,并且是饿汉式加载容器启动时就创建好了。

(一)什么是Bean

Spring中的Bean简单来讲就是一个个被Spring容器管理的Java对象,我们写了一个类之后,这个类只是一个单纯的Java类,可以通过new的方式去创建它。当我们把这个类添加到Spring的容器里之后,这个类就变成了Bean,由Spring容器管理,可以通过自动注入的方式去使用。

(二)如何往Spring容器中添加Bean

这里列出四种常用的添加Bean的方式。

1、@Bean: 写一个普通的类时最常用的添加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 {}@Configurationpublic class MainConfig {    @Bean    public Teacher teacher(){        return new Teacher();    }}

在不指定@Scope的情况下,所有bean的实例都是单实例的bean,并且是饿汉式加载(容器启动时就创建好了)。可以通过注解@Lazy实现懒加载(在调用时被加载)。

@Bean//@Lazypublic User user(){    return new User();}

指定@Scope为prototype表示为多实例,并且是懒汉式加载(使用时才会创建)

@Bean@Scope(value = "prototype")public User user(){    return new User();}

列出其他的几种Bean作用域:

singleton  单例(默认)prototype  多实例request  同一次请求session  同一个会话级别

(四)Bean的常用注解

有几个注解经常会和@Bean一起使用

4.1 Conditional

Conditional注解的意思是条件,即满足条件的情况下才会生效
比如我在Bean中配置了Conditional:

@Bean@Conditional(value = 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 = {Controller.class, Service.class}),        @ComponentScan.Filter(type = FilterType.CUSTOM,value = {TestFilterType.class})},useDefaultFilters = false)public class MainConfig {    @Bean    @Scope(value = "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 = metadataReader.getClassMetadata();        if (classMetadata.getClassName().contains("dao")){            return true;        }        return false;    }}

4.3 @Import

@Import可以用来往容器中导入第三方的组件,也可以起到和@Bean一样的作用:

@Configuration//@Import(value = {Teacher.class, Student.class})//@Import(value = {MyImportSelector.class})@Import(value = {MyBeanDefinitionRegister.class})public class MainConfig {}

第一种方式直接导入对应的类,这里和直接写@Bean效果一致

@Import(value = {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 销毁方法");    }}@Configurationpublic 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初始化");    }}@Configurationpublic class MainConfig {    @Bean    public Student student(){        return new Student();    }}

3、BeanPostProcessor

BeanPostProcessor在所有Bean的初始化前和初始化后都会被调用

@Componentpublic 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@Configurationpublic class MainConfig {    @Bean(initMethod = "init",destroyMethod = "destory")    public Teacher teacher(){        return new Teacher();    }    @Bean    public Student student(){        return new Student();    }}

(六)总结

别看Bean这个概念听起来简单,里面的内容还真不少。Spring的核心之一IOC也就是对Bean进行管理。我是鱼仔,我们下期再见!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/122169.html

相关文章

  • Spring AOP从零单排-织入时期源分析

    摘要:何为简单点来定义就是切面,是一种编程范式。定义一个切面的载体定义一个切点定义一个为,并指定对应的切点一个注册配置类,启动容器,初始化时期获取对象,获取对象时期,并进行打印好了,这样我们整体的代理就已经完成。 问题:Spring AOP代理中的运行时期,是在初始化时期织入还是获取对象时期织入? 织入就是代理的过程,指目标对象进行封装转换成代理,实现了代理,就可以运用各种代理的场景模式。 ...

    honmaple 评论0 收藏0
  • Spring AOP 源分析系列文章导读

    摘要:在写完容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了天时间阅读了方面的源码。从今天开始,我将对部分的源码分析系列文章进行更新。全称是,即面向切面的编程,是一种开发理念。在中,切面只是一个概念,并没有一个具体的接口或类与此对应。 1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解。在写完 Spring IOC 容器源码分析系列...

    张春雷 评论0 收藏0
  • 程序语言

    摘要:一面应该还问了其他内容,但是两次面试多线程面试问题和答案采访中,我们通常会遇到两个主题采集问题和多线程面试问题。多线程是关于并发和线程的。我们正在共享重要的多线程面试问题和答案。。 2016 年末,腾讯,百度,华为,搜狗和滴滴面试题汇总 2016 年未,腾讯,百度,华为,搜狗和滴滴面试题汇总 【码农每日一题】Java 内部类(Part 2)相关面试题 关注一下嘛,又不让你背锅!问:Ja...

    mtunique 评论0 收藏0
  • 程序语言

    摘要:一面应该还问了其他内容,但是两次面试多线程面试问题和答案采访中,我们通常会遇到两个主题采集问题和多线程面试问题。多线程是关于并发和线程的。我们正在共享重要的多线程面试问题和答案。。 2016 年末,腾讯,百度,华为,搜狗和滴滴面试题汇总 2016 年未,腾讯,百度,华为,搜狗和滴滴面试题汇总 【码农每日一题】Java 内部类(Part 2)相关面试题 关注一下嘛,又不让你背锅!问:Ja...

    stefan 评论0 收藏0
  • Spring IOC 容器源分析 - 获取单例 bean

    摘要:简介为了写容器源码分析系列的文章,我特地写了一篇容器的导读文章。在做完必要的准备工作后,从本文开始,正式开始进入源码分析的阶段。从缓存中获取单例。返回以上就是和两个方法的分析。 1. 简介 为了写 Spring IOC 容器源码分析系列的文章,我特地写了一篇 Spring IOC 容器的导读文章。在导读一文中,我介绍了 Spring 的一些特性以及阅读 Spring 源码的一些建议。在...

    lufficc 评论0 收藏0

发表评论

0条评论

PrototypeZ

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<