Uma breve análise da configuração automática do springboot

Springboot é essencialmente um andaime de mola, na verdade ainda é mola, mas o springboot nos ajuda a fazer as configurações que precisamos fazer ao usar o spring. Este artigo discutirá brevemente como o sprigboot nos ajuda a configurar o spring.

Referência: https://www.bilibili.com/video/BV1eD4y1w7Rp?p=2&vd_source=e1ec5e4886fca4c5f06887c81aefd01a

1. A ideia básica do springboot

Depois de adicionar a dependência spring-boot-starter-web, definimos um controlador e escrevemos uma interface como a seguir, que pode ser acessada no navegador: Verifique o console e veja se o Tomcat foi iniciado.

@RestController
public class UserController {
    
    
    @GetMapping("/test")
    public String test(){
    
    
        return  "rrrr";
    }
}

Na verdade, este é o conteúdo do spring mvc. O processo do spring mvc é o seguinte. Quando o projeto springboot é iniciado, o Tomcat precisa ser iniciado e o DispatcherServlet precisa ser conhecido no Tomcat, para que o Tomcat possa encontrar o método de solicitação por meio do DispatcherServlet. Ao mesmo tempo, DispatcherServlet precisa conhecer a classe UserController. Como saber? Basta pegar o contêiner Spring.UserController é anotado com @RestController e é um Bean no contêiner.
Insira a descrição da imagem aqui
E o contêiner? A inicialização do contêiner requer um arquivo de configuração semelhante ao seguinte: Quando o contêiner é iniciado, ele precisa verificar a classe UserController acima.

AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();//使用注解配置的容器
applicationContext.register(c);//c是一个配置类,Class类型

1.1 início do Tomcat

Ao iniciar o projeto springboot, você pode iniciar o tomcat assim: existe um método dedicado para iniciar o tomcat, para que você possa chamar o método startTomcat ao iniciar o projeto springboot e passar um objeto contêiner spring para iniciar o tomcat.

	/**
     * 启动内嵌的tomcat,先引入pom,并进行相关的配置
     */
    public static void startTomcat(ApplicationContext context){
    
    
        //1。 进行必要的tomcat配置
        Tomcat t = new Tomcat();

        //2。输入地址想要访问到控制器,需要servlet,需要mvc的分发器(dispatcher),分发器需要容器才能找到controller
        //t.addServlet(context,"dispatcher",new DispatcherServlet(webContex));
    }

Pré-requisito: Deve haver uma dependência do tomcat.
Desvantagem: É codificado. Se você quiser mudar para um contêiner da web, como o netty, isso não pode ser feito.

1.2 Use a ideia de programação orientada a interface

public interface WebServer {
    
    
    public void start();
}

public class JettyWebServer implements WebServer{
    
    
    @Override
    public void start() {
    
    
        //启动jetty就需要new一些jetty的恭喜,所以需要引入jetty的依赖(此时jetty和tomcat的依赖都有)
    }
}

public class TomcatWebServer implements WebServer{
    
    
    @Override
    public void start() {
    
    
		//1.1中startTomcat的代码
    }
}

Supondo que a classe acima seja usada para iniciar o contêiner, você pode iniciar o tomcat ou jetty da seguinte maneira após iniciar o projeto springboot: o usuário adiciona manualmente tomcat ou jetty beans ao contêiner, para que o método getWebServer seja usado quando o O projeto springboot é iniciado Obtenha automaticamente o Bean adicionado pelo usuário, permitindo assim que o usuário personalize o tipo de contêiner de serviço da web.

	//springboot项目启动时执行
	WebServer webServer = getWebServer(applicationContext);
    webServer.start();

    public static WebServer getWebServer(WebApplicationContext applicationContext){
    
    
        //从容器中拿到WebServer的Bean,可能会有多个(用户自己决定)
        Map<String, WebServer> webServers = applicationContext.getBeansOfType(WebServer.class);

        if(webServers.size() == 0){
    
    

        }
        if(webServers.size() > 1){
    
    

        }
        //返回唯一的webserver对象
        return  webServers.entrySet().stream().findFirst().get().getValue();
    }

Premissa: O projeto pai do springboot precisa ter dependências do Tomcat e do jetty, porque a interface WebServer acima é definida no projeto pai do springboot.
Desvantagens: Os usuários ainda precisam escrever seus próprios beans, o que obviamente não é o que o springboot faz.

1.3 Escolha o contêiner da web que deseja usar com base no pom

Defina a seguinte classe com base em 1.2.@Conditional significa que o Bean só terá efeito se atender às condições da classe na anotação. Neste momento, as condições só precisam julgar se há uma dependência do Tomcat ou uma dependência do jetty, e então os beans podem ser adicionados automaticamente ao contêiner, eliminando a necessidade de beans definidos pelo usuário. Na verdade, agora existe uma amostra de configuração automática.

@Configuration//这个类需要确保生效,可以使用import
public class WebServerAutomaticConfiguration {
    
    
    @Bean
    @Conditional(TomcatCondition.class)//条件满足才创建bean
    public TomcatWebServer getTomcatServer(){
    
    
        return new TomcatWebServer();
    }

    @Bean
    @Conditional(JettyCondition.class)//条件满足才创建bean
    public JettyWebServer getJettyWebServer(){
    
    
        return new JettyWebServer();
    }
}

Considerando as condições da classe condicional na anotação, deve-se julgar se uma determinada dependência foi introduzida.Como julgar se uma determinada dependência foi introduzida? Use o carregador de classes para carregar uma determinada classe na dependência. Se o carregamento for bem-sucedido, significa que essa dependência existe.
Na verdade, isso é melhor, mas também tem desvantagens:
Desvantagem: WebServerAutomaticConfiguration precisa ser adicionado ao contêiner, mas não está no caminho do pacote da classe de configuração, portanto, precisa ser processado separadamente com a anotação Import. Existem muitos tais classes que são configuradas automaticamente, e isso não pode ser feito.

**Nota: **Como acima, as dependências do Tomcat e do jetty são necessárias no projeto pai do springboot, porque elas precisam ser executadas no projeto pai do springboot para iniciar seu código, e depois de herdar o projeto pai do springboot, também haverá estes duas dependências, portanto em Subprojetos sempre terá dependências do tomcat e do jetty. O que devo fazer? É muito simples, basta deixar o subprojeto não herdar a dependência do cais do projeto pai.

        <!--
                这里springboot是使用jetty依赖依赖,但是在user中,引入springboot后会有jetty和tomcat,
            这样就有问题了,所以要保证这个依赖不被user项目引用。这样user中使用jetty时需要自己重新加入依赖,同时要排除springboot中的tomcat依赖。
        -->
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-server</artifactId>
            <version>10.0.14</version>
            <optional>true</optional>
        </dependency>

2. Configuração automática do springboot

2.1 Classe de configuração de contêiner

@SpringBootApplication
public class DemoApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(DemoApplication.class, args);
    }
}

@Target({
    
    ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {
    
    @Filter(
    type = FilterType.CUSTOM,
    classes = {
    
    TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {
    
    AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
    
    
//……………………
}

O método run é passado DemoApplication.class. Você pode ver pelas anotações que esta é uma classe de configuração. Na verdade, essa classe de configuração é usada para criar contêineres. Ao mesmo tempo, ComponentScannão há caminho de pacote para varredura, então o Spring usará como padrão o DemoApplication.classcaminho de varredura como caminho de varredura (alterar a classe de configuração em outros locais não funcionará).

2.2 Itens de configuração automática

spring-boot-starterHá um spring-boot-autoconfigureprojeto no projeto pai do springboot que registra as classes de configuração automática de muitos projetos de terceiros.Quando o springboot é iniciado, essas classes de configuração serão carregadas no contêiner para concluir a configuração automática, assim como a WebServerAutomaticConfigurationclasse acima.
Insira a descrição da imagem aqui
Mas há um problema: uma determinada dependência, como mybatis, spring-boot-autoconfigurenão possui uma classe de configuração automática correspondente. Portanto, a classe de configuração automática do mybatis está no próprio projeto do mybatis.Como o springboot encontra a classe de configuração neste momento?

Neste momento, precisamos usar o famoso arquivo spring.factorise.

2.3 primavera.fatorar

Insira a descrição da imagem aqui

autoconfigureConforme mostrado na figura, também existe um projeto no projeto pai de mybatis , na verdade também existe um spring.factorise, que é o caminho da classe de configuração automática.

Então a função do arquivo spring.factorise é identificar que se trata de um projeto de configuração automática, e ao mesmo tempo, dar o caminho da classe de configuração automática, para que o container possa carregar esta classe e concluir a configuração automática conforme em 1.3.

Nota: Na verdade, este arquivo também existe no spring-boot-autoconfigureprojeto.

Desta forma, a varredura em lote da classe de configuração automática é realizada e, ao mesmo tempo, pode ser filtrada conforme haja uma classe correspondente, por exemplo, existe uma classe de configuração automática de es, mas meu projeto spring-boot-autoconfigurefaz não necessariamente usá-lo.Neste momento, é necessário escolher se deseja verificar a classe de configuração es de acordo com a existência de uma dependência.

Suplemento: É por isso que devemos configurar a fonte de dados quando introduzimos a dependência do mysql.

2.4 Implementação do dpringboot

@Import({AutoConfigurationImportSelector.class})As etapas descritas em 2.3 estão todas na classe anotada com @EnableAutoConfiguration.

@Target({
    
    ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({
    
    AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    
    
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {
    
    };

    String[] excludeName() default {
    
    };
}

A lógica de carregamento da configuração está no AutoConfigurationImportSelectormétodo getAutoConfigurationEntry

    protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    
    
        if (!this.isEnabled(annotationMetadata)) {
    
    
            return EMPTY_ENTRY;
        } else {
    
    
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);//获取所有配置类,会扫描所有的spring.factories
            configurations = this.removeDuplicates(configurations);//去重
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.getConfigurationClassFilter().filter(configurations);//过滤掉没有引入依赖的配置类路径
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }

Se houver algum erro, critique e corrija!

Acho que você gosta

Origin blog.csdn.net/baidu_40120883/article/details/129912776
Recomendado
Clasificación