Статьи

Изучение Spring Controller с представлением JSTL

Давайте улучшим наше предыдущее Spring JDBC-приложение с дополнительным изучением разработки контроллеров Spring MVC. Я покажу еще одно упражнение по написанию нового контроллера, который обрабатывает HTML-форму и использует теги JSTL на страницах просмотра JSP.

Чтобы включить JSTL в приложении Spring MVC, вам нужно добавить следующее в WebAppConfig конфигурации WebAppConfig . Давайте переместим его за пределы WebApp.java и в его собственный файл класса верхнего уровня в src/main/java/springweb/WebAppConfig.java .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
package springweb;
 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
 
@Configuration
@EnableWebMvc
@ComponentScan("springweb.controller")
public class WebAppConfig {
    @Bean
    public InternalResourceViewResolver viewResolver() {
        InternalResourceViewResolver result = new InternalResourceViewResolver();
        result.setPrefix("/");
        result.setSuffix(".jsp");
        return result;
    }
}

Внутри bean-компонента InternalResourceViewResolver вы определяете, где найти страницы JSP, в которых могут быть теги JSTL. Установщик prefix — это путь относительно вашего местоположения src/webapp . Это позволяет полностью скрыть файлы JSP, если вы хотите. Например, установив его в "/WEB-INF/jsp" вы можете переместить и сохранить все файлы JSP в src/webapp/WEB-INF/jsp который является частным в веб-приложении. suffix — это просто расширение файла. Эти два значения позволяют вам возвращать имя представления внутри контроллера только с базовым именем вашего файла JSP, которое может быть коротким как «/ myform» или «/ index» и т. Д.

Если вы хотите использовать Tomcat в качестве веб-контейнера, вам также необходимо добавить JSTL-зависимость JAR, поскольку сервер Tomcat не поставляется со стандартной библиотекой тегов! Так что добавьте это в файл pom.xml сейчас.

1
2
3
4
5
<dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

Пока вы находитесь в файле pom.xml , вы можете добавить плагин Tomcat Maven, чтобы при запуске веб-приложения вы могли меньше вводить в командной строке.

01
02
03
04
05
06
07
08
09
10
11
12
13
<project>
...
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.1</version>
            </plugin>
        </plugins>
    </build>
...
</project>

При этом вы сможете запускать mvn tomcat7:run в корне вашего проекта без префикса плагина.

Так что же JSTL приносит в ваше приложение? Ну, совсем немного на самом деле. Это позволяет использовать некоторые стандартные теги JSP, которые часто используются при написании представлений JSP. Я продемонстрирую это с помощью набора контроллеров и представлений для сбора комментариев пользователей из приложения. Обратите внимание, что я постараюсь показать вам, как работать с Spring Controller только самым простым способом. Spring на самом деле поставляется с пользовательским JSP-тегом form который гораздо более мощный в использовании. Я зарезервирую это как другую статью в другое время. Сегодня давайте сосредоточимся на том, чтобы больше узнать о базовом Spring Controller и JSTL, а также немного о сервисе данных Spring JDBC.

Мы хотим записать комментарии пользователей, поэтому давайте добавим таблицу базы данных для хранения этой информации. Добавьте следующий DDL в ваш файл src/main/resources/schema.sql . Опять же, это для базы данных H2 в последней настройке проекта статьи.

1
2
3
4
5
6
7
8
9
CREATE TABLE COMMENT (
  ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
  TEXT VARCHAR(10240) NOT NULL,
  FROM_USER VARCHAR(15) NULL,
  FROM_USER_IP VARCHAR(15) NULL,
  FROM_URL VARCHAR(1024) NULL,
  TAG VARCHAR(1024) NULL,
  TS DATETIME NOT NULL
);

На этот раз мы напишем класс модели данных для соответствия этой таблице. Давайте добавим src/main/java/springweb/data/Comment.java

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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package springweb.data;
 
import java.util.Date;
 
public class Comment {
    Long id;
    String text;
    String fromUrl;
    String fromUser;
    String fromUserIp;
    String tag;
    Date ts;
 
    public Long getId() {
        return id;
    }
 
    public void setId(Long id) {
        this.id = id;
    }
 
    public String getText() {
        return text;
    }
 
    public void setText(String text) {
        this.text = text;
    }
 
    public String getFromUrl() {
        return fromUrl;
    }
 
    public void setFromUrl(String fromUrl) {
        this.fromUrl = fromUrl;
    }
 
    public String getFromUser() {
        return fromUser;
    }
 
    public void setFromUser(String fromUser) {
        this.fromUser = fromUser;
    }
 
    public String getFromUserIp() {
        return fromUserIp;
    }
 
    public void setFromUserIp(String fromUserIp) {
        this.fromUserIp = fromUserIp;
    }
 
    public String getTag() {
        return tag;
    }
 
    public void setTag(String tag) {
        this.tag = tag;
    }
 
    public Date getTs() {
        return ts;
    }
 
    public void setTs(Date ts) {
        this.ts = ts;
    }
 
    private String getTrimedComment(int maxLen) {
        if (text == null)
            return null;
        if (text.length() <= maxLen)
            return text;
        return text.substring(0, maxLen);
    }
 
    @Override
    public String toString() {
        return "Comment{" +
                "id=" + id +
                ", ts=" + ts +
                ", text='" + getTrimedComment(12) + '\'' +
                '}';
    }
 
    public static Comment create(String commentText) {
        Comment result = new Comment();
        result.setText(commentText);
        result.setTs(new Date());
        return result;
    }
}

Как и в предыдущем разделе, мы напишем сервис данных для обработки вставки и извлечения модели данных. Мы добавляем новый файл src/main/java/springweb/data/CommentService.java

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
61
62
63
package springweb.data;
 
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
import org.springframework.stereotype.Repository;
 
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
@Repository
public class CommentService {
    public static Log LOG = LogFactory.getLog(CommentService.class);
 
    private JdbcTemplate jdbcTemplate;
    private SimpleJdbcInsert insertActor;
    private RowMapper<Comment> commentBeanRowMapper = new BeanPropertyRowMapper<Comment>(Comment.class);
 
    @Autowired
    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
        this.insertActor = new SimpleJdbcInsert(dataSource)
            .withTableName("COMMENT")
            .usingGeneratedKeyColumns("ID");
    }
 
    public void insert(Comment comment) {
        LOG.info("Inserting Comment + " + comment);
 
        Map<String, Object> parameters = new HashMap<String, Object>(2);
        parameters.put("TEXT", comment.getText());
        parameters.put("FROM_USER", comment.getFromUser());
        parameters.put("FROM_USER_IP", comment.getFromUserIp());
        parameters.put("FROM_URL", comment.getFromUrl());
        parameters.put("TAG", comment.getTag());
        parameters.put("TS", comment.getTs());
        Number newId = insertActor.executeAndReturnKey(parameters);
        comment.setId(newId.longValue());
 
        LOG.info("New Comment inserted. Id=" + comment.getId());
    }
 
    public List<Comment> findComments() {
        String sql = "SELECT " +
                "ID as id, " +
                "TEXT as text, " +
                "TAG as tag, " +
                "TS as ts, " +
                "FROM_USER as fromUser, " +
                "FROM_USER_IP as fromUserIp, " +
                "FROM_URL as fromUrl " +
                "FROM COMMENT ORDER BY TS";
        List<Comment> result = jdbcTemplate.query(sql, commentBeanRowMapper);
        LOG.info("Found " + result.size() + " Comment records.");
        return result;
    }
}

Поскольку мы не использовали какой-либо модный ORM, а просто JDBC, нам придется писать SQL в службе данных. Но благодаря положительным SimpleJdbcInsert Spring, это значительно облегчает жизнь с помощью таких помощников, как SimpleJdbcInsert , который обрабатывает вставку БД и извлечение автоматически сгенерированного ключа и т. Д. А также обратите внимание, что в запросе мы используем BeanPropertyRowMapper в Spring для автоматического преобразования BeanPropertyRowMapper JDBC в Java-бин. Comment объекта! Просто, прямо и быстро.

Теперь мы добавляем контроллер Spring в src/main/java/springweb/controller/CommentController.java для обработки веб-запросов.

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
package springweb.controller;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import springweb.data.Comment;
import springweb.data.CommentService;
 
import javax.servlet.http.HttpServletRequest;
import java.util.List;
 
@Controller
public class CommentController {
 
    @Autowired
    private CommentService commentService;
 
    @RequestMapping(value="/comments")
    public ModelAndView comments() {
        List<Comment> comments = commentService.findComments();
        ModelAndView result = new ModelAndView("/comments");
        result.addObject("comments", comments);
        return result;
    }
 
    @RequestMapping(value="/comment")
    public String comment() {
        return "comment";
    }
 
    @RequestMapping(value="/comment", method = RequestMethod.POST)
    public ModelAndView postComment(HttpServletRequest req, @RequestParam String commentText) {
        String fromUrl = req.getRequestURI();
        String user = req.getRemoteUser();
        String userIp = req.getRemoteAddr();
        Comment comment = Comment.create(commentText);
        comment.setFromUserIp(userIp);
        comment.setFromUser(user);
        comment.setFromUrl(fromUrl);
        commentService.insert(comment);
        ModelAndView result = new ModelAndView("comment-posted");
        result.addObject("comment", comment);
        return result;
    }
}

В этом контроллере мы отображаем /comment URL для обработки отображения формы HTML, которая возвращает представление comment.jsp . Метод по умолчанию для обработки HTTP GET . Обратите внимание, что мы переназначили один /comment тот же URL /comment URL /comment для обработки HTTP POST в отдельном postComment() ! Посмотрите, насколько чистым может быть Spring Controller в этой демонстрации для обработки HTTP-запроса. Обратите особое внимание на параметры, объявленные в postComment() . Spring автоматически обрабатывает объект HTTP-запроса и сопоставляет его с вашим методом только на основании объявленных вами типов! В некоторых случаях вы должны быть явными с помощью аннотации, такой как @RequestParam , но Spring выполняет анализ HTTP-запроса и извлечения за вас! Это сэкономит вам тонны повторяющегося кода котельной пластины, если мы напишем прямой код сервлета.

Теперь давайте посмотрим на представление и как использовать JSTL. URL-адрес /comments отображается в файл представления src/main/webapp/comments.jsp , в котором будут перечислены все объекты модели Comment .

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
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:choose>
<c:when test="${empty comments}">
    <p>There are no comments in system yet.</p>
</c:when>
<c:otherwise>
    <table border="1">
    <tr>
        <td>INDEX</td>
        <td>TIME</td>
        <td>FROM</td>
        <td>COMMENT</td>
    </tr>
    <c:forEach items="${comments}" var="comment" varStatus="status">
    <tr valign="top">
        <td>${status.index}</td>
        <td>${comment.ts}</td>
        <td>${comment.fromUserIp}</td>
        <%-- The c:out will escape html/xml characters. --%>
        <td><pre><c:out value="${comment.text}"/></pre></td>
    </tr>
    </c:forEach>
    </table>
</c:otherwise>
</c:choose>

Довольно стандартные вещи на JSTL. Далее следует HTML-форма для отправки комментария в файле src/main/webapp/comment.jsp .

1
2
3
4
5
<form action="comment" method="POST">
<textarea name="commentText" rows="20" cols="80"></textarea>
<br/>
<input type="submit" value="Post"/>
</form>

Когда форма опубликована и успешно обработана, мы просто возвращаемся на новую страницу в файле src/main/webapp/comment-posted.jsp качестве вывода.

1
<p>Your comment has been posted. Comment ID=${comment.id}</p>

Если вы все сделали правильно, вы должны запустить mvn tomcat7:run и просмотреть http://localhost:8080/spring-web-annotation/comment чтобы увидеть форму. Перейдите по ссылке /comments URL, чтобы проверить все оставленные комментарии.

Обратите внимание, что, несмотря на то, что я использовал Spring Controller в качестве бэкэнда, все представления находятся в базовом JSTL, и даже форма — это просто основные элементы HTML! Я сделал это, чтобы вы могли увидеть, насколько гибким может быть Spring Controller.

Я знаю, что это много кода для публикации сегодня в блоге, но я хотел закончить и попытаться показать рабочую демонстрацию с примечаниями к учебнику. Я предпочитаю сделать его одним постом с содержимым файла, а не загружать проект где-то еще. Это облегчает сопоставление моих заметок и объяснений с кодом.

И это завершит наш урок на сегодня. Пожалуйста, оставьте записку, если вы найдете это полезным.

Ссылка: Изучение Spring Controller с представлением JSTL от нашего партнера JCG Земьяна Дена в блоге A Programmer’s Journal .