Рубрики
Без рубрики

Весенние аннотации: Аннотации основных рамок

Автор оригинала: David Landup.

Вступление

Spring Framework – это очень надежная структура, выпущенная в 2002 году. Его основные функции могут быть применены к простым приложениям Java или расширены до сложных современных веб-приложений.

Поскольку он постоянно обновляется и следует новым архитектурным и программным парадигмам, он предлагает поддержку многих других фреймворков, которые работают рука об руку с ним.

С таким огромным набором функций вполне нормально, что он знакомит нас с некоторыми новыми аннотациями, которые являются ключевой частью разработки приложений Spring.

Конфигурация Spring полностью настраивается, что первоначально было сделано с помощью XML-конфигурации файлов. Однако этот подход устарел, и большинство людей в настоящее время прибегают к конфигурации аннотаций .

Тем не менее, эта серия статей направлена на то, чтобы раскрыть возможности, которые вы, как разработчик, должны настроить и использовать Spring framework:

  • Аннотации фреймворка Spring: @RequestMapping и его варианты
  • Весенние аннотации: Основные аннотации
  • Весенние аннотации: Весенние облачные аннотации
  • Весенние Аннотации: Аннотации Testng

Примечание : В этой статье предполагается, что вы знакомы с принципом Пружинной инверсии управления .

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

Давайте взглянем на основные аннотации, которые составляют почти все приложения Spring:

@Бин

A @Bean является базовым объектом в Spring Framework. Все сводится к JavaBeans – классам, которые инкапсулируют объекты в один. Они представляют собой тип POJO (Обычный старый объект Java).

Все объекты JavaBeans должны быть Сериализуемыми , все поля должны быть закрытыми, все поля должны иметь задатчики и получатели, должен быть конструктор без аргументов, а доступ к полям осуществляется исключительно конструктором или методами получения/задания:

public class Developer implements java.io.Serializable {
   private int id;
   private String name;

   public Developer() {}
   public void setId(int id) {this.id = id;}
   public int getId() {return id;}
   public void setName(String name) {this.name = name;}
   public String getName() {return name;}
}

С точки зрения Spring – бобы создаются и управляются контейнером Spring IoC. Это просто экземпляры объектов, управляемые Spring.

Чтобы сообщить Spring, какими экземплярами объектов он должен управлять, мы просто помечаем методы, в которых мы их создаем, аннотацией @Bean .

При обнаружении этого метода он будет выполнен, и возвращенное значение будет сохранено в BeanFactory :

@Configuration
public class ConfigurationClass {
    @Bean
    public Developer developer() {
        return new Developer();  
    }
}

Это то же самое, что использовать старый подход XML для регистрации компонента:


    

Теперь, чтобы внедрить этот компонент в качестве зависимости в другой компонент, у нас просто есть другой компонент, вызывающий метод компонента разработчика:

@Configuration
public class ConfigurationClass() {
    
    @Bean
    public Manager manager() {
        return new Manager(developer());
    }

    @Bean
    public Developer developer() {
        return new Developer();  
    }
}

@Требуется

Аннотация @Требуется используется для методов и конструкторов установщика. Как следует из названия, он сообщает Spring, что эти поля необходимы для правильной инициализации компонента.

Если поля не заполнены во время настройки, компонент не сможет инициализироваться, что приведет к исключению, и приложение не сможет выполнить сборку:

public class Developer implements java.io.Serializable {
    private int id;
    private String name;

    public Developer() {}
   
    @Required
    public void setId(int id) {
        this.id = id;
    }
   
    public int getId() {
        return id;
    }
   
    @Required
    public void setName(String name) {
        this.name = name;
    }
   
    public String getName() {
        return name;
    }
}

Чтобы заполнить поле во время настройки, подобное этому, мы присваиваем имена свойств с помощью XML:



@Autowired

Аннотация @Autowired используется для дальнейшего контроля над внедрением зависимостей. Он используется для подключения одного компонента к другому без создания экземпляра первого.

Опять же, вместо подключения зависимостей через XML, что было громоздко, мы просто помечаем наши зависимости как @Autowired . Основываясь на нашем базовом классе, где расположены все наши компоненты, Spring выполняет всю проводку за нас.

Чтобы объявить базовый пакет наших компонентов, мы можем просто добавить тег в файл контекста нашего приложения:


Все @Компоненты помеченные классы (включая производные, такие как @Service , @Controller и @Репозиторий ) будут зарегистрированы как компоненты, имеющие право на автоматическое подключение.

@Автоматическое подключение к свойствам

Вместо явного, императивного создания экземпляра:

public class ProductController {
    private ProductService productService = new ProductService();

    public void someMethod() {
        List productList = productService.getProductList();
    }
}

Мы используем декларативный подход:

public class ProductController {

    @Autowired
    private ProductService productService;

    public void someMethod() {
        List productList = productService.getProductList();
    }
}

В этой реализации мы никогда не создаем экземпляр класса ProductService , отделяя его от ProductController , если хотим его протестировать.

Конечно, чтобы автоматически подключить поле, его необходимо зарегистрировать как компонент в контейнере Spring IoC. В нашем случае это @Service аннотированный компонент, но об этом позже.

Существуют также другие варианты использования аннотации @Autowired .

@Автоматическое подключение к сеттерам

Очень похоже на аннотацию @Required , мы также можем использовать @Autowired на установщиках:

public class ProductController {

    private ProductService productService;

    @Autowired
    public void setProductService(ProductService productService) {
        this.productService = productService;
    }
}

При автоматическом подключении такого сеттера нет необходимости заполнять его через XML.

Это так называемая инъекция зависимостей на основе установщика .

@Автоматическое подключение к конструкторам

Аннотацию @Autowired также можно использовать в конструкторах:

public class ProductService {

    private ProductDao productDao;
    
    @Autowired
    public ProductService(ProductDao productDao) {
        this.productDao = productDao;
    }
}

Это так называемая инъекция зависимостей на основе конструктора .

Необходимый Флаг

Пометив компонент как @Autowired , Spring ожидает, что он будет доступен при построении других зависимостей. Если нет, нас встретит исключение и неудачная сборка.

Если вы не можете гарантировать, что компонент будет доступен, или если он не всегда необходим, вы можете использовать флаг требуется , чтобы пометить его как необязательный:

public class ProductController {

    @Autowired(required = false)
    private ProductService productService;
}

Таким образом, если компонент обслуживания продукта недоступен, все будет работать гладко.

@Квалификатор

Аннотация @Квалификатор используется для прояснения случаев, когда мы хотели бы автоматически подключить более одного компонента одного и того же типа.

Например, в компании у нас, скорее всего, будет более одного сотрудника, и у каждого сотрудника есть своя соответствующая должность – разработчик , ведущий разработчик , менеджер , генеральный директор и т. Д…

@Component
public class Developer implements Employee {}

@Component
public class Manager implements Employee {}

Если бы мы должны были автоматически подключить сотрудника, было бы неясно, какой компонент мы хотим подключить автоматически:

@Controller
public class CompanyController {
    @Autowired
    private Employee employee;
}

Нас встретили бы с ошибкой:

org.springframework.beans.factory.NoSuchBeanDefinitionException: 
    No unique bean of type [com.stackabuse.employee] is defined: 
        expected single matching bean but found 2: [developer, manager]

Чтобы избежать такой ситуации, мы добавляем квалификаторы:

@Component
@Qualifier("developer")
public class Developer implements Employee {}

@Component
@Qualifier("manager")
public class Manager implements Employee {}

И когда автоматическая проводка:

@Controller
public class CompanyController {
    @Autowired
    @Qualifier("developer")
    private Employee employee;
}

Это проясняет, какой компонент мы хотели бы подключить автоматически, и код работает просто отлично.

@ComponentScan

Важной аннотацией для Spring является @ComponentScan аннотация. Он определяет, какие пакеты содержат аннотированные классы. Таким образом, Spring знает, какими классами ему нужно управлять, и он всегда используется вместе с аннотацией @Configuration .

Например, у нас есть пакет com.stackabuse.controller , содержащий все наши контроллеры, где каждый класс помечен @Controller . Чтобы Spring знал, что этот пакет содержит компоненты, требующие управления, мы используем аннотацию @ComponentScan и добавляем пакет.

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

Во многих случаях мы просто определяем один базовый пакет , содержащий все наши компоненты, такие как com.stackabuse . Хотя в некоторых случаях мы хотели бы включить несколько базовых пакетов или базовых пакетов :

@Configuration
@ComponentScan(basePackage = "com.stackabuse")
public class SomeApplication {
    // some code
}

Если мы хотим определить несколько базовых пакетов:

@Configuration
@ComponentScan(basePackage = {"com.package1", "com.package2})
public class SomeApplication {
    // some code
}

Типобезопасной альтернативой для базовых пакетов является basePackageClasses :

@Configuration
@ComponentScan(basePackageClasses =  Developer.class) 
public class SomeApplication {
    // some code
}

Примечание : Если базовый пакет вообще не определен, пакет, в котором находится класс, будет использоваться в качестве базового пакета.

@Ленивый

По умолчанию компоненты и компоненты инициализируются с нетерпением. Если мы хотим изменить это поведение, мы можем сделать это, используя аннотацию @Lazy .

Git Essentials

Ознакомьтесь с этим практическим руководством по изучению Git, содержащим лучшие практики и принятые в отрасли стандарты. Прекратите гуглить команды Git и на самом деле изучите это!

Его можно использовать либо на уровне класса, который аннотирован как @Компонент , либо на уровне метода, который аннотирован как @Компонент .

Если он аннотирован, компонент/компонент не будет инициализирован до тех пор, пока другой компонент явно не обратится к нему, и это необходимо для бесперебойной работы приложения:

@Lazy
@Bean
class SomeResource {}

Мы также могли бы пометить @Конфигурация класс как @Ленивый :

@Lazy
@Configuration
public class AppConfig {
    // some code
}

В этом случае все компоненты, определенные в App Config , также будут лениво инициализированы.

@Конфигурация

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

Это одна из причин, по которой разработчики смогли отказаться от использования конфигурации на основе XML , а простота аннотации делает конфигурацию на основе Java предпочтительной.

@Configuration
public class AppConfig {
     @Bean
     public SomeBean someBean() {
         // Instantiation, configuration, returning the bean
}

@Значение

Аннотация @Value имеет довольно много вариантов использования весной и гарантирует статью для себя. Я постараюсь быть кратким и расскажу о наиболее распространенных и очевидных случаях использования в этом.

Его можно использовать для:

  • Назначение полей значениям по умолчанию
  • Чтение переменных среды
  • Использование выражений языка выражений Spring (SpEL)
  • Значения по умолчанию для параметров, если они используются в методе/конструкторе

Тем не менее, давайте рассмотрим все эти варианты использования один за другим.

Значения Полей По Умолчанию

Если вы хотите присвоить полю значение по умолчанию, аннотация @Value довольно проста:

@Value("Hello World!")
private String helloString;

Несмотря на то, что мы не создавали экземпляр этой строки и не присваивали ей значение явно, мы сделали это с помощью аннотации.

@Значение аннотация предназначена для использования со строками. Если вы попытаетесь применить его к другому типу, он будет работать только в том случае, если Spring сможет легко конвертировать между ними – например, boolean send int s:

@Value("true")
private boolean accepted;

@Value("53")
private int userId;

Свойства среды чтения

Предположим, что среди других свойств наш файл application.properties содержит некоторые переменные среды:

sa.website_name = Stack Abuse

Например, давайте прочитаем это свойство и присвоим его строке в нашем классе конфигурации. Для этого нам также нужно определить источник свойств:

@PropertySource("classpath:application.properties")
@Configuration
public class AppConfig {
    @Value("${sa.website_name}")
    private String websiteName;
}

Вообще говоря, ${...} используется в качестве заполнителя свойств весной. Вы, вероятно, уже знакомы с этим, если вы занимались весенними технологиями.

Если свойство недоступно или не определено, мы можем столкнуться с проблемой. В этом случае мы можем определить значения по умолчанию для заполнителей, если они определены неправильно:

@PropertySource("classpath:application.properties")
@Configuration
public class AppConfig {
    @Value("${sa.website_name}:Backup Value")
    private String websiteName;
}

Таким образом, если sa.website_name не существует, значение, присвоенное строке, будет Значение резервной копии .

Использование заклинания

Аналогично синтаксису заполнителя, Язык выражений Spring (SpEL) использует синтаксис #{...} для хранения выражений:

@Value("#{systemProperties['java.home']}")
private String someValue;

Если мы решим добавить некоторые свойства, которые могут быть недоступны, то снова столкнемся с проблемой. Чтобы избежать таких случаев, мы также можем определить значения “резервного копирования” по умолчанию для заклинаний:

@Value("#{systemProperties['unknownproperty'] ?: 'Backup Value'}")
private String someValue;

Значения Параметров По умолчанию

При применении к методу аннотация @Value присвоит значение по умолчанию всем параметрам метода:

@Value("Hello")
public String hello(String str1, String str2) {
    return str1 + str2;
}

Этот метод будет печатать:

HelloHello

С другой стороны, если мы применим метод @Value как к методу, так и к параметру, параметру будет присвоено новое значение:

@Value("Hello")
public String hello(String str1, @Value("World") String str2) {
    return str1 + str2;
}

Результатом в этом случае будет:

HelloWorld

@dependsOn

Если боб зависит от некоторых других бобов для правильного создания экземпляра, Spring может гарантировать, что все бобы, от которых он зависит, будут созданы до него. Однако нам нужно указать, какие из них, используя аннотацию @dependsOn .

Аннотация принимает массив строк, которые соответствуют именам рассматриваемых компонентов. Это означает, что вы можете передать любое допустимое имя компонента в качестве аргумента, если оно правильно аннотировано аннотацией @Component или @Bean .

@Configuration
public class AppConfig {
    @Bean("firstBean")
    @DependsOn(value = {"secondBean", "thirdBean"})
    public FirstBean firstBean() {
        return new FirstBean();
    }
    
    @Bean("secondBean")
    public SecondBean secondBean() {
        return new SecondBean();
    }
    
    @Bean("thirdBean")
    public ThirdBean thirdBean() {
        return new ThirdBean();
    }
}

Несмотря на то, что Первый компонент расположен перед вторым и третьим, мы отметили, что правильная работа зависит от создания второго и третьего . Сделав это, Spring сначала определит эти два, а затем FirstBean .

@Первичный

Аннотация @Primary часто используется вместе с аннотацией Квалификатора . Он используется для определения компонента “по умолчанию” для автоматического подключения, когда дополнительная информация недоступна.

Это дает приоритет аннотированному компоненту, если существует более одного компонента одного и того же типа, как следует из названия:

@Component
@Qualifier("developer")
@Primary
public class Developer implements Employee {}

@Component
@Qualifier("manager")
public class Manager implements Employee {}

Это та же проблема, с которой мы столкнулись в предыдущей части статьи, где мы определили классификатор, позволяющий аннотации @Autowired выбирать между квалифицированными компонентами.

Однако на этот раз нам не нужно добавлять аннотацию @Квалификатор к аннотации @Autowired , поскольку был объявлен основной компонент/по умолчанию:

@Controller
public class CompanyController {
    @Autowired
    private Employee employee;
}

Это создаст экземпляр компонента Developer .

@Область применения

Аннотация @Scope применяется на уровне компонента и определяет его видимость/жизненный цикл. Если он применяется вместе с аннотацией @Component , он определяет область действия для экземпляров аннотированного типа. Если используется в методе @Bean , область действия применяется к возвращаемому экземпляру.

Существует две основные области, еще четыре из которых предназначены для веб-приложений:

  • синглтон
  • прототип
  • запрос
  • сессия
  • приложение
  • websocket

Одноэлементная Область

Если другое имя области не используется, по умолчанию используется значение одноэлементный . Одноэлементная область действия гарантирует только один экземпляр возвращаемого экземпляра аннотированного метода. Объект будет сохранен в контейнере Spring и кэширован, что позволит использовать его в любом месте приложения:

@Bean
@Scope("singleton")
public CompanyCEO companyCEO() {
    return new CompanyCEO();
}

Прототип

В отличие от одноэлементной области, применение прототипа области гарантирует новый экземпляр аннотированного компонента каждый раз, когда мы его запрашиваем.

@Bean
@Scope("prototype")
public Developer developer() {
    return new Developer();  
}

Запрос

Область действия запроса гарантирует создание одного компонента для каждого HTTP-запроса:

// This method will be called on every HTTP request
@Bean
@Scope("request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public SetupClass someSetup() {
    // Run some setup on each http request
}

Альтернативой было бы использовать аннотацию 4.3 @Requestscoped , которая по умолчанию включает прокси-сервер.

Сессия

Очень похоже на область запроса|/, область сеанса создаст экземпляр аннотированного компонента с зависящим от жизненного цикла сеансом HTTP.

// This method will be called on every HTTP session
@Bean
@Scope("session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public SetupClass someSetup() {
    // Run some setup on each http session
}

Приложение

Область приложения работает аналогично одноэлементной области. Жизненный цикл компонента приложения с областью действия зависит от приложения или, скорее, от ServletContext .

Основное различие между этими двумя заключается в том, что приложение имеет более широкую область применения в том смысле, что оно может распространяться на другие приложения, работающие на том же ServletContext .

@Scope("application")
@Component
public class Application {}

Опять же, начиная с 4.3, вы можете заменить эту аннотацию на @applicationScope .

WebSocket

Если мы используем область websocket , мы связываем жизненный цикл нашего компонента с жизненным циклом сеанса WebSocket .

При первом вызове компонент создается и сохраняется для дальнейшего использования в том же сеансе:

// This method will be called on every websocket session
@Bean
@Scope("websocket", proxyMode = ScopedProxyMode.TARGET_CLASS)
public SetupClass someSetup() {
    // Run some setup on each websocket session
}

Вывод

Фреймворк Spring-это мощный и надежный фреймворк, который действительно изменил игру, когда дело доходит до разработки веб-приложений. Среди множества его проектов было бы неплохо начать с базовой структуры и опираться на нее.

Основная структура знакомит нас с различными аннотациями, которые делают нашу жизнь проще и продуктивнее. Обработка этих аннотаций обязательна для каждого разработчика Java/Spring.