Справочное руководство Spring 5: сканирование компонентов

Spring Boot Java Spring

Эта статья участвовала в третьем этапе курса «High Yield Update» тренировочного лагеря для создателей Nuggets. Подробнее см.:Dig Li Project | Идет третий этап тренировочного лагеря создателя, «написание» личного влияния.

сканирование компонентов

В предыдущей статье мы говорили о конфигурации annotation-config, которая в основном используется для внедрения свойств внутри bean-компонентов. Сам компонент должен быть определен конфигурацией. Если вы хотите использовать конфигурацию для определения bean-компонентов, вы можете использовать компонентное сканирование следующим образом:

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

component-scan сканирует аннотации в пути к классам, включая (@Component, @Repository, @Service, @Controller, @RestController, @ControllerAdvice и @Configuration), конечно, component-scan по умолчанию включает annotation-config, и мы можем использовать аннотации, упомянутые в предыдущей статье, непосредственно в этих bean-компонентах конфигурации.

@Component

@Component указывает, что компонент является компонентом, а @Component является общим прототипом для любого компонента, управляемого Spring. @Repository, @Service и @Controller — это специальные аннотации @Component для более конкретных случаев использования (соответственно на уровне сохраняемости, обслуживания и представления). Таким образом, вы можете аннотировать свои классы компонентов с помощью @Component, однако, аннотируя их с помощью @Repository, @Service или @Controller, ваши классы становятся более семантическими. Обычно больше подходит для дальнейшей обработки бизнес-логики в АОП.

Метааннотации и составные аннотации

Мета-аннотации — это аннотации, которые можно использовать в других аннотациях. Как упоминалось ранее, @Component — это мета-аннотация @Service. следующее:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component 
public @interface Service {

    // ....
}

@Component приведет к тому, что @Service будет рассматриваться как @Component. Конечно, вы также можете комбинировать мета-аннотации или настраивать мета-аннотации. Например, аннотация Spring @SessionScope жестко кодирует имя области как сеанс, но по-прежнему позволяет использовать пользовательский режим proxyMode. В следующем списке показано определение аннотации sessionScope:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Scope(WebApplicationContext.SCOPE_SESSION)
public @Interface SessionScope {

    /**
     * Alias for {@link Scope#proxyMode}.
     * <p>Defaults to {@link ScopedProxyMode#TARGET_CLASS}.
     */
    @AliasFor(annotation = Scope.class)
    ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;

}

Вы не можете определить proxyMode в @SessionScope следующим образом:

@Service
@SessionScope
public class SessionScopedService {
    // ...
}

Вы также можете переопределить proxyMode следующим образом:

@Service
@SessionScope(proxyMode = ScopedProxyMode.INTERFACES)
public class SessionScopedUserService implements UserService {
    // ...
}

#@ComponentScan и фильтры

Как мы упоминали выше, если вы хотите использовать компонентное сканирование, вам необходимо настроить его в XML.context:component-scan, На самом деле вы также можете использовать форму аннотаций следующим образом:

@Configuration
@ComponentScan(basePackages = "com.flydean.beans")
public class AppConfig {

}

@ComponentScan может настроить некоторые фильтры для фильтрации нежелательных компонентов. Следующее:

@Configuration
@ComponentScan(basePackages = "com.flydean.beans",
        includeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
        excludeFilters = @ComponentScan.Filter(BeanA.class))
public class BeanAConfig {
}

В следующей таблице показаны поддерживаемые типы фильтров и примеры:

Filter type Пример выражения описывать
аннотация (по умолчанию) org.example.SomeAnnotation тип базового целевого компонента
assignable org.example.SomeClass Класс (или интерфейс), которому может быть назначен целевой компонент (расширенный или реализованный).
aspectj org.example..*Service+ Соответствует типу AspectJ целевого компонента
regex org\.example\.Default.* Регулярное выражение, соответствующее имени в целевом первичном ключе.
custom org.example.MyTypeFilter Пользовательская реализация org.springframework.core.type.TypeFilter

Метаданные бина определяются внутри компонента

Компоненты Spring также могут предоставлять контейнеру метаданные определения компонента. Вы можете добиться этого, используя те же аннотации @Bean, которые используются для определения метаданных компонента в классах с аннотациями @Configuration. В следующем примере показано, как это сделать:

@Component
public class FactoryMethodComponent {

    @Bean
    @Qualifier("public")
    public BeanA publicInstance() {
        return new BeanA();
    }

    public void doWork() {
        // Component method implementation omitted
    }
}

InjectionPoint

Начиная с SpringFramework 4.3, вы также можете объявлять параметры фабричного метода типа InjectionPoint для создания bean-компонентов.

Обратите внимание, что это относится только к фактическому созданию экземпляра компонента, а не к внедрению существующих экземпляров. Следовательно, эта функция наиболее уместна для bean-компонентов с областью действия прототипа.

@Component
public class InjectPointFactoryMethodComponent {

    @Bean
    @Scope("prototype")
    public BeanA prototypeInstance(InjectionPoint injectionPoint) {
        return new BeanA("prototypeInstance for " + injectionPoint.getMember());
    }
}

Методы @Bean в обычных компонентах Spring обрабатываются иначе, чем соответствующие методы в классах Spring @Configuration. Разница в том, что классы @Component не дополнены cglib для перехвата вызовов методов и полей. Прокси-сервер cglib — это метод вызова класса @Configuration, который создает ссылку метаданных компонента на взаимодействующий объект через метод или поле в методах @Bean.

Вы можете объявлять методы @Bean как статические методы, позволяя вызывать их без создания содержащего их класса конфигурации в качестве экземпляра. Это особенно важно при определении bean-компонентов постпроцессора (например, типа BeanFactoryPostProcessor или BeanPostProcessor), поскольку такие bean-компоненты инициализируются на ранней стадии жизненного цикла контейнера и на этом этапе не следует запускать другие части конфигурации.

Из-за технических ограничений вызовы статических методов @Bean никогда не перехватываются контейнером, даже внутри классов @Configuration (как описано ранее в этом разделе): подклассы cglib могут переопределять только нестатические методы. Следовательно, прямой вызов другого метода @Bean эквивалентен стандартному новому методу Java, в результате чего отдельный экземпляр возвращается непосредственно из самого фабричного метода.

Обратите внимание: обычные методы @Bean в классах @Configuration должны быть переопределяемыми, то есть их нельзя объявлять закрытыми или окончательными.

Назовите компонент автоматического обнаружения

По умолчанию атрибут value может быть предоставлен @Component, @Repository, @Service и @Controller) для присвоения имени bean-компоненту.

Если такая аннотация не содержит значения, генератор имени компонента по умолчанию вернет неполное имя класса в нижнем регистре. Например, если обнаружены следующие классы компонентов, именами будут myMovieLister и movieFinderImpl:

@Service("myMovieLister")
public class SimpleMovieLister {
    // ...
}
@Repository
public class MovieFinderImpl implements MovieFinder {
    // ...
}

Если вы не хотите полагаться на стратегию именования компонентов по умолчанию, вы можете предоставить собственную стратегию именования компонентов. Во-первых, реализуйте интерфейс BeanNameGenerator и обязательно включите конструктор без аргументов по умолчанию. Затем укажите полное имя класса при настройке сканера, как показано в примере аннотации и определения bean-компонента ниже:

public class MyNameGenerator implements BeanNameGenerator {
    @Override
    public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
        return null;
    }
}
@Configuration
@ComponentScan(basePackages = "com.flydean", nameGenerator = MyNameGenerator.class)
public class BeanNameConfig {
}

Обеспечить область для автоматически обнаруженных компонентов

Как и в случае с обычными компонентами, управляемыми Spring, по умолчанию и наиболее распространенной областью действия для автоматически определяемых компонентов является singleton. Однако иногда вам нужна другая область, которую можно указать с помощью аннотации @Scope. Имя области может быть указано в аннотации, как показано в следующем примере:

@Scope("prototype")
@Component("beanA")
public class BeanA {

    public BeanA(){

    }

    public BeanA(String name){

    }
}

Разбор пользовательской области

Чтобы обеспечить настраиваемую стратегию разрешения области вместо того, чтобы полагаться на методы на основе аннотаций, вы можете реализовать интерфейс ScopeMetadataResolver. Следующее:

public class MyScopeResolver implements ScopeMetadataResolver {
    @Override
    public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
        return null;
    }
}
@Configuration
@ComponentScan(basePackages = "com.flydean", scopeResolver = MyScopeResolver.class)
public class BeanScopeResolverConfig {
}

scoped-proxy

При использовании некоторых областей, не являющихся одноэлементными, может потребоваться создание прокси-серверов для объектов с областью действия. Для этого в элементе сканирования компонента может быть атрибут scoped-proxy. Три возможных значения: no, interfaces и targetClass. Например, следующая конфигурация создаст стандартный динамический прокси JDK:

@Configuration
@ComponentScan(basePackages = "com.flydean", scopedProxy = ScopedProxyMode.INTERFACES)
public class ScopedProxyConfig {
}

Создать индекс компонентов-кандидатов

Хотя сканирование пути к классам выполняется очень быстро, можно повысить производительность запуска больших приложений, создав статический список кандидатов во время компиляции.

Для создания индекса необходимо добавить дополнительную зависимость к каждому модулю, содержащему компонент, являющийся целью директивы сканирования компонентов. В следующем примере показано, как использовать Maven:

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-indexer</artifactId>
        <version>5.1.8.RELEASE</version>
        <optional>true</optional>
    </dependency>
</dependencies>

Этот процесс создает файл META-INF/spring.components, который включается в файл JAR.

Индексирование включается автоматически, когда в пути к классам обнаруживается META-INF/Spring.components. Если раздел индекса доступен для некоторых библиотек (или вариантов использования), но не может быть создан для всего приложения, вы можете сделать это, установив для spring.index.ignore значение true (либо как системное свойство, либо в файле spring.properties по адресу корень пути к классам) ), чтобы вернуться к обычному расположению пути к классам (как если бы индексов вообще не было).

Примеры в этом разделе могут относиться кcomponent-scan.

Дополнительные руководства см.блог флайдина