La solució que s’ha desenvolupat és una solució proposada amb caràcter pedagògic. Hi ha altres solucions que poden treballar-se més, però fora de l’abast d’aquesta unitat.
A una implementació amb base de dades, i per exemple fent servir Hibernate, es podrien fer les relacions entre els objectes de domini amb anotacions. En aquesta solució es farà sense fer servir cap tipus de framework addicional per a la capa de domini, i per resoldre la relació es farà servir una List de Comment a News.
El codi de la solució d’aquesta activitat el podeu descarregar del següent
.
No obstant això, es recomana desenvolupar el projecte pas a pas, com es detalla a continuació.
Creació del projecte, dependències i configuració
Creeu un nou projecte Maven/Project from Archetype amb l’arquetipus maven-archetype-webapp
i anomeneu-lo “noticies”.
Des de la pestanya Files de NetBeans o des del mateix explorador de fitxers del sistema operatiu, creeu la carpeta Java dins de la carpeta src/main. Ara, si torneu a la pestanya Projects de NetBeans veureu que ha aparegut la carpeta Source Packages on crear els nostres paquets.
Canvieu el fitxer de configuració web.xml.
<web-app version="3.0" 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/web-app_3_0.xsd">
<display-name>Notícies</display-name>
</web-app>
Afegiu-hi les dependències necessàries, com a qualsevol projecte Spring MVC.
A la secció Dependències de pom.xml heu d’afegir-hi les següents:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.0.3.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>7.0</version>
</dependency>
A més, aprofitareu per fer servir la codificació UTF8 en tot el projecte afegint la propietat que defineix aquesta característica en el mateix fitxer pom.xml:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
Creeu el DispatcherServlet-servlet.xml en una nova carpeta de nom Spring dins de WEB-INF. El contingut és el que es mostra a continuació:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<mvc:annotation-driven />
<context:component-scan base-package="cat.xtec.ioc" />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
Definiu el Dispatcher Servlet
(Front Controller
) afegint el següent contingut al fitxer de configuració web.xml:
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/DispatcherServlet-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Capa de domini
Creeu el paquet cat.xtec.ioc.domain
, on residiran les classes de domini. Fareu una per a cada entitat; és a dir, una per a News
i l’altra per a Comments
.
Dins del paquet cat.xtec.ioc.domain
creeu la classe News
amb el codi que es mostra a continuació:
package cat.xtec.ioc.domain;
import java.util.List
public class News {
private String newsId;
private String contentNews;
private List<Comment> comments;
public News(String newsId, String contentNews) {
this.newsId = newsId;
this.contentNews = contentNews;
}
//..getters and setters omesos al text, no al codi
}
Fixeu-vos que Comments
no hi és al constructor, ja que només l’omplirem quan ens interessi per retornar el model amb la vista.
Afegiu-hi també la classe Comment
amb el codi següent:
package cat.xtec.ioc.domain;
public class Comment {
private String commentId;
private String news;
private String user;
private String contentComment;
public Comment(String commentId, String news, String user, String contentComment) {
this.commentId = commentId;
this.news = news;
this.user = user;
this.contentComment = contentComment;
}
//..getters and setters omesos al text, no al codi
}
Capa de persistència
Desenvolupeu les interfícies repositori per a la persistència i les implementacions que simularan la persistència de les dades a memòria. Una interfície i una que la implementa per a cada entitat.
Creeu els paquets cat.xtec.ioc.domain.repository
i cat.xtec.ioc.domain.repository.impl
, on situarem les interfícies i les classes, respectivament.
Creeu la interfície NewsRepository
en el paquet cat.xtec.ioc.domain.repository
amb el codi següent:
package cat.xtec.ioc.domain.repository;
import cat.xtec.ioc.domain.News;
import java.util.List;
public interface NewsRepository {
List <News> getAllNews();
}
Creeu la InMemoryNewsRepository
al paquet cat.xtec.ioc.domain.repository.impl
amb el codi que es mostra a continuació:
package cat.xtec.ioc.domain.repository.impl;
import cat.xtec.ioc.domain.News;
import cat.xtec.ioc.domain.repository.NewsRepository;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Repository;
@Repository
public class InMemoryNewsRepository implements NewsRepository {
private List<News> listOfNews = new ArrayList<News>();
public InMemoryNewsRepository() {
News firstN = new News("010", "Les condicions meteorològiques milloraran en els propers dies...");
News secondN = new News("020", "L'atur ha baixat un 3% el darrer any ...");
News thirdN = new News("020", "La natalitat baixa en les zones més deprimides ...");
listOfNews.add(firstN);
listOfNews.add(secondN);
listOfNews.add(thirdN);
}
public List<News> getAllNews() {
return listOfNews;
}
}
A InMemoryNewsRepository
heu creat la llista de News
que representen les dades de la nostra aplicació com si l’haguéssiu obtingut d’una base de dades. Al constructor es creen dades d’exemple. El mètode que implementen és getAllNews
, que simplement retorna la llista.
Creeu la interfície CommentRepository
en el paquet cat.xtec.ioc.domain.repository
amb el codi següent:
package cat.xtec.ioc.domain.repository;
import cat.xtec.ioc.domain.Comment;
import java.util.List;
public interface CommentRepository {
List <Comment> getAllComments();
List <Comment> getCommentsByNews(String news);
}
En aquest cas heu afegit el mètode getCommentsByNews
perquè el necessitareu quan vulgueu mostrar una notícia i els seus comentaris.
Creeu la InMemoryCommentRepository
al paquet cat.xtec.ioc.domain.repository.impl
amb el codi que es mostra a continuació:
package cat.xtec.ioc.domain.repository.impl;
import cat.xtec.ioc.domain.Comment;
import cat.xtec.ioc.domain.repository.CommentRepository;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Repository;
@Repository
public class InMemoryCommentRepository implements CommentRepository {
private List<Comment> listOfComments = new ArrayList<Comment>();
public InMemoryCommentRepository() {
Comment comment010a = new Comment("010a","010","admin","El temps sempre igual.");
Comment comment010b = new Comment("010b","010","maria","Uf! Quina calor.");
Comment comment010c = new Comment("010c","010","joan","Paciència.");
Comment comment020a = new Comment("020a","020","admin","Això és una bona notícia.");
Comment comment020b = new Comment("020b","020","joan","Jo he trobat feina.");
Comment comment030a = new Comment("030a","030","admin","Haurem de posar-nos les piles");
listOfComments.add(comment010a);
listOfComments.add(comment010b);
listOfComments.add(comment010c);
listOfComments.add(comment020a);
listOfComments.add(comment020b);
listOfComments.add(comment030a);
}
public List<Comment> getAllComments() {
return listOfComments;
}
public List<Comment> getCommentsByNews(String news) {
List<Comment> returnedComments = new ArrayList<Comment>();
for (Comment comment : listOfComments) {
if (comment != null && comment.getNews() != null
&& comment.getNews().equals(news)) {
returnedComments.add(comment);
}
}
return returnedComments;
}
}
El constructor crea un seguit de comentaris per a les diverses notícies que havíeu creat abans.
Capa de servei
Només creareu la NewsServiceImpl
com a servei i la corresponent interfície NewsService
. Això és perquè només tindrà un mètode que ha de tornar la llista de totes les notícies, però a cada element News
de la llista s’omplirà a la vegada la seva llista de Comments
amb els objectes corresponents.
Creeu el paquet cat.xtec.ioc.service
i a dins la interfície NewsService
amb el codi que es mostra:
package cat.xtec.ioc.service;
import cat.xtec.ioc.domain.News;
import java.util.List;
public interface NewsService {
List<News> getAllNews();
}
Creeu el paquet cat.xtec.ioc.service.impl
i a dins la NewsServiceImpl
implementant la interfície anterior i amb el codi següent:
package cat.xtec.ioc.service.impl;
import cat.xtec.ioc.domain.News;
import cat.xtec.ioc.domain.repository.CommentRepository;
import cat.xtec.ioc.domain.repository.NewsRepository;
import cat.xtec.ioc.service.NewsService;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class NewsServiceImpl implements NewsService {
@Autowired
private NewsRepository newsRepository;
@Autowired
private CommentRepository commentRepository;
public List<News> getAllNews() {
List<News> listOfNews = newsRepository.getAllNews();
for (News aNews : listOfNews) {
aNews.setComments(commentRepository.getCommentsByNews(aNews.getNewsId()));
}
return listOfNews;
}
}
Aquest servei retorna la llista de notícies i en cada una d’elles ha afegit la llista dels possibles comentaris que pugui tenir associats.
Capa de presentació
En primer lloc, construireu el controlador NewsController
, que recollirà les peticions, cridarà la capa de servei i retornarà el ModelAndView
.
Creeu el paquet cat.xtec.ioc.controller
i a dins el controlador NewsController
amb el codi que es mostra a continuació:
package cat.xtec.ioc.controller;
import cat.xtec.ioc.service.NewsService;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class NewsController {
@Autowired
private NewsService newsService;
@RequestMapping("/")
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
ModelAndView modelview = new ModelAndView("news");
modelview.getModelMap().addAttribute("news", newsService.getAllNews());
return modelview;
}
}
Aquest controlador agafarà les peticions a nivell de root de l’aplicació.
Creeu la carpeta Views dins de la carpeta WEB-INF. A Views, creeu la vista news.jsp amb el codi que es mostra a continuació:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="ca">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
<title>Notícies</title>
</head>
<body>
<section>
<div class="jumbotron">
<div class="container">
<h1>Notícies</h1>
<p>Llista de notícies</p>
</div>
</div>
</section>
<section class="container">
<div class="row">
<c:forEach items="${news}" var="aNews">
<div class="col-sm-6 col-md-3" style="padding-bottom: 15px">
<div class="thumbnail">
<div class="caption">
<h3>${aNews.newsId}</h3>
<p>${aNews.contentNews}</p>
<c:forEach items="${aNews.comments}" var="elItem" >
<p>${elItem.user}</p>
<p>${elItem.contentComment}</p>
</c:forEach>
</div>
</div>
</div>
</c:forEach>
</div>
</section>
</body>
</html>
Si executeu l’aplicació us ha de quedar semblant a la que es mostra en la imatge: