Как насчет Spring MVC?
Вот мои предположения:
- вы используете Spring Framework
- у вас есть какой-то объект, скажем «Новости», который вы хотите опубликовать в своих каналах
- ваша сущность «Новости» имеет creationDate, title и shortDescription
- у вас есть некоторый репозиторий / dao, скажем «NewsRepository», который будет возвращать новости из вашей базы данных
- Вы хотите написать как можно меньше
- Вы не хотите форматировать Atom (XML) вручную
На самом деле вам НЕ нужно использовать Spring MVC в вашем приложении. Если вы это сделаете, перейдите к шагу 3.
Шаг 1: добавьте зависимость Spring MVC в ваше приложение
С Maven это будет:
|
1
2
3
4
5
|
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>3.1.0.RELEASE</version></dependency> |
Шаг 2: добавьте Spring MVC DispatcherServlet
С web.xml это будет:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
<servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup></servlet><servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/feed</url-pattern></servlet-mapping> |
Обратите внимание, я установил шаблон URL на «/ feed», что означает, что я не хочу, чтобы Spring MVC обрабатывал любые другие URL в моем приложении (я использую другую веб-среду для остальной части приложения). Я также даю ему совершенно новый contextConfigLocation, где хранится только конфигурация mvc.
Помните, что когда вы добавляете DispatcherServlet в приложение, в котором уже есть Spring (например, из ContextLoaderListener), ваш контекст наследуется от глобального, поэтому вам не следует создавать bean-компоненты, которые существуют там снова, или включать xml, который их определяет. Следите за тем, как контекст Spring поднимается дважды, и обратитесь к документации по Spring или сервлету, чтобы понять, что радует.
Шаг 3. добавить ROME — библиотека для обработки формата Atom
С Maven это:
|
1
2
3
4
5
|
<dependency> <groupId>net.java.dev.rome</groupId> <artifactId>rome</artifactId> <version>1.0.0</version></dependency> |
Шаг 4. написать свой очень простой контроллер
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
@Controllerpublic class FeedController { static final String LAST_UPDATE_VIEW_KEY = 'lastUpdate'; static final String NEWS_VIEW_KEY = 'news'; private NewsRepository newsRepository; private String viewName; protected FeedController() {} //required by cglib public FeedController(NewsRepository newsRepository, String viewName) { notNull(newsRepository); hasText(viewName); this.newsRepository = newsRepository; this.viewName = viewName; } @RequestMapping(value = '/feed', method = RequestMethod.GET) @Transactional public ModelAndView feed() { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName(viewName); List<News> news = newsRepository.fetchPublished(); modelAndView.addObject(NEWS_VIEW_KEY, news); modelAndView.addObject(LAST_UPDATE_VIEW_KEY, getCreationDateOfTheLast(news)); return modelAndView; } private Date getCreationDateOfTheLast(List<News> news) { if(news.size() > 0) { return news.get(0).getCreationDate(); } return new Date(0); }} |
И вот тест для него, на случай, если вы хотите скопировать и вставить (а кто нет?):
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
@RunWith(MockitoJUnitRunner.class)public class FeedControllerShould { @Mock private NewsRepository newsRepository; private Date FORMER_ENTRY_CREATION_DATE = new Date(1); private Date LATTER_ENTRY_CREATION_DATE = new Date(2); private ArrayList<News> newsList; private FeedController feedController; @Before public void prepareNewsList() { News news1 = new News().title('title1').creationDate(FORMER_ENTRY_CREATION_DATE); News news2 = new News().title('title2').creationDate(LATTER_ENTRY_CREATION_DATE); newsList = newArrayList(news2, news1); } @Before public void prepareFeedController() { feedController = new FeedController(newsRepository, 'viewName'); } @Test public void returnViewWithNews() { //given given(newsRepository.fetchPublished()).willReturn(newsList); //when ModelAndView modelAndView = feedController.feed(); //then assertThat(modelAndView.getModel()) .includes(entry(FeedController.NEWS_VIEW_KEY, newsList)); } @Test public void returnViewWithLastUpdateTime() { //given given(newsRepository.fetchPublished()).willReturn(newsList); //when ModelAndView modelAndView = feedController.feed(); //then assertThat(modelAndView.getModel()) .includes(entry(FeedController.LAST_UPDATE_VIEW_KEY, LATTER_ENTRY_CREATION_DATE)); } @Test public void returnTheBeginningOfTimeAsLastUpdateInViewWhenListIsEmpty() { //given given(newsRepository.fetchPublished()).willReturn(new ArrayList<News>()); //when ModelAndView modelAndView = feedController.feed(); //then assertThat(modelAndView.getModel()) .includes(entry(FeedController.LAST_UPDATE_VIEW_KEY, new Date(0))); }} |
Обратите внимание: здесь я использую fest-assert и mockito. Зависимости:
|
01
02
03
04
05
06
07
08
09
10
11
12
|
<dependency> <groupId>org.easytesting</groupId> <artifactId>fest-assert</artifactId> <version>1.4</version> <scope>test</scope></dependency><dependency> <groupId>org.mockito</groupId> <artifactId>mockito-all</artifactId> <version>1.8.5</version> <scope>test</scope></dependency> |
Шаг 5. напишите свой очень простой вид
Вот где происходит все волшебное форматирование. Обязательно ознакомьтесь со всеми методами класса Entry, так как вы можете захотеть использовать / fill довольно много.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
import org.springframework.web.servlet.view.feed.AbstractAtomFeedView;[...]public class AtomFeedView extends AbstractAtomFeedView { private String feedId = 'tag:yourFantastiSiteName'; private String title = 'yourFantastiSiteName: news'; @Override protected void buildFeedMetadata(Map<String, Object> model, Feed feed, HttpServletRequest request) { feed.setId(feedId); feed.setTitle(title); setUpdatedIfNeeded(model, feed); } private void setUpdatedIfNeeded(Map<String, Object> model, Feed feed) { @SuppressWarnings('unchecked') Date lastUpdate = (Date)model.get(FeedController.LAST_UPDATE_VIEW_KEY); if (feed.getUpdated() == null || lastUpdate != null || lastUpdate.compareTo(feed.getUpdated()) > 0) { feed.setUpdated(lastUpdate); } } @Override protected List<Entry> buildFeedEntries(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { @SuppressWarnings('unchecked') List<News> newsList = (List<News>)model.get(FeedController.NEWS_VIEW_KEY); List<Entry> entries = new ArrayList<Entry>(); for (News news : newsList) { addEntry(entries, news); } return entries; } private void addEntry(List<Entry> entries, News news) { Entry entry = new Entry(); entry.setId(feedId + ', ' + news.getId()); entry.setTitle(news.getTitle()); entry.setUpdated(news.getCreationDate()); entry = setSummary(news, entry); entry = setLink(news, entry); entries.add(entry); } private Entry setSummary(News news, Entry entry) { Content summary = new Content(); summary.setValue(news.getShortDescription()); entry.setSummary(summary); return entry; } private Entry setLink(News news, Entry entry) { Link link = new Link(); link.setType('text/html'); link.setHref(newsAbsoluteUrl + news.getId()); //because I have a different controller to show news at http://yourfanstasticsiteUrl.com/news/ID entry.setAlternateLinks(newArrayList(link)); return entry; }} |
Шаг 6. добавьте ваши классы в контекст Spring
Я использую XML-подход. потому что я старый и я люблю XML. Нет, серьезно, я использую xml, потому что я могу несколько раз объявить FeedController с разными представлениями (RSS 1.0, RSS 2.0 и т. Д.).
Так что это вышеупомянутая весна-mvc.xml
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
<?xml version='1.0' encoding='UTF-8'?> xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd'> <bean class='org.springframework.web.servlet.view.ContentNegotiatingViewResolver'> <property name='mediaTypes'> <map> <entry key='atom' value='application/atom+xml'/> <entry key='html' value='text/html'/> </map> </property> <property name='viewResolvers'> <list> <bean class='org.springframework.web.servlet.view.BeanNameViewResolver'/> </list> </property> </bean> <bean class='eu.margiel.pages.confitura.feed.FeedController'> <constructor-arg index='0' ref='newsRepository'/> <constructor-arg index='1' value='atomFeedView'/> </bean> <bean id='atomFeedView' class='eu.margiel.pages.confitura.feed.AtomFeedView'/></beans> |
И вы сделали.
Меня несколько раз просили поместить весь рабочий код в публичный репозиторий, так что на этот раз все наоборот. Я опишу вещи, которые я уже опубликовал, и вы можете получить коммит из битбакета .
Ссылка: Atom Feeds с Spring MVC от нашего партнера JCG Якуба Набрдалика в блоге Solid Craft .