首页 > 编程学习 > Spring Bean 详解

Spring Bean 详解

发布时间:2022/8/25 21:55:53

Spring Bean 详解

Ioc实例化Bean的三种方式

1 创建Bean

1 使用无参构造函数

这也是我们常用的一种。在默认情况下,它会通过反射调⽤⽆参构造函数来创建对象。如果类中没有⽆参构造函数,将创建 失败。

  • class: 为需要注册Bean类文件的位置

applicationContext.xml配置文件

image-20220825154455666

测试类:

/**
 * @author : look-word
 * 2022-08-25 11:36
 **/
public class IocTest {
    @Test
    public void testIoc() {
        ApplicationContext context = // 读取配置文件
                new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        // 容器中根据名称获取Bean
        ConnectionUtils connectionUtils
                = (ConnectionUtils) context.getBean("connectionUtils");
        System.out.println(connectionUtils);
    }
}

结果:

# 输出了对象的地址
com.lagou.edu.utils.ConnectionUtils@3ecd23d9

2 使用静态方法创建

  • 简单来说,就是调用某个类的静态方法创建对象

在实际开发中,我们使⽤的对象有些时候并不是直接通过构造函数就可以创建出来的,它可能在创 建的过程 中会做很多额外的操作。此时会提供⼀个创建对象的⽅法,恰好这个⽅法是static修饰的 ⽅法,即是此种情况:

例如,我们在做Jdbc操作时,会⽤到java.sql.Connection接⼝的实现类,如果是mysql数据库,那 么⽤的就 是JDBC4Connection,但是我们不会去写 JDBC4Connection connection = new JDBC4Connection() ,因为我们要注册驱动,还要提供URL和凭证信息, ⽤ DriverManager.getConnection ⽅法来获取连接。那么在实际开发中,尤其早期的项⽬没有使⽤Spring框架来管理对象的创建,但是在设计时使⽤了 ⼯⼚模式 解耦,那么当接⼊spring之后,⼯⼚类创建对象就具有和上述例⼦相同特征,即可采⽤ 此种⽅式配置。

CreateBeanFactory

/**
 * @author : look-word
 * 2022-08-25 15:50
 **/
public class CreateBeanFactory {
    /**
     * 使⽤静态⽅法创建对象的配置⽅式
     */
    public static ConnectionUtils getInstanceStatic(){
        return new ConnectionUtils();
    }
}

applicationContext.xml配置文件

image-20220825155323252


3 使⽤实例化调用成员⽅法创建

  • 简单来说,就是创建一个类,然后再通过这个类的某个方法创建我们需要的对象。(不推荐,需要创建额外对象)

CreateBeanFactory:

    /**
     * 使⽤成员⽅法创建对象的配置⽅式
     */
    public  ConnectionUtils getInstance(){
        return new ConnectionUtils();
    }

applicationContext.xml配置文件

image-20220825155855227


2 lazy—init 延迟加载

配置方式

xml配置延迟加载:

    <!--
        lazy-init:延迟加载,true代表延迟,false代表立即加载,默认是false 
    -->
    <bean id="lazyResult" class="com.lagou.edu.pojo.Result" lazy-init="true"/>

注解配值延迟加载:

可以配置到许多地方,如类似,方法上等等

@Lazy
@Component
public class XXXX {
    ...
}

加载原理

当使用上述三种配置后,Spring在扫描加载Bean时会读取@Lazy和@Component注解相应值,并设置Bean定义的lazyInit属性。读取注解配置时最终会调用ClassPathBeanDefinitionScanner及其子类实现的doScan方法,在这个方法中完成注解的读取配置。

public class ClassPathBeanDefinitionScanner 
        extends ClassPathScanningCandidateComponentProvider {
    protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
       // 不管是读取注解或者XML配置方式bean,最终读取加载Bean时都会进入到该方法
       // 对相应的包进行处理
       // beanDefinitions是保存返回bean定义的集合
       Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
       // 遍历多个包下的类
       for (String basePackage : basePackages) {
          // 获取满足条件的bean定义集合
          Set<BeanDefinition> candidates = 
                  findCandidateComponents(basePackage);
          // 对每个bean定义进行处理
          for (BeanDefinition candidate : candidates) {
             ScopeMetadata scopeMetadata = this.scopeMetadataResolver
                     .resolveScopeMetadata(candidate);
             candidate.setScope(scopeMetadata.getScopeName());
             String beanName = this.beanNameGenerator
                     .generateBeanName(candidate, this.registry);
             // 这个方法会处理@ComponentScan中的lazyInit值,因为在使用
             // @ComponentScan注解时会首先把该值赋值到beanDefinitionDefaults
             // 默认bean定义值的对象中,在postProcessBeanDefinition方法中
             // 会首先应用一次这些默认值,其中就包括lazyInit、autowireMode等
             if (candidate instanceof AbstractBeanDefinition) {
                postProcessBeanDefinition(
                        (AbstractBeanDefinition) candidate, beanName);
             }
             // 读取@Lazy、@Primary和@DependsOn等注解值
             if (candidate instanceof AnnotatedBeanDefinition) {
                AnnotationConfigUtils
                        .processCommonDefinitionAnnotations(
                                (AnnotatedBeanDefinition) candidate);
             }
             // 如果候选者满足要求则将其注册到Bean定义中心
             if (checkCandidate(beanName, candidate)) {
                BeanDefinitionHolder definitionHolder = 
                        new BeanDefinitionHolder(candidate, beanName);
                definitionHolder = AnnotationConfigUtils
                        .applyScopedProxyMode(scopeMetadata, 
                            definitionHolder, this.registry);
                beanDefinitions.add(definitionHolder);
                // 注册bean定义
                registerBeanDefinition(definitionHolder, this.registry);
             }
          }
       }
       return beanDefinitions;
    }
    protected void postProcessBeanDefinition(
            AbstractBeanDefinition beanDefinition, String beanName) {
       // 此处会应用默认值,如lazyInit、autowireMode、initMethod等
       beanDefinition.applyDefaults(this.beanDefinitionDefaults);
       if (this.autowireCandidatePatterns != null) {
          beanDefinition.setAutowireCandidate(PatternMatchUtils
                  .simpleMatch(this.autowireCandidatePatterns, beanName));
       }
    }
}

经过ClassPathBeanDefinitionScanner或子类实现的扫描读取后,延迟加载的配置便被配置到了Bean定义中,等初始化时再使用该属性,这里需要注意的是@ComponentScan延迟加载属性是可以被@Lazy覆盖的,因为@Lazy是在@ComponentScan后面处理的。

使用细节

Spring框架延迟加载属性在调用getBean之后将会失效,因为getBean方法是初始化bean的入口,这不难理解,那么平时我们使用@Autowired等自动注入注解时能和@Lazy注解一起使用吗?接下来我们从两个实例来说明一下,这两个实例都是使用平时的使用用法,在Component上添加@Lazy注解,且让其实现InitializingBean接口,当Bean被加载时我们便能得知,看其是否会生效,示例如下:

@Lazy失效实例

声明一个Controller控制器:

@Controller
public class TestController implements InitializingBean{
    @Autowired
    private TestService testService;
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("testController Initializing");
    }
}

再声明一个Service服务类:

@Lazy
@Service
public class TestService implements InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("testService Initializing");
    }
}

启动程序后控制台输出:

testService Initializing
testController Initializing

启动完Spring程序后输出了TestService里面打印的字符串。这就奇怪了,明明使用了@Lazy注解,但是却并没有其作用,在Spring启动项目时还是加载了这个类?简单来说,就是在DI注入的时候,获取容器中获取对应的Bean,Autowired按照默认类型获取Resource按照默认名称获取,所以才会导致延迟加载失效问题。

@Lazy有效实例

修改先前的Controller

启动后会发现,延迟加载失效问题解决了。

@Lazy
@Controller
public class TestController implements InitializingBean{
    @Autowired
    private TestService testService;
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("testController Initializing");
    }
}

3 BeanFactory和FactoryBean

  • BeanFactory是接口,提供了OC容器最基本的形式,给具体的IOC容器的实现提供了规范。
  • FactoryBean也是接口,为IOC容器中Bean的实现提供了更加灵活的方式,FactoryBean在IOC容器的基础上给Bean的实现加上了一个简单工厂模式和装饰模式(如果想了解装饰模式参考:修饰者模式(装饰者模式,Decoration) 我们可以在getObject()方法中灵活配置。其实在Spring源码中有很多FactoryBean的实现类.

区别:

BeanFactory是个Factory,也就是IOC容器或对象工厂,FactoryBean是个Bean。在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。但对FactoryBean而言,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似

4 后置处理器

Spring提供了两种后处理bean的扩展接⼝:

  • BeanFactoryPostProcessor
    • 在BeanFactory初始化之后可以使⽤BeanFactoryPostProcessor进⾏后置处理做⼀些事情
  • BeanPostProcessor
    • 在Bean对象实例化(并不是Bean的整个⽣命周期完成)之后可以使⽤BeanPostProcessor进⾏后置处 理做⼀些事情

springBean 声明周期

image-20220825214412887

springBean 声明周期

Copyright © 2010-2022 mfbz.cn 版权所有 |关于我们| 联系方式|豫ICP备15888888号