最近在学习SpringMvc,自己搭建一次,也算做做笔记了,分享一下,用的Eclipse jee版,Eclipse新建web项目就不说了,在前面有篇文章有介绍,直接说配置Spring吧,肯定要先在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.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<!-- 配置开始 -->
<!-- a1加载Spring容器配置 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- a2设置Spring容器加载配置文件路径 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- b1配置Spring核心控制器 DispatcherServlet -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- b2设置控制器加载配置文件路径 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:servlet-context.xml</param-value>
</init-param>
<!-- b3启动顺序,让这个Servlet随Servlet容器一起启动。 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- b3拦截所有.action请求 -->
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
<!-- c1设定编码格式 -->
<filter>
<filter-name>characterEncoding</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>
<!-- c2设定编码格式范围 -->
<filter-mapping>
<filter-name>characterEncoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
这里配了一个servlet一个application容器,配置文件的位置都放在根路径下,我们就可以放在resource下。顾名思义
先看servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- 返回数据解析。prefix:前缀, suffix:后缀 也就是配置views的目录-->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<!-- 相当于注册了DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter两个bean -->
<annotation-driven />
<!-- 注解controller所在的包, 扫描路径 -->
<context:component-scan base-package="com.springMvc.controller"/>
</beans:beans>
关于HandlerMapping和handlerAdapter这里就直接转下别人博客里的解释了:如下
SpringMVC中,满足条件的请求进入到负责请求分发的DispatcherServlet,DispatcherServlet根据请求url到控制器的映射(HandlerMapping中保存),HandlerMapping最终返回HandlerExecutionChain,其中包含了具体的处理对象handler(也即我们编程时写的controller)以及一系列的拦截器interceptors,此时DispatcherServlet会根据返回的HandlerExecutionChain中的handler找到支持这一处理器类型的适配器(handlerAdapter),在处理器适配器中最终会去调用控制器的请求响应方法并返回结果视图(ModelAndView)
然后是application.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- 注解所在的包, 扫描dao路径 -->
<context:component-scan base-package="com.springMvc.dao"/>
<!-- 注解所在的包, 扫描service路径 -->
<context:component-scan base-package="com.springMvc.service"/>
<!-- 引入jdbc.properties -->
<context:property-placeholder location="classpath:jdbc.properties" ignore-unresolvable="true"/>
<!-- 配置一个数据源,用下dbcp连接池 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<!-- 用的mysql数据库,连接到数据库mydb(自己新建) -->
<property name="driverClassName" value="${driver}" />
<property name="url" value="${url}" />
<!-- 用户名密码,根据本地数据库自定义 -->
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</bean>
<!-- 定义jdbc模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
这里就是配下要扫描注解的路径,然后配个数据库连接,基本上配置文件就写好了,我们可以简单写点小程序了,在建好的web项目里继续划分出dao,service,control(Eclipse搭建web项目那篇文章的例子)
结构很清晰的说。好像忘了还有log4j,先配好吧,方便调试啥的
log4j.rootCategory=INFO,A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
同样放在resource下,log4j.properties。简单配置下只输出info到控制台。然后终于可以开始写代码了,写什么好呢,想了想就做个简单的记账系统吧,手机上有很多这种应用,记录每天花的钱,收入的钱,然后有个汇总啥的,功能不多,好实现。看来代码还是不能先写,先搞定数据库,如下设计
当然我也没设计过这种软件,只是凭空想象的,简单弄这么两个表,应该一看就明白了,数据库id都设自增长,账目时间,金额,出入方向,账目类型就是可以自定义类型(吃饭,娱乐,工资)然后类型细分的话又做了个类型表,可以用父类ID来规划子父类关系,比如吃饭再分为早饭,午饭,晚饭等,都是扯淡,没啥好说的。至于金额是int,这里数据库按最小单位的分来记录,所以不带小数点的。然后又了数据库就可以对应写两个entity实体类了
package com.springMvc.dao.entity;
import java.sql.Date;
public class Account {
private String id;//数据库ID
private Date acc_time;//时间
private Integer acc_jine;//金额
private boolean acc_shouzhi;//出入
private Integer acc_type;//分类
private String acc_beizhu;//备注
//getters and setters
package com.springMvc.dao.entity;
public class Type {
private Integer type_id;//数据库ID
private String type_name;//类型描述
private Integer type_parent;//父类ID getters and setters略
然后是Manager这里先简单写个账目的Manager,写两个
package com.springMvc.dao.manager;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import com.springMvc.dao.entity.Account;
/*通过Spring注解定义一个Dao,这里简单写下略去了Dao直接写在Manager里了*/
@Repository
public class AccountManager {
/*自动注入jdbcTemplate的bean*/
@Autowired
private JdbcTemplate jdbcTemplate;
//logger
private final static Logger logger = Logger.getLogger(AccountManager.class);
public int insertAccount(Account ac){
logger.info("----------------账目插入数据库,dao层--!");
/*Sql*/
String sql = "insert into account_info (acc_time,acc_jine,acc_shouzhi,acc_type,acc_beizhu)"
+ "values(?,?,?,?,?)";
/*入参*/
Object[] args = {ac.getAcc_time(),ac.getAcc_jine(),ac.isAcc_shouzhi(),ac.getAcc_type(),ac.getAcc_beizhu()};
return jdbcTemplate.update(sql,args);//返回成功失败
}
}
package com.springMvc.dao.manager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.stereotype.Repository;
import com.springMvc.model.TypeInfo;
@Repository
public class TypeManager {
/*自动注入jdbcTemplate的bean*/
@Autowired
private JdbcTemplate jdbcTemplate;
//logger
private final static Logger logger = Logger.getLogger(TypeManager.class);
/**
* 提供一个查询所有最底层类的方法,供给前台插入数据时选择
*/
public List<TypeInfo> selectAllTypes(){
logger.info("----------------查询可用类型,dao层--!");
/*这里我设想的是id不是别人的父类id肯定就是最子类了,sql不太擅长,有更好的方法欢迎提出*/
String sql = "select type_id,type_name from account_type where type_id not in"
+ "(select distinct type_parent from account_type)";
final List<TypeInfo> resultList = new ArrayList<TypeInfo>();
jdbcTemplate.query(sql,
new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws SQLException {
TypeInfo ty = new TypeInfo();
ty.setType_id(rs.getInt("type_id"));
ty.setType_name(rs.getString("type_name"));
resultList.add(ty);
}
});
return resultList;
}
}
一个account manager一个type manager,分别提供插入方法,和提供插入时选择账目类型列表。基本先够用了吧,这里返回类型列表用到了一个类型的model,如下
package com.springMvc.model;
public class TypeInfo {
private Integer type_id;//id
private String type_name;//描述
public Integer getType_id() {
return type_id;
}
public void setType_id(Integer type_id) {
this.type_id = type_id;
}
public String getType_name() {
return type_name;
}
public void setType_name(String type_name) {
this.type_name = type_name;
}
}
然后写service吧
package com.springMvc.service;
import com.springMvc.dao.entity.Account;
public interface AccountService {
/**
* 插入账目
*/
public void insertAccout(Date acc_time,Integer acc_jine,Integer acc_shouzhi,Integer acc_type,Integer acc_beizhu);
}
package com.springMvc.service;
import java.util.List;
import com.springMvc.model.TypeInfo;
public interface TypeService {
/**
* 调取可用类型
*/
public List<TypeInfo> selectAllTypes();
}
package com.springMvc.service.Impl;
import java.sql.Date;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.springMvc.dao.entity.Account;
import com.springMvc.dao.manager.AccountManager;
import com.springMvc.service.AccountService;
@Service //标注为服务层的一个service
public class AccountServiceImpl implements AccountService{
//logger
private final static Logger logger = Logger.getLogger(AccountServiceImpl.class);
@Autowired//自动装配dao bean
private AccountManager accountManager;
@Override
public void insertAccout(Date acc_time,Integer acc_jine,Integer acc_shouzhi,Integer acc_type,Integer acc_beizhu) {
logger.info("----------------插入账目进入Service层--!");
Account ac = new Account();
ac.setAcc_time(acc_time);
ac.setAcc_jine(acc_jine);
ac.setAcc_shouzhi(acc_shouzhi==1?true:false);
ac.setAcc_type(acc_type);
accountManager.insertAccount(ac);
}
}
package com.springMvc.service.Impl;
import java.util.List;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.springMvc.dao.manager.TypeManager;
import com.springMvc.model.TypeInfo;
import com.springMvc.service.TypeService;
@Service
public class TypeServiceImpl implements TypeService{
//logger
private final static Logger logger = Logger.getLogger(TypeServiceImpl.class);
@Autowired//自动装配dao bean
private TypeManager typeManager;
@Override
public List<TypeInfo> selectAllTypes() {
logger.info("----------------调取可用类型,Service层--!");
return typeManager.selectAllTypes();
}
}
两个service接口,两个实现类,没什么好解释的,然后写两个controller,一个是进入记账页面时调取可选类型的action,一个是保存记账的action如下
package com.springMvc.controller;
import java.sql.Date;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import com.springMvc.service.AccountService;
@Controller
public class AccountController {
//logger
private final static Logger logger = Logger.getLogger(AccountController.class);
@Autowired//自动装配Service bean
private AccountService accountService;
@RequestMapping(value = "/insert.action")
public ModelAndView insertAccount(@RequestParam("acc_time") Date acc_time,
@RequestParam("acc_jine") Integer acc_jine,
@RequestParam("acc_shouzhi") Integer acc_shouzhi,
@RequestParam("acc_type") Integer acc_type,
@RequestParam("acc_beizhu") String acc_beizhu){
logger.info("----------------插入账目进入Controller--!");
accountService.insertAccout(acc_time, acc_jine, acc_shouzhi, acc_type, acc_beizhu);
/*返回主页由于我们的index.jsp放在了view外的web-inf外,所以写了两个../取上上层目录,
* 正常应该把index.jsp直接放到view文件夹下*/
return new ModelAndView("../../index");
}
}
package com.springMvc.controller;
import java.util.List;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.springMvc.model.TypeInfo;
import com.springMvc.service.TypeService;
@Controller
public class TypeController {
//logger
private final static Logger logger = Logger.getLogger(TypeController.class);
@Autowired//自动装配Service bean
private TypeService typeService;
@RequestMapping(value = "/toInsertPage.action")
public ModelAndView listAllType(){
logger.info("----------------调取可用type进入Controller--!");
List<TypeInfo> types = typeService.selectAllTypes();
//分别是视图名称,(通过前后缀的配置就是view下的insert.jsp,)后面是模型对象
return new ModelAndView("insert", "types", types);
}
}
controller写好了,做前端的也写好就Ok了,这里没有前端的同事,只好自己写了,我们把helloworld页面加个跳转按钮,连接到toInsertPage.action,这样请求可用账目类型,返回到insert.jsp.然后insert.jsp填写账目表单验证数据后提交到insert.action返回到index.jsp。其实就一个insert.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 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>Insert title here</title>
<script type="text/javascript">
function checkData(){
var acc_time = document.getElementById("acc_time").value;
var acc_jine = document.getElementById("acc_jine").value;
var acc_beizhu = document.getElementById("acc_beizhu").value;
var regDate=/^(\d{4})-(\d{2})-(\d{2})$/;
var regJine=/^[0-9]*[1-9][0-9]*$/;
if(!regDate.test(acc_time)){
alert("输入正确日期");
return false;
}
if(!regJine.test(acc_jine)){
alert("输入正确金额");
return false;
}
if(acc_beizhu.length>100){
alert("备注过长");
return false;
}
}
</script>
</head>
<body>
<form action="insert.action" method="post" οnsubmit="return checkData()">
<table cellpadding="0" cellspacing="0">
<tr>
<th>时间:</th><td><input type="text" name="acc_time" id="acc_time"/>
格式为2010-02-01这里简单实现下功能,为学框架嘛,就没搞什么日期控件
</td>
</tr>
<tr>
<th>出入:</th>
<td>
<input type="radio" checked="checked" value="1" id="churu1" name="acc_shouzhi"/><label for="churu1">出</label>
<input type="radio" value="0" id="churu0" name="acc_shouzhi"/><label for="churu2">入</label>
</td>
</tr>
<tr>
<th>金额:</th>
<td><input type="text" name="acc_jine" id="acc_jine"/>
单位为分,同上,简单实现下功能,输入正整数</td>
</tr>
<tr>
<th>类型:</th>
<td><select name="acc_type">
<c:forEach items="${types }" var="type" varStatus="status">
<option value="${type.type_id}">${type.type_name}</option>
</c:forEach>
</select></td>
</tr>
<tr>
<th>备注:</th>
<td><input type="text" name="acc_beizhu" id="acc_beizhu"></td>
</tr>
<tr>
<th colspan="2"><input type="submit"/></th>
</tr>
</table>
</form>
</body>
</html>
tomcat run一下,进入hello world,点添加账目,跳转填写表单,填写内容后提交,跳回hello world页,logger info如下:流程很清晰了,就这样吧