Três dias para entender o ensaio estereotipado da entrevista de primavera

Trecho do meu site de entrevistas: topjavaer.cn

O que é a Primavera?

Spring é uma estrutura de contêiner leve de Inversão de Controle (IoC) e Aspect Oriented (AOP). O Site de Entrevista Java Mais Abrangente: O Site de Entrevista Java Mais Abrangente

Vantagens da Primavera

  • O baixo acoplamento é obtido por meio da inversão do controle e da injeção de dependência .
  • Oferece suporte à programação orientada a aspectos e separa a lógica de negócios do aplicativo dos serviços do sistema.
  • Reduza o código clichê com aspectos e modelos.
  • Suporte para transações declarativas. Ele pode ser liberado de códigos de gerenciamento de transações monótonos e complicados e gerenciar transações de forma flexível de maneira declarativa, melhorando a eficiência e a qualidade do desenvolvimento.
  • É conveniente integrar várias estruturas excelentes. Internamente fornece suporte direto para vários frameworks excelentes (tais como: Hessian, Quartz, MyBatis, etc.).
  • Facilitar o teste do programa. O Spring oferece suporte ao Junit4 e os programas Spring podem ser testados adicionando anotações.

Quais padrões de design o Spring usa?

1. Modo fábrica simples : BeanFactoryé a personificação do modo fábrica simples, e o objeto Bean é obtido passando um identificador único.

@Override
public Object getBean(String name) throws BeansException {
    
    
    assertBeanFactoryActive();
    return getBeanFactory().getBean(name);
}

2. Padrão de método de fábrica : FactoryBeanÉ um padrão típico de método de fábrica. O Spring getBean()chamará automaticamente o método do bean quando o bean for obtido usando a chamada getObject(). Cada Bean corresponderá a um FactoryBean, como SqlSessionFactorycorrespondente SqlSessionFactoryBean.

3. Modo Singleton : Uma classe possui apenas uma instância, fornecendo um ponto de acesso global para acessá-la. O Spring cria instâncias de Bean que são singletons por padrão.

4. Modo Adaptador : Adaptador no SpringMVC HandlerAdatper. Como o aplicativo terá várias implementações de Controlador, se você precisar chamar diretamente o método Controlador, precisará primeiro determinar qual Controlador manipula a solicitação e, em seguida, chamar o método correspondente. Ao adicionar um novo Controller, a lógica original precisa ser modificada, o que viola o princípio aberto-fechado (fechado para modificação, aberto para extensão).

Para isso, o Spring fornece uma interface adaptadora. Cada tipo de Controller corresponde a uma classe HandlerAdapterde implementação . Quando chega uma requisição, o SpringMVC chama getHandler()para obter o Controller correspondente, em seguida obtém o Controller correspondente e HandlerAdapterfinalmente chama o método para processar a requisição. Na verdade, o método chamado It is Controller . Cada vez que você adiciona um novo Controller, você só precisa adicionar uma classe adaptadora sem modificar a lógica original.HandlerAdapterhandle()handleRequest()

Adaptadores de processador comuns: SimpleControllerHandlerAdapter, HttpRequestHandlerAdapter, AnnotationMethodHandlerAdapter.

// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

public class HttpRequestHandlerAdapter implements HandlerAdapter {
    
    

    @Override
    public boolean supports(Object handler) {
    
    //handler是被适配的对象,这里使用的是对象的适配器模式
        return (handler instanceof HttpRequestHandler);
    }

    @Override
    @Nullable
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
        throws Exception {
    
    

        ((HttpRequestHandler) handler).handleRequest(request, response);
        return null;
    }
}

5. Modo proxy : o aop do spring usa proxy dinâmico, existem duas maneiras JdkDynamicAopProxye Cglib2AopProxy.

6. Modo observador : O local comumente usado para o modo observador na primavera é a implementação do ouvinte, como ApplicationListener.

7. Modo de modelo : No Spring jdbcTemplate, hibernateTemplateetc., o modo de modelo é usado.

Este artigo foi incluído no depósito do Github, que inclui base de computador, base de Java, multithreading, JVM, banco de dados, Redis, Spring, Mybatis, SpringMVC, SpringBoot, distribuído, microsserviços, padrões de design, arquitetura, recrutamento escolar e compartilhamento de recrutamento social, etc. Pontos de conhecimento básico, bem-vindo à estrela~

Endereço do Github: Endereço do Github

Se você não consegue acessar o Github, pode acessar o endereço gitee.

Link: endereço do gitee

O que é AOP?

A programação orientada a aspectos , como um complemento à orientada a objetos, encapsula a lógica comum (gerenciamento de transações, log, cache etc.) em aspectos e os separa dos códigos de negócios, o que pode reduzir a duplicação de código no sistema e reduzir o acoplamento entre módulos. Aspectos são aquelas lógicas comuns que não têm nada a ver com negócios, mas são chamadas por todos os módulos de negócios.

Quais são as implementações do AOP?

Existem duas maneiras de implementar o AOP: proxy estático e proxy dinâmico.

proxy estático

Proxy estático: a classe proxy é gerada durante a fase de compilação e a notificação é inserida no bytecode Java durante a fase de compilação, também conhecida como aprimoramento de tempo de compilação. AspectJ usa proxies estáticos.

Desvantagem: O objeto proxy precisa implementar a mesma interface que o objeto de destino e o método de implementação da interface terá código redundante. Ao mesmo tempo, uma vez que o método é adicionado à interface, tanto o objeto alvo quanto o objeto proxy devem ser mantidos.

proxy dinâmico

Proxy dinâmico: A classe proxy é criada durante a execução do programa. A estrutura AOP não modificará o bytecode, mas gerará temporariamente um objeto proxy na memória. O método de negócios será aprimorado durante o tempo de execução e nenhuma nova classe será gerada .

Princípio de implementação do Spring AOP

SpringO AOPprincípio de realização é realmente muito simples, é realizado por meio de proxy dinâmico . Se configurarmos um aspecto Springpara um determinado objeto, quando criarmos este , na verdade, criaremos um objeto proxy desse objeto, e nossas chamadas subsequentes para o método do meio, na verdade, chamarão o método proxy reescrito pela classe proxy. No entanto , dois proxies dinâmicos são usados, ou seja, o proxy dinâmico do JDK e o proxy dinâmico do CGLib .beanSpringbeanbeanbeanSpringAOP

Qual é a diferença entre o proxy dinâmico JDK e o proxy dinâmico CGLIB?

Existem duas formas principais de proxy dinâmico no Spring AOP: proxy dinâmico JDK e proxy dinâmico CGLIB.

Por fim, gostaria de compartilhar com vocês um repositório Github, que possui mais de 300 PDFs de livros clássicos de informática compilados por Dabin, incluindo linguagem C, C++, Java, Python, front-end, banco de dados, sistema operacional, rede de computadores, estrutura de dados e algoritmo, aprendizado de máquina, vida útil da programação , etc., você pode começar, da próxima vez que procurar um livro, pesquise diretamente nele, o armazém é atualizado continuamente ~

Endereço do Github

Se você não conseguir acessar o Github, poderá acessar o endereço da nuvem de código.

Endereço da nuvem de código

Proxy dinâmico do JDK

Se a classe de destino implementar a interface, o Spring AOP escolherá usar a classe de destino do proxy dinâmico JDK. A classe de proxy é gerada dinamicamente de acordo com a interface implementada pela classe de destino e não há necessidade de escrevê-la. Tanto a classe de proxy dinâmica gerada quanto a classe de destino implementam a mesma interface. O núcleo do proxy dinâmico JDK é InvocationHandlerinterface e Proxyclasse.

Desvantagem: A classe de destino deve ter uma interface implementada . Se uma classe não implementar a interface, essa classe não poderá ser dinamicamente representada pelo JDK.

proxy dinâmico CGLIB

Conquistado por herança . Se a classe de destino não implementar a interface, o Spring AOP escolherá usar CGLIB para fazer proxy dinamicamente da classe de destino. CGLIB (Biblioteca de Geração de Código) pode gerar dinamicamente o bytecode da classe em tempo de execução, criar dinamicamente o objeto de subclasse da classe de destino e aprimorar a classe de destino no objeto de subclasse.

CGLIB é um proxy dinâmico por meio de herança, portanto, se uma classe for marcada como final, ela não poderá usar CGLIB como um proxy dinâmico.

Vantagens: A classe de destino não precisa implementar uma interface específica, que é mais flexível.

Quando usar qual proxy dinâmico?

  1. Se o objeto de destino implementa a interface, por padrão, o proxy dinâmico do JDK será usado para implementar o AOP
  2. AOP pode ser forçado a usar CGLIB se o objeto de destino implementa a interface
  3. Se o objeto de destino não implementar a interface, a biblioteca CGLIB deve ser usada

A diferença entre os dois :

  1. O proxy dinâmico jdk usa a classe Proxy em jdk para criar objetos proxy, que são implementados usando tecnologia de reflexão e não precisam importar outras dependências. cglib precisa introduzir dependências relacionadas: asm.jar, que usa a tecnologia de aprimoramento de bytecode para alcançar.
  2. Quando a classe de destino implementa a interface, o Spring Aop usa o método de proxy dinâmico jdk para aprimorar o método por padrão e usa o método de proxy dinâmico cglib para aprimorar o método quando a interface não é implementada.

Termos relacionados ao Spring AOP

(1) Aspect : Aspect é uma combinação de notificação e pointcut. Juntos, conselhos e pontos de corte definem o conteúdo geral de um aspecto.

(2) Ponto de junção : refere-se ao método.No Spring AOP, um ponto de junção sempre representa a execução de um método. Um ponto de junção é um ponto durante a execução do aplicativo no qual um aspecto pode ser inserido. Esse ponto pode ser quando um método é chamado, quando uma exceção é lançada ou até mesmo quando um campo é modificado. O código de aspecto pode usar esses pontos para ser inserido no fluxo normal do aplicativo e adicionar um novo comportamento.

(3) Conselho (Advice): Na terminologia AOP, o trabalho do aspecto é chamado de conselho.

(4) Pointcut : A definição de um pointcut corresponde a um ou mais pontos de junção nos quais o conselho deve ser tecido. Geralmente especificamos esses pointcuts usando nomes explícitos de classe e método ou usando expressões regulares para definir os nomes de classe e método a serem correspondidos.

(5) Introdução : A introdução nos permite adicionar novos métodos ou propriedades às classes existentes.

(6) Target Object (Target Object): Um objeto que é aconselhado por um ou mais aspectos. Geralmente é um objeto proxy.

(7) Tecelagem : Tecelagem é o processo de aplicar aspectos a objetos-alvo e criar novos objetos proxy. A tecelagem pode ser realizada nos seguintes pontos do ciclo de vida do objeto de destino:

  • Tempo de compilação: os aspectos são integrados quando a classe de destino é compilada. O compilador de tecelagem do AspectJ tece aspectos dessa maneira.
  • Período de carregamento da classe: o aspecto é entrelaçado quando a classe de destino é carregada na JVM. São necessários carregadores de classes especiais, que aprimoram o bytecode de uma classe de destino antes que ela seja trazida para o aplicativo. A tecelagem de tempo de carregamento do AspectJ5 suporta aspectos de tecelagem dessa maneira.
  • Tempo de execução: os aspectos são integrados em algum ponto durante a execução do aplicativo. Geralmente, ao entrelaçar um aspecto, o contêiner AOP criará dinamicamente um objeto proxy para o objeto de destino. O Spring AOP tece aspectos dessa maneira.

O site de entrevistas em Java mais abrangente

Quais são os tipos de conselhos de primavera?

Na terminologia AOP, o trabalho dos aspectos é chamado de conselho. O conselho é, na verdade, um segmento de código que é acionado pela estrutura Spring AOP quando o programa está em execução.

Os aspectos da primavera podem aplicar 5 tipos de conselhos:

  1. Pré-notificação (Antes): chama a função de notificação antes que o método de destino seja chamado;
  2. Pós-notificação (Depois): A notificação é chamada depois que o método de destino é concluído e não importa qual seja a saída do método neste momento;
  3. After-returning: a notificação é invocada depois que o método de destino é executado com sucesso;
  4. Notificação de exceção (após o lançamento): chama a notificação após o método de destino lançar uma exceção;
  5. Em torno da notificação (Around): a notificação envolve o método notificado e executa a lógica personalizada antes e depois que o método notificado é chamado.

O que é injeção de dependência?

No processo de criação de um objeto no Spring, as propriedades das quais o objeto depende são injetadas no objeto. Existem duas formas principais de injeção de dependência: injeção de construtor e injeção de propriedade.

O que são COIs?

IOC: Inversão de controle , todo o ciclo de vida do bean é gerenciado pelo container Spring. Realize o controle de outros objetos por meio de reflexão, incluindo inicialização, criação, destruição, etc., libere o processo de criação manual de objetos e reduza o acoplamento entre as classes.

Benefícios dos COIs?

O cerne da ideia do ioc é que os recursos não são geridos por quem os utiliza, mas por terceiros que não utilizam os recursos, o que pode trazer muitos benefícios. Primeiro, o gerenciamento centralizado de recursos permite a configuração de recursos e o gerenciamento fácil. Em segundo lugar, reduz-se o grau de dependência entre as duas partes que utilizam os recursos, que é o que chamamos de grau de acoplamento.

Em outras palavras, a Parte A não precisa depender diretamente da Parte B para atingir um determinado objetivo, ela só precisa informar à organização terceirizada o que deseja alcançar. Por exemplo, a Parte A precisa de um par de meias e a Parte B vende um par de meias e quer vender as meias Saindo, você não precisa encontrar um vendedor diretamente para concluir a venda de meias. Também só precisa encontrar um terceiro e dizer a alguém que quero vender um par de meias. Agora, ambas as partes não precisam ir diretamente ao vendedor para realizar transações, o que equivale a uma interface aberta dentro do programa, e o vendedor é passado como parâmetro por um terceiro. A e B não dependem um do outro e somente quando realizam atividades de transação, A e B estão em contato. vice-versa. Quais são os benefícios disso? A Parte A e a Parte B podem existir independentemente quando a outra parte não existe, e é garantido que não haverá contato quando não estiverem negociando, e podem entrar em contato facilmente quando quiserem troca. As atividades de transação de A e B não exigem que as duas partes se encontrem, o que evita o problema de falha na transação causada pela desconfiança mútua entre as duas partes. Porque a transação é contatada por um terceiro, e tanto a Parte A quanto a Parte B acreditam que o terceiro é confiável. Assim, as transações podem ser geradas e executadas de forma confiável e flexível.

Essa é a ideia central do IOC. Exemplos disso são abundantes na vida. Alipay é um enorme contêiner IOC em todo o sistema Taobao. Um terceiro, além das duas partes da transação, fornece um centro de gerenciamento de recursos confiável que pode alterar com flexibilidade a parte da transação. Além disso, as agências de pessoal também são terceiros, exceto agências de emprego e pessoas físicas.

Processo de inicialização do contêiner IOC?

  1. Leia arquivos de configuração de XML.
  2. Analisar marcas de bean em BeanDefinition, como analisar elementos de propriedade, e injetá-los em instâncias de BeanDefinition.
  3. Registre o BeanDefinition no contêiner BeanDefinitionMap.
  4. O BeanFactory cria beans instanciados e inicializados com base nas informações de definição do BeanDefinition.

A inicialização e a injeção de dependência de singleton beans são geralmente executadas durante a fase de inicialização do contêiner. Somente os singleton beans de carregamento lento (lazy-init é verdadeiro) são inicializados e a dependência injetada quando o aplicativo chama getBean() pela primeira vez.

// AbstractApplicationContext
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);

Múltiplas instâncias de beans não serão instanciadas quando o contêiner iniciar, mesmo se lazy-init for definido como false, ele será instanciado apenas quando getBean() for chamado.

loadBeanDefinitionsO modo de modelo é adotado e a lógica BeanDefinitionde carregamento é concluída por cada subclasse.

Ciclo de vida do feijão

1. Chame o construtor do bean para criar o bean

2. Injeção de dependência de propriedades chamando o método setter por meio de reflexão

3. Se o Bean implementar BeanNameAwarea interface, o Spring irá chamar setBeanName(), Beandefinir o nome (o id da tag do bean no arquivo xml)

4. Se o Bean implementar BeanFactoryAwarea interface, o Spring chamará setBeanFactory()para definir a fábrica de bean para o Bean

5. Se eles existirem BeanPostProcessor, o Spring chamará seus postProcessBeforeInitializationmétodos (pré-inicialização) para processá-los antes que o Bean seja inicializado

6. Se o bean implementa InitializingBeana interface, o Spring chamará seu afterPropertiesSetmétodo e, em seguida, chamará o método init-method definido por xml.Os dois métodos têm funções semelhantes e são executados quando o bean é inicializado.

7. Se presente BeanPostProcessor, o Spring chamará seus postProcessAfterInitializationmétodos (pós-inicialização) para processar o Bean depois que ele for inicializado

8. O Bean está inicializado e pronto para ser utilizado pela aplicação. Existem dois casos aqui:

8.1 Se o bean for um singleton, o contêiner retornará o bean ao usuário e o armazenará no pool de cache. Se o Bean implementar DisposableBeana interface, o Spring chamará seu destorymétodo, e depois chamará destory-methodo método .Esses dois métodos possuem funções similares e são executados antes que a instância do Bean seja destruída.

8.2 Se o Bean tiver várias instâncias, o contêiner retorna o Bean ao usuário e o ciclo de vida restante é controlado pelo usuário.

public interface BeanPostProcessor {
    
    
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    
    
		return bean;
	}
	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    
    
		return bean;
	}
}
public interface InitializingBean {
    
    
	void afterPropertiesSet() throws Exception;
}

A diferença entre BeanFactory e FactoryBean?

BeanFactory : O contêiner que gerencia os beans e os beans gerados no Spring são todos gerenciados pela implementação dessa interface.

FactoryBean : Geralmente é usado para criar beans mais complexos. Beans gerais podem ser configurados diretamente com xml, mas se muitos outros beans e lógica complexa estiverem envolvidos no processo de criação de um bean, é mais problemático configurar diretamente com xml. desta vez, considere o uso do FactoryBean, que pode ocultar os detalhes da instanciação de beans complexos.

Quando a classe de implementação configurada pelo atributo class do tag bean no arquivo de configuração é FactoryBean, o que é retornado pelo método getBean() não é o próprio FactoryBean, mas o objeto retornado pela chamada do método FactoryBean#getObject(), que é equivalente ao proxy FactoryBean#getObject() O método getBean() foi adicionado. Se você deseja obter o FactoryBean, deve usar '&' + beanName para obtê-lo.

Mybatis fornece configuração SqlSessionFactoryBeanque pode ser SqlSessionFactorysimplificada :

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
    
    
  @Override
  public void afterPropertiesSet() throws Exception {
    
    
    notNull(dataSource, "Property 'dataSource' is required");
    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
    state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
              "Property 'configuration' and 'configLocation' can not specified with together");
    this.sqlSessionFactory = buildSqlSessionFactory();
  }

  protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
    
    
	//复杂逻辑
  }
    
  @Override
  public SqlSessionFactory getObject() throws Exception {
    
    
    if (this.sqlSessionFactory == null) {
    
    
      afterPropertiesSet();
    }
    return this.sqlSessionFactory;
  }
}

Configure SqlSessionFactoryBean em xml:

<bean id="tradeSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="trade" />
    <property name="mapperLocations" value="classpath*:mapper/trade/*Mapper.xml" />
    <property name="configLocation" value="classpath:mybatis-config.xml" />
    <property name="typeAliasesPackage" value="com.bytebeats.mybatis3.domain.trade" />
</bean>

O Spring irá criá-lo na inicialização do aplicativo SqlSessionFactorye armazená-lo com sqlSessionFactoryeste nome.

Qual é a diferença entre BeanFactory e ApplicationContext?

BeanFactory e ApplicationContext são as duas principais interfaces do Spring, e ambas podem ser usadas como contêineres do Spring. Onde ApplicationContext é uma subinterface de BeanFactory.

A diferença entre os dois é a seguinte:

1. Diferença funcional. BeanFactory é a interface de nível mais baixo no Spring, que inclui as definições de vários beans, lê documentos de configuração de bean, gerencia o carregamento e a instanciação de beans, controla o ciclo de vida dos beans e mantém as dependências entre os beans.

Como uma derivação de BeanFactory, a interface ApplicationContext não apenas fornece as funções de BeanFactory, mas também fornece funções de estrutura mais completas, como herança de MessageSource, suporte à internacionalização, modo de acesso de arquivo de recurso unificado e carregamento de vários arquivos de configuração ao mesmo tempo.

2. A diferença nos métodos de carregamento. BeanFactroy usa carregamento lento para injetar beans, ou seja, somente quando um bean é usado (chamada getBean()), o bean é carregado e instanciado. Desta forma, não podemos encontrar alguns problemas existentes de configuração do Spring. Se uma determinada propriedade do Bean não for injetada, após o BeanFacotry ser carregado, uma exceção não será lançada até que o método getBean seja chamado pela primeira vez.

O ApplicationContext cria todos os beans de uma vez quando o contêiner é iniciado. Dessa forma, quando o contêiner é iniciado, podemos encontrar erros de configuração no Spring, o que é benéfico para verificar se as propriedades dependentes são injetadas. Depois que o ApplicationContext é iniciado, todos os beans singleton são pré-carregados, portanto, quando necessário, não há necessidade de aguardar a criação dos beans, pois eles já foram criados.

Comparado com o BeanFactory básico, a única desvantagem do ApplicationContext é que ele ocupa espaço de memória. Quando o aplicativo configura muitos beans, o programa inicia lentamente.

3. A diferença no modo de criação. BeanFactory geralmente é criado programaticamente e ApplicationContext também pode ser criado declarativamente, como usando ContextLoader.

4. Diferenças nos métodos de registro. Ambos BeanFactory e ApplicationContext suportam o uso de BeanPostProcessor e BeanFactoryPostProcessor, mas a diferença entre os dois é que BeanFactory precisa ser registrado manualmente, enquanto ApplicationContext é registrado automaticamente.

Quais são as maneiras de injetar feijão em recipientes?

1、@Configuração + @Bean

@Configuration é usado para declarar uma classe de configuração e, em seguida, usar a anotação @Bean para declarar um bean e adicioná-lo ao contêiner Spring.

@Configuration
public class MyConfiguration {
    
    
    @Bean
    public Person person() {
    
    
        Person person = new Person();
        person.setName("大彬");
        return person;
    }
}

2. A forma de escanear anotações específicas por meio de pacotes

@ComponentScan é colocado em nossa classe de configuração e, em seguida, podemos especificar um caminho para verificar beans com anotações específicas e adicioná-los ao contêiner.

Anotações específicas incluem @Controller, @Service, @Repository, @Component

@Component
public class Person {
    
    
    //...
}
 
@ComponentScan(basePackages = "com.dabin.test.*")
public class Demo1 {
    
    
    public static void main(String[] args) {
    
    
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
        Person bean = applicationContext.getBean(Person.class);
        System.out.println(bean);
    }
}

3. Importação da anotação @Import

A anotação @Import não é frequentemente usada para desenvolvimento, mas também é muito importante. É frequentemente usada em extensões do Spring. É frequentemente usada com anotações personalizadas e, em seguida, importa um arquivo de configuração para o contêiner.

@ComponentScan
/*把用到的资源导入到当前容器中*/
@Import({
    
    Person.class})
public class App {
    
    
    public static void main(String[] args) throws Exception {
    
    
        ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
        System.out.println(context.getBean(Person.class));
        context.close();
    }
}

4. Implemente BeanDefinitionRegistryPostProcessor para pós-processamento.

Quando o contêiner Spring for iniciado, ele executará o método postProcessBeanDefinitionRegistry de BeanDefinitionRegistryPostProcessor, que deve pós-processar o beanDefinition depois que o beanDefinition for carregado. Você pode ajustar o beanDefinition no contêiner IOC aqui, interferindo assim na inicialização subsequente dos beans.

No código abaixo, registramos manualmente o BeanDefinition da pessoa com o beanDefinitionRegistry. No final, a pessoa foi adicionada com sucesso ao applicationContext.

public class Demo1 {
    
    
    public static void main(String[] args) {
    
    
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        MyBeanDefinitionRegistryPostProcessor beanDefinitionRegistryPostProcessor = new MyBeanDefinitionRegistryPostProcessor();
        applicationContext.addBeanFactoryPostProcessor(beanDefinitionRegistryPostProcessor);
        applicationContext.refresh();
        Person bean = applicationContext.getBean(Person.class);
        System.out.println(bean);
    }
}
 
class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    
    
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    
    
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Person.class).getBeanDefinition();
        registry.registerBeanDefinition("person", beanDefinition);
    }
    
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    
    
    }
}

5. Use a interface FactoryBean

Conforme mostrado no código abaixo, use @Configuration + @Bean para adicionar PersonFactoryBean ao contêiner. Em vez de injetar Person diretamente no contêiner, injete PersonFactoryBean e obtenha o bean do tipo Person do contêiner.

@Configuration
public class Demo1 {
    
    
    @Bean
    public PersonFactoryBean personFactoryBean() {
    
    
        return new PersonFactoryBean();
    }
 
    public static void main(String[] args) {
    
    
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class);
        Person bean = applicationContext.getBean(Person.class);
        System.out.println(bean);
    }
}
 
class PersonFactoryBean implements FactoryBean<Person> {
    
    
    @Override
    public Person getObject() throws Exception {
    
    
        return new Person();
    }

    @Override
    public Class<?> getObjectType() {
    
    
        return Person.class;
    }
}

O site de entrevistas em Java mais abrangente

escopo do bean

1. singleton : Singleton, o bean no Spring é singleton por padrão.

2. protótipo : Cada solicitação criará uma nova instância de bean.

3. request : Cada requisição HTTP irá gerar um novo bean, que só é válido dentro da requisição HTTP atual.

4. sessão : Cada solicitação HTTP irá gerar um novo bean, que só é válido dentro da sessão HTTP atual.

5. global-session : escopo da sessão global.

Quais são as formas de montagem automática do Spring?

A montagem automática do Spring possui três modos: byType (de acordo com o tipo), byName (de acordo com o nome), constructor (de acordo com o construtor).

por tipo

Encontre o bean do mesmo tipo da dependência e injete-o em outro bean. Este processo precisa ser concluído com a ajuda de injeção setter, então o método set deve existir, caso contrário a injeção falha.

Quando houver vários beans de instância do mesmo tipo e nomes diferentes no arquivo xml, a injeção de dependência do contêiner Spring ainda falhará, porque há muitas opções adequadas e o contêiner Spring não pode saber qual injetar. , precisamos fornecer ajuda para o contêiner Spring , especifica qual instância de Bean injetar. Os beans de instância que não precisam ser injetados podem ser filtrados <bean>definindo o autowire-candidate do rótulo como false

<bean id="userDao"  class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" />

<!-- autowire-candidate="false" 过滤该类型 -->
<bean id="userDao2" autowire-candidate="false" class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" />

<!-- byType 根据类型自动装配userDao-->
<bean id="userService" autowire="byType" class="com.zejian.spring.springIoc.service.impl.UserServiceImpl" />

porNome

Corresponde ao nome da propriedade com o nome do bean e injeta o bean dependente, se encontrado.

<bean id="userDao"  class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" />
<bean id="userDao2" class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" />

<!-- byName 根据名称自动装配,找到UserServiceImpl名为 userDao属性并注入-->
<bean id="userService" autowire="byName" class="com.zejian.spring.springIoc.service.impl.UserServiceImpl" />

constructor

Se houver uma única instância, a correspondência de parâmetro será realizada primeiro por tipo (independentemente de o nome corresponder ou não). Quando houver várias instâncias do mesmo tipo, a primeira correspondência será por nome. Se nenhum nome correspondente for encontrado, o falha na injeção.

Diferença entre @Autowired e @Resource?

Autowire é uma anotação de primavera. Por padrão, @Autowired é correspondido por tipo (byType). Se precisar corresponder por nome (byName), você pode usar a anotação @Qualifier combinada com @Autowired. @Autowired pode passar uma required=falsepropriedade, false indica que quando a instância userDao existir, ela será injetada ou ignorada, se for true, deve ser injetada, se a instância userDao não existir, uma exceção será lançada.

public class UserServiceImpl implements UserService {
    
    
    //标注成员变量
    @Autowired
    @Qualifier("userDao1")
    private UserDao userDao;   
 }

Resource é uma anotação de j2ee, que é injetada automaticamente por padrão no modo byName. @Resource tem dois atributos importantes: nome e tipo. O atributo name especifica o nome do bean e o atributo type especifica o tipo de bean. Portanto, se for utilizado o atributo name, a estratégia de injeção automática é baseada no modo byName, e se for utilizado o atributo type, a estratégia de injeção automática é baseada no modo byType. Se nem o nome nem o atributo type forem especificados, o contêiner Spring injetará o modo byName por padrão por meio da tecnologia de reflexão.

@Resource(name="userDao")
private UserDao  userDao;//用于成员变量

//也可以用于set方法标注
@Resource(name="userDao")
public void setUserDao(UserDao userDao) {
    
    
   this.userDao= userDao;
}

As duas injeções automáticas de dependência de assembly acima não são adequadas para tipos de valor simples, como int, boolean, long, String e Enum. Para esses tipos, o contêiner Spring também fornece injeção @Value.

@Value é semelhante a @Autowired e @Resource, e também é usado para injetar propriedades, exceto que @Value é usado para obter valores de arquivos de propriedades e @Value pode analisar SpEL (expressões Spring).

Por exemplo, o arquivo jdbc.properties é o seguinte:

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/test?characterEncoding=UTF-8&allowMultiQueries=true
jdbc.username=root
jdbc.password=root

Use a anotação @Value para obter os valores de jdbc.url e jdbc.username, conforme a seguir:

public class UserServiceImpl implements UserService {
    
    
    //占位符方式
    @Value("${jdbc.url}")
    private String url;
    //SpEL表达方式,其中代表xml配置文件中的id值configProperties
    @Value("#{configProperties['jdbc.username']}")
    private String userName;

}

O que a anotação @Qualifier faz

Quando você precisa criar vários beans do mesmo tipo e deseja conectar apenas um deles com atributos, pode usar @Qualifieranotações e remover a @Autowiredambigüidade especificando qual bean deve ser conectado.

Qual é a diferença entre @Bean e @Component?

Ambos usam anotações para definir beans. @Bean é para montar beans usando código Java, e @Component é para montar beans automaticamente.

A anotação @Component é usada em uma classe para indicar que uma classe será usada como uma classe de componente e diz ao Spring para criar um bean para essa classe, e cada classe corresponde a um Bean.

A anotação @Bean é usada em um método para indicar que o método retornará um Bean. @Bean precisa ser usado na classe de configuração, ou seja, a anotação @Configuration precisa ser adicionada à classe.

@Component
public class Student {
    
    
    private String name = "lkm";
 
    public String getName() {
    
    
        return name;
    }
}

@Configuration
public class WebSocketConfig {
    
    
    @Bean
    public Student student(){
    
    
        return new Student();
    }
}

A anotação @Bean é mais flexível. Quando for necessário montar uma classe de terceiros no container Spring, pois não há como adicionar a anotação @Component no código fonte, pode-se usar apenas a anotação @Bean, e claro o método xml também pode ser usado .

A diferença entre @Component, @Controller, @Repositor e @Service?

@Component: O componente mais comum que pode ser injetado no contêiner de mola para gerenciamento.

@Controller: Marca a classe como um controlador Spring Web MVC.

@Service: Marca a classe como um componente da camada de negócios.

@Repository: Marque a classe como um componente de acesso a dados, ou seja, um componente DAO.

Quais são os métodos de implementação da transação Spring?

Uma transação é uma série de operações executadas atomicamente. O mecanismo de transação do Spring inclui principalmente transações declarativas e transações programáticas.

  • Transações programáticas : gerencie transações programaticamente, o que traz grande flexibilidade, mas é difícil de manter.
  • Transação declarativa : separa o código de gerenciamento da transação do método de negócios e o encapsula por meio de aop. As transações declarativas do Spring tornam desnecessário lidarmos com operações como obtenção de conexões, fechamento de conexões, confirmação de transações e reversões. Use @Transactionalanotações para habilitar transações declarativas.

@TransactionalAs propriedades relevantes são as seguintes:

Atributos tipo descrever
valor Corda Um descritor de qualificação opcional especificando o gerenciador de transações a ser usado
propagação enumeração: Propagação Configurações opcionais de comportamento de propagação de transação
isolamento enumeração: Isolamento Configurações de nível de isolamento de transação opcionais
somente leitura boleano Transações de leitura/gravação ou somente leitura, leitura/gravação padrão
tempo esgotado int (em granularidade de segundos) Configuração de tempo limite da transação
rollbackFor Matriz de objetos de classe, deve herdar de Throwable Uma matriz de classes de exceção que fez com que a transação fosse revertida
rollbackForClassName Uma matriz de nomes de classe, deve herdar de Throwable Uma matriz de nomes de classe de exceção que fez com que a transação fosse revertida
noRollbackFor Matriz de objetos de classe, deve herdar de Throwable Uma matriz de classes de exceção que não fará com que a transação seja revertida
noRollbackForClassName Uma matriz de nomes de classe, deve herdar de Throwable Uma matriz de nomes de classe de exceção que não causará reversão de transação

Quais são os comportamentos de propagação da transação?

Sete comportamentos de propagação de transação são definidos na interface TransactionDefinition:

  1. PROPAGATION_REQUIREDSe existir uma transação, a transação atual será suportada. Se não houver transação, inicie uma nova transação. Se os dois métodos da chamada aninhada forem anotados com transações e executados no mesmo thread, os dois métodos usarão a mesma transação. Se estiver executando em um thread diferente, uma nova transação será iniciada.
  2. PROPAGATION_SUPPORTSSe houver uma transação, suporte a transação atual. Se não houver transação, então a execução não transacional.
  3. PROPAGATION_MANDATORYSe já existir uma transação, a transação atual será suportada. Uma exceção é lançada se nenhuma transação existir IllegalTransactionStateException.
  4. PROPAGATION_REQUIRES_NEWSempre inicie uma nova transação. Precisa usar JtaTransactionManager como gerenciador de transações.
  5. PROPAGATION_NOT_SUPPORTEDSempre executa de forma não transacional, suspendendo todas as transações existentes. Precisa usar JtaTransactionManager como gerenciador de transações.
  6. PROPAGATION_NEVERSempre executa de forma não transacional, lançando uma exceção se houver uma transação ativa.
  7. PROPAGATION_NESTEDExecuta em uma transação aninhada se existir uma transação ativa. Executado pela propriedade PROPAGATION_REQUIRED se não houver transação ativa.

A diferença entre PROPAGATION_NESTED e PROPAGATION_REQUIRES_NEW:

Quando usado PROPAGATION_REQUIRES_NEW, a transação interna e a transação externa são duas transações independentes. Depois que a transação interna é confirmada, a transação externa não pode ser revertida. As duas transações não afetam uma à outra.

Quando usado PROPAGATION_NESTED, a reversão da transação externa pode causar a reversão da transação interna. A exceção da transação interna não causará o rollback da transação externa, é uma transação aninhada real.

Em que circunstâncias as transações do Spring falharão?

1. Problema de direitos de acesso

Existem quatro tipos principais de permissões de acesso java: privado, padrão, protegido e público, e suas permissões aumentam da esquerda para a direita.

Se a permissão de acesso do método de transação não for definida como pública, isso fará com que a transação falhe, pois o spring exige que o método proxy seja public.

Observando o código fonte, você pode ver que há um julgamento no método AbstractFallbackTransactionAttributeSourceda classe , se o método de destino não for público, ele retornará nulo, ou seja, as transações não são suportadas.computeTransactionAttribute

protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
    
    
    // Don't allow no-public methods as required.
    if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
    
    
      return null;
    }
	...
}

2. O método é modificado com final

Se o método de transação for modificado com final, isso fará com que a transação falhe. Como a camada subjacente das transações spring usa aop, ou seja, por meio do jdk dynamic proxy ou cglib, ela nos ajuda a gerar classes proxy e implementar funções de transação em classes proxy.

Mas se um método for modificado com final, em sua classe proxy, o método não pode ser reescrito para adicionar funções de transação.

Da mesma forma, se um método for estático, ele não pode ser transformado em um método de transação por meio de um proxy dinâmico.

3. O objeto não é gerenciado pelo spring

A premissa de usar a transação spring é: o objeto precisa ser gerenciado pelo spring e uma instância de bean precisa ser criada. Se a classe não estiver anotada com @Controller, @Service, @Component, @Repository, etc., ou seja, a classe não for entregue ao spring para gerenciamento, então seus métodos não gerarão transações.

4. A tabela não suporta transações

Se o mecanismo de armazenamento usado pelo MySQL for myisam, as transações não serão suportadas. Porque o mecanismo de armazenamento myisam não suporta transações.

5. Chamada interna do método

Conforme mostrado no código a seguir, se o método update não for @Transactionalanotado , chamar @Transactionalo método updateOrder anotado invalidará a transação no método updateOrder.

Devido à auto-invocação, chamando o próprio método desta classe sem passar pela classe proxy do Spring, a transação só terá efeito quando a transação for chamada externamente.

@Service
public class OrderServiceImpl implements OrderService {
    
    

    public void update(Order order) {
    
    
        this.updateOrder(order);
    }

    @Transactional
    public void updateOrder(Order order) {
    
    
        // update order
    }
}

Solução:

1. Declarar outro serviço e alterar a chamada interna para chamada externa

2. Use transações programáticas

3. Use AopContext.currentProxy() para obter o objeto proxy

@Servcie
public class OrderServiceImpl implements OrderService {
    
    
    
   public void update(Order order) {
    
    
        ((OrderService)AopContext.currentProxy()).updateOrder(order);
   }

    @Transactional
    public void updateOrder(Order order) {
    
    
        // update order
    }
 }

6. Transação não aberta

Se for um projeto de primavera, você precisará configurar manualmente os parâmetros relacionados à transação no arquivo de configuração. Se você esquecer de configurar, a transação definitivamente não terá efeito.

Se for um projeto springboot, nenhuma configuração manual é necessária. Porque springboot já DataSourceTransactionManagerAutoConfigurationabriu a transação para nós na classe.

7. Exceção engolida

Às vezes, a transação não será revertida, pode ser que a exceção seja capturada manualmente no código. Como o próprio desenvolvedor capturou a exceção e engoliu a exceção sem lançá-la manualmente, nesse caso, a transação de primavera não será revertida.

Se você deseja que a transação de primavera seja revertida normalmente, você deve lançar uma exceção que ela possa manipular. Se nenhuma exceção for lançada, o spring considera o programa como normal.

Como o Spring resolve o problema da dependência circular?

Em primeiro lugar, existem duas formas de injeção de feijão.

Injeção de construtor e injeção de propriedade.

Para a dependência circular injetada pelo construtor, o Spring não pode manipulá-la e lançará BeanCurrentlylnCreationExceptionuma exceção diretamente.

Para a dependência circular de injeção de atributo (no modo singleton), a dependência circular é processada por meio do cache de três níveis.

Dependências circulares que não são objetos singleton não podem ser tratadas.

Vamos analisar como lidar com a dependência circular de injeção de propriedades no modo singleton:

Em primeiro lugar, a inicialização do objeto singleton do Spring é dividida em três etapas:

  1. createBeanInstance: instanciar o bean, usar o construtor para criar o objeto e alocar memória para o objeto.
  2. populateBean: para injeção de dependência.
  3. initializeBean: inicializa o bean.

Para resolver o problema de dependência circular de singletons, o Spring usa um cache de três níveis:

singletonObjects: O mapa de objeto singleton inicializado, nome do bean --> instância do bean

earlySingletonObjects : Instanciação completa do mapa de objeto singleton não inicializado, nome do bean --> instância do bean

singletonFactories : Mapa de fábrica de objeto singleton, nome do bean --> ObjectFactory, singletonFactory será adicionado após a conclusão da instanciação de objeto singleton.

Depois de chamar createBeanInstance para instanciação, chame addSingletonFactory para colocar o objeto singleton em singletonFactory.

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
    
    
    Assert.notNull(singletonFactory, "Singleton factory must not be null");
    synchronized (this.singletonObjects) {
    
    
        if (!this.singletonObjects.containsKey(beanName)) {
    
    
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.add(beanName);
        }
    }
}

Se A depende do objeto de instância de B e B também depende do objeto de instância de A.

  1. A first completa a instanciação e se adiciona a singletonFactory
  2. Em seguida, execute a injeção de dependência e descubra que você depende do objeto B, então tente obter (B)
  3. É descoberto que B ainda não foi instanciado, instanciar B
  4. Então B descobriu que dependia do objeto A durante a inicialização, então ele tentou get(A), tentou o cache de primeiro nível singletonObjects e o cache de segundo nível earlySingletonObjects, mas não o encontrou, tentou o cache de terceiro nível singletonFactory, porque A adicionou-se a singletonFactory durante a inicialização, para que B possa obter o objeto A e, em seguida, mover A do cache de terceiro nível para o cache de segundo nível
  5. Depois que B obtém o objeto A, ele conclui a inicialização com êxito e, em seguida, se coloca no cache de primeiro nível singletonObjects
  6. Retorne para A neste momento, e A pode obter o objeto de B neste momento e concluir com sucesso sua própria inicialização

Pode-se ver a partir disso que a dependência circular da injeção de atributo é realizada principalmente adicionando o bean instanciado a singletonFactory. Beans que usam injeção de dependência de construtor executarão injeção de dependência quando instanciados e não serão adicionados a singletonFactory. Por exemplo, A e B usam injeção de dependência de construtor. Quando A chama o construtor para instanciação, descobre que depende de B. Se B não foi instanciado, ele instanciará B. Neste momento, A não foi instanciado. não serão adicionados a singtonFactory. Enquanto B depende de A, B irá para o cache de terceiro nível para encontrar o objeto A e descobrirá que ele não existe, então ele instanciará A novamente e A será instanciado duas vezes, resultando em uma exceção sendo lançada.

Resumo: 1. Use o cache para identificar os nós que foram percorridos; 2. Use referências Java para definir o endereço do objeto com antecedência e, em seguida, conclua o objeto.

Processo de inicialização de primavera

  1. Leia o arquivo web.xml.

  2. Crie um ServletContext para fornecer um ambiente de hospedagem para o contêiner ioc.

  3. Acione o evento de inicialização do contêiner e chame o método contextLoaderListener.contextInitialized(), que inicializará um contexto de aplicativo WebApplicationContext, que é o contêiner ioc do Spring. Depois que o contêiner ioc for inicializado, ele será armazenado no ServletContext.

  4. Inicialize o Servlet configurado em web.xml. Como DispatcherServlet, usado para corresponder e processar cada solicitação de servlet.

O bean singleton do Spring tem problemas de segurança de simultaneidade?

Quando vários usuários solicitam um serviço ao mesmo tempo, o contêiner atribuirá um encadeamento a cada solicitação. Nesse momento, vários encadeamentos executarão a lógica de negócios correspondente à solicitação simultaneamente. atributos de membro da instância), você deve considerar os problemas de segurança do encadeamento.

Beans sem estado e beans com estado

  • Um bean com variáveis ​​de instância, que podem armazenar dados, não é thread-safe.
  • Beans sem variáveis ​​de instância não podem salvar dados e são thread-safe.

Beans sem estado no Spring são adequados para o modo singleton, que pode compartilhar instâncias para melhorar o desempenho. Beans com estado não são seguros em um ambiente multiencadeado e geralmente usam Prototypepadrões ou são usados ThreadLocal​​para resolver problemas de segurança de encadeamento.

Como o Spring Bean garante a segurança da simultaneidade?

Os beans do Spring são todos singletons por padrão. Em alguns casos, singletons não são seguros para simultaneidade.

Tomemos, Controllerpor exemplo , se Controllerdefinirmos variáveis ​​de membro em . Quando várias solicitações chegam, todas elas inserem o mesmo Controllerobjeto e modificam o valor dessa variável de membro, para que afetem umas às outras e haja problemas de segurança de simultaneidade.

Como devemos resolvê-lo?

Para evitar que várias solicitações HTTP interfiram umas nas outras, as seguintes medidas podem ser tomadas:

1. Protótipo de variável singleton

Para projetos da web, você pode adicionar anotações na Controllerclasse Scope("prototype")ou @Scope("request"), para projetos não-web, adicionar anotações na Componentclasse @Scope("prototype").

Este método é muito simples de implementar, mas aumenta muito a sobrecarga de recursos do servidor de criação, instanciação e destruição do Bean.

2. Tente evitar o uso de variáveis ​​de membro

Sob condições de negócios, as variáveis ​​de membro podem ser substituídas por variáveis ​​locais no método. Este método é pessoalmente considerado o mais adequado.

3. Use classes seguras para simultaneidade

Se você precisar usar variáveis ​​de membro em beans singleton, considere usar contêineres seguros para simultaneidade, como ConcurrentHashMap, ConcurrentHashSetetc., e agrupar nossas variáveis ​​de membro nesses contêineres seguros para simultaneidade para gerenciamento.

4. Segurança de simultaneidade para microsserviços ou distribuídos

Se o impacto de microsserviços ou serviços distribuídos for considerado, o método 3 não é adequado. Nesse caso, o middleware de cache distribuído que pode compartilhar determinadas informações, como o Redis, pode ser usado. Dessa forma, é possível garantir que diferentes instâncias de serviço do mesmo serviço tenham as mesmas informações compartilhadas.

O princípio da anotação @Async

Quando chamamos uma interface ou método de terceiros, não precisamos esperar o retorno do método antes de executar outra lógica. Neste momento, se o tempo de resposta for muito longo, isso afetará muito a eficiência de execução do programa . Portanto, neste momento, precisamos usar métodos assíncronos para executar nossa lógica em paralelo. No springboot, você pode usar a anotação @Async para implementar operações assíncronas.

Etapas para implementar a operação assíncrona usando a anotação @Async:

1. Primeiro adicione a anotação @EnableAsync à classe de inicialização.

@Configuration
@EnableAsync
public class App {
    
    
    public static void main(String[] args) {
    
    
         ApplicationContext ctx = new  
             AnnotationConfigApplicationContext(App.class);
        MyAsync service = ctx.getBean(MyAsync.class);
        System.out.println(service.getClass());
        service.async1();
        System.out.println("main thread finish...");
    }
}

2. Adicione a anotação @Async ao método correspondente.

@Component
public class MyAsync {
    
    
    @Async
    public void asyncTest() {
    
    
        try {
    
    
            TimeUnit.SECONDS.sleep(20);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("asyncTest...");
    }
}

Execute o código, saída do console:

main thread finish...
asyncTest...

Isso prova que o método asyncTest é executado de forma assíncrona.

princípio:

Postamos uma anotação @EnableAsync na classe de inicialização principal para usar @Async para entrar em vigor. A função de @EnableAsync é importar AsyncConfigurationSelector por meio de @import. O método selectImports de AsyncConfigurationSelector define ProxyAsyncConfiguration como um contêiner de injeção de Bean. Injete a classe AsyncAnnotationBeanPostProcessor por meio de @Bean em ProxyAsyncConfiguration.

código mostra como abaixo:

@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
    
    
}

public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
    
    
	public String[] selectImports(AdviceMode adviceMode) {
    
    
		switch (adviceMode) {
    
    
			case PROXY:
				return new String[] {
    
     ProxyAsyncConfiguration.class.getName() };
			//...
		}
	}
}

public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {
    
    
    @Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
    public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
    
    
        //创建postProcessor
        AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
        //...
    }
}

AsyncAnnotationBeanPostProcessor geralmente cria um aprimorador AsyncAnnotationAdvisor. No método buildAdvice de AsyncAnnotationAdvisor, AnnotationAsyncExecutionInterceptor é criado.

public class AsyncAnnotationBeanPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor {
    
    
    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
    
    
        super.setBeanFactory(beanFactory);
        //创建一个增强器
        AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
        //...
        advisor.setBeanFactory(beanFactory);
        this.advisor = advisor;
    }
}


public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
    
    
    public AsyncAnnotationAdvisor(
            @Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
    
    
        //增强方法
        this.advice = buildAdvice(executor, exceptionHandler);
        this.pointcut = buildPointcut(asyncAnnotationTypes);
    }

    // 委托给AnnotationAsyncExecutionInterceptor拦截器
    protected Advice buildAdvice(
            @Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
    
    
        //拦截器
        AnnotationAsyncExecutionInterceptor interceptor = new AnnotationAsyncExecutionInterceptor(null);
        interceptor.configure(executor, exceptionHandler);
        return interceptor;
    }
}

AnnotationAsyncExecutionInterceptor herda de AsyncExecutionInterceptor e implementa indiretamente MethodInterceptor. O método de invocar implementado pelo interceptador envia a chamada do método original para o novo conjunto de encadeamentos para execução, realizando assim a assincronia do método.

public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport implements MethodInterceptor, Ordered {
    
    
    public Object invoke(final MethodInvocation invocation) throws Throwable {
    
    
        //...
        //构建放到AsyncTaskExecutor执行Callable Task
        Callable<Object> task = () -> {
    
    
            //...
        };
        //提交到新的线程池执行
        return doSubmit(task, executor, invocation.getMethod().getReturnType());
    }
}

Pela análise acima, podemos ver que a anotação @Async realmente implementa chamadas assíncronas por meio do proxy.

Em que devo prestar atenção ao usar @Async?

1. Ao usar @Aysnc, é melhor configurar um thread pool Executor para permitir a reutilização de thread para economizar recursos ou definir um ThreadFactory com base na implementação do pool de threads para SimpleAsyncTaskExecutor, caso contrário, SimpleAsyncTaskExecutor será usado por padrão e o executor criará um novo toda vez que é chamado de thread.

2. Chamar o método assíncrono desta classe não funcionará. Este método ignora o proxy e chama o método diretamente, e a anotação @Async será inválida.


Por fim, gostaria de compartilhar com vocês um repositório Github, que possui mais de 300 PDFs de livros clássicos de informática compilados por Dabin, incluindo linguagem C, C++, Java, Python, front-end, banco de dados, sistema operacional, rede de computadores, estrutura de dados e algoritmo, aprendizado de máquina, vida útil da programação , etc., você pode começar, da próxima vez que procurar um livro, pesquise diretamente nele, o armazém é atualizado continuamente ~

Endereço do Github

Se você não conseguir acessar o Github, poderá acessar o endereço da nuvem de código.

Endereço da nuvem de código

Acho que você gosta

Origin blog.csdn.net/Tyson0314/article/details/131080494
Recomendado
Clasificación