springboot揭秘00-基于java配置的spring容器

文章目录

  • 【README】
  • 【1】基本概念:@Configuration与@Bean
  • 【2】使用AnnotationConfigApplicationContext实例化spring容器
    • 【2.1】使用java配置简单构建spring容器
      • 【2.1.1】AnnotationConfigApplicationContext与@Component及JSR-330注解类一起使用
    • 【2.2】使用register(Class<?>)以编程方式构建容器
      • 【2.2.1】AnnotationConfigApplicationContext#refresh()源码
    • 【2.3】启用AnnotationConfigApplicationContext扫描功能
      • 【2.3.1】补充:@Configuration注解
    • 【2.4】支持使用AnnotationConfigWebApplicationContext构建springweb容器
  • 【3】组合基于java的配置
    • 【3.1】使用 @Import注解组合多个配置类
      • 【3.1.1】对@Import引入的BeanDefinition注入依赖(装配@Import注解引入的bean)
      • 【3.1.2】全限定导入bean以便导航
    • 【3.2】以xml配置为中心并使用component-scan元素扫描java配置类(配置spring容器的2种方式)
      • 【3.2.1】代码实践
    • 【3.3】以java配置为中心并使用@ImportResoure注解引入xml配置
      • 【3.3.1】代码实践
    • 【3.4】使用@PropertySource加载属性文件
  • 【4】使用@Bean注解
    • 【4.1】声明@Bean
    • 【4.2】@Bean标注方法返回的bean的装配(注入依赖)
    • 【4.3】接收生命周期回调
      • 【4.3.1】@Bean注册的bean生命周期回调代码实践
    • 【4.4】指定bean作用范围
      • 【4.4.1】使用@Scope注解
      • 【4.4.2】查找方法注入Lookup method injection(仅了解,本文不展开)
      • 【4.4.3】自定义bean的名称
  • 【5】所有@Configuration类使用CGLIB进行子类化
    • 【5.1】java配置类内部运行原理(重要)

【README】

本文内容总结自spring官方文档 spring-framework-reference 章节4.12【Java-based container configuration】, 墙裂推荐;

代码参见: github: springbootDiscover chapter00

1)新增maven依赖:(因为 @Configuration 与 @Bean注解定义在org.springframework.context.annotation包下)

<dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>6.1.10</version>
    </dependency>
  </dependencies>


【1】基本概念:@Configuration与@Bean

1)java配置支持spirng容器的 核心组件是@Configuration标注的类 。这些类主要包含@Bean标注的方法,这些方法定义springIOC容器管理对象的初始化,配置,以及实例化逻辑。

2)使用@Configuration标注的类表明这些类可以被spring IOC容器作为bean定义的数据源。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {
  @Bean
  public MyService myService() {
      return new MyServiceImpl();
  }
}

AppConfig等价于下面的xml配置;

<beans>
  <bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>

3)@Bean的角色: 如上述代码所示, @Bean扮演了 <bean>元素的角色 ; 接下来,我们介绍使用java配置创建spring容器的多种方法;



【2】使用AnnotationConfigApplicationContext实例化spring容器

1)下面章节描述spring的AnnotationConfigApplicationContext,它是spring3引入的;

2)作为ApplicationContext的实现类,AnnotationConfigApplicationContext不仅接收@Configuration类作为输入,也可以接受@Component类,以及被JSR-330注解标注的类; (JSR, Java Specification Request, java规范请求 )

  • JSR-330注解介绍(JSR-330注解,就是支持java实现依赖注入的注解;如 @Inject ,@Qualilfier;),参见spirng 使用JSR-330标准注解 ,github JSR-330: Dependency Injection for Java. , Java Community Process: JSR 330: Dependency Injection for Java

在这里插入图片描述

3)当@Configuration类作为输入,@Configuration类本身会被注册为bean定义,该类中所有被@Bean标注的方法返回的bean也会注册为bean定义

  • 当@Component与JSR-330注解标注的类作为输入,这些类也会被注册为bean定义;并且假定在必要时,这些类中使用依赖注入元注解如@Autowired or @Inject;


【2.1】使用java配置简单构建spring容器

1)与使用spring xml文件作为输入数据源实例化ClassPathXmlApplicationContext容器类似,@Configuration也可以作为输入数据源,实例化AnnotationConfigApplicationContext容器(即注解配置的spring容器);(基于注解实例化容器可以完全不使用xml文件) ;其实 ClassPathXmlApplicationContext 与 AnnotationConfigApplicationContext 是兄弟,它们都是spring容器ApplicationContext的子类,只不过配置信息加载的方式不同,一个是xml文件,一个是注解;

【JavaBasedContainerMain】基于java配置的spring容器构建

public class JavaBasedContainerMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig00.class);
        HelloService helloService = context.getBean(HelloService.class);
        helloService.sayHello("tom");
    }
}

【运行结果】

hello tom

【AppConfig00】 @Configuration类(配置类)

@Configuration
public class AppConfig00 {
    @Bean
    public HelloService helloService() {
        return new HelloService();
    }
}

【HelloService】服务类

public class HelloService {

    public void sayHello(String user) {
        System.out.println("hello " + user);
    }
}


【2.1.1】AnnotationConfigApplicationContext与@Component及JSR-330注解类一起使用

1)AnnotationConfigApplicationContext不局限于与@Configuration类一起使用,任何 @Component或 JSR330注解类也可以作为其构造参数传入。

【JavaBasedContainerUsingJsr330Main】 基于java的使用Jsr-330注解的容器

public class JavaBasedContainerUsingJsr330Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Jsr330Service.class, Jsr330Depository.class);
        context.getBean(Jsr330Service.class).saveUser("zhangsan");
    }
}

注意: 上述代码中, Jsr330Service与Jsr330Depository需要使用spring的依赖注入注解@Autowire进行装配;(如果使用JSR-330注解如@Qualifier,会报错

【Jsr330Service】

package com.tom.chapter00.classwithjsr330;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class Jsr330Service {

    // @Autowired是spring注解 ,这里不能使用 @Qualifier (JSR-330注解)
    @Autowired
    private Jsr330Depository jsr330Depository;

    public void saveUser(String userName) {
        jsr330Depository.saveUser(userName);
    }
}

【Jsr330Depository】

package com.tom.chapter00.classwithjsr330;

import org.springframework.stereotype.Component;

@Component
public class Jsr330Depository {

    public void saveUser(String userName) {
        System.out.println("saveUser() name=" + userName + " 成功");
    }
}


【2.2】使用register(Class<?>)以编程方式构建容器

1)AnnotationConfigApplicationContext可以通过使用无参构造器进行实例化,实例化后使用register方法进行配置 ;当使用编程方式构建容器时,这个方法特别有用;

【SpringContainerUsingAnnotationConfigApplicationContextNoArgConstructor】 使用AnnotationConfigApplicationContext无参构造器实例化注解配置spring容器 ;

package com.tom.chapter00;

import com.tom.chapter00.service.HelloService;
import com.tom.chapter00.service.HelloService02;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class SpringContainerUsingAnnotationConfigApplicationContextNoArgConstructor {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(AppConfig00.class, AppConfig02.class); // 调用register()方法注册配置
        context.refresh(); // 注册完成后,调用 refresh方法刷新 
        context.getBean(HelloService.class).sayHello("Musk");
        context.getBean(HelloService02.class).sayHello("Trump");
    }
}

【步骤】 使用register(Class<?>)以编程方式构建容器步骤 ;

  • 步骤1:首先使用AnnotationConfigApplicationContext无参构造器实例化spring容器;
  • 步骤2:调用register()方法注册配置(查看源码可知,这是在收集BeanDefinition);
  • 步骤3:注册完成后,调用 refresh方法刷新(查看源码可知,根据BeanDefinition实例化bean) ;

【运行效果】

HelloService#sayHell(): hello Musk
HelloService2#sayHell():  hello Trump

【AppConfig00】

@Configuration
public class AppConfig00 {
    @Bean
    public HelloService helloService() {
        return new HelloService();
    }
}

【HelloService】

public class HelloService { 
    public void sayHello(String user) {
        System.out.println("HelloService#sayHell(): hello " + user);
    }
}


【2.2.1】AnnotationConfigApplicationContext#refresh()源码

public void refresh() throws BeansException, IllegalStateException {
        this.startupShutdownLock.lock();

        try {
            this.startupShutdownThread = Thread.currentThread();
            StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
            this.prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);
                StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                beanPostProcess.end();
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (Error | RuntimeException var12) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var12);
                }

                this.destroyBeans();
                this.cancelRefresh(var12);
                throw var12;
            } finally {
                contextRefresh.end();
            }
        } finally {
            this.startupShutdownThread = null;
            this.startupShutdownLock.unlock();
        }

    }


【2.3】启用AnnotationConfigApplicationContext扫描功能

1)使用xml配置扫描注解标注的bean,如下:

<beans>
  <context:component-scan base-package="com.acme"/>
</beans>

上述xml配置,spring将会扫描com.acme包中@Component标注的类,这些类被作为spring beanDefinition在容器中注册;

2)AnnotationConfigApplicationContext公开了scan()方法,支持相同的组件扫描功能

【SpringContainerUsingAnnotationConfigApplicationContextScan】

package com.tom.chapter00;

import com.tom.chapter00.service.HelloService02WithComponentAnnotation;
import com.tom.chapter00.service.HelloServiceWithComponentAnnotation;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class SpringContainerUsingAnnotationConfigApplicationContextScan {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.scan("com.tom.chapter00.service"); // 调用scan()方法收集BeanDefinition
        context.refresh(); // 根据BeanDefinition实例化bean 
        context.getBean(HelloServiceWithComponentAnnotation.class).sayHello("Musk");
        context.getBean(HelloService02WithComponentAnnotation.class).sayHello("Trump");
    }
}

【步骤】 AnnotationConfigApplicationContext使用组件扫描实例化bean:

  • 步骤1:首先使用AnnotationConfigApplicationContext无参构造器实例化注解配置spring容器;
  • 步骤2:调用scan()方法扫描指定包下BeanDefinition(查看源码可知,这是在收集BeanDefinition);
  • 步骤3:注册完成后,调用 refresh方法刷新(查看源码可知,根据BeanDefinition实例化bean) ;

【执行效果】

HelloServiceWithComponentAnnotation#sayHello: hello Musk
HelloService02WithComponentAnnotation#sayHello: 02 hello Trump

【HelloServiceWithComponentAnnotation】

package com.tom.chapter00.service;

import org.springframework.stereotype.Component;

@Component
public class HelloServiceWithComponentAnnotation {

    public void sayHello(String user) {
        System.out.println("HelloServiceWithComponentAnnotation#sayHello: hello " + user);
    }
}


【2.3.1】补充:@Configuration注解

1)@Configuration:使用@Component注解的注解(而@Component称为元注解,因为它是标注其他注解的注解); 因此@Configuration类可以被scan()扫描到;

  • 假设@Configuration类声明在com.a.b.c包下(及任意子包下),AnnotationConfigApplicationContext容器在调用scan()方法时,会扫描到该@Configuration类;而调用refresh()方法时,@Configuration类中被@Bean标注的方法会被处理,把方法返回值注册为容器中的bean;

【SpringContainerUsingAnnotationConfigApplicationContextScanConfiguration】 扫描@Configuration类 (扫描com.tom.chapter00包及其子包下@Component及@Configuration类)

public class SpringContainerUsingAnnotationConfigApplicationContextScanConfiguration {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        System.out.println("before scan()");
        context.scan("com.tom.chapter00");
        System.out.println("after scan()");
        System.out.println("before refresh()");
        context.refresh();
        System.out.println("after refresh()");
        context.getBean(HelloService02.class).sayHello("Musk");
    }
}

【执行效果】

before scan()
after scan()
before refresh()
AppConfig00 构造器
AppConfig02 构造器
after refresh()
HelloService2#sayHell():  hello Musk 

【AppConfig02】 @Configuration类,包名为com.tom.chapter00.config,是com.tom.chapter00的子包

package com.tom.chapter00.config;

import com.tom.chapter00.service.HelloService02;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig02 {

    public AppConfig02() {
        System.out.println("AppConfig02 构造器");
    }

    @Bean
    public HelloService02 helloService02() {
        return new HelloService02();
    }
}

【HelloService02】

public class HelloService02 {

    public void sayHello(String user) {
        System.out.println("HelloService2#sayHell():  hello " + user);
    }
}


【2.4】支持使用AnnotationConfigWebApplicationContext构建springweb容器

1)AnnotationConfigApplicationContext的WebApplicationContext变体可以与AnnotationConfigWebApplicationContext一起使用;

  • 当配置spring ContextLoaderListener servlet监听器,springmvc DispatcherServlet等,可以使用AnnotationConfigWebApplicationContext作为参数

2)以下web.xml代码片段配置了一个典型的springmvc web容器;

<web-app>
  <!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext
       instead of the default XmlWebApplicationContext -->
  <context-param>
      <param-name>contextClass</param-name>
      <param-value>
          org.springframework.web.context.support.AnnotationConfigWebApplicationContext
      </param-value>
  </context-param>

  <!-- Configuration locations must consist of one or more comma- or space-delimited
       fully-qualified @Configuration classes. Fully-qualified packages may also be
       specified for component-scanning -->
  <context-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>com.acme.AppConfig</param-value>
  </context-param>

  <!-- Bootstrap the root application context as usual using ContextLoaderListener -->
  <listener>
      <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <!-- Declare a Spring MVC DispatcherServlet as usual -->
  <servlet>
      <servlet-name>dispatcher</servlet-name>
      <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      <!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext
           instead of the default XmlWebApplicationContext -->
      <init-param>
          <param-name>contextClass</param-name>
          <param-value>
              org.springframework.web.context.support.AnnotationConfigWebApplicationContext
          </param-value>
      </init-param>
      <!-- Again, config locations must consist of one or more comma- or space-delimited
           and fully-qualified @Configuration classes -->
      <init-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>com.acme.web.MvcConfig</param-value>
      </init-param>
  </servlet>

  <!-- map all requests for /app/* to the dispatcher servlet -->
  <servlet-mapping>
      <servlet-name>dispatcher</servlet-name>
      <url-pattern>/app/*</url-pattern>
  </servlet-mapping>
</web-app>


【3】组合基于java的配置

【3.1】使用 @Import注解组合多个配置类

1)@Import注解支持从其他配置类加载bean Definition,就像在xml文件中使用 <import>元素来帮助模块化配置一样;

【ImportAnnotationMain】@Import注解测试main

package com.tom.chapter00.config.importannotation;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class ImportAnnotationMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfigB.class);
        context.getBean(DiyA.class).print();
    }
}

代码说明: 在实例化AnnotationConfigApplicationContext容器时,无需指定AppConfigA.class与AppConfigB.class, 而仅需要指定AppConfigB.class即可; 因为AppConfigB 通过@Import注解引入了 AppConfigA中的BeanDefinition

【AppConfigA】

package com.tom.chapter00.config.importannotation;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfigA { 

    @Bean
    public DiyA diyA() {
        return new DiyA();
    }
}

public class DiyA {
    public void print() {
        System.out.println("this is diy a");
    }
}

【AppConfigB】

@Configuration
@Import(AppConfigA.class) // AppConfigB使用@Import引入或导入AppConfigA 
public class AppConfigB {

    @Bean
    public DiyB diyB() {
        return new DiyB();
    }
}

public class DiyB {
    public void print() {
        System.out.println("this is diy b");
    }
} 


【3.1.1】对@Import引入的BeanDefinition注入依赖(装配@Import注解引入的bean)

1)大多数场景中,一个@Configuration配置类A中的Bean依赖另一个@Configuration配置类B中的bean;

  • 这在xml配置文件中很好解决,只需要配置类A中的bean元素使用ref元素引入配置类B的bean即可;

2)通过java配置的容器中,需要使用类似于ref元素的语法对@Import引入的bean进行装配;

  • 注意: @Configuration标注的配置类始终是一个bean;这意味着我们可以使用 @Autowire注入元数据,即装配@Configuration类

【ImportAnnotationInjectDependencyMain】

public class ImportAnnotationInjectDependencyMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SystemConfig01.class);
        context.getBean(UserService.class).saveUser("Trump");
    }
}

【配置类】

@Configuration
@Import({UserServiceConfig.class, UserRepositoryConfig.class})
public class SystemConfig01 {
}

@Configuration
public class UserServiceConfig {

    private @Autowired UserRepository userRepository; // 这个repository依赖于另一个配置类UserRepositoryConfig中的bean 

    public @Bean UserService userService() {
        return new UserService(userRepository);
    }
}

@Configuration
public class UserRepositoryConfig {

    public @Bean UserRepository userRepository() {
        return new UserRepository(); 
    }
}

【业务bean】

public class UserService {

    private UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void saveUser(String name) {
        userRepository.saveUser(name);
    }
}
public class UserRepository {

    public void saveUser(String name) {
        System.out.println("UserRepository#saveUser(): 保存用户成功, name=" + name);
    }
}


【3.1.2】全限定导入bean以便导航

1)3.1.1中的问题: UserServiceConfig使用@Autowired装配UserRepository,但这个UserRepository具体定义在哪个@Configuration类是不清楚的;这不利于类的导航查找;

  • 解决方法:明确装配的bean所属的配置类;考虑装配另一个配置类B本身到当前配置类A;

【UserServiceConfig2】 修改后的版本,使用另一个配置类本身装配当前配置类

@Configuration
public class UserServiceConfig2 {
    // 装配的是@Configuration类的bean,而不是具体的Repository bean
    private @Autowired UserRepositoryConfig userRepositoryConfig; 

    public @Bean UserService userService() {
        return new UserService(userRepositoryConfig.userRepository());
    }
}

【UserServiceConfig】之前的版本,使用具体bean装配当前配置类;

@Configuration
public class UserServiceConfig {

    private @Autowired UserRepository userRepository;

    public @Bean UserService userService() {
        return new UserService(userRepository);
    }
}

2) UserServiceConfig2中的代码还有点问题: UserServiceConfig2 与 UserRepositoryConfig高度耦合了;

  • 解决方法:@Autowired UserRepositoryConfig userRepositoryConfig,修改 UserRepositoryConfig 为抽象定义,如接口或抽象类

【ImportAnnotationInjectDependencyMain03】

public class ImportAnnotationInjectDependencyMain03 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SystemConfig03.class);
        context.getBean(UserService.class).saveUser("Trump");
    }
}

【总配置类-SystemConfig03】 引入的UserRepositoryConfigImpl是IUserRepositoryConfig接口的实现类

@Configuration
// 导入的是用户仓库配置接口的实现类的class(而非其接口IUserRepositoryConfig的class)
@Import({UserServiceConfig3.class, UserRepositoryConfigImpl.class}) 
public class SystemConfig03 {
}

【其他配置类】

@Configuration
public class UserServiceConfig3 {

    // 这里的属性类型为接口,非具体实现,以解耦
    private @Autowired IUserRepositoryConfig userRepositoryConfig; 

    public @Bean UserService userService() { 
        return new UserService(userRepositoryConfig.userRepository());
    }
}
// 用户仓库配置接口定义  
public interface IUserRepositoryConfig { 
    public UserRepository userRepository();
}
// 用户仓库配置接口的实现类  
public class UserRepositoryConfigImpl implements IUserRepositoryConfig { 
    @Override
    public UserRepository userRepository() {
        return new UserRepository();
    }
} 


【3.2】以xml配置为中心并使用component-scan元素扫描java配置类(配置spring容器的2种方式)

1)spring提供的@Configuration配置类的支持并不是100%替换xml配置;xml配置也有一些优点,如xml命名空间就是一种完美的配置容器的方式;

2)配置spring容器有2种方式

  • 使用xml文件方式,且spring容器使用ClassPathXmlApplicationContext类;
  • 使用java配置方式,且spring容器使用AnnotationConfigApplicationContext 类,且可以按需使用@ImportResource注解引入xml配置文件


【3.2.1】代码实践

【ComposeJavaAndXmlMain】

public class ComposeJavaAndXmlMain {
    public static void main(String[] args) {
        ApplicationContext container = new ClassPathXmlApplicationContext("chapter00/beans00.xml");
        container.getBean(ComposeJavaAndXmlService.class).saveUser("Trump");
    }
}

【执行效果】

ComposeJavaAndXmlRepository#saveUser(): name=Trump, url=www.baidu.com

【beans00.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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <beans>
        <!-- 扫描@Component注解标注的类(包括@Configuration),注册到spring容器 -->
        <context:component-scan base-package="com.tom.chapter00.composejavaandxml"/>
        <context:property-placeholder location="classpath:/chapter00/jdbc.properties"/>

        <bean class="com.tom.chapter00.composejavaandxml.ComposeJavaAndXmlRepository">
            <property name="url" value="${jdbc.url}"/>
        </bean>
    </beans>
</beans>

【/chapter00/jdbc.properties】 属性文件

jdbc.url=www.baidu.com

【ComposeJavaAndXmlService】

public class ComposeJavaAndXmlService {

    private ComposeJavaAndXmlRepository composeJavaAndXmlRepository;
    public ComposeJavaAndXmlService(ComposeJavaAndXmlRepository composeJavaAndXmlRepository) {
        this.composeJavaAndXmlRepository = composeJavaAndXmlRepository;
    }
    public void saveUser(String name) {
        composeJavaAndXmlRepository.saveUser(name);
    }
}

【ComposeJavaAndXmlRepository】

public class ComposeJavaAndXmlRepository {

    private String url;

    public void saveUser(String name) {
        System.out.println("ComposeJavaAndXmlRepository#saveUser(): name=" + name + ", url=" + url);
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }
}

【补充】文件目录结构

在这里插入图片描述



【3.3】以java配置为中心并使用@ImportResoure注解引入xml配置

1)在spring应用中, @Configuration注解配置spring容器是主要方式,但仍然有可能使用到xml配置,可以通过@ImportResoure注解引入xml配置;

【3.3.1】代码实践

【ImportResourceComposeJavaAndXmlMain】

public class ImportResourceComposeJavaAndXmlMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext container = new AnnotationConfigApplicationContext(UserRepositoryConfigUsingImportResource.class);
        container.getBean(ComposeJavaAndXmlRepository.class).saveUser("Trump");
    }
}

【执行效果】

ComposeJavaAndXmlRepository#saveUser(): name=Trump, url=www.baidu.com

【UserRepositoryConfigUsingImportResource】 使用 @ImportResource引入xml配置的配置类

@Configuration
@ImportResource("classpath:/chapter00/beans01.xml")
public class UserRepositoryConfigUsingImportResource {
    private @Value("${jdbc.url}") String url;

    public @Bean ComposeJavaAndXmlRepository composeJavaAndXmlRepository() {
        ComposeJavaAndXmlRepository composeJavaAndXmlRepository = new ComposeJavaAndXmlRepository();
        composeJavaAndXmlRepository.setUrl(url);
        return composeJavaAndXmlRepository;
    }
}

【beans01.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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <beans>
        <context:property-placeholder location="classpath:/chapter00/jdbc.properties"/>
    </beans>
</beans>

【jdbc.properties】

jdbc.url=www.baidu.com


【3.4】使用@PropertySource加载属性文件

1)使用@PropertySource加载属性文件,以访问所有定义的属性;

【属性文件】

// kafka.properties
kafka.cluster.url=192.168.1.1
kafka.cluster.name=TomKafka
    
// redis.properties
redis.cluster.url=192.168.1.2
redis.cluster.name=TomRedis

【属性文件路径】

在这里插入图片描述

【PropertySourceMain】

public class PropertySourceMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(PropertySourceConfig.class);
        // 获取bean
        TomCluster tomCluster = context.getBean(TomCluster.class);
        System.out.println(tomCluster);
        System.out.println(tomCluster.getKafkaClusterUrl());
        // 通过Environment获取属性
        ConfigurableEnvironment env = context.getEnvironment();
        System.out.println(env.getProperty("kafka.cluster.name"));
        System.out.println(env.getProperty("kafka.cluster.url"));
        System.out.println(env.getProperty("redis.cluster.name"));
        System.out.println(env.getProperty("redis.cluster.url"));
    }
}

【运行效果】

TomCluster{kafkaClusterUrl='192.168.1.1', kafkaClusterName='TomKafka', redisClusterUrl='192.168.1.2', redisClusterName='TomRedis'}
192.168.1.1
TomKafka
192.168.1.1
TomRedis
192.168.1.2

【PropertySourceConfig】

package com.tom.chapter00.propertysource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
@PropertySource(value = {"classpath:chapter00/kafka.properties", "classpath:chapter00/redis.properties"})
public class PropertySourceConfig {

    @Bean
    public TomCluster tomCluster() {
        return new TomCluster();
    }
}

【TomCluster】

package com.tom.chapter00.propertysource;

import org.springframework.beans.factory.annotation.Value;

public class TomCluster {

    @Value("${kafka.cluster.url}") 
    private String kafkaClusterUrl;
    @Value("${kafka.cluster.name}")
    private String kafkaClusterName;
    @Value("${redis.cluster.url}")
    private String redisClusterUrl;
    @Value("${redis.cluster.name}")
    private String redisClusterName;

    public String getKafkaClusterUrl() {
        return kafkaClusterUrl;
    }

    public String getKafkaClusterName() {
        return kafkaClusterName;
    }

    public String getRedisClusterUrl() {
        return redisClusterUrl;
    }

    public String getRedisClusterName() {
        return redisClusterName;
    }

    @Override
    public String toString() {
        return "TomCluster{" +
                "kafkaClusterUrl='" + kafkaClusterUrl + '\'' +
                ", kafkaClusterName='" + kafkaClusterName + '\'' +
                ", redisClusterUrl='" + redisClusterUrl + '\'' +
                ", redisClusterName='" + redisClusterName + '\'' +
                '}';
    }
}


【4】使用@Bean注解

1)@Bean注解定义: @Bean注解类似于xml文件中的bean元素,该注解支持bean元素的一些属性,如inin-method , destory-method, autowiring ,name 等。你可以在 @Configuration类或@Component类中使用 @Bean注解;


【4.1】声明@Bean

1)可以使用@Bean标注一个方法以此来定义bean; 你使用这个方法在ApplicationContext中注册bean的定义; bean类型指定为方法返回类型; 默认情况下,方法名称就是bean名称

【BeanAnnotationMain】

public class BeanAnnotationMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext container = new AnnotationConfigApplicationContext(BeanAnnotationConfig.class);
        System.out.println(container.getBean(UserService.class));
        System.out.println(container.getBean("userService"));
    }
}
// com.tom.chapter00.config.importannotation.injectdependency.UserService@6aeb35e6
// com.tom.chapter00.config.importannotation.injectdependency.UserService@6aeb35e6

container.getBean(“userService”) 与 container.getBean(UserService.class) 获取的是同一个bean,所以UserService userService()方法注册的bean的名称就是方法名称userService;

【BeanAnnotationConfig】

package com.tom.chapter00.beanannotation;

import com.tom.chapter00.config.importannotation.injectdependency.UserService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class BeanAnnotationConfig {

    @Bean
    public UserService userService() {
        return new UserService();
    }
}



【4.2】@Bean标注方法返回的bean的装配(注入依赖)

1)当@Bean注册的bean依赖另一个bean时,仅需要调用另一个bean的注册方法即可注入依赖;如下;

【BeanAnnotationMain2】

public class BeanAnnotationMain2 {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext container = new AnnotationConfigApplicationContext(BeanAnnotationConfig2.class);
        System.out.println(container.getBean("repositoryA"));
        container.getBean(ServiceA.class).printRepository();
    }
}

【执行效果】 显然, 容器中名为repositoryA的bean,与ServiceA中装配的类型为RepositoryA的bean,是同一个bean;

com.tom.chapter00.beanannotation.repository.RepositoryA@4c583ecf
repositoryA=com.tom.chapter00.beanannotation.repository.RepositoryA@4c583ecf  

【BeanAnnotationConfig2】配置类 (调用方法即可注入依赖

@Configuration
public class BeanAnnotationConfig2 {

    public @Bean RepositoryA repositoryA() {
        return new RepositoryA();
    }
    public @Bean ServiceA serviceA() {
        return new ServiceA(repositoryA()); // 调用方法即可注入依赖
    }
}

【ServiceA】

public class ServiceA {

    private RepositoryA repositoryA;

    public ServiceA(RepositoryA repositoryA) {
        this.repositoryA = repositoryA;
    }

    public void printRepository() {
        System.out.println("repositoryA=" + repositoryA);
    }
}


【4.3】接收生命周期回调

1)任何使用@Bean定义的类都可以使用 来自JSR-250的@PostConstruct 与 @PreDestory注解;

  • 参见: JSR-250的@PostConstruct 与 @PreDestory注解

2)spring支持bean的生命周期回调;如果bean实现了InitializingBean, DisposableBean或者 Lifecycle, 它们对应的方法就会被容器调用;

  • 以Aware为后缀的标准接口集合也支持生命周期回调,如BeanFactoryAware, BeanNameAware, MessageSourceAware, ApplicationContextAware;

3)@Bean注解支持指定任意初始化与销毁的回调方法,这类似于xml配置文件中 <bean>元素中 init-method 与 destroy-method属性

【@Bean注解定义】


package org.springframework.context.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
    @AliasFor("name")
    String[] value() default {};

    @AliasFor("value")
    String[] name() default {};

    boolean autowireCandidate() default true;

    String initMethod() default "";

    String destroyMethod() default "(inferred)";
}



【4.3.1】@Bean注册的bean生命周期回调代码实践

【BeanAnnotationLifeCycleMain】

public class BeanAnnotationLifeCycleMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext container = new AnnotationConfigApplicationContext(BeanAnnotationLifeCycleConfig.class);
        // 注册了关闭钩子,当容器销毁bean时才会调用 destory()方法
        container.registerShutdownHook();
    }
}

【执行效果】

BeanWithLifeCycleCallback init()
BeanWithLifeCycleCallback destory()

【BeanAnnotationLifeCycleConfig】 @Bean注解指定bean初始时方法与销毁方法

@Configuration
public class BeanAnnotationLifeCycleConfig {

    @Bean(initMethod = "init", destroyMethod = "destory")
    public BeanWithLifeCycleCallback beanWithInitMethod() {
        return new BeanWithLifeCycleCallback();
    }
}

【BeanWithLifeCycleCallback】带有生命周期回调的Bean

public class BeanWithLifeCycleCallback {
    public void init() {
        System.out.println("BeanWithLifeCycleCallback init()");
    }
    public void destory() {
        System.out.println("BeanWithLifeCycleCallback destory()");
    }

}

【补充】上述注册生命周期初始化回调方法init,等同于在构造器中执行init()方法;

@Configuration
public class BeanAnnotationLifeCycleConfig2 {

    @Bean
    public BeanWithLifeCycleCallback beanWithInitMethod() {
        BeanWithLifeCycleCallback beanWithLifeCycleCallback = new BeanWithLifeCycleCallback();
        beanWithLifeCycleCallback.init(); // 类似与显式调用 init() 方法 
        return beanWithLifeCycleCallback;
    }
}

【提示】如果我们直接使用java配置容器,我们可以直接对目标对象或bean执行任何操作,而并不是总是依赖spring的生命周期回调, 如BeanAnnotationLifeCycleConfig2那样



【4.4】指定bean作用范围

【4.4.1】使用@Scope注解

1)你可以指定使用@Bean定义的bean的作用范围。你可以使用任何一种标准的作用范围。beans-factory-scopes

在这里插入图片描述

2)默认scope作用范围是singleton-单例,可以使用@Scope注解重写scope;

【ScopeOverwriteMain】重写bean的作用域main入口

public class ScopeOverwriteMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext container = new AnnotationConfigApplicationContext(ScopeOverrideConfig.class);
        // 获取 RepositoryA实例,每次获取都会创建一个新实例,因为其scope为prototype
        System.out.println(container.getBean(RepositoryA.class));
        System.out.println(container.getBean(RepositoryA.class));

        // 获取 RepositoryB实例,每次获取都会返回同一个实例,因为其scope为singleton
        System.out.println(container.getBean(RepositoryB.class));
        System.out.println(container.getBean(RepositoryB.class));
    }
//    com.tom.chapter00.beanannotation.repository.RepositoryA@59474f18
//    com.tom.chapter00.beanannotation.repository.RepositoryA@65fb9ffc
//    com.tom.chapter00.beanannotation.repository.RepositoryB@3e694b3f
//    com.tom.chapter00.beanannotation.repository.RepositoryB@3e694b3f
}

【ScopeOverrideConfig】

package com.tom.chapter00.beanannotation.scope.scopeoverride;

import com.tom.chapter00.beanannotation.repository.RepositoryA;
import com.tom.chapter00.beanannotation.repository.RepositoryB;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

@Configuration
public class ScopeOverrideConfig {

    @Bean
    @Scope("prototype") // 原型作用域,每次获取bean都会创建1个新实例 
    public RepositoryA repositoryA() {
        return new RepositoryA();
    }

    @Bean
    @Scope("singleton") // 单例作用域,自始至终都只有1个实例 
    public RepositoryB repositoryB() {
        return new RepositoryB();
    }
}


【4.4.2】查找方法注入Lookup method injection(仅了解,本文不展开)

1)查找方法注入是你很少使用到的高级功能; 查找方法注入在单例范围bean依赖原型范围bean场景下有用

public abstract class CommandManager {
  public Object process(Object commandState) {
      // grab a new instance of the appropriate Command interface
      Command command = createCommand();

      // set the state on the (hopefully brand new) Command instance
      command.setState(commandState);
      return command.execute();
  }

  // okay... but where is the implementation of this method?
  protected abstract Command createCommand();
}

2)使用java配置支持,你可以创建CommandManager子类,其抽象方法createCommand()被重写,以便查找新的原型command对象;

@Bean
@Scope("prototype")
public AsyncCommand asyncCommand() {
  AsyncCommand command = new AsyncCommand();
  // inject dependencies here as required
  return command;
}

@Bean
public CommandManager commandManager() {
  // return new anonymous implementation of CommandManager with command() overridden
  // to return a new prototype Command object
  return new CommandManager() {
      protected Command createCommand() {
          return asyncCommand();
      }
  }
}

解说:CommandManager是单例的,但其createCommand()方法返回的Command对象是原型的;



【4.4.3】自定义bean的名称

1)默认情况下,@Bean注解的方法名作为实例化bean的名称,即bean名称=repositoryA1(参见下文的BeanNameConfig);但若代码指定了@Bean注解的name属性=userRepo,所以bean名称=userRepo;

2)@Bean注解的name属性接收一个string数组,以便为一个bean指定多个别名;

【BeanNameConfigMain】bean名称配置测试main

public class BeanNameConfigMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext container = new AnnotationConfigApplicationContext(BeanNameConfig.class);
        System.out.println(container.getBean("repositoryA1"));
        System.out.println(container.getBean("userRepo"));
        System.out.println(container.getBean("userRepo1"));
        System.out.println(container.getBean("userRepo2"));
        System.out.println(container.getBean("userRepo3"));
    }
}

【执行结果】

com.tom.chapter00.beanannotation.repository.RepositoryA@692f203f
com.tom.chapter00.beanannotation.repository.RepositoryA@48f2bd5b
com.tom.chapter00.beanannotation.repository.RepositoryA@7b2bbc3 // 相同 
com.tom.chapter00.beanannotation.repository.RepositoryA@7b2bbc3// 相同 
com.tom.chapter00.beanannotation.repository.RepositoryA@7b2bbc3// 相同 

【BeanNameConfig】

@Configuration
public class BeanNameConfig {

    @Bean
    public RepositoryA repositoryA1() {
        return new RepositoryA();
    }

    @Bean("userRepo") // 为bean指定1个别名 
    public RepositoryA repositoryA2() {
        return new RepositoryA();
    }

    @Bean(name = {"userRepo1", "userRepo2", "userRepo3"}) // 为bean指定3个别名
    public RepositoryA repositoryAWithMultipleName() {
        return new RepositoryA();
    }
}

【@Bean注解源码】

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
    @AliasFor("name")
    String[] value() default {};

    @AliasFor("value")
    String[] name() default {};

    boolean autowireCandidate() default true;

    String initMethod() default "";

    String destroyMethod() default "(inferred)";
}


【5】所有@Configuration类使用CGLIB进行子类化

1)@Bean标注的方法被调用2次,会有什么效果?

【BeanMoreInfoConfigMain】

public class BeanMoreInfoConfigMain {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext container = new AnnotationConfigApplicationContext(BeanMoreInfoConfig.class);
        System.out.println(container.getBean("userService1", UserService.class).getUserRepository());
        System.out.println(container.getBean("userService2", UserService.class).getUserRepository());
    }
}

【打印效果】 调用2次userRepository(), 获取的是同一个UserRepository bean; 为什么? ;

// com.tom.chapter00.config.importannotation.injectdependency.UserRepository@4c583ecf
// com.tom.chapter00.config.importannotation.injectdependency.UserRepository@4c583ecf

【BeanMoreInfoConfig】

@Configuration
public class BeanMoreInfoConfig {

    @Bean
    public UserService userService1() {
        return new UserService(userRepository()); // 第1次调用userRepository()
    }

    @Bean
    public UserService userService2() {
        return new UserService(userRepository()); // 第2次调用userRepository()
    }

    @Bean
    public UserRepository userRepository() {
        return new UserRepository();
    }
}


【5.1】java配置类内部运行原理(重要)

1)由上述可知:调用2次userRepository(), 获取的是同一个UserRepository bean;

  • 原理解说:所有 @Configuration类在启动时使用CGLIB子类化。 简单理解是: @Configuration类作为父类,CGLIB通过字节码增强产生一个子类; 在获取bean时,首先子类中子方法会校验缓存中是否存在该bean;若有则直接返回,否则再调用父方法并创建新实例

2)本例的注意点:

  • 本例演示的场景是基于单例bean,若bean范围不是单例,则运行原理不同;
  • 注意到,为了让java配置的容器可以正常运行,你必须在依赖清单中包含CGLIB jar;
  • 由于CGLIB在启动时动态增强了一些功能,所以有一些限制条件;
    • @Configuration类不应该是final,否则不能被继承;
    • @Configuration类应该有一个无参构造器;


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/904514.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

线程和进程延续

1.线程和进程启动终止方式 1.创建子进程&#xff0c;子线程 2.退出进程/线程 3.回收僵尸进程资源 / 回收线程资源 4.进程终止函数 / 线程清理函数 2.线程状态切换 pthread_create()变为就绪态&#xff0c;调度后成为运行态&#xff0c;pthread_join()成为阻塞态…

为微信小程序换皮肤之配置vant

微信小程序自带的控件虽然具有很好的通用性和简洁性&#xff0c;但在面对一些复杂的交互场景和个性化的设计需求时&#xff0c;可能会显得力不从心。其功能的相对基础使得开发者在实现诸如多步骤复杂表单提交、实时数据交互与可视化展示、高度定制化的界面布局等方面&#xff0…

K8S配置storage-class

简介 Kubernetes支持NFS存储&#xff0c;需要安装nfs-subdir-external-provisioner&#xff0c;它是一个存储资源自动调配器&#xff0c;它可将现有的NFS服务器通过持久卷声明来支持Kubernetes持久卷的动态分配。该组件是对Kubernetes NFS-Client Provisioner的扩展&#xff0…

html生成图片方案总结

动态图片生成是我们日常开发中经常遇到的需求&#xff0c;比如宣传海报生成&#xff0c;电商商品图动态生成等&#xff0c;本文总结出三种常见的 HTML 生成图片的方案。 一、html2canvas html2canvas库能够将 HTML 元素渲染为 Canvas&#xff0c;然后将其转换为图片。它的优点…

【力扣刷题实战】相同的树

大家好&#xff0c;我是小卡皮巴拉 文章目录 目录 力扣题目&#xff1a; 相同的树 题目描述 示例 1&#xff1a; 示例 2&#xff1a; 示例 3&#xff1a; 解题思路 题目理解 算法选择 具体思路 解题要点 完整代码&#xff08;C语言&#xff09; 兄弟们共勉 &#…

如何使用AdsPower指纹浏览器克服爬虫技术限制,安全高效进行爬虫!

随着中国开发者日益成熟&#xff0c;应用质量明显提升&#xff0c;越来越多的开发者选择出海寻找机会扩大市场。但“应用出海”说起来容易&#xff0c;做起来难。其中&#xff0c;最大的困恼就是对海外市场缺乏了解。 很多开发者会选择使用网络爬虫&#xff08;Web Crawling&a…

Python小游戏18——中国象棋

首先&#xff0c;你需要安装Pygame库。如果你还没有安装它&#xff0c;可以使用以下命令进行安装&#xff1a; bash pip install pygame 运行结果 代码如下&#xff1a; python import pygame import sys # 初始化Pygame pygame.init() # 屏幕尺寸 SCREEN_WIDTH 800 SCREEN_HE…

轴承寿命预测 (Python 预测模型全家桶)

往期精彩内容&#xff1a; Python-凯斯西储大学&#xff08;CWRU&#xff09;轴承数据解读与分类处理 Pytorch-LSTM轴承故障一维信号分类(一)-CSDN博客 Pytorch-CNN轴承故障一维信号分类(二)-CSDN博客 Pytorch-Transformer轴承故障一维信号分类(三)-CSDN博客 三十多个开源…

【IC验证_systemverilog】信号类型

IC验证_systemverilog 1.信号声明2.变量类型3.数据类型4.符号 1.信号声明 语法&#xff1a; 变量类型 信号类型 符号转换 位宽 信号名 深度&#xff1b;2.变量类型 &#xff08;1&#xff09;说明&#xff1a; systemverilog中的信号类型主要分为线网类型&#xff08;wire&a…

camera和lidar外参标定

雷达和相机的外参标定&#xff08;外部参数标定&#xff09;指的是确定两者之间的旋转和平移关系&#xff0c;使得它们的坐标系可以对齐。 文章目录 无目标标定livox_camera_calibdirect_visual_lidar_calibration 有目标标定velo2cam_calibration 无目标标定 livox_camera_ca…

照片不完整?来试试智能扩图,简直不要太满意!(不是广告)

生活中有些照片拍过之后&#xff0c;当时觉得很满意&#xff0c;但过段时间就恨当初没有拍一张完整的图片&#xff01; ——来自小白的感慨 当时跟家里的叮当一起去旅游&#xff0c;我给他拍了一张好看的照片&#xff1a; 今天这张照片如果是整图就好了&#xff01;好气哦&am…

在银河麒麟系统中Qt连接达梦数据库

解决在银河麒麟系统中使用Qt连接达梦数据库提示&#xff1a;project Error library odbc is not defined问题 一、编译ODBC 下载解压unixODBC&#xff08;http://www.unixodbc.org/unixODBC-2.3.1.tar.gz&#xff09; 打开终端&#xff0c;切换到unixODBC-2.3.1目录下&#x…

【WebDriver】浏览器驱动下载及其配置

一、Windows电脑环境搭建-Chrome浏览器 行业内&#xff0c;Chrome (谷歌) 浏览器对于自动化程序来讲是比较稳定的. 自动化程序推荐使用 Chrome 浏览器的原因有几个&#xff1a; 开发者工具&#xff1a;Chrome 提供强大的开发者工具&#xff0c;方便调试和测试自动化脚本。 稳…

时序分解 | TTNRBO-VMD改进牛顿-拉夫逊算法优化变分模态分解

时序分解 | TTNRBO-VMD改进牛顿-拉夫逊算法优化变分模态分解 目录 时序分解 | TTNRBO-VMD改进牛顿-拉夫逊算法优化变分模态分解效果一览基本介绍程序设计参考资料 效果一览 基本介绍 (创新独家)TTNRBO-VMD改进牛顿-拉夫逊优化算优化变分模态分解TTNRBO–VMD 优化VMD分解层数K和…

深度图和RGB图对齐

坐标系间的转换_坐标系转换-CSDN博客 深度图与彩色图的配准与对齐_彩色 深度 配准-CSDN博客 kinect 2.0 SDK学习笔记&#xff08;四&#xff09;--深度图与彩色图对齐_mapdepthframetocolorspace-CSDN博客 相机标定&#xff08;三&#xff09;-相机成像模型_相机小孔成像模型…

天润融通突破AI客服局限,三大关键提升文本机器人问答效果

近期&#xff0c;AI客服再次登上热搜&#xff0c;引发网友集体吐槽&#xff0c;比如AI客服虽然态度客气&#xff0c;但听不懂客户诉求&#xff0c;回答问题驴唇不对马嘴&#xff0c;解决不了问题...... 更有网友将这些问题升级到&#xff0c;企业就是不想解决问题才交给AI客服…

微服务之间调用,OpenFeign传递用户(RequestInterceptor接口)

场景&#xff1a;微服务之黑马商城项目-登录拦截器在网关完成用户的校验&#xff0c;并将用户信息&#xff08;用户id&#xff09;存入请求头&#xff0c;假设将购物车里面的商品进行结算就会生成订单并清空购物车&#xff0c;这里涉及到了交易服务模块远程调用购物车模块&…

Java避坑案例 - “激进”的线程池扩容策略及实现

文章目录 问题思路线程池的默认行为自定义线程池扩容策略Code实现小结 问题 Java 线程池是先用工作队列来存放来不及处理的任务&#xff0c;满了之后再扩容线程池。当我们的工作队列设置得很大时&#xff0c;最大线程数这个参数显得没有意义&#xff0c;因为队列很难满&#x…

OpenAI低调发布多智能体工具Swarm:让多个智能体协同工作!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;专注于分享AI全维度知识&#xff0c;包括但不限于AI科普&#xff0c;AI工…

C++设计模式结构型模式———适配器模式

文章目录 一、引言二、适配器模式三、类适配器四、总结 一、引言 适配器模式是一种结构型设计模式&#xff0c;它在日常生活中有着广泛的应用&#xff0c;比如各种转换接头和电源适配器&#xff0c;它们的主要作用是解决接口不兼容的问题。就像使用电源适配器将220V的市电转换…