Activitats

Notícies i comentaris

L’objectiu d’aquesta activitat és que l’estudiant repassi l’arquitectura i el procés d’una aplicació web Spring MVC, és a dir, les capes:

  • presentació
  • domini
  • servei
  • persistència

Així mateix, el procés de crides entre si; en realitat, entre les seves classes.

Es proposa el desenvolupament d’una aplicació senzilla per mostrar notícies i els comentaris que els usuaris van fent sobre cada notícia.

L’aplicació podria tenir aquest model de domini:

Figura Model de domini de notícies i comentaris

Fixeu-vos que en aquest cas hi ha dues entitats. L’aplicació ha de mostrar la llista de totes les notícies i els comentaris fets sobre elles. Podeu fer servir repositoris en memòria, tal com s’ha fet a Estoc de medicaments.

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

enllaç ( 10.8 MB )
.

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:

Figura Sortida de la llista de notícies i comentaris

Anar a la pàgina anterior:
Spring MVC, aplicació web
Anar a la pàgina següent:
Exercicis d'autoavaluació