Desmistificando o ciclo de vida do framework Spring e a explicação detalhada do singleton [perguntas da entrevista com respostas super detalhadas]

Índice

I. Introdução

1.1. Apresentar o conceito de framework Spring e Bean

Segundo, a fase de instanciação do Bean

2.1. Processo de instanciação do Bean

2.2. Introduzir o uso de construtores padrão e métodos de fábrica

3. Fase de inicialização do bean

3.1. Uso da interface InitializingBean e anotação @PostConstruct

3.2. Configuração do método de inicialização do bean e sequência de execução

Em quarto lugar, o estágio de destruição de Bean

4.1. Uso da interface DisposableBean e anotação @PreDestroy

4.2. Configuração do método de destruição do bean e ordem de execução

5. Explicação das perguntas da entrevista

5.1. Ciclo de vida do feijão

para concluir:

5.2 O JavaBean no Spring é um singleton ou múltiplas instâncias?

Argumento:

5.3 Quando o JavaBean de instância única e o JavaBean de várias instâncias são criados?

Argumento:

5.4 Vamos inicializar o JavaBean quando usarmos um singleton?

Argumento:


I. Introdução

1.1. Apresentar o conceito de framework Spring e Bean

oi amigos! Deixe-me analisar a história do Spring Framework e do Bean para você.

O framework Spring é como um santo padroeiro, ele assume o gerenciamento de aplicativos Java. É como um super organizador de festas, permitindo que você gerencie e controle facilmente seus aplicativos. Seu objetivo é fornecer uma maneira eficiente e flexível de criar aplicativos corporativos.

Então, o que é Feijão? Beans são, na verdade, objetos reais no framework Spring. Você pode pensar em Bean como amiguinhos se divertindo na festa da primavera! Cada Bean tem seu próprio ciclo de vida, assim como o ser humano tem fases de nascimento, crescimento, trabalho e aposentadoria.

Em primeiro lugar, a instanciação do Bean é como o nosso nascimento, o Spring é responsável por criar os objetos Bean e carregá-los no container. Então, a atribuição de atributos de Bean é como aprendemos novas habilidades no processo de crescimento, eles são injetados com vários atributos e se tornam coloridos.

Em seguida, é a fase de inicialização do Bean. Assim como precisamos nos preparar antes de iniciar o trabalho, o Bean também pode executar algumas operações de inicialização, incluindo chamar métodos de inicialização específicos ou fazer algum trabalho posterior por meio do pós-processador Bean.

Por fim, quando a aplicação não precisa mais de um Bean, ela pode realizar algumas operações de destruição, como liberação de recursos, fechamento de conexões de banco de dados, etc., assim como nos aposentamos.

Resumindo, o quadro Spring e o Bean são como festas, em que esses amiguinhos vão e vêm em diversas fases, trazendo alegria e realização. Não se preocupe, porém, o Spring Framework é um organizador de festas responsável e garantirá que cada bean seja gerenciado e cuidado adequadamente!

Espero que esta introdução bem-humorada lhe dê uma melhor compreensão da estrutura e dos beans do Spring!

Segundo, a fase de instanciação do Bean

2.1. Processo de instanciação do Bean

O processo de instanciação do Bean refere-se ao processo de conversão do Bean definido em uma instância de objeto disponível. No framework Spring, a instanciação do Bean pode ser dividida nas seguintes etapas:

  1. Carregando definição de Bean: Use arquivos de configuração (como arquivos de configuração XML) ou anotações para carregar definições de Bean no contêiner, e o framework Spring é responsável pela análise.

  2. Instanciar Bean: Cria uma instância de Bean na memória de acordo com as informações de definição e configuração do Bean. Isso pode ser feito por meio de instanciação de construtor, instanciação de método de fábrica ou por meio de reflexão de objeto, etc.

  3. Atribuição de atributos: atribua atributos a objetos Bean instanciados, incluindo tipos de dados básicos, tipos de referência e tipos de coleção. Os atributos podem ser injetados nas dependências por meio de anotações ou arquivos de configuração XML.

  4. Retorno de chamada da interface Aware: Se o Bean implementar a interface Aware, o Spring detectará e chamará automaticamente o método de retorno de chamada correspondente, por exemplo, a interface ApplicationContextAware pode obter o objeto ApplicationContext.

  5. Método de inicialização personalizado: Se o bean estiver configurado com um método de inicialização (pode ser por meio de anotações ou arquivos de configuração XML), o Spring chamará esse método para executar algumas operações de inicialização personalizadas após o bean concluir a atribuição de propriedade.

  6. Chamada de método de pós-processador: Se um pós-processador Bean (BeanPostProcessor) estiver configurado, o Spring detectará e chamará automaticamente seus métodos relacionados para operações de processamento adicionais.

  7. O Bean está pronto: Após as etapas acima, o processo de instanciação do Bean está concluído e pode ser gerenciado e utilizado pelo container.

2.2. Introduzir o uso de construtores padrão e métodos de fábrica

Construtor Padrão : O construtor padrão é um construtor sem argumentos, que não passa nenhum parâmetro para o processo de criação do objeto. O Spring chamará automaticamente o construtor padrão para instanciar o objeto Bean. Este método é amplamente utilizado e é conveniente criar objetos sem passar parâmetros de outras maneiras. Por exemplo:

public class MyBean {
    // 默认构造函数
    public MyBean() {
        // 初始化操作
    }
}

Método fábrica : Um método fábrica é uma forma de instanciar um objeto Bean através de um método especial. Normalmente, esse método é definido em uma classe de fábrica dedicada. Através do método fábrica, podemos ter mais flexibilidade para criar objetos Bean, podendo customizar operações como passagem de parâmetros. Por exemplo:

public class MyBeanFactory {
    // 工厂方法
    public static MyBean createMyBean() {
        // 创建Bean对象的逻辑
        return new MyBean();
    }
}

Exemplo de código: Mostre como usar o construtor padrão e o método de fábrica para instanciar o objeto Bean:

public class MyBean {
    private String message;

    // 默认构造函数
    public MyBean() {
        this.message = "Hello, world!";
    }

    // getter和setter方法

    public static MyBean createMyBean() {
        MyBean myBean = new MyBean();
        myBean.setMessage("Hello, Spring!");
        return myBean;
    }
}

3. Fase de inicialização do bean

3.1. Uso da interface InitializingBean e anotação @PostConstruct

A interface InitializingBean usa

Esta interface define um método afterPropertiesSet(). Quando todas as propriedades do Bean estiverem definidas, o contêiner Spring chamará automaticamente este método para concluir a operação de inicialização.

import org.springframework.beans.factory.InitializingBean;

public class MyBean implements InitializingBean {
    private String message;
    
    public void setMessage(String message) {
        this.message = message;
    }
    
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("InitializingBean: Bean is being initialized with message: " + message);
    }
}

Uso da anotação @PostConstruct

Essa anotação é marcada no método de inicialização do Bean, indicando que o método será executado automaticamente após as propriedades do Bean serem definidas para concluir a operação de inicialização do Bean.

import javax.annotation.PostConstruct;

public class MyBean {
    private String message;
    
    public void setMessage(String message) {
        this.message = message;
    }
    
    @PostConstruct
    public void init() {
        System.out.println("@PostConstruct: Bean is being initialized with message: " + message);
    }
}

Vale ressaltar que a interface InitializingBean e a anotação @PostConstruct podem ser utilizadas ao mesmo tempo, mas esses dois métodos não são obrigatórios, você pode escolher um deles para inicializar o Bean de acordo com sua necessidade.

No Spring, você também pode especificar o método de inicialização do Bean por meio do arquivo de configuração e usar o atributo init-method da tag <bean> no arquivo de configuração XML para defini-lo. Os exemplos são os seguintes:

<bean id="myBean" class="com.example.MyBean" init-method="init">
    <property name="message" value="Hello World" />
</bean>

3.2. Configuração do método de inicialização do bean e sequência de execução

Seja usando a interface InitializingBean, anotação @PostConstruct ou arquivo de configuração XML, eles podem ser usados ​​para especificar o método de inicialização do Bean, e a ordem de execução é a mesma: primeiro execute a injeção de dependência do atributo e depois execute o método de inicialização.

public class Example {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        
        MyBean bean = context.getBean(MyBean.class);
        // Bean已经被初始化完成并且可以使用
        
        ((ConfigurableApplicationContext) context).close();
    }
}

No exemplo acima, um objeto ApplicationContext é criado e um arquivo de configuração XML é carregado. Ao chamar o método getBean() para obter uma instância de MyBean, o contêiner Spring executará automaticamente as operações de injeção e inicialização de propriedades. Ao fecharmos a aplicação, podemos chamar o método ((ConfigurableApplicationContext) context).close() para fechar o container.

Em quarto lugar, o estágio de destruição de Bean

4.1. Uso da interface DisposableBean e anotação @PreDestroy

A interface DisposableBean usa

Essa interface define um método destroy(). Quando o Bean precisar ser destruído, o contêiner Spring chamará automaticamente esse método para concluir o trabalho de limpeza. Os exemplos são os seguintes:

import org.springframework.beans.factory.DisposableBean;

public class MyBean implements DisposableBean {
    private String message;
    
    public void setMessage(String message) {
        this.message = message;
    }
    
    @Override
    public void destroy() throws Exception {
        System.out.println("DisposableBean: Bean is being destroyed with message: " + message);
    }
}

Uso da anotação @PreDestroy

Essa anotação é marcada no método de destruição do bean, indicando que o método será executado automaticamente quando o bean precisar ser destruído para concluir o trabalho de limpeza. Os exemplos são os seguintes:

import javax.annotation.PreDestroy;

public class MyBean {
    private String message;
    
    public void setMessage(String message) {
        this.message = message;
    }
    
    @PreDestroy
    public void cleanup() {
        System.out.println("@PreDestroy: Bean is being destroyed with message: " + message);
    }
}

A destruição do Bean também pode ser especificada através do arquivo de configuração para destruir o método do Bean, usando o atributo destroy-method da tag <bean> no arquivo de configuração XML para definir. Os exemplos são os seguintes:

<bean id="myBean" class="com.example.MyBean" destroy-method="cleanup">
    <property name="message" value="Goodbye World" />
</bean>

4.2. Configuração do método de destruição do bean e ordem de execução

O método destroy do bean é executado primeiro e, em seguida, o contêiner é fechado ou destruído.

public class Example {
    public static void main(String[] args) {
        // 创建并启动Spring容器
        ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        
        // 获取Bean实例
        MyBean bean = context.getBean(MyBean.class);
        
        // 使用Bean...
        
        // 手动关闭容器
        context.close();
    }
}

No exemplo acima, criamos um objeto ApplicationContext e carregamos um arquivo de configuração XML. Ao chamar o método getBean() para obter uma instância de MyBean, o contêiner Spring executará automaticamente o método de destruição do Bean. Quando fechamos manualmente o container, podemos chamar o método context.close() para acionar a destruição do Bean.

5. Explicação das perguntas da entrevista

5.1. Ciclo de vida do feijão

1. Carregar Spring Bean através de XML, anotação Java (anotação) e configuração Java (classe de configuração), etc.

2.BeanDefinitionReader: Analisa a definição de Bean. Durante o processo de inicialização do contêiner Spring, o Bean será analisado na estrutura BeanDefinition dentro do Spring; entende-se que converter a tag <bean> em spring.xml na estrutura BeanDefinition é um pouco semelhante à análise XML

3.BeanDefinition: Contém muitas propriedades e métodos. Por exemplo: id, class (nome da classe), escopo, ref (bean dependente), etc. Na verdade, é para armazenar as informações de definição do bean (como <bean>) no atributo correspondente do BeanDefinition correspondente. Por exemplo:

<bean id="" class="" scope=""> -----> BeanDefinition(id/class/scope)

4.BeanFactoryPostProcessor: É uma interface de extensão da função do container Spring.

Perceber:

1) Após o BeanFactoryPostProcessor carregar o BeanDefinition no contêiner de mola,

Executado antes do bean ser instanciado

2) Processar os metadados do bean (BeanDefinition), ou seja, BeanDefinition

Preenchimento de atributos, modificação e outras operações

5. BeanFactory: fábrica de feijão. Produz os vários grãos de que necessitamos de acordo com as nossas necessidades.

Por exemplo:

BeanFactory -> List<BeanDefinition>

BeanDefinition(id/class/scope/init-method)

<bean class="com.zking.spring02.biz.BookBizImpl"/>

foreach(BeanDefinition bean : List<BeanDefinition>){

   //Instancia o objeto de acordo com o mecanismo de reflexão do atributo da classe

   // propriedade de definição de atribuição reflexiva

}

6. Interface aware-aware: No desenvolvimento real, muitas vezes é necessário usar os recursos funcionais do próprio contêiner Spring

Por exemplo: BeanNameAware, ApplicationContextAware, etc.

BeanDefinition implementa BeanNameAware, ApplicationContextAware

7.BeanPostProcessor: pós-processador. Depois que o objeto Bean for instanciado e injetado, adicione a lógica personalizada antes e depois de chamar o método de inicialização. (Semelhante à notificação circular do AOP) Pré-condição: Se for detectado que o objeto Bean implementa o pós-processador BeanPostProcessor, os métodos Before e After serão executados

BeanPostProcessor

1)Antes

2) Chame o bean de inicialização (InitializingBean e init-method, a inicialização do bean é considerada completa)

3)Depois

8.destory: destruir

para concluir:

1.xml/annotation/configuration configure o JavaBean.

2. BeanDefinitionReader analisa o Javabean configurado para obter BeanDefinition e, finalmente, obtém uma coleção de listas.

3. Acione BeanFactoryPostProcessor, o JavaBean executa seu próprio negócio antes da inicialização.

4. O beanFactory no spring percorrerá e inicializará todos os objetos JavaBean por meio da coleção List<BeanDefinition> (instanciação de reflexão).

5. Se nosso próprio JavaBean precisa mobilizar recursos no contexto da primavera, precisamos implementar a *interface de percepção consciente

6. Se seu próprio JavaBean foi inicializado e precisa ser estendido, você precisa usar o BeanPostProcessor para alcançá-lo.

7. Destruir (destruir).

5.2 O JavaBean no Spring é um singleton ou múltiplas instâncias?

1. O padrão é uma única instância, mas várias instâncias podem ser configuradas

2. A vantagem de uma única instância é economizar memória, mas a desvantagem é que há poluição variável (a vantagem de várias instâncias é que não há poluição variável, mas consome muita memória)

Uma história curta e engraçada para expressar a diferença entre instâncias únicas e múltiplas e as vantagens e desvantagens de cada uma.


Em uma escola de magia de fantasia, há um professor especial chamado "Magic Man". Ele tem habilidades supermágicas e é responsável por ensinar aos alunos todos os tipos de feitiços mágicos.

Para Magic Man, ele é um caso único completo. Ele é o único em toda a escola com poderes mágicos tão poderosos, e todos os alunos e funcionários recorrerão a ele em busca de ajuda e orientação. Ele está sempre cercado pelo centro da escola e todos perseguem sua existência.

No entanto, devido ao status especial do Magic Man, seu poder mágico permeou todo o ambiente escolar. Seja na sala de aula ou em outros lugares da escola, está cheio de sua atmosfera mágica única. Às vezes, quando os alunos tentavam lançar seus feitiços, eram afetados pelo poder do Magicman, que produzia efeitos inesperados.

Em contraste, existe outro professor chamado "Múltiplas Nuvens". Ela é um caso múltiplo e pode ser encontrada em todas as classes. Ela possui poderes mágicos gentis e flexíveis, hábeis em se adaptar às necessidades e circunstâncias de seus alunos.

À medida que os alunos lançam seus feitiços sob a orientação de Duodiyun, ela os ajuda a atingir seu potencial máximo, dependendo do ambiente e das necessidades da classe. Como seus poderes mágicos não contaminam a influência de outras classes, os alunos podem explorar e desenvolver suas habilidades mágicas com mais liberdade.

Esta história nos diz que singletons e instâncias múltiplas têm suas próprias vantagens e desvantagens. A vantagem de um singleton está no gerenciamento e autoridade centralizados, mas também é propenso ao problema de poluição variável. Múltiplas instâncias podem fornecer serviços customizados para diferentes cenários, evitando o problema potencial de poluição variável.

Ao projetar software, devemos escolher o modo apropriado de acordo com as necessidades específicas. Singletons podem ser usados ​​para situações em que o acesso globalmente unificado e os recursos compartilhados são necessários. Para cenários que exigem flexibilidade e nenhuma poluição variável, os casos múltiplos serão uma escolha melhor. Precisamos equilibrar as vantagens e desvantagens de instâncias únicas e múltiplas e fazer uma escolha sábia com base em necessidades específicas.

Argumento:

ParamAction.java

package com.csdn.xw.aop.beanLife;

import java.util.List;

public class ParamAction {
	private int age;
	private String name;
	private List<String> hobby;
	private int num = 1;
	// private UserBiz userBiz = new UserBizImpl1();

	public ParamAction() {
		super();
	}

	public ParamAction(int age, String name, List<String> hobby) {
		super();
		this.age = age;
		this.name = name;
		this.hobby = hobby;
	}

	public void execute() {
		// userBiz.upload();
		// userBiz = new UserBizImpl2();
		System.out.println("this.num=" + this.num++);
		System.out.println(this.name);
		System.out.println(this.age);
		System.out.println(this.hobby);
	}
}

spring-context.xml

  <bean id="paramAction" class="com.csdn.xw.aop.beanLife.ParamAction">
        <constructor-arg name="name" value="三丰"></constructor-arg>
        <constructor-arg name="age" value="21"></constructor-arg>
        <constructor-arg name="hobby">
            <list>
                <value>抽烟</value>
                <value>烫头</value>
                <value>大保健</value>
            </list>
        </constructor-arg>
    </bean>

classe de teste de teste

package com.csdn.xw.aop.beanLife;

import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

/*
 * spring	bean的生命週期
 * spring	bean的單例多例
 */
public class Demo2 {
	// 体现单例与多例的区别
	@Test
	public void test1() {
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml");
//		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml");
		ParamAction p1 = (ParamAction) applicationContext.getBean("paramAction");
		ParamAction p2 = (ParamAction) applicationContext.getBean("paramAction");
		// System.out.println(p1==p2);
		p1.execute();
		p2.execute();
		
//		单例时,容器销毁instanceFactory对象也销毁;多例时,容器销毁对象不一定销毁;
		applicationContext.close();
	}

	// 体现单例与多例的初始化的时间点 instanceFactory
	@Test
	public void test2() {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml");
	}

	// BeanFactory会初始化bean对象,但会根据不同的实现子类采取不同的初始化方式
	// 默认情况下bean的初始化,单例模式立马会执行,但是此时XmlBeanFactory作为子类,单例模式下容器创建,bean依赖没有初始化,只有要获取使用bean对象才进行初始化
	@Test
	public void test3() {
		// ClassPathXmlApplicationContext applicationContext = new
		// ClassPathXmlApplicationContext("/spring-context.xml");

		Resource resource = new ClassPathResource("/spring-context.xml");
		BeanFactory beanFactory = new XmlBeanFactory(resource);
//		InstanceFactory i1 = (InstanceFactory) beanFactory.getBean("instanceFactory");
		
	}

}

resultado da operação:

Desta vez, definimos manualmente várias instâncias de scope="prototype" e damos uma olhada

5.3 Quando o JavaBean de instância única e o JavaBean de várias instâncias são criados?

 Caso único: JavaBean é inicializado com o contexto Spring, o objeto do contêiner nasce e o objeto do contêiner está morto

 Vários exemplos: o JavaBean é criado quando é usado e é destruído seguindo a JVM

Argumento:

InstanceFactory.java

package com.csdn.xw.aop.beanLife;

public class InstanceFactory {
	public void init() {
		System.out.println("初始化方法");
	}

	public void destroy() {
		System.out.println("销毁方法");
	}

	public void service() {
		System.out.println("业务方法");
	}
}

spring-context.xml

  <bean id="instanceFactory" class="com.csdn.xw.aop.beanLife.InstanceFactory"
          scope="prototype" init-method="init" destroy-method="destroy"></bean>

Você pode ver que a configuração do xml acima tem várias instâncias. Vamos chamar test2 para ver se nossa "inicialização do Bean" está concluída?

Pode-se ver que não, pois só pegamos o objeto context do spring e não o objeto Bean. Agora mudamos para singleton scope="singleton" para ver o resultado.

 Neste momento, descobrimos que o JavaBean foi criado independentemente de termos obtido o objeto Bean ou não.

Resumo: Por que o singleton é inicializado depois que o contêiner é criado? Porque a ideia de um singleton é criá-lo não importa quando você o usa. Se você esperar que seu navegador envie uma solicitação para criá-lo, isso reduzirá muito a experiência do usuário. De qualquer forma, ele será apenas criado uma vez. Por que não criá-lo quando você iniciar o projeto? Tudo bem, demorado no projeto de inicialização. Se houver várias instâncias, você criará cem se houver cem? Você cria mil deles, e se eu tiver mil e só usar um? Os 999 restantes são desperdiçados, portanto, várias instâncias só serão criadas quando você as usar.

5.4 Vamos inicializar o JavaBean quando usarmos um singleton?

BeanFactory inicializará objetos bean, mas adotará diferentes métodos de inicialização de acordo com diferentes subclasses de implementação. Por padrão, a inicialização do bean será executada imediatamente no modo singleton, mas neste momento XmlBeanFactory é usado como uma subclasse. Criação de contêiner no modo singleton, beans A dependência não é inicializada, apenas para obter o objeto bean a ser inicializado.

Argumento:

Chame test3 para verificar os resultados

Como uma subclasse de XmlBeanFactory, o contêiner é criado no modo singleton e a dependência do bean não é inicializada, então vamos pegá-lo e usá-lo

 Neste ponto, o Bean é criado.

Meu compartilhamento acabou aqui, bem-vindo à área de comentários para discutir e se comunicar! !

Se você achar útil, dê um joinha ♥ ♥

Acho que você gosta

Origin blog.csdn.net/weixin_74318097/article/details/132358501
Recomendado
Clasificación