基於注解的控制器 |
- 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; } }
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; } }
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); } }
服務層的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; } }
當然最重頭戲的還是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"; } }
如下是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視圖中顯示。