Spring技术入门(六)

Spring Web MVC

官方文档的介绍:

The Spring Web model-view-controller (MVC) framework is designed around a DispatcherServlet that dispatches requests to handlers, with configurable handler mappings, view resolution, locale, time zone and theme resolution as well as support for uploading files. The default handler is based on the @Controller and @RequestMapping annotations, offering a wide range of flexible handling methods.

要想深入理解还需要多看官方文档,多实践操作才行,这里只是简单的介绍哈,对我们入门来说只要了解这么多就可以了。

Spring Web MVC最核心的几个几部分包括:

前端控制器:DispatcherServlet;

应用控制器:HandlerMapping(处理映射)、ViewResolver(视图解析);

页面控制器:ModelAndView

一般我们用到这几个就可以了,当然复杂的还要了解许多其他的。

下面我们来写一个小例子:

新建一个项目,然后了加入如下的jar包:

新建HelloController.java

package cn.ailot.web;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/spring")
public class HelloController{
    @RequestMapping("/hello.form")
    public String execute() throws Exception{
        System.out.println("执行HelloController");
        //这里返回的hello就是试图解析器里面的hello.jsp页面
        return "hello";
    }
}

然后在src文件夹下面新建一个名为sping-mvc.xml的文件,代码如下:

<?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"
    xmlns:util="http://www.springframework.org/schema/util"
 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.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util.xsd">
    <!-- 开启@RequestMapping注解映射 -->
    <mvc:annotation-driven/>
    <!--开启组件扫描定义-->
    <context:component-scan base-package="cn.ailot.web"></context:component-scan>
   <!-- 定义视图解析器ViewResolver -->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
</beans>

接下来配置web.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>springmvc</display-name>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  <!-- 处理编码问题 -->
  <filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
  </filter>
  <!-- 加载sping配置文件 -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring-mvc.xml</param-value>
  </context-param>

  <servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>
    org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <!--启动容器的时候初始化改servlet-->
    <load-on-startup>1</load-on-startup>
  </servlet>
  <!--servlet映射-->
  <servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>*.form</url-pattern>
  </servlet-mapping>
</web-app>

现在就可以启动服务试试,看能否正常启动,如果可以就表示配置没错。

地址栏里输入:http://localhost:8080/springmvc/spring/hello.form

会看到hello.jsp的内容:

以上就是一个简单的Spring MVC的例子。

下面我们来做一个登录的案例,Sping MVC接受参数有好多种方法,最常用到的有:

1、HttpServletRequest获取;

2、@RequestParam注解;

3、封装成实体参数。

下面我们来一个一个的实现,

实体user.java

package cn.ailot.entity;

public class User {

    private int id;
    private String name;
    private String pwd;
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }
}

接口UserDao.java

package cn.ailot.dao;

import cn.ailot.entity.User;

/**
 * 用户数据访问接口
 * @author litao
 *
 */
public interface UserDao {

    /**
     * 根据用户名查找系统用户
     * @param name
     * @return
     */
    public User findByName(String name);

}

异常捕获:

用户名或密码错:

NameOrPwdException.java

package cn.ailot.service;

public class NameOrPwdException extends Exception {

    public NameOrPwdException(){}

    public NameOrPwdException(String message){
        super(message);
    }

    public NameOrPwdException(Throwable cause){
        super(cause);
    }

    public NameOrPwdException(String message,Throwable cause){
        super(message,cause);
    }
}

空值异常:

NullParamException.java

package cn.ailot.service;

public class NullParamException extends Exception{

    public NullParamException(){}

    public NullParamException(String message){
        super(message);
    }

    public NullParamException(String message,Throwable cause){
        super(message,cause);
    }

}

业务实现UserService.java

package cn.ailot.service;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;

import cn.ailot.dao.UserDao;
import cn.ailot.entity.User;

@Service
public class UserService  {

    @Autowired
    private UserDao userDao;

    /**
     * 登录系统
     * @throws NameOrPwdException
     * @throws NullParamException
     */
    public User login(String name,String pwd) throws NameOrPwdException, NullParamException{
        if(name==null||name.equals("") || pwd == null|| pwd.equals("")){
            throw new NullParamException("登录参数不能为空!");
        }
        User user = userDao.findByName(name);
        if(user != null && pwd.equals(user.getPwd())){
            return user;
        }
        throw new NameOrPwdException("用户名或密码错误");
    }
}

配置数据库连接:

db.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/test
user=root
pwd=root

数据库脚本:

-- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
  `id` varchar(10) NOT NULL,
  `name` varchar(10) DEFAULT NULL,
  `pwd` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of users
-- ----------------------------
INSERT INTO `users` VALUES (‘1‘, ‘admin‘, ‘admin‘);

spring-mvc.xml配置文件

<?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"
    xmlns:util="http://www.springframework.org/schema/util"
    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.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/util
        http://www.springframework.org/schema/util/spring-util.xsd">
     <!-- 实例化本地数据库连接参数 -->
    <util:properties id="jdbcProps" location="classpath:db.properties"/>
    <!-- 开启@RequestMapping注解映射 -->
    <mvc:annotation-driven/>
    <!--开启组件扫描定义-->
    <context:component-scan base-package="cn.ailot"></context:component-scan>   

    <!-- 定义视图解析器ViewResolver -->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
</beans>

创建数据库连接:

package cn.ailot.dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class JdbcDataSource{

    private String driver;

    @Value("#{jdbcProps.url}")
    private String url;

    @Value("#{jdbcProps.user}")
    private String user;

    @Value("#{jdbcProps.pwd}")
    private String pwd;

    public String getDriver() {
        return driver;
    }
    /*必须写在set方法上,否则注入无效*/
    @Value("#{jdbcProps.driver}")
    public void setDriver(String driver) {
        try{
            Class.forName(driver);
            this.driver = driver;
        }catch(Exception e){
            throw new RuntimeException(e);
        }
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getUser() {
        return user;
    }

    public void setUser(String user) {
        this.user = user;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    public Connection getConnection() throws SQLException{
        Connection conn = DriverManager.getConnection(url,user,pwd);
        return conn;
    }

    public void close(Connection conn){
        if(conn!=null){
            try {
                conn.close();
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }
        }
    }

}

执行数据库操作:

MysqlUserDao.java

package cn.ailot.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import cn.ailot.entity.User;

@Repository("userDao")  //必须指定Bean ID,否则默认加载的是mysqlUserDao
public class MysqlUserDao implements UserDao{

    @Autowired
    private JdbcDataSource dataSource;

    /**
     * 根据唯一用户名查询系统用户,如果没有找到用户信息返回null
     */
    @Override
    public User findByName(String name) {
        // TODO Auto-generated method stub
        System.out.println("利用JDBC技术");
        String sql = "select id,name,pwd from users where name=?";
        Connection conn = null;
        try {
            conn = dataSource.getConnection();
            PreparedStatement ps = conn.prepareStatement(sql);
            ps.setString(1, name);
            ResultSet rs = ps.executeQuery();
            User user = null;
            while(rs.next()){
                user = new User();
                user.setId(rs.getInt("id"));
                user.setName(rs.getString("name"));
                user.setPwd(rs.getString("pwd"));
            }
            rs.close();
            ps.close();
            return user;
        } catch (Exception e) {
            // TODO: handle exception
            throw new RuntimeException(e);
        }finally{
            dataSource.close(conn);
        }
    }

}

接下来写一个JUnit测试,

TestCase.java

package cn.ailot.test;

import java.util.Properties;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.ailot.dao.JdbcDataSource;
import cn.ailot.entity.User;
import cn.ailot.service.UserService;

public class TestCase {

    @Test
    public void testUserService() throws Exception{
        String cfg = "spring-mvc.xml";
        ApplicationContext ac = new ClassPathXmlApplicationContext(cfg);
        //打印加载的bean名称
        System.out.println(java.util.Arrays.toString(ac.getBeanDefinitionNames()));
        Properties obj = ac.getBean("jdbcProps",Properties.class);
        JdbcDataSource ds = ac.getBean("jdbcDataSource",JdbcDataSource.class);
        System.out.println(obj);
        System.out.println(ds);
        System.out.println(ds.getConnection());
        UserService service = ac.getBean("userService",UserService.class);
        User user = service.login("admin", "admin");
        System.out.println(user);
    }
}

然后右键->Run As->JUnit Test

如果代码无误,会出现如下显示:

接下就要构建登录的具体方法了(类似struts中的action):

LoginController.java

(同时写了三种接受参数的方法)

package cn.ailot.controller;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import cn.ailot.entity.User;
import cn.ailot.service.NameOrPwdException;
import cn.ailot.service.NullParamException;
import cn.ailot.service.UserService;

@Controller
@RequestMapping("/login")
public class LoginController{

    @Resource   //请求sping注入资源userService
    private UserService userService;

    @RequestMapping("/login.form")
    public String loginForm(){
        //可以向表单传递一些参数
        return "loginForm"; //映射到loginForm.jsp
    }

    /**
     * 利用HttpServletRequest对象进行传参
     * @param req
     * @return
     */
    @RequestMapping("/loginAction1.form")
    //spring MVC自动参数注入HttpServletRequest
    public String checLogin1(HttpServletRequest req){
        System.out.println("--方法一--");
        //优点,直接简洁,缺点,需要自己处理数据类型转换
        String name = req.getParameter("name");
        String pwd = req.getParameter("pwd");
        System.out.println(name);
        System.out.println(pwd);
        try {
            User user = userService.login(name, pwd);
            //登录成功将登录信息保存到当前会话中
            req.getSession().setAttribute("user", user);
            return "success";   //映射到登录成功的success.jsp
        } catch (NameOrPwdException e) {
            // TODO: handle exception
            e.printStackTrace();
            req.setAttribute("message", e.getMessage());
            return "loginForm"; //映射到登录页面loginForm.jsp
        } catch (NullParamException e) {
            // TODO: handle exception
            e.printStackTrace();
            req.setAttribute("message", e.getMessage());
            return "redirect:login.form";//重定向
        } catch (RuntimeException e){
            e.printStackTrace();
            req.setAttribute("message", e.getMessage());
            return "error";
        }
    }

    /**
     * 使用@RequestParam注解获取请求参数的值
     * @param name
     * @param pwd
     * @param req
     * @return
     */
    @RequestMapping("/loginAction2.form")
    public String checLogin2(
            String name,
            @RequestParam("pwd")String pwd, //映射表单属性不同的参数
            HttpServletRequest req){
        System.out.println("--方法二--");
        //优点,自动转换数据类型,缺点,可能出现数据类型转换异常
        System.out.println(name);
        System.out.println(pwd);
        try {
            User user = userService.login(name, pwd);
            //登录成功将登录信息保存到当前会话中
            req.getSession().setAttribute("user", user);
            return "success";   //映射到success.jsp
        } catch (NameOrPwdException e) {
            // TODO: handle exception
            e.printStackTrace();
            req.setAttribute("message", e.getMessage());
            return "loginForm";
        } catch (NullParamException e) {
            // TODO: handle exception
            e.printStackTrace();
            req.setAttribute("message", e.getMessage());
            return "loginForm";
        } catch (RuntimeException e){
            e.printStackTrace();
            req.setAttribute("message", e.getMessage());
            return "error";
        }
    }

    /**
     * 使用自动机制封装成实体参数的方式获取请求参数的值
     * @param user
     * @param req
     * @return
     */
    @RequestMapping("/loginAction3.form")
    public String checLogin3(User user,HttpServletRequest req){
        System.out.println("--方法三--");
        //自动填充到bean对象
        System.out.println(user);
        try {
            user = userService.login(user.getName(), user.getPwd());
            //登录成功将登录信息保存到当前会话中
            req.getSession().setAttribute("user", user);
            return "success";   //映射到success.jsp
        } catch (NameOrPwdException e) {
            // TODO: handle exception
            e.printStackTrace();
            req.setAttribute("message", e.getMessage());
            return "loginForm";
        } catch (NullParamException e) {
            // TODO: handle exception
            e.printStackTrace();
            req.setAttribute("message", e.getMessage());
            return "loginForm";
        } catch (RuntimeException e){
            e.printStackTrace();
            req.setAttribute("message", e.getMessage());
            return "error";
        }
    }

}

登录页面:

loginForm.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!-- 获取项目基本路径 -->
<c:url var="base" value="/"></c:url>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>LoginForm</title>
<link rel="stylesheet" type="text/css" href="${base }css/style.css">
</head>
<body>
    <h6>${message }</h6>
    <%-- <h6>${next }</h6> --%>
    <form method="post" action="${base }login/loginAction1.form">
        <div>
            <h2>登录 loginAction1.form</h2>
            <p><label>用户</label><input type="text" name="name"></p>
            <p><label>密码</label><input type="password" name="pwd"></p>
            <h3><input type="submit" value="登录"></h3>
        </div>
    </form>

    <form method="post" action="${base }login/loginAction2.form">
        <div>
            <h2>登录 loginAction2.form</h2>
            <p><label>用户</label><input type="text" name="name"></p>
            <p><label>密码</label><input type="password" name="pwd"></p>
            <h3><input type="submit" value="登录"></h3>
        </div>
    </form>

    <form method="post" action="${base }login/loginAction3.form">
        <div>
            <h2>登录 loginAction3.form</h2>
            <p><label>用户</label><input type="text" name="name"></p>
            <p><label>密码</label><input type="password" name="pwd"></p>
            <h3><input type="submit" value="登录"></h3>
        </div>
    </form>

</body>
</html>

需要注意的是,这里我用到的是JSTL标签,所以在lib里面要引入jstl.jar、standard.jar这个两个包。

样式文件style.css

*{
    margin:0;
    padding:0;
}
h6{
    text-align: center;
    color: red;
    padding:10px;
    font-size:14px;
}
form{
    padding:10px;
    float:left;
}
form div{
    border:1px gray solid;
    width:320px;
    padding:8px;
}
form p input{
    widht:180px;
}
form h2 input {
    text-align:center;
}
form h2{
    background: black;
    color:white;
    padding:4px;
}
form p{
    background:#ddd;
    padding:4px 8px 0 8px;
}
form h3{
    background: #ddd;
    padding:8px;
}

在浏览器里输入http://localhost:8080/springmvc/login/login.form

运行结果如图所示:

添加登录成功的页面success.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<c:url var="base" value="/"></c:url>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>success</title>
<link rel="stylesheet" type="text/css" href="${base }css/style.css">
</head>
<body>
    <center>
    <h2>${user.name }登录成功</h2>
    </center>
</body>
</html>

输入用户名和密码admin登录就可以看到登录成功的界面。

源码:下载地址

时间: 2024-10-05 02:46:53

Spring技术入门(六)的相关文章

深入探索spring技术内幕(六): JDK动态代理和cglib生成代理

[ JDK生成代理 ] JDK中给我们提供了一个Proxy类可以动态的给我们生成代理. 假定我们要做一个权限管理系统, 需要控制用户对某一个方法的访问. 如果user为null, 那么不让用户访问save方法. ① 接口类: PersonService public interface PersonService { public void save(); } ② 实现类: PersonServiceImpl public class PersonServiceImpl implements P

Spring Cloud 入门 之 Config 篇(六)

原文地址:Spring Cloud 入门 之 Config 篇(六) 博客地址:http://www.extlight.com 一.前言 随着业务的扩展,为了方便开发和维护项目,我们通常会将大项目拆分成多个小项目做成微服务,每个微服务都会有各自配置文件,管理和修改文件起来也会变得繁琐.而且,当我们需要修改正在运行的项目的配置时,通常需要重启项目后配置才能生效. 上述的问题将是本篇需要解决的问题. 二.介绍 2.1 简单介绍 Spring Cloud Config 用于为分布式系统中的基础设施和微

Spring Cloud 入门教程(六): 用声明式REST客户端Feign调用远端HTTP服务

首先简单解释一下什么是声明式实现? 要做一件事, 需要知道三个要素,where, what, how.即在哪里( where)用什么办法(how)做什么(what).什么时候做(when)我们纳入how的范畴. 1)编程式实现: 每一个要素(where,what,how)都需要用具体代码实现来表示.传统的方式一般都是编程式实现,业务开发者需要关心每一处逻辑 2)声明式实现: 只需要声明在哪里(where )做什么(what),而无需关心如何实现(how).Spring的AOP就是一种声明式实现,

Spring Boot 入门(六):集成 treetable 和 zTree 实现树形图

本篇文章是接着Spring Boot 入门(五):集成 AOP 进行日志管理写的,主要集成了树形图,在部门列表或者权限列表中,树形图经常被用上.主要是根据相应的 API 凭借 html 字符串 1.treetable 1 <link href="/plugins/treeTable/themes/default/treeTable.css" rel="stylesheet" type="text/css" /> 2 <scrip

Spring boot入门到精通视频教程

14套java精品高级架构课,缓存架构,深入Jvm虚拟机,全文检索Elasticsearch,Dubbo分布式Restful 服务,并发原理编程,SpringBoot,SpringCloud,RocketMQ中间件,Mysql分布式集群,服务架构,运 维架构视频教程 14套精品课程介绍: 1.14套精 品是最新整理的课程,都是当下最火的技术,最火的课程,也是全网课程的精品: 2.14套资 源包含:全套完整高清视频.完整源码.配套文档: 3.知识也 是需要投资的,有投入才会有产出(保证投入产出比是

Spring Cloud 入门教程(五): Ribbon实现客户端的负载均衡

接上节,假如我们的Hello world服务的访问量剧增,用一个服务已经无法承载, 我们可以把Hello World服务做成一个集群. 很简单,我们只需要复制Hello world服务,同时将原来的端口8762修改为8763.然后启动这两个Spring Boot应用, 就可以得到两个Hello World服务.这两个Hello world都注册到了eureka服务中心.这时候再访问http://localhost:8761, 可以看到两个hello world服务已经注册.(服务与注册参见Spr

【Zigbee技术入门教程-01】Zigbee无线组网技术入门的学习路线

[Zigbee技术入门教程-01]Zigbee无线组网技术入门的学习路线 广东职业技术学院  欧浩源 一.引言    在物联网技术应用的知识体系中,Zigbee无线组网技术是非常重要的一环,也是大家感觉比较难以掌握的一个部分.Zigbee无线组网技术之所以让你感有学习难度,不是因为它真的复杂,而是它看起来很复杂,让人望而止步.另一方面则是Zigbee技术在应用层面上将硬件和软件完成融为一个体系,要求开发人员既要有扎实的硬件技术,又要有清晰的软件思维.    目前,尽管有不少关于Zigbee无线组

Spring快速入门

时间:2017-1-29 02:15 Spring的IOCSpring的AOP,AspectJSpring的事务管理,三大框架的整合 --什么是Spring 1.Spring是分层的JavaSE/EE full-stack(一站式)轻量级开源框架.    1)分层:        *   SUN公司提供的EE的三层结构:Web层.业务层.数据访问层(持久层)        *   Struts2是Web层基于MVC设计模式框架.        *   Hibernate是持久层的一个ORM框架.

Java - Struts框架教程 Hibernate框架教程 Spring框架入门教程(新版) sping mvc spring boot spring cloud Mybatis

https://www.zhihu.com/question/21142149 http://how2j.cn/k/hibernate/hibernate-tutorial/31.html?tid=63 https://www.zhihu.com/question/29444491/answer/146457757 1. Java - Struts框架教程Struts 是Apache软件基金会(ASF)赞助的一个开源项目.通过采用JavaServlet/JSP技术,实现了基于Java EEWeb