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

четверг, 15 декабря 2011 г.

Quartz + Spring

В больших проекта часто бывает необходимо выполнять какой-то участок кода по расписанию. Для этого подойдет популярная библиотека Quarz.
Рассмотрим, как можно интегрировать Quartz (1.x) в Spring.

Для начала подхватим зависимость в maven:

    org.quartz-scheduler
    quartz
    1.8.5   


Создадим бин, отвечающий за выполнение задачи:
@Component
public class SomeWorkBean implements InitializingBean, DisposableBean {
    @Override
    public void afterPropertiesSet() {
    }

    public void doWork() {
        System.out.println("doing some work");
    }

    @Override
    public void destroy() throws Exception {
    }
}

теперь можно конфигурировать Spring:
   





    
    



    
       
      



    
        
            
        
    
    
        
            
        
    


где workJob - обьявление JobDetailBean,
simpleTrigger - триггер, обьявляющий критерии выполнения кода в SomeWorkBean,
schedulerFactoryBean - фабрика для создания шедьюлера

Все, теперь метод SomeWorkBean.doWork() будет выполняться каждую минуту. Для более гибкого задание расписания, можно использовать другие триггеры, например CronTrigger

среда, 28 сентября 2011 г.

RESTful веб-сервис на Jersey + Spring 3

Пишем простой REST веб-сервис.

Как известно, начиная с третьей версии у Spring появилась возможность легко создавать REST веб сервис с помощью Spring MVC (на аннотациях @RequestMapping). Но я хочу рассмотреть классический Jax-RS с помощью Jersey. Итак, начнем.

Для начала определим maven зависимости:




    com.sun.jersey
    jersey-server
    1.9.1




    com.sun.jersey.contribs
    jersey-spring
    1.9.1
    
        
            org.springframework
            spring
        
        
            org.springframework
            spring-core
        
        
    



    com.sun.xml.bind
    jaxb-impl
    2.1.9

exclusions в зависимости jersey+spring необходимы потому, что библиотека требует зависимости спринга версий 2 или 2.5. А мы ей их дать не можем, тк используем третью версию. В исключениях требуется указать все библиотеки Spring, которые у нас есть в проекте.

Теперь можно писать сам сервис. Будем доставать продукты по их id:
@Path("/product/{id}")
@Component
@Scope("prototype")
public class ProductRSController {

    @Autowired
    private ProductService srv;

    @GET
    @Produces(MediaType.APPLICATION_XML)
    public Product getDescription(@PathParam("id") String productId) {
        Product product = srv.getProduct(productId);
        return product;
    }
}
Пометив метод с помощью аннотаций @GET и @Produces(MediaType.APPLICATION_XML), мы указали, что url вида http://myserver.com/myapp/rs/product/123 (где myserver.com - наш сервер, myapp - имя развернутого приложения), будет возвращать в ответ сериализованный обьект Product в xml.

Класс ProductService представляет собой сервис бин, с методом Product getProduct(String productId) {...}

Не забудем настроить web.xml:

    contextConfigLocation/WEB-INF/spring-context.xml


    org.springframework.web.context.ContextLoaderListener



    Jersey Web Application
    com.sun.jersey.spi.spring.container.servlet.SpringServlet
    1



    Jersey Web Application
    /rs/*

и spring-context.xml:




Теперь можно разворачивать приложение и зайти по URL http://myserver.com/myapp/rs/product/123. В случае успеха мы увидим полученный XML, если конечно у нас есть Product с id = 123.

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

зависимости:

    com.sun.xml.bind
    jaxb-impl
    2.1.9



    com.sun.jersey
    jersey-client
    1.9.1


сам клиент будет выглядеть так:
public class RESTClient {

    public Product recieveObject() {
        Client c = Client.create();
        WebResource r = client.resource("http://myserver.com/myapp/rs/");
        Product product = response = r.path("product/123").
                accept(MediaType.APPLICATION_XML_TYPE).
                get(Product.class);

        return product;
    }
    
    public String recieveXML() {
        Client c = Client.create();
        WebResource r = client.resource("http://myserver.com/myapp/rs/");
        String product = response = r.path("product/123").
                accept(MediaType.APPLICATION_XML_TYPE).
                get(String.class);

        return product;
    }
}
метод recieveObject() возвращает десериализованный обьект Product, а метод recieveXML() возвращает XML.

С помощью Generics можно сделать клиент универсальным:
public class RESTClient {

    private static final Client client;

    private String resource = "http://myserver.com/myapp/rs/";

    static {
        client = Client.create();
    }

    public RESTClient() {
    }

    public RESTClient(String resource) {
        this.resource = resource;
    }

    public void recieve() {

        //object
        Product product = recieve(Product.class, "product/", "123");
        System.out.println("response.getProductId: " + product.getProductId());
        
        //string 
        String xml = recieve(String.class, "product/", "123");
        System.out.println("response: " + xml);
    }

    private <T> T recieve(Class<T> type, String URI, String id) {
        WebResource r = client.resource(resource);

        T response = r.path(URI + id).
                accept(MediaType.APPLICATION_XML_TYPE).
                get(type);

        return response;
    }
}


Для настройки вывода данных в формате JSON необходимо проделать следующее:

Добавить зависимость:

            com.sun.jersey
            jersey-json
            1.10
        

Создать класс JAXBContextResolver в том же package, где лежит контроллер
@Provider
public final class JAXBContextResolver implements ContextResolver<jaxbcontext> {

    private final JAXBContext context;

    private final Set<class> types;

    private final Class[] cTypes = {MyEntity1.class, MyEntity1.class};

    public JAXBContextResolver() throws Exception {
        this.types = new HashSet(Arrays.asList(cTypes));
        this.context = new JSONJAXBContext(JSONConfiguration.natural().build(), cTypes);
    }

    @Override
    public JAXBContext getContext(Class<?> objectType) {
        return (types.contains(objectType)) ? context : null;
    }
}
где MyEntity1, MyEntity2 - отображаемые в JSON обьекты


и в контроллере указывать формат данных @Produces("application/json; charset=utf-8")

суббота, 9 июля 2011 г.

Singleton и Prototype scope в Spring

Singleton scope означает что экземпляр бина создается один раз, при инициализации контекста. Не стоит сравнивать этот scope со stateless bean в EJB. EJB контейнер при каждом обращении выдает новый экземпляр бина. Поэтому в stateless бине состояние не сохраняется. Таким образом Singleton scope в Spring - тоже statefull. В случае со scope Prototype, можно создавать его новые экземпляры, когда это надо, с помощью фабрики. (statefull)

В обьявлении этих бинов нет ничего сложного:



Но проблема появляется, когда нужна зависимость SingletonBean от PrototypeBean. В этом случае можно поступить несколькими способами:
ApplicationContextAware или BeanNameAware method injection; Lookup method injection; Arbitrary method replacement.
Расскажу о самом простом (по крайней мере для меня) из них - аналоге BeanNameAware.

PrototypeBean может представлять из себя что угодно. В данном контексте нам это не важно, так что будем рассматривать его просто как POJO класс:
public class PrototypeBean {
...
}

а в SingletonBean используем фабрику org.springframework.beans.factory.ObjectFactory:
public class SingletonBean {

  private ObjectFactory factory;

  public void createPrototypeBean() {
     //здесь можем получить сколько угодно экземпляров PrototypeBean
     PrototypeBean bean = factory.getObject();
  }

  ...

  public void setFactory(ObjectFactory beanFactory) throws BeansException {
     this.factory = beanFactory;
  }
}

Чтобы явно указать ObjectFactory экземпляр какого бина создавать, в конфигурации пропишем ObjectFactory как property Singleton-бина: