如何使用嵌入式 Jetty 打包和部署 Web 应用程序

How to package and deploy web app with embedded Jetty

我正在开发的 Web 应用程序必须 运行 在 嵌入式 Jetty 上运行(没有外部 Web 应用程序容器)。所有的逻辑和内容都准备好了。我只想了解如何使用 Maven 打包和部署带有嵌入式 Jetty 的 Web 应用程序。以下是详细问题:

  1. 目前我 运行 我的网络应用程序在 Jetty 9.xIntellij IDEA 但我想无论如何它都是外部码头。那么什么是jetty嵌入式服务器。考虑到我的网络应用程序的结构(见下文),我怎样才能做到这一点?

  2. 我认为包文件一定不是 war 文件,因为它在嵌入式码头服务器上必须是 运行ning。我想一定是可执行jar。但是如何使用 Maven 获取它?

  3. 如何将它部署到目标机器上(它只是本地 PC,而不是服务器)?

这是我的项目结构,供您了解情况并参考您的回答:

我真的需要你们的帮助。

2016 年 7 月 4 日更新

我创建了 spring 引导演示应用程序,我已经完成了@techtabu 给我的 spring 引导文档中写的所有内容,但我得到的只是下一个错误:

我还没有打包和部署我的演示应用程序。刚刚按下 "run 'MySpringBootDemoApp'" 按钮。

不仅如此,我的 spring 启动应用程序无法像通常的外部码头那样自行打开浏览器。

这是什么原因。请帮助我!

2016 年 7 月 6 日更新

package com.dvdexchange.controller;


import com.dvdexchange.model.Disk;
import com.dvdexchange.model.MyDisk;
import com.dvdexchange.model.Takenitem;
import com.dvdexchange.service.impl.DiskServiceImpl;
import com.dvdexchange.service.impl.TakenItemServiceImpl;
import com.dvdexchange.service.impl.UserServiceImpl;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.context.SecurityContextHolder;
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 javax.servlet.http.HttpServletRequest;
import java.util.*;

/**Главный контроллер, отвечающий за обработку запросов пользователя по доступу
 **на разные адреса веб-сервиса**/

@Controller
public class MainController {
    private UserServiceImpl userService = new UserServiceImpl();
    private DiskServiceImpl diskService = new DiskServiceImpl();
    private TakenItemServiceImpl takenItemService = new TakenItemServiceImpl();

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String home() {
        return "login";                                 /*Первая страница, которую должен видеть пользователь - это страница авторизации и она же страница приветствия*/
    }

    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public ModelAndView login(@RequestParam(value = "error", required = false) String error,               /*данные о предыдущей неудачной авторизации из предыдущей попытки*/
                              @RequestParam(value = "logout", required = false) String logout, HttpServletRequest request) {   /*или об успешном выходе из аккаунта с данными о запросе на сервер*/

        ModelAndView model = new ModelAndView();
        if (error != null) {
            model.addObject("error", getErrorMessage(request, "SPRING_SECURITY_LAST_EXCEPTION"));         /*Получение сообщения об ошибке и запись в модель для отображения пользователю*/
        }

        if (logout != null) {
            model.addObject("msg", "Вы успешно вышли из учетной записи.");                                /*Запись данных об успешном выходе из аккаунта в модель для отображения пользователю*/
        }
        model.setViewName("login");

        return model;
    }

    @RequestMapping(value = "/diskLib")
    public ModelAndView diskLib() {
        String loggedUserEmail = SecurityContextHolder.getContext().getAuthentication().getName();
        List<MyDisk> diskList = new ArrayList<MyDisk>();
        List<MyDisk> testDiskList;                     /*Для предварительной проверки множества на валидность*/
        int givenDisksCount = 0;                       /*Количество взятых у пользователя дисков нужно для того, чтобы корректно блокировать кнопку "удалить диск",
                                                       **если все диски пользователя отданы*/

        testDiskList = userService.getUserDisksWithStatus(loggedUserEmail);


        if (testDiskList != null) {             //Множество может быть пустым и нужно позаботиться о валидности результата при таком исходе,
            diskList = testDiskList;            //поэтому используется testDiskList. Валидное пустое множество это не null, а множество( или список), в котором 0 элементов

            //Сортировка дисков по id по возрастанию
            Collections.sort(diskList, new Comparator<MyDisk>() {
                public int compare(MyDisk o1, MyDisk o2) {
                    return o1.getId() - o2.getId();
                }
            });
        }

        for (MyDisk disk : diskList) {
            if (disk.isGiven()) {
                givenDisksCount++;
            }
        }

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("CurrentUserEmail", loggedUserEmail);
        modelAndView.addObject("AllDisksOfTheUser", diskList);
        modelAndView.addObject("GivenDisksCount", givenDisksCount);
        modelAndView.setViewName("diskLib");             /*Указываем для какой страницы предназначены данные (модель). Аналогично в остальных методах*/
        return modelAndView;
    }

    @RequestMapping(value = "/borrowDisk")
    public ModelAndView borrowDisk() {
        String loggedUserEmail = SecurityContextHolder.getContext().getAuthentication().getName();
        List<Disk> allFreeDisks = new ArrayList<Disk>();
        List<Disk> testDisks;

        testDisks = diskService.getAllFreeDisksForCurrentUser(loggedUserEmail);

        if (testDisks != null) {             //Множество может быть пустым и нужно позаботиться о валидности результата при таком исходе,
            allFreeDisks = testDisks;        //поэтому используется testDisks. Валидное пустое множество это не null, а множество( или список), в котором 0 элементов

            //Сортировка дисков по id по возрастанию
            Collections.sort(allFreeDisks, new Comparator<Disk>() {
                public int compare(Disk o1, Disk o2) {
                    return o1.getId() - o2.getId();
                }
            });
        }

        int borrowCount = 0;                                                      /*Количество взятых пользователем дисков*/
        testDisks = diskService.getAllDisksTakenByLoggedUser(loggedUserEmail);

        if (testDisks != null) {
            borrowCount = testDisks.size();
        }

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("CurrentUserEmail", loggedUserEmail);
        modelAndView.addObject("AllFreeDisks", allFreeDisks);                            /*Множество всех свободных для аренды дисков для текущего пользователя*/
        modelAndView.addObject("BorrowCount", borrowCount);
        modelAndView.addObject("BorrowLimit", TakenItemServiceImpl.maxBorrowedDiskCount);         /*Существует лимит на количество дисков, которые может взять каждый пользователь в аренду*/
        modelAndView.setViewName("borrowDisk");

        return modelAndView;
    }

    @RequestMapping(value = "/takenDisks")
    public ModelAndView takenDisks() {
        String loggedUserEmail = SecurityContextHolder.getContext().getAuthentication().getName();
        List<Disk> takenDisks = new ArrayList<Disk>();
        List<Disk> testTakenDisks;

        testTakenDisks = diskService.getAllDisksTakenByLoggedUser(loggedUserEmail);

        if (testTakenDisks != null) {             //Множество может быть пустым и нужно позаботиться о валидности результата при таком исходе,
            takenDisks = testTakenDisks;          //поэтому используется testFreeDisks. Валидное пустое множество это не null, а множество( или список), в котором 0 элементов

            //Сортировка дисков по id по возрастанию
            Collections.sort(takenDisks, new Comparator<Disk>() {
                public int compare(Disk o1, Disk o2) {
                    return o1.getId() - o2.getId();
                }
            });
        }

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("CurrentUserEmail", loggedUserEmail);
        modelAndView.addObject("TakenDisks", takenDisks);                        /*Кол-во дисков, отданных пользователем в аренду*/
        modelAndView.addObject("BorrowCount", takenDisks.size());
        modelAndView.addObject("BorrowLimit", TakenItemServiceImpl.maxBorrowedDiskCount);          /*Существует лимит на количество дисков, которые может взять каждый пользователь в аренду*/

        modelAndView.setViewName("takenDisks");
        return modelAndView;
    }

    @RequestMapping(value = "/givenDisks")
    public ModelAndView givenDisks() {
        String loggedUserEmail = SecurityContextHolder.getContext().getAuthentication().getName();
        List<Takenitem> givenDisks = new ArrayList<Takenitem>();
        List<Takenitem> testGivenDisks;
        testGivenDisks = takenItemService.getAllDisksGivenByLoggedUser(loggedUserEmail);

        if (testGivenDisks != null) {             //Множество может быть пустым и нужно позаботиться о валидности результата при таком исходе,
            givenDisks = testGivenDisks;          //поэтому используется testFreeDisks. Валидное пустое множество это не null, а множество( или список), в котором 0 элементов

            //Сортировка дисков по id по возрастанию
            Collections.sort(givenDisks, new Comparator<Takenitem>() {
                public int compare(Takenitem o1, Takenitem o2) {
                    return o1.getIddisk() - o2.getIddisk();
                }
            });
        }

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("CurrentUserEmail", loggedUserEmail);
        modelAndView.addObject("GivenDisks", givenDisks);                    /*Множество отданных дисков*/
        modelAndView.addObject("GivenCount", givenDisks.size());             /*Кол-во отданных дисков*/
        modelAndView.addObject("TotalCount", userService.getUserDisksWithStatus(loggedUserEmail).size());         /*Общее кол-во дисков собственных пользователя*/

        modelAndView.setViewName("givenDisks");
        return modelAndView;
    }

    /*Получаем исключение с необходимым текстом*/
    private String getErrorMessage(HttpServletRequest request, String key) {

        Exception exception = (Exception) request.getSession().getAttribute(key);

        String error = "";
        /*Получение понятного для пользователя текста ошибки в зависимостит от причин отказа в доступе*/
        if (exception instanceof BadCredentialsException) {           /*Если неверно введен пароль, а логин верный и аккаунт исправен и не заблокирован*/
            error = "Неверные \"Логин\" и/или \"Пароль\"!";         /*Но для безопасности все-равно нельзя указывать, что именно введено неправильно*/
        } else if (exception instanceof LockedException) {            /*Если аккаунт заблокирован*/
            error = exception.getMessage();
        } else {
            error =  "Неверные \"Логин\" и/или \"Пароль\"!";          /*если неверны логин и пароль или только логин*/
        }

        return error;
    }

}

不要注意评论 - 它们是俄语的。

我同意 deffymo 的评论。使用 Spring-引导。使用嵌入式服务器要容易得多。如果您使用的是 IntelliJ,请在 create New Project window、select Spring Initializr 上创建一个 Spring 启动项目。另请参考spring-boot documentation.

但是,请记住 spring-boot 使用 Tomcat 作为默认的嵌入式服务器。您应该排除 Tomcat 并在依赖项中添加码头。查看 docs 如何操作。

But how can I get it using Maven?

在您的 pom 文件中将包装定义为 jar。

<groupId>your-group</groupId>
<artifactId>your-project-name</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>

How can I deploy it on target machine (it's going to be just local PC - not server)?

运行 jar 文件作为 java jar.

java -jar your-project-name.jar

您可能还想看看 maven shade plugin

来自描述

"this plugin provides the capability to package the artifact in an uber-jar,"

您可以使用 java -jar yourapp.jar

启动您的应用程序