一、創建Maven項目
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.cheery</groupId>
<artifactId>webstore</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<spring-version>5.0.5.RELEASE</spring-version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring-version}</version>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.4.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
</project>
二、Domain 層
package com.webstore.domain;
import java.io.Serializable;
import java.math.BigDecimal;
public class Product implements Serializable {
private static final long serialVersionUID = 3678107792576131001L;
private String productId;
private String name;
private BigDecimal unitPrice;
private String description;
private String manufacturer;
private String category;
private long unitsInStock;
private long unitsInOrder;
private boolean discontinued;
private String condition;
public Product() {
super();
}
public Product(String productId, String name, BigDecimal unitPrice) {
this.productId = productId;
this.name = name;
this.unitPrice = unitPrice;
}
public String getProductId() {
return productId;
}
public void setProductId(String productId) {
this.productId = productId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public BigDecimal getUnitPrice() {
return unitPrice;
}
public void setUnitPrice(BigDecimal unitPrice) {
this.unitPrice = unitPrice;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getManufacturer() {
return manufacturer;
}
public void setManufacturer(String manufacturer) {
this.manufacturer = manufacturer;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public long getUnitsInStock() {
return unitsInStock;
}
public void setUnitsInStock(long unitsInStock) {
this.unitsInStock = unitsInStock;
}
public long getUnitsInOrder() {
return unitsInOrder;
}
public void setUnitsInOrder(long unitsInOrder) {
this.unitsInOrder = unitsInOrder;
}
public boolean isDiscontinued() {
return discontinued;
}
public void setDiscontinued(boolean discontinued) {
this.discontinued = discontinued;
}
public String getCondition() {
return condition;
}
public void setCondition(String condition) {
this.condition = condition;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Product other = (Product) obj;
if (productId == null) {
if (other.productId != null)
return false;
} else if (!productId.equals(other.productId))
return false;
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((productId == null) ? 0 : productId.hashCode());
return result;
}
}
三、Repository 數據層
先假設 NamedParameterJdbcTemplate 已經配置好了
package com.webstore.domain.repository;
import java.util.List;
import com.webstore.domain.Product;
public interface ProductRepository {
List<Product> getAllProducts();
}
package com.webstore.domain.repository.impl;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;
import com.webstore.domain.Product;
import com.webstore.domain.repository.ProductRepository;
@Repository
public class InMemoryProductRepository implements ProductRepository {
@Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
@Override
public List<Product> getAllProducts() {
Map<String, Object> params = new HashMap<String, Object>();
List<Product> result = jdbcTemplate.query("SELECT * FROM products", params, new ProductMapper());
return result;
}
private static final class ProductMapper implements RowMapper<Product> {
public Product mapRow(ResultSet rs, int rowNum) throws SQLException {
Product product = new Product();
product.setProductId(rs.getString("ID"));
product.setName(rs.getString("NAME"));
product.setDescription(rs.getString("DESCRIPTION"));
product.setUnitPrice(rs.getBigDecimal("UNIT_PRICE"));
product.setManufacturer(rs.getString("MANUFACTURER"));
product.setCategory(rs.getString("CATEGORY"));
product.setCondition(rs.getString("CONDITION"));
product.setUnitsInStock(rs.getLong("UNITS_IN_STOCK"));
product.setUnitsInOrder(rs.getLong("UNITS_IN_ORDER"));
product.setDiscontinued(rs.getBoolean("DISCONTINUED"));
return product;
}
}
}
四、Service 業務邏輯層
package com.webstore.service;
import java.util.List;
import com.webstore.domain.Product;
public interface ProductService {
List<Product> getAllProducts();
}
package com.webstore.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.webstore.domain.Product;
import com.webstore.domain.repository.ProductRepository;
import com.webstore.service.ProductService;
@Service
public class ProductServiceImpl implements ProductService {
@Autowired
private ProductRepository productRepository;
@Override
public List<Product> getAllProducts() {
return productRepository.getAllProducts();
}
}
五、Controller 控制層
package com.webstore.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import com.webstore.service.ProductService;
@Controller
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping("/products")
public String list(Model model) {
model.addAttribute("products", productService.getAllProducts());
return "products";
}
}
六、views 視圖模板
/webstore/src/main/webapp/WEB-INF/views/products.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Products</title>
<link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
</head>
<body>
<section>
<div class="jumbotron">
<div class="container">
<h1>Products</h1>
<p>All the available products in our store</p>
</div>
</div>
</section>
<section class="container">
<div class="row">
<c:forEach items="${products}" var="product">
<div class="col-sm-6 col-md-3">
<div class="thumbnail">
<div class="caption">
<h3>${product.name}</h3>
<p>${product.description}</p>
<p>$${product.unitPrice}</p>
<p>Available ${product.unitsInStock} units in stock</p>
</div>
</div>
</div>
</c:forEach>
</div>
</section>
</body>
</html>
七、配置
Web application context:
Spring’s container uses dependency injection (DI) to manage the beans that make up an application. An application context creates beans, associates beans together based on bean configuration, and dispenses beans upon request. A web application context is an extension of the application context, and is designed to work with the standard servlet context (javax.servlet.ServletContext). The web application context typically contains front-end related beans such as views and view resolvers, and so on.
Front Controller Pattern :
In a Spring MVC project, we must configure a servlet mapping to direct all the HTTP requests to a single front servlet. The front servlet mapping is a design pattern where all requests for a particular web application are directed to the same servlet. By adapting the Front Controller design, we make front servlet have total control over the incoming HTTP request so that it can dispatch the HTTP request to the desired controller.
One such front servlet given by Spring MVC framework is the Dispatcher servlet
package com.webstore.config;
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.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
@Configuration
@EnableWebMvc
@ComponentScan("com.webstore")
public class WebMvcConfig implements WebMvcConfigurer {
@Bean
public InternalResourceViewResolver getInternalResourceViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setViewClass(JstlView.class);
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
}
MVC之外的組件,如Repository的配置,也可以寫在WebApplicationContextConfig 裏,但爲了方便管理,應該爲各個不同層的組件分別定義一個配置類,並在應用初始化註冊這些組件。Spring 提供了 AbstractAnnotationConfigDispatcherServletInitializer 類來快速配置。getRootConfigClasses 註冊MVC 之外的配置類,getServletConfigClasses 註冊MVC 配置類,也就是 Dispatcher servlet 的配置類。
RepositoryConfig 定義了數據層的相關組件
package com.webstore.config;
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
@Configuration
@ComponentScan("com.webstore")
public class RepositoryConfig {
@Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase db = builder.setType(EmbeddedDatabaseType.HSQL).addScript("db/sql/create-table.sql")
.addScript("db/sql/insert-data.sql").build();
return db;
}
@Bean
public NamedParameterJdbcTemplate getJdbcTemplate() {
return new NamedParameterJdbcTemplate(dataSource());
}
}
應用初始化時,應把各個層的配置類都註冊:
package com.webstore.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { RepositoryConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebApplicationContextConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
配置數據層兩個 sql 腳本:
db/sql/create-table.sql
DROP TABLE PRODUCTS IF EXISTS;
CREATE TABLE PRODUCTS (
ID VARCHAR(25) PRIMARY KEY,
NAME VARCHAR(50),
DESCRIPTION VARCHAR(250),
UNIT_PRICE DECIMAL,
MANUFACTURER VARCHAR(50),
CATEGORY VARCHAR(50),
CONDITION VARCHAR(50),
UNITS_IN_STOCK BIGINT,
UNITS_IN_ORDER BIGINT,
DISCONTINUED BOOLEAN
);
db/sql/insert-data.sql
INSERT INTO PRODUCTS VALUES ('P1234', 'iPhone 6s', 'Apple iPhone 6s smartphone with 4.00-inch 640x1136 display and 8-megapixel rear camera',500,'Apple','Smartphone','New',450,0,false);
INSERT INTO PRODUCTS VALUES ('P1235', 'Dell Inspiron','Dell Inspiron 14-inch Laptop (Black) with 3rd Generation Intel Core processors',700,'Dell','Laptop','New',1000,0,false);
INSERT INTO PRODUCTS VALUES ('P1236', 'Nexus 7', 'Google Nexus 7 is the lightest 7 inch tablet With a quad-core Qualcomm Snapdragon™ S4 Pro processor', 300,'Google','Tablet','New',1000,0,false);