Показаны сообщения с ярлыком spring. Показать все сообщения
Показаны сообщения с ярлыком spring. Показать все сообщения

суббота, 14 июля 2018 г.

Способы создания/уничтожения бинов и паттерн "фабрика"

Коротко - паттерн "фабрика" - это реализация класса который обеспечивает настройку, создание и получение обьектов с помощью фабричных методов.
Spring
Вы можете отмечать классы аннотациями @Component, @Service, @Repository, @Controller, @Named - и тогда при наличии аннотации @ComponentScan для класса конфигурации приложения контейнер Spring автоматически определит бины и внедрит их куда нужно. Также бины можно создавать с помощью настроек XML - но мы это не будем рассматривать. Есть еще способ - это фабричные методы в классе конфигурации приложения, отмеченные аннотацией @Bean. Наиболее яркий пример - это бин, реализующий интерфейс DataSource. Вот класс конфига приложения:
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = {"PKG"})
public class AppContext {

    @Bean(destroyMethod = "close")
    public DataSource dataSource() {
        BasicDataSource bds = new BasicDataSource();
        bds.setDriverClassName("org.apache.derby.jdbc.EmbeddedDriver");
        bds.setUrl("jdbc:derby:./db/primer");
        bds.setPassword("12345");
        bds.setUsername("romka");
        return bds;
    }
....................   
}
Теперь можно внедрить этот бин куда и как угодно - через поле / метод / конструктор, например:
@Inject
private DataSource ds;
JavaEE CDI
Таким же функционалом обладает контейнер CDI JavaEE - только здесь такие фабричные методы называют продюсерами а используются аннотации @Produces и @Disposes и использовать их можно в любом бине CDI, аналог примера выше для CDI:
@Named
public class DSproducer {

    @Produces @ApplicationScoped
    @Named("ds") 
     public DataSource dataSource() {
        BasicDataSource bds = new BasicDataSource();
        bds.setDriverClassName("org.apache.derby.jdbc.EmbeddedDriver");
        bds.setUrl("jdbc:derby:./db/primer");
        bds.setPassword("12345");
        bds.setUsername("romka");
        return bds;
    }

    public void dsClose (@Disposes DataSource ds) {
        ds.close();
    }

}
отметим, что аннотация  @Disposes применяется к параметру метода уничтожения и кроме того - методы создания и уничтожения бина должны быть в одном классе. По сути @Produces - это аналог @Bean, а метод уничтожения с аннотацией @Disposes - это аналог параметра "destroyMethod" аннотации @Bean из Spring. Метод уничтожения вызывается по окончании действия контекста данного бина (здесь - @ApplicationScoped).
И да - точно так же теперь можно внедрить этот бин куда и как угодно - через поле / метод / конструктор (все эти способы контейнер CDI поддерживает точно так же как и Spring), например:
@Inject
private DataSource ds;


Spring - как бину узнать свое имя

Для этого надо реализовать интерфейс BeanNameAware и его единственный метод setBeanName(String str):

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope("singleton")
public class HashOfFile implements BeanNameAware {
   
    private String myName;
   
    @Override
    public void setBeanName (String myName) {
        this.myName=myName;
    }

   
    @PostConstruct
    public void afterBirn(){
       System.out.println("HashOfFile: my name is = " + myName);
    }
..................................
}
Пример исходного кода - https://github.com/harp077/hofat

Интеграция Spring и JPA EclipseLink

Класс конфигурации приложения:

@Configuration
@EnableTransactionManagement
@ComponentScan(basePackages = {"jennom"})
@EnableLoadTimeWeaving
@PropertySource(value = "classpath:jennom.properties")
public class AppContext {


    @Bean
    public JpaTransactionManager jpaTransactionManager() {
        JpaTransactionManager jtm = new JpaTransactionManager();
        jtm.setDataSource(this.ds());
        jtm.setJpaDialect(new EclipseLinkJpaDialect());
        jtm.setPersistenceUnitName("jennomPU");
        return jtm;
    }

    @Bean(destroyMethod = "close")
    public DataSource ds() {
        BasicDataSource dsNodes = new BasicDataSource();
        dsNodes.setDriverClassName("org.apache.derby.jdbc.EmbeddedDriver");
        dsNodes.setUrl("jdbc:derby:./db/primer");
        dsNodes.setPassword("12345");
        dsNodes.setUsername("romka");
        return dsNodes;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean eemf = new LocalContainerEntityManagerFactoryBean();
        eemf.setDataSource(this.ds());
        eemf.setJpaVendorAdapter(elJpaVendorAdapter());
        eemf.setPackagesToScan("jennom.jpa");
        eemf.setPersistenceUnitName("jennomPU");
        eemf.setJpaDialect(new EclipseLinkJpaDialect());
        eemf.setSharedCacheMode(SharedCacheMode.ENABLE_SELECTIVE); 
        return eemf;
    }

    @Bean
    public EclipseLinkJpaVendorAdapter elJpaVendorAdapter() {
        EclipseLinkJpaVendorAdapter jpaVendorAdapter = new EclipseLinkJpaVendorAdapter();
        jpaVendorAdapter.setDatabase(Database.DERBY);
        jpaVendorAdapter.setGenerateDdl(true);
        jpaVendorAdapter.setShowSql(true);
        jpaVendorAdapter.setDatabasePlatform("org.eclipse.persistence.platform.database.DerbyPlatform");
        return jpaVendorAdapter;
    }

}

после этого вы можете создавать свой DAO:


@Service
@Repository
@Transactional
public class EjbDaoJPA {

    @PersistenceContext(unitName = "jennomPU")
    private EntityManager em;

    @Transactional(readOnly=true)
    public List<JpaNodes> findAllNodesJPQL () {
        List<JpaNodes> nodes=em.createNamedQuery("JpaNodes.findAll", JpaNodes.class).getResultList();
        return nodes;
    }
    @Transactional(readOnly=true)
    public JpaNodes findNodeJPQL (int id) {
        JpaNodes nodes=em.createNamedQuery("JpaNodes.findById", JpaNodes.class)
        .setParameter("id", id).getSingleResult();
        return nodes;
    }   
    public void removeNode (JpaNodes jpaNode) {
        em.remove(jpaNode);
    } 

    public void createNode (JpaNodes jpaNode) {
        em.persist(jpaNode);
    }   

    public void editNode (JpaNodes jpaNode) {
        em.merge(jpaNode);
    } 
      public void clearAllNodesSQL () {

        em.createNativeQuery("delete from nodes");
    } 

    public void clearAllNodesJPQL () {
        em.createNamedQuery("JpaNodes.clear", JpaNodes.class);
    }   

}


Сущность JpaNodes

@Entity
@Cacheable(true)
@Table(name="nodes")
@NamedQueries ({
    @NamedQuery(name="JpaNodes.clear",
            query="delete from JpaNodes"),   
    @NamedQuery(name="JpaNodes.findAll",
            query="select c from JpaNodes c order by c.ip"),   
    @NamedQuery(name="JpaNodes.findByIP",
            query="select c from JpaNodes c where c.ip = :ip"),
    @NamedQuery(name="JpaNodes.findById",
            query="select c from JpaNodes c where c.id = :id")   
})
public class JpaNodes implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO) /// !!!!!!!! = IDENTITY
    private int id;
   
    @Basic   
    @Column(name = "time", nullable=true, insertable=true, updatable=true, length=255)    
    private String time="";
   
    @Basic 
    @Size(min=1,max=5,message="stateFar")
    @Column(name = "StateFar", nullable=true, insertable=true, updatable=true, length=255)    
    private String StateFar="-";
   
    @Basic
    @NotNull(message="IP/DNS not null")
    //@Pattern(regexp="",message="")
    @Column(name = "ip", nullable=false, insertable=true, updatable=true, length=255)    
    private String ip;
   
    @Basic   
    @Column(name = "info", nullable=true, insertable=true, updatable=true, length=255)    
    private String info="";
   
    @Basic 
    @NotNull(message="Type not null")
    @Column(name = "tip", nullable=false, insertable=true, updatable=true, length=255)    
    private String tip="";   
   
    @Basic   
    @Column(name = "loss", nullable=true, insertable=true, updatable=true, length=255)    
    private String loss="0";
   
    @Basic
    @Size(min=1,max=5,message="state")
    @Column(name = "state", nullable=true, insertable=true, updatable=true, length=255)    
    private String state="-";
    ...........геттеры + сеттеры + equals + hashCode + toString}

пятница, 13 июля 2018 г.

Асинхронный вызов методов

При таком вызове метод выполняется в отдельном потоке не прерывая основной поток приложения. Иногда такой прием называют "паттерн асинхронность".
Spring
Класс конфигурации приложения надо отметить аннотацией @EnableAsync. Если это веб-приложение - то достаточно снабдить метод аннотацией @Async - и любой контейнер сервлетов (например Tomcat) запустит метод в отдельном потоке. Для десктоп-приложения надо будет добавить в класс конфигурации следующий бин:
@Configuration
@EnableAsync
public class AppContext {
   
    @Bean
    public TaskExecutor taskExecutor() {
        return new SimpleAsyncTaskExecutor();
    }
........................................
}

JavaEE
Такой же функционал обеспечивает контейнер EJB из "коробки" без всяких дополнительных настроек (начиная с JavaEE 6 - EJB 3.1 - Например TomEE v1.7.4 с профайлом jaxrs - http://tomee.apache.org/download-archive.html или http://openejb.apache.org/downloads.html ) - достаточно в бине EJB отметить метод аннотацией @Asynchronous - она применима для всех типов бинов EJB - @Stateless, @Stateful, @Singleton. Если отметить весь класс - то все его методы станут асинхронными.

суббота, 7 июля 2018 г.

Уточнение внедрения бинов

Бывают ситуации когда есть несколько реализаций бина одного и того же типа - тогда при внедрении надо будет уточнять какая именно реализация внедряется:
Spring:
в стеке Spring для этого есть аннотация @Qualifier("beanName"):
public class MyClass {
    @Inject
    @Qualifier("finebean1")
    private Fine fine1;

    @Inject
    @Qualifier("finebean2")
    private Fine fine2;
 ..........................................  
}
Соответственно в файле конфигурации есть 2 реализации бина одинакового типа:
@Configuration
public class AppCfg {

   @Bean("
finebean1")
   public Fine oneFine() {
       ................

   }
   @Bean("
finebean2")
   public
Fine twoFine() {
       .................

   }
}
JavaEE CDI:
Допустим продюсер (фабрика) реализует 2 бина одного типа:
public class MyProducer {
    @Produces 
    @SessionScoped
    @Named("finebean1") 
     public Fine getFine1() {
        return new Fine();
    }

    @Named("finebean1")   
    public void killFine1 (@Disposes Fine fine1) {
        fine1.close();
    }

    @Produces 
    @SessionScoped
    @Named("finebean2") 
     public Fine getFine2() {
        return new Fine();
    }

    @Named("finebean2")   
    public void killFine2 (@Disposes Fine fine2) {
        fine2.close();
    }

}
Здесь есть аналог @Qualifier - аннотация @Named, которую поддерживает и Spring и при внедрении уточнение бинов выглядит так:
public class MyClass {
    @Inject
    @Named("finebean1")
    private Fine fine1;

    @Inject
    @Named("finebean2")
    private Fine fine2;
 ..........................................  
}
также можно использовать и для аннотации @Resource:
public class AmbiguousResourceFine {
    @Resource
    @Named("fineDay")
    private Fine fine;
....................................
  }
@Named можно использовать как замену для @Component / @Service / @Controller / @Repository
Для аннотации @Resource имя можно задать как параметр:
public class MyResss {
    @Resource(name = "rrresss")
    private Resss resss;

}
Для поддержки аннотаций @Named и @Inject CDI стека JavaEE в проекте Spring надо добавить зависимость:
<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

Для поддержки всех аннотаций CDI в проект Spring можно добавить зависимость:
<dependency>
     <groupId>javax.enterprise</groupId>
     <artifactId>cdi-api</artifactId>
     <version>1.2</version>
</dependency>

тогда будут работать все аннотации из CDI, которые поддерживает Spring - @Named, @Inject, @PostConstruct, @PreDestroy, @Resource и др.

пятница, 22 июня 2018 г.

Зависимые бины и аннотация @DependsOn

Бывают случаи когда ваш бин зависит от 1 или нескольких других бинов приложения Spring - здесь на помощь приходит аннотация @DependsOn, которая заставляет контейнер сначала создавать бины зависимостей - и только потом зависящий от них бин. В параметрах аннотации указываются имена бинов. Аннотацию можно использовать или в файле конфигурации приложения совместно с аннотацией @Bean:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
 
@Configuration
public class AppConfig {

   @Bean("beanOne")
   @DependsOn(value = { "beanTwo", "beanThree" })
   public BeanOne getBeanOne() {
      return new BeanOne();
   }

   @Bean("beanTwo")
   public BeanTwo getBeanTwo() {
      return new BeanTwo();
   }

   @Bean("beanThree")
   public BeanThree getBeanThree() {
      return new BeanThree();
   }
}
 или аннотировать класс бина:
@Component
@DependsOn(value = { "beanTwo", "beanThree" })
 public class BeanOne {
 
   @Autowired
   private BeanTwo beanTwo;

   @Autowired
   private BeanThree beanThree;

...............
}
Аналогичный механизм есть для синглтонов EJB стека JavaEE - только там в качестве параметров аннотации указывают имена классов и кроме того, учитывая что контейнер EJB по-умолчанию использует ленивое создание бинов (в отличие от Spring - он по-умолчанию создает бины сразу при старте приложения), то аналогичная аннотация используется как правило совместно с аннотацией @Startup, которая заставляет EJB создавать бины сразу при старте приложения - пример:
import javax.ejb.DependsOn;
import javax.ejb.Singleton;
import javax.ejb.Startup;

@Singleton
@Startup
@DependsOn("EjbConfig")
public class EjbMain {

    .....................

}

четверг, 21 июня 2018 г.

Spring - класс JdbcDaoSupport и паттерн DAO

Паттерн DAO - Data Access Object - очень коротко - это когда все методы доступа к данным вы собираете (инкапсулируете) в одном классе.
Класс JdbcDaoSupport является оболочкой для JdbcTemplate и позволяет удобно делать свои DAO расширяя класс JdbcDaoSupport - при этом в нашем классе для работы необходимо только указать через конструктор реализацию DataSource при создании экземпляра нашего DAO. Напишем свой класс DaoJDBC который расширяет класс JdbcDaoSupport и сделаем внедрение через конструктор бина, реализующего DataSource:

@Repository
public class DaoJDBC extends JdbcDaoSupport {
   
    @Inject   // или @Autowired
    public DaoJDBC(DataSource dataSource) {
        this.setDataSource(dataSource);
    }

    public void clearAll() {
        String sql = "delete from hosts";
        this.getJdbcTemplate().update(sql);
    }
.................// + другие операции с базой
}
при этом экземпляр JdbcTemplate будет создан автоматически и будет доступен из нашего DAO через this.getJdbcTemplate() - дальше в классе DaoJDBC нам доступен весь инструментарий JdbcTemplate через this.getJdbcTemplate().

Spring - класс JdbcTemplate и внедрение через конструктор - constructor injection.

Spring обеспечивает очень удобную поддержку JDBC с помощью класса JdbcTemplate, которому для работы необходимо только указать через конструктор реализацию DataSource при создании экземпляра JdbcTemplate.
Пусть например мы хотим воспользоваться паттерном DAO (Data Access Object - обьект доступа к данным ) - напишем свой класс DaoJDBC который расширяет класс JdbcTemplate и сделаем внедрение через конструктор бина, реализующего DataSource:
@Repository
public class DaoJDBC extends JdbcTemplate {
   
    @Inject   // или @Autowired
    public DaoJDBC(DataSource dataSource) {
        this.setDataSource(dataSource);
    }

    public void clearAll() {
        String sql = "delete from hosts";
        this.update(sql);
    }
.................// + другие операции с базой
}
 Здесь как раз и происходит внедрение через конструктор бина dataSource,  который мы рассматривали в предыдущей заметке. Дальше в классе DaoJDBC нам доступен весь инструментарий JdbcTemplate через this.
Можно сделать и по-другому - сделать реализацию бина JdbcTemplate в файле конфигурации приложения - а дальше внедрять его куда нам нужно и использовать:
@Inject   // или @Autowired
private JdbcTemplate jdbcTemplate;
В этом случае файл конфигурации приложения следующий:
@Configuration
@ComponentScan(basePackages = {"PKG"})
public class AppContext {

   @Bean(destroyMethod = "close")
    public DataSource dataSource() {
        BasicDataSource bds = new BasicDataSource();
        bds.setDriverClassName("org.apache.derby.jdbc.EmbeddedDriver");
        bds.setUrl("jdbc:derby:./db/primer");
        bds.setPassword("12345");
        bds.setUsername("romka");
        return bds;
    }
    
    @Bean
    public JdbcTemplate jdbcTemplate () {
        return new JdbcTemplate(this.dataSource());
    }
}
И еще - класс JdbcTemplate является безопасным в отношении потоков.
Исходники - https://sourceforge.net/projects/j-pkg/

Spring - интерфейс DataSource и JDBC пул соединений

Для доступа к данным в JDBC необходима реализация интерфейса javax.sql.DataSource - таких реализаций может быть много - в том числе и таких, которые на каждую операцию с БД открывают новое соединение - это очень затратно и медленно - мы их сразу опустим и рассмотрим пул как рабочую схему. Существует много реализаций пула:
- Apache commons dbcp2  (dbcp = database connection pool);
- HikariCP;
а также c3p0, BoneCP, Vibur и много других - настраиваются они все идентично и мы рассмотрим Apache.
Пул постоянно поддерживает несколько готовых соединений с базой - для текущей операции с базой практически мгновенно предоставляется готовое соединение из пула - а затем когда операция будет отработана это соединение менеджер пула помечает как свободное и возвращает в пул - в результате почти не тратится время на создание нового соединения если сравнивать с обычным подходом - это очень краткое и упрощенное описание работы пула.
Необходимо будет добавить зависимость в maven:
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-dbcp2</artifactId>
    <version>2.4.0</version>
</dependency>
в свою очередь эта зависимость "зацепит" и другую - commons-pool2. Будем использовать БД Derby в embedded-mode.
В файл конфигурации приложения Spring надо будет добавить бин, реализующий интерфейс javax.sql.DataSource с помощью класса BasicDataSource из apache commons dbcp2:
@Bean(destroyMethod = "close")
    public DataSource dataSource() {
        BasicDataSource bds = new BasicDataSource();
        bds.setDriverClassName("org.apache.derby.jdbc.EmbeddedDriver");
        bds.setUrl("jdbc:derby:./db/primer");
        bds.setPassword("12345");
        bds.setUsername("romka");
        return bds;
    }
Вот и все пока - а дальше мы покажем как его использовать.
Да чуть не забыл - вот пример с исходниками

суббота, 9 июня 2018 г.

Spring - внешний файл свойств приложения

Файл конфигурации приложения:

@Configuration
@ComponentScan(basePackages = {"jspv"})
@PropertySource(value = {"file:cfg/jivam.properties"})
//@PropertySource(value = {"classpath:hofat.properties"})
public class AppContext {
   
    @Value("${top}")
    private String top;
  
    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }

   
}
то что нужно добавить выделено жирным шрифтом - это аннотация @PropertySource и бин propertySourcesPlaceholderConfigurer. Теперь в любом компоненте Spring из пакета "jspv" можно внедрять свойства из внешнего файла:
    @Value("${top}")
    private String top;
пример содержимого файла свойств:
####
perevod=en
skin=com.jtattoo.plaf.mcwin.McWinLookAndFeel
top=JIVAM - Java Image Viewer And Manipulator, build 26-01-18.
###
исходники лежат на https://sourceforge.net/projects/jivam/files/?source=navbar

четверг, 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 ...