• Spring MVC---基于注解的控制器



    基于注解的控制器

    • SpringMVC是一个基于DispatcherServlet的MVC框架,每个请求最先访问的是DispatcherServlet,DispatcherServlet负责将每一个Request转发到相应的Handler,Handler处理后再返回相应的模型(Model)和视图(View)。在使用注解的Spring MVC中,处理器Handler是基于@Controller和@RequestMapping两个注解的,这两个注解可以提供非常灵活的匹配和处理方式。
    @Controller和@RequestMapping注解
    • @Controller注解类型

      声明一个控制器类,Spring使用扫描机制来找到应用程序中所有基于注解的控制器类,控制器类的内部包含每个动作相应的处理方法,如下是一个@Controller的例子。

    package com.example.controller;
    
    import org.springframework.web.servlet.mvc.support.RedirectAttributes;
    ...
    @Controller
    public class ProductController {
        //request-handling methods here
    }

      为了保证Spring能扫描到控制器类,需要完成两个配置,首先要在Spring MVC的配置文件中声明Spring-context,如下所示:

    <beans 
        ...
        xmlns:context="http://www.springframework.org/schema/context"
        ...
    >

      其次需要应用<component-scan/>元素,在该元素中指定控制器类的基本包。例如所有的控制器类都在com.example.controller及其子包下,则该元素如下:

    <context:component-scan base-package="com.example.controller"/>

      确保所有控制器都在基本包下,并且不要指定太广泛的基本包,否则会导致Spring MVC扫描无关的包。

    • @RequestMapping注解类型

      该注解类型在控制器类的内部定义每一个动作相应的处理方法,一个采用@RequestMapping注释的方法将成为一个请求处理方法,并由调度程序在接收到对应的URL请求时调用,下面是一个RequestMapping注解方法的控制器类。

    package com.example.controller;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    ...
    
    @Controller
    public class ProductController {
        
        @RequestMapping(value="/productInput")
        public String inputProduct(){
            //do something here
            return "ProductForm";
        }
    }

      使用RequestMapping注解的value属性将URL映射到方法,在如上的例子中,我们将productInput映射到inputProduct()方法,通过http://localhost:8081/SpringMVC/productInput访问inputProduct方法。RequestMapping方法除了有value属性外,还有method属性,该属性用来指示该方法仅处理哪些http方法,例如,仅当HTTP POST或PUT方法时,调用下面的processOrder方法。

    @RequestMapping(value="/order_process", method={RequestMethod.POST, RequestMethod.PUT})
    public String processOrder(){
        //do something here
        return "OrderForm";
    }

      如果method属性只有一个HTTP方法值,则无需{},直接为method=RequestMethod.POST,如果未指定method属性,则请求处理方法可以处理任意HTTP方法。此外RequestMapping注释类型也可以用来注释一个控制器类,如下所示:

    import org.springframework.stereotype.Controller;
    ...
    
    @Controller
    @RequestMapping(value="/customer")
    public class CustomerController{
    @RequestMapping(value="/delete", method={RequestMethod.POST, RequestMethod.PUT})
    public String deleteCustomer(){
    //do something here
    return ...;
    } }

       在这种情况下,所有的方法都将映射为相对于类级别的请求,如例子中的deleteCustomer方法,由于控制器类映射使用"/customer",而deleteCustomer方法映射为"/delete",则需要通过http://localhost:8081/SpringMVC/customer/delete。

    应用基于注解的控制器

      本节将通过一个例子说明基于注解的控制器,展示了一个包含两个请求处理方法的控制器类,项目目录结构如下:

     

      我们首先对类进行介绍,Product是产品类,包括产品的名称、描述和价格属性,该类实现了Serializable接口,而ProductForm类与HTML表单相映射,是Product在服务端的代表,表单对象传递ServletRequest给其他组件,还有当数据校验失败时,表单对象将用于保存和展示用户在原始表单上的输入。DBOperator类负责与mysql数据库打交道,ProductService定义了服务层的接口,ProductServiceImpl实现了ProductService中定义的接口。

    Product类代码如下,包含属性和属性的set和get方法:

    public class Product implements Serializable{
        private static final long serialVersionUID = 1L;
        private String name;
        private String description;
        private double price;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getDescription() {
            return description;
        }
        public void setDescription(String description) {
            this.description = description;
        }
        public double getPrice() {
            return price;
        }
        public void setPrice(double price) {
            this.price = price;
        }
        public static long getSerialversionuid() {
            return serialVersionUID;
        }
    }
    View Code

    ProductForm代码如下,和Product类唯一区别就是不用实现Serializable接口:

    public class ProductForm {
        private String name;
        private String description;
        private double price;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getDescription() {
            return description;
        }
        public void setDescription(String description) {
            this.description = description;
        }
        public double getPrice() {
            return price;
        }
        public void setPrice(double price) {
            this.price = price;
        }
    }
    View Code

    DBOperator类包含两个方法,一个是执行sql语句,一个是从数据库中查询数据:

    public class DBOperator {
        private JdbcTemplate jdbcTemplate;
        //通过setter的方式实现依赖注入
        public void setJdbcTemplate(JdbcTemplate jdbcTemplate){
            this.jdbcTemplate = jdbcTemplate;
        }
        public Map query(String sql,int id){
            Map productInfo = this.jdbcTemplate.queryForMap(sql, id);
            return productInfo;
        }
        public void insert(String sql,Object[] obj){
            this.jdbcTemplate.update(sql, obj);
        }
    }
    View Code

      服务层的ProductService和实现类ProductServiceImpl如下,ProductServiceImpl类中通过@Autowired和@Service进行DBOperator对象的依赖,@Autowired注解会使DBOperator的一个实例被注入到ProductServiceImpl实例中,为了能是类能被spring扫描到,必须为其标注为@Service。

    public interface ProductService {
        public Long addProduct(Product product);
        public Product queryProduct(long productID);
    }
    
    @Service
    public class ProductServiceImpl implements ProductService{
        //生成产品的ID,是数据库表中的主键
        private AtomicLong generator = new AtomicLong();
        
        @Autowired
        private DBOperator operator;
        @Override
        public Long addProduct(Product product) {
            // TODO Auto-generated method stub
            Long id = generator.incrementAndGet();
            String name = product.getName();
            String description = product.getDescription();
            double price = product.getPrice();
            Object[] obj = new Object[]{id,name,description,price};
            String sql = "insert into t_product values(?,?,?,?)";
            operator.insert(sql,obj);
            return id;
        }
    
        @Override
        public Product queryProduct(long productID) {
            // TODO Auto-generated method stub
            String sql = "select * from t_product where id=?";
            Map productInfo = this.operator.query(sql,(int)productID);
            String name = (String) productInfo.get("name");
            String description = (String) productInfo.get("description");
            double price = (double) productInfo.get("price");
            Product product = new Product();
            product.setDescription(description);
            product.setName(name);
            product.setPrice(price);
            return product;
        }
    }
    View Code

      当然最重头戏的还是ProductController类,该类中的方法通过RequestMapping指定Request和Handler的对应关系,而在saveProduct方法的参数中通过RedirectAttribute实现页面的重定向。

    @Controller
    public class ProductController {
        private static final Log logger = LogFactory.getLog(ProductController.class);
        
        @Autowired
        private ProductService productService;
        
        @RequestMapping(value="/productInput")
        public String inputProduct(){
            //logger.info("inputProduct called");
            return "ProductForm";
        }
        
        @RequestMapping(value="/productSave", method=RequestMethod.POST)
        public String saveProduct(ProductForm productForm, RedirectAttributes redirectAttributes){
            //logger.info("saveProduct called");
            Product product = new Product();
            product.setName(productForm.getName());
            product.setDescription(productForm.getDescription());
            product.setPrice(productForm.getPrice());
            //add product
            Long productID = productService.addProduct(product);
            redirectAttributes.addAttribute("message", "The product was successfully added.");
            return "redirect:/product_view/" + productID;
        }
        
        @RequestMapping(value="/product_view/{id}")
        public String viewProduct(@PathVariable Long id, Model model){
            Product product = productService.queryProduct(id);
            model.addAttribute("product", product);
            return "ProductDetails";
        }
    }
    View Code

      如下是web.xml配置文件,应用servlet和servlet-mapping元素,servlet元素内的<load-on-startup>元素是可选的。如果存在,则它将在应用程序启动时装载servlet并调用它的init方法,若不存在,则在该servlet的第一个请求时加载,可以通过<init-param>中的<param-value>元素指定配置文件的路径,如果没有<init-param>元素,则默认路径为程序的WEB-INF目录下的servletName-servlet.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_2_5.xsd" id="WebApp_ID" version="2.5">
      <servlet>
          <servlet-name>springmvc</servlet-name>
          <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
          <init-param>
              <param-name>contextConfigLocation</param-name>
              <param-value>/WEB-INF/config/springmvc-config.xml</param-value>
          </init-param>
          <load-on-startup>1</load-on-startup>
      </servlet>
      <servlet-mapping>
          <servlet-name>springmvc</servlet-name>
          <url-pattern>/</url-pattern>
      </servlet-mapping>
    </web-app>

      如下是配置文件(springmvc-config.xml),其中包括指定控制器类所在的包、默认访问路径请求的html、配置数据源(注意用户名和密码需要改为自己数据库的用户名和密码)通过setter的方式在DBOperator对象中注入jdbcTemplate对象,最后通过viewResolver指定视图文件所在的路径以及后缀。

    <?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:p="http://www.springframework.org/schema/p" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd"> <context:component-scan base-package="com.example.controller"/> <context:component-scan base-package="service"/> <context:component-scan base-package="mysqloperator"/> <mvc:annotation-driven/>
    <mvc:resources mapping="/*.html" location="/"/> <!-- <bean name="/productInput" class="Controller.InputProductController"/> <bean name="/productSave" class="Controller.SaveProductController"/> --> <!-- 配置数据源 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <!-- 数据库驱动 --> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <!-- 连接数据库的URL 数据库名为已经创建好的User --> <property name="url" value="jdbc:mysql://localhost/Product"/> <!-- 连接数据库的用户名 --> <property name="username" value="root"/> <!-- 连接数据的密码 --> <property name="password" value="123"/> </bean> <!-- 配置JDBC模板 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <!-- 默认必须使用数据源 --> <property name="dataSource" ref="dataSource"/> </bean> <bean id="dbOperator" class="mysqloperator.DBOperator"> <property name="jdbcTemplate" ref="jdbcTemplate"/> </bean> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp" /> </bean> </beans>

    商品表单视图(ProductForm.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">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>商品信息</title>
    </head>
    <body>
    <div id="global">
    <form action="productSave" method="post">
        <fieldset>
            <legend>Add a product</legend>
            <p>
                <label for="name">Product Name:</label>
                <input type="text" id="name" name="name" tabindex="1">
            </p>
            <p>
                <label for="description">Description:</label>
                <input type="text" id="description" name="description" tabindex="2">
            </p>
            <p>
                <label for="price">Price:</label>
                <input type="text" id="price" name="price" tabindex="3">
            </p>
            <p id="buttons">
                <input id="reset" type="reset" tabindex="4">
                <input id="submit" type="submit" tabindex="5" value="Add Product">
            </p>
        </fieldset>
    </form>
    </div>
    </body>
    </html>

    商品详细信息表单(ProductDetail.jsp)问价如下:

    <%@ page language="java" contentType="text/html; charset=ISO-8859-1"
        pageEncoding="ISO-8859-1"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
    <title>Save Product</title>
    </head>
    <body>
    <div id="global">
        <h4>The product has been saved.</h4>
        <p>
            <h5>Details:</h5>
            Product Name: ${product.name }<br />
            Description: ${product.description } <br />
            Price: $${product.price }
        </p>
    </div>
    </body>
    </html>

      最后在Tomcat服务器下运行该web程序,报logging版本冲突异常(You have more than one version of 'org.apache.commons.logging.Log' visible,),将导入的jar包中的commons-logging-1.2.jar删除即可,并在浏览器地址栏中输入:http://localhost:8081/SpringMVC/productInput,即可实现对ProductForm.jsp视图的访问,然后输入商品信息,点击Add Product按钮,即可将商品信息添加到数据库(需要提前创建数据库(User)和数据库中的表(t_product(id,name,description,price))),并从数据库中查询插入信息并在ProductDetail.jsp视图中显示。

            

  • 相关阅读:
    中国的网游,发人深省
    .NET开源Bug管理软件BugTracker.NET使用小记
    开源的学术论文排版软件TeX简介
    在sqlplus中批量执行sql命令
    DotNetBar中ExpandableSplitter使用技巧
    上传一很酷的黑色背景.vssettings文件
    瑞雪兆丰年2008年的第一场雪
    Oracle用Start with...Connect By子句递归查询
    【转载】Making new features with topology tools(ArcInfo and ArcEditor only)
    Geoprocess Execute出错原因?Vb+AE9.2
  • 原文地址:https://www.cnblogs.com/zhanglei93/p/6264796.html
Copyright © 2020-2023  润新知