четверг, 24 мая 2018 г.

Spring - ресурсы

Spring предоставляет унифицированный механизм для доступа к ресурсам независимым от протокола способом. Приложение может работать с файловым ресурсом одинаковым образом, где бы он не был: в файловой системе, в classpath или на удаленном сервере.
   В основе всей поддержки ресурсов Spring находится интерфейс Resource. В нем определены 10 методов: contentLength(), exists(), getDescription(), getFile(), getFileName(), getURI(), getURL(), isOpen(), isReadable() и lastModified(). Есть еще 1 метод: createRelative() - он создает новый экземпляр Resource, используя путь относительно экземпляра, на котором он вызывается. Можно сделать свои реализации Resource, но в большинстве случаев будет применяться одна из встроенных реализаций для доступа к файлу (класс FileSystemResource), пути классов (класс ClassPathResource) или URL-ресурсам (класс UrlResource). Для поиска и создания экземпляров Resource Spring внутри использует другой интерфейс, ResourceLoader , и его стандартную реализацию DefaultResourceLoader. Однако обычно вы не будете взаимодействовать с DefaultResourceLoader, а вместо этого работать с другой реализацией ResourceLoader — ApplicationContext:
public class ResDemo {
   public static void main(String[] args) throws Exception{
      ApplicationContext ctx = new
                     AnnotationConfigApplicationContext(AppConfig.class);
      Resource r1 = ctx.getResource("file:///c:/tmp/tst.txt");
      Resource r2 = ctx.getResource("classpath:tst.txt");
      Resource r3 = ctx.getResource("http://www.google.com");
   }
}
Для протоколов file: и http: Spring возвращает экземпляр UrlResource. Она включает класс FileSystemResource, но DefaultResourceLoader вообще не использует этот класс. Причина в том, что стандартная стратегия загрузки ресурсов в Spring трактует URL и файл как один и тот же тип ресурса, но с отличающимися протоколами (т.е. file: и http:). Если экземпляр FileSystemResource обязателен, применяйте FileSystemResourceLoader. Получив экземпляр Resource, вы можете работать с содержимым ресурса по своему усмотрению, используя getFile(),
getInputStream() или getURL(). В ряде случаев, например, когда применяется протокол http:, вызов getFile() дает в результате исключение FileNotFoundException. По этой причине рекомендуется использовать метод getInputStream() - он будет работать со всеми возможными типами ресурсов.

События жизненного цикла бинов

Spring
В Spring их два - метод выполняемый сразу после создания бина + метод выполняемый перед уничтожением бина. Наиболее простой способ использования этих методов - это аннотировать их с помощью @PostConstruct и @PreDestroy соответственно - это аннотации из JSR-250, который вошел в Java SE начиная с версии 6 а также входит в Java EE CDI (Contexts and Dependency Injection) и который поддерживается Spring. Есть еще 2 способа сделать это (с помощью XML и интерфейсов) - но я специально не говорю об этом чтобы не "грузить" читателей, т к все эти способы эквивалентны.
@Component
public class MyBean {

    @PostConstruct
    public void afterBirn() {
       .....код выполняемый сразу после создания бина....
    }
    ................

    @PreDestroy
    public void beforeKill() {
       .....код выполняемый перед уничтожением бина....
    }

}
Методы должны быть только по 1 на событие + без аргументов + именно void. Методы уничтожения @PreDestroy работают для синглтонов - для бинов со scope=prototype Spring не запускает эти методы согласно проектному решению. Минус обратных вызовов уничтожения в Spring в том, что они не запускаются автоматически - нужно не забыть вызвать AbstractApplicationContext.destroy() перед закрытием приложения. Когда приложение выполняется как сервлет, указанный метод destroy() можно вызвать в методе destroy() сервлета. Для Desktop-приложений чтобы обеспечить выполнение методов @PreDestroy необходимо в контексте Spring зарегистрировать хук:

public static void main(String args[]) {
   // Shutdown Spring container gracefully in non-web applications !!
   //ConfigurableApplicationContext ctx = new
   AbstractApplicationContext ctx = new       AnnotationConfigApplicationContext(AppContext.class);
                ctx.registerShutdownHook();
 // main method exits, hook is called prior to the app shutting down...
 // @PreDestroy methods is called before close App               
}
Теперь перед выходом из метода main будет обеспечено выполнение всех методов отмеченных как @PreDestroy. Вместо AbstractApplicationContext можно использовать ConfigurableApplicationContext.
JavaEE
Точно такие же аннотации ( @PostConstruct и @PreDestroy ) поддерживают бины EJB (Enterprise Java Beans) и CDI (Contexts and Dependency Injection) из стека JavaEE. Stateful-бины EJB (сохраняющие состояние) помимо вышеуказанных 2-х также поддерживают еще 3 события:
@PrePassivate - метод выполняемый перед пассивацией - сериализацией и сохранением на носитель бина;
@PostActivate - метод выполняемый после активации - десериализации и восстановления бина с носителя;
@Remove - после выполнения этого метода контейнер EJB уничтожит бин.

Режимы загрузки бинов: Spring - @Lazy и JavaEE(EJB) - @Startup

Если в Spring бин аннотирован как @Lazy(true), то он будет создан только при первом обращении к нему:
@Component
@Lazy(true)
public class MyBean {
.........
}
Соответственно при @Lazy(false) бин создается сразу при старте приложения (режим по-умолчанию в Spring) - кстати для сравнения - аналогично работает аннотация @Startup для синглтонов EJB стека JavaEE. По умолчанию Spring создает все бины при старте приложения, т е работает режим @Lazy(false) - а вот в CDI и EJB из стека JavaEE все наоборот - там бины создаются лениво при первом обращении к ним. Если c помощью @Lazy аннотирован класс конфигурации Spring - то это будет работать для всех методов отмеченных аннотацией @Bean:
@Lazy(true)
@Configuration
@ComponentScan(basePackages = "my.app")
public class AppConfig {
    @Bean
    public Region getRegion(){
        return new Region();
    }
    @Bean
    public Country getCountry(){
        return new Country();
    }
}
Можно также использовать в классе конфигурации для отдельного бина:
@Configuration
@ComponentScan(basePackages = "my.app")
public class AppConfig {
    @Bean
    @Lazy(true)
    public Region getRegion(){
        return new Region();
    }

}
А также при внедрении совместно с @Autowired или @Inject:
    @Lazy(true)
    @Autowired
    private City city;

среда, 23 мая 2018 г.

Spring - внедрение через метод установки - Setter Injection

Spring поддерживает аннотацию @Inject, которая является частью специфика­ции JSR-330 (JavaEE CDI - Contexts and Dependency lnjection). Аннотация @Inject эквивалентна аннотации @Autowired в Spring. Для внедрения через метод нужно только добавить аннотацию @Autowired или @Inject к методу установки:

@Component("eprst")
public class Eprst {

   private Provider provider;

   @Autowired
   public void setProvider(Provider provider) {
       this.provider = provider;
   }
   ...
}
Для получения тех же результатов вместо @Autowired или @Inject можно также использовать @Resource(name="provider"). Аннотация @Resource присутствует в стандарте JSR-250 и поддерживает параметр name для уточнения DI. В итоге можно использовать несколько аннотаций для внедрения:
аннотация @Autowired, определяемая самим фреймворком Spring;
аннотация @Inject из JSR-330;
аннотация @Resource из JSR-250.

Обьявление бинов - аннотирование классов для автоматического определения

 Spring
       По умолчанию аннотация @ComponentScan в классе конфигурации Spring ищет классы, отмеченные одной из аннотаций:
     @Component – универсальная аннотация, указывающая, что класс
     является компонентом Spring;
     @Controller – указывает, что класс определяет контроллер
     Spring MVC;
     @Repository – указывает, что класс определяет репозиторий дан-
     ных;
     @Service – указывает, что класс определяет службу;
     @Named("myBean") - аннотация из JavaEE CDI - Contexts and Dependency Injection (JSR-330) - равнозначна @Component или @Service при аннотировании класса;
     Spring поддерживает аннотации JavaEE CDI (JSR-330 = @Inject, @Named, @Singleton и др) - только для этого необходимо добавить зависимость в Maven:
    <dependency>
      <groupId>javax.inject</groupId>
      <artifactId>javax.inject</artifactId>
      <version>1</version>
    </dependency>

Пример:
@Service("message")
public class HelloWorld {
    ..........
}
     Здесь аннотация @Service указывает на то, что этот бин предоставляет службы, которые используются другими бинами, параметр аннотации - имя бина. Во время начальной загрузки ApplicationContext Spring будет искать эти компоненты и создавать бины с указанными именами.
     Применение @Component дает тот же эффект, что и @Service. Обе аннотации инструктируют Spring о том, что класс является кандидатом на автоматическое обнаружение с применением конфигурации, основанной на аннотациях. На практике @Service является специализацией @Component, отражающей тот факт, что класс предоставляет бизнес-службу другим уровням приложения.
JavaEE
Здесь никакого класса конфигурации не нужно - все предоставляет сервер JavaEE - работает так называемый принцип конфигурации по умолчанию принятый для JavaEE - если вы ничего не настраиваете то работает конфигурация по умолчанию. Только для включения механизма  CDI - Contexts and Dependency Injection необходимо наличие даже пустого файла beans.xml в каталоге META-INF или WEB-INF - вот этот файл:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>
после этого все классы (если они не отмечены исключающими аннотациями) будут источниками для бинов CDI.

@Named - бин CDI c именем из имени класса - только первая буква будет малой
@Named("myBean") - бин CDI c заданным именем
@Vetoed - появилась в JavaEE-7 - запрещает создавать бины CDI из отмеченного класса
Для бинов EJB работают аннотации:
@Singleton - без комментариев)
@Stateless - бин без сохранения состояния
@Stateful - бин с сохранением состояния

 

Bean Scope

Spring:
По умолчанию все компоненты Spring единичны.
Обьявление в бине:
@Component
@Scope("prototype")
public class Primer {
.........
}
или так:
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
Начиная с версии 4.3 в web-приложении можно также использовать аннотации:
@ApplicationScope
@RequestScope
@SessionScope
 Бывают:
- singleton = В каждом контейнере Spring может быть создан только
один компонент (по умолчанию);
- prototype = Позволяет создавать произвольное количество
компонентов (по одному на каждое обращение)
- request = Область действия ограничивается HTTP-запросом.         Применяется только в веб-приложениях Spring (Spring MVC);
- session = Область действия ограничивается HTTP-сеансом. Применяется только в веб-приложениях Spring (Spring MVC);
- global-session = Область действия ограничивается
глобальным HTTP-сеансом. Применяется только в портлетах;
      Понятие единичных компонентов ограничено областью действия контекста Spring. В отличие от истинных классов-одиночек, гарантирующих существование единственного экземпляра на каждый загрузчик классов (classloader), для единичных компонентов в Spring гарантируется только наличие единственного экземпляра компонента в контексте приложения – ничто не мешает создать экземпляр того же класса традиционным способом или даже создать несколько объявлений бина для одного и того же класса.
          Теперь для сравнения стек JavaEE:
JavaEE CDI:
@RequestScoped
@SessionScoped
@ApplicationScoped
@Dependent - по умолчанию - аналог "prototype" в Spring
@Singleton - В реализации CDI данного скоупа есть одна особенность: в процессе инъекции клиент получает ссылку на реальный объект созданный контейнером, а не proxy. В результате чего могут быть проблемы неоднозначности данных, если состояние синглтона будет меняться, а использующие его бины, например, были или будут сериализованы. 
JavaEE EJB:
@Stateful - сохраняющие состояние бины;
@Stateless - не сохраняющие состояние бины;
@Singleton  
И еще - по своей природе Request-бины потокобезопасны - отработал запрос и удалился.
 

Доступ к Spring context из бина

Здесь поможет интерфейс ApplicationContextAware - он объявляет метод setApplicationContext(). Контейнер Spring автоматически обнаружит компоненты, реализующие этот интерфейс, и обеспечит передачу бину ссылки на контекст:

public class ExampleService implements ApplicationContextAware {

     private ApplicationContext context;

     public void setApplicationContext(ApplicationContext context) {
          this.context = context;
     }
     ...
}

Взаимодействие между приложениями и Watch Service

Взаимодействие между разными приложениями Java можно сделать с помощью файловой системы, базы данных и JMS. Рассмотрим случай ФС - в Java ...