首页 > 其他分享 >Spring Bean 详解

Spring Bean 详解

时间:2022-08-25 21:55:11浏览次数:172  
标签:Lazy candidate Spring Bean 详解 public 加载

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 声明周期

标签:Lazy,candidate,Spring,Bean,详解,public,加载
From: https://www.cnblogs.com/look-word/p/16625875.html

相关文章

  • spring boot中的缓存管理
    springboot默认的缓存管理常用的缓存注解@EnableCaching:在类上使用,表示使用基于注解的方式进行缓存管理@Cacheable:用在类或者方法上。该注解用在方法上时,在方法执行......
  • 10.Java中Map的entrySet() 详解以及用法
    一、Map.entry是什么?Map是java中的接口,Map.Entry是Map的一个内部接口。此接口为泛型,定义为Entry<K,V>。它表示Map中的一个实体(一个key-value对)接口中有get......
  • springboot集成slf4j配置日志
    slf4j简介slf4j是对所有日志框架制定的一种规范、标准、接口,而不是一个框架的具体实现。springboot集成slf4j的简单示例springboot内部已经集成slf4j。@SpringBootAppl......
  • springboot项目的部署
    方式1:打包成jar包pom.xml中指定项目的打包方式<!--可省略--><packaging>jar</packaging>使用maven进行打包将jar包放到任意一个目录下,执行如下命令nohup命......
  • SpringBoot - 文件上传原理
    文件上传原理来个例子客户端<formrole="form"th:action="@{/upload}"method="post"enctype="multipart/form-data"><divclass="form-group"><label......
  • Centos7安装Jenkins详解;
    1.什么是Jenkins?jerkins官方的描述作为领先的开源自动化服务器,Jenkins提供了数百个插件来支持构建、部署和自动化任何项目。jenkins是一个开源提供友好界面的持续集成工......
  • bean实例化三种方式
    实例化bean的方式有三种,如下:1、无参构造方法实例化2、工厂静态方法实例化3、工厂普通方法实例化此处演示的项目结构如下:  方法一:无参构造方法实例化(注意,该类......
  • Spring Boot 运行的时候提示日志错误
    提示的错误信息为:Causedby:java.lang.IllegalArgumentException:LoggerFactoryisnotaLogbackLoggerContextbutLogbackisontheclasspath.EitherremoveLog......
  • Spring(一)- 初始 + DI+scope
    1、获取bean实例的三种方式1.1id属性1.1.1jar<properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><spring.version>4.3.1......
  • Spring 项目启动错误提示 LoggingApplicationListener
    启动Spring项目的时候提示下面的错误信息:Exceptioninthread"main"java.lang.IllegalArgumentException:Cannotinstantiateinterfaceorg.springframework.contex......