один,@Import
изучение@Import
Когда эта аннотация была сделана, редактор думал о проблеме.Функция этой аннотации заключается в том, чтобы импортировать конфигурациюConfiguration
класс, так где именно он используется? Думая, что наш проект не будет использовать эту аннотацию для импорта конфигурации, мы все создаем новый классxxxxxxConfiguration.java
, а затем сразу поставить всеBean
Все компоненты объявлены, и следующий код кажется нам знакомым, ха-ха.
/**
* xx配置类,里边会有n个bean
* @Author jiawei huang
* @Since 2019年8月26日
* @Version 1.0
*/
@Configuration
public class CustomConfig {
@Bean
public Marker zuulProxyMarkerBean() {
return new Marker();
}
......
}
Но задумывались ли вы когда-нибудь о проблеме, когда класс конфигурацииCustomConfig
не в@SpringBootApplication
Может ли он по-прежнему помещаться внутри пакета и его подпакетов? Ответ - нет. потому что это неspringboot
в диапазоне сканирования по умолчанию. Подробности можно посмотретьSpringBoot инкапсулирует наш собственный Starter
То, что я говорю, имеет смысл? Давайте проведем эксперимент.UserConfig
для конфигурацииUser
Объект, который находитсяcom.example
Пакет,DemoApplication.java
родыcom.example.demo
пакет, на данный моментSpringBoot
не могу сканироватьUserConfig
и вводитьUser
объект.
UserConfig.java
/**
* @Author jiawei huang
* @Since 2019年8月26日
* @Version 1.0
*/
@Configuration
public class UserConfig {
@Bean
public User getUser() {
return new User();
}
}
Использование следующей инъекции кода сообщит об ошибке:
@Autowired
private User user;
The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)
Как это сделать? Есть два решения:
- 1. Используйте
@ComponentScan("com.**")
Аннотировать предложение - 2. Используйте
@Import
Импорт аннотаций
Первый способ простой и грубый, и вроде бы ничего плохого в нем нет, но это исходя из того, что вы знаетеbean
На основе приблизительного пути к объекту в стороннем пакете jarbean
Но не всеcom
Названо в начале, что смущает.
Основываясь на приведенной выше структуре пути, мы имеемDemoApplication.java
Присоединяйся@Import(UserConfig.class)
Эта аннотация решит проблему.
Кроме того,@Import
Эквивалент файла конфигурации Spring xml<import />
Этикетка.
два,ImportSelector
@Import
Аннотация позволяет нам импортировать указанный набор классов конфигурации --@Configuration
Украшенный класс, как только имя класса будет указано, будет проанализирован. Напротив,ImportSelector
Позволит нам динамически выбирать класс конфигурации, который мы хотим импортировать, на основе условий, другими словами, он является динамическим.ImportSelector
При использовании мы хотим создать класс, реализующийImportSelector
интерфейс и переопределитьString[] selectImports(AnnotationMetadata importingClassMetadata);
метод.
Предположим, мы хотим реализовать такую функцию, мы создаемCustomImportSelector
класс, при использованииCustomImportSelector
Когда элемент является классом, мы возвращаемUserConfig
класс конфигурации, при использованииCustomImportSelector
Когда элемент является интерфейсом, мы возвращаемStudentConfig
Класс конфигурации.
UserConfig
иStudentConfig
существуетDemoApplication
Внешний слой, в противном случае эти два класса конфигурации будут разрешены весной по умолчанию.
/**
*
* @Author jiawei huang
* @Since 2019年8月26日
* @Version 1.0
*/
@Configuration
public class UserConfig {
@Bean
public User getUser() {
return new User();
}
}
/**
*
* @Author jiawei huang
* @Since 2019年8月26日
* @Version 1.0
*/
@Configuration
public class StudentConfig {
@Bean
public Student getStudent() {
return new Student();
}
}
@SpringBootApplication
// 1、很明显,这里CustomImportSelector修饰的是一个类,我们将会返回UserConfig
@Import(CustomImportSelector.class)
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
/**
*
* @Author jiawei huang
* @Since 2019年8月19日
* @Version 1.0
*/
@RestController
public class MyController {
@Autowired(required = false)
private Student student;
@Autowired(required = false)
private User user;
@RequestMapping("/getStudent")
private String getStudent() {
return "student=[" + student + "],user=[" + user + "]";
}
}
/**
*
* @Author jiawei huang
* @Since 2019年8月26日
* @Version 1.0
*/
public class CustomImportSelector implements ImportSelector {
/**
* importingClassMetadata:被修饰的类注解信息
*/
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 注意,自定义注解这里是拿不到的
System.out.println(importingClassMetadata.getAnnotationTypes());
// 如果被CustomImportSelector导入的组件是类,那么我们就实例化UserConfig
if (!importingClassMetadata.isInterface()) {
return new String[] { "com.example.UserConfig" };
}
// 此处不要返回null
return new String[] { "com.example.StudentConfig" };
}
}
Откройте браузер, вызовите интерфейс, get возвращает следующее, чтобы доказатьStudent
не вводится как bean-компонент, аUser
успешно введен
В-третьих, поговорим о принципе
Где разрешаются аннотации во время загрузки Spring?
Весенняя исходная версия:5.1.6.RELEASE
Редактор грубо отлаживает исходный код, процесс парсинга этих двух аннотаций унифицирован вConfigurationClassParser$DeferredImportSelectorGroupingHandler
в классеprocessImports()
Метод реализован, и примерный исходный код метода выглядит следующим образом:
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
// 1、如果该配置类被ImportSelector修饰,则当成ImportSelector进行处理
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector, this.environment, this.resourceLoader, this.registry);
if (selector instanceof DeferredImportSelector) {
this.deferredImportSelectorHandler.handle(
configClass, (DeferredImportSelector) selector);
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, currentSourceClass, importSourceClasses, false);
}
}
// 2、如果该配置类被ImportBeanDefinitionRegistrar修饰,则当成ImportBeanDefinitionRegistrar进行处理
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar, this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
// 3、如果该配置类被Import修饰,则当成Import进行处理
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}
}
С момента запуска Spring до выполнения разбора аннотаций общая ссылка вызова выглядит следующим образом:
SpringApplication-refreshContext()
->AbstractApplicationContext-refresh()-postProcessBeanFactory()
->PostProcessorRegistrationDelegate-invokeBeanDefinitionRegistryPostProcessors()
->ConfigurationClassPostProcessor-processConfigBeanDefinitions()
->ConfigurationClassParser-parse()
->ConfigurationClassParser-processImports()
ConfigurationClassParser
даSpring
предоставлено для разбора@Configuration
класс конфигурации, через который вы получитеConfigurationClass
список объектов.
4. Резюме
На самом деле, вообще в проекте мы действительно не используем приведенные выше аннотации. Иногда мы усваиваем знания, но не всегда можем придумать сценарий применения технологии, что раздражает. На самом деле это не так.Понимание тонкостей технологий со временем принесет нам много способностей, таких как написание более качественного кода, более легкое понимание исходного кода фреймворка, фреймворк быстро запускается, скорость устранения ошибок высока, а хвастовство будет более мощным.
Однако, помимо спроса, технология может не иметь большого значения. Когда мы получаем спрос, мы можем использовать наш мозг, чтобы увидеть, можно ли использовать этот спрос. Это похоже на схему реализации корзины ниже:
Мы можем использовать связанные операции redis для реализации таких операций, как эти количества товаров, но вы должны построить мне таблицу для их хранения.Конечно, это не невозможно, но кеш проще и эффективнее. С идентификатором пользователя в качестве ключа и идентификатором продукта в качестве поля для решения проблемы можно использовать структуру данных хэша redis.
Редактор считает, что не стоит торопиться сначала реализовать требования, можно сначала мозгом посмотреть, какие технические моменты можно использовать, а потом писать код.