关于SpringMVC的一点学习笔记
- 1、 maven依赖/目录结构
- 2、配置文件
- 3、从前端请求开始
- 4、Controller
- 5、Service
- 6、Dao
- 7、mybatis
- 8、utils公共类
- 9、 分页查询 QueryPageBean / PageResult
- 10、静态页面Freemarker用在经常访问但不经常变化的页面场景中
- 11、Reids
- 12、Echarts
- 13、认证、授权Spring Security
- 14、批处理任务Spring Schedule
- 参考资料
1、 maven依赖/目录结构
- pom文件
大型工程可转列一个公共common模块,用于提供公共的方法类,如日期格式处理、SMS短信等;
设置一个interface接口模块,用于Controller与Service分离的情况(如dubbo等),但对于小工程这种分离会降低调试效率,因为Controller与Service是分离的,通过接口的maven install来更新,比较慢。可先开发,后分离。
参考一个较为齐全的pom:
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>health_parent</artifactId>
<groupId>cn.cathayinfo</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>health_common</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
</dependency>
<!-- Mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</dependency>
<dependency>
<groupId>com.github.miemiedev</groupId>
<artifactId>mybatis-paginator</artifactId>
</dependency>
<!-- MySql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
<!-- dubbo相关 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</dependency>
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-dysmsapi</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency>
</dependencies>
</project>
- 父子工程:parent
parent组织全部工程,并统一依赖版本 - build:Tomcat轻量级Tomcat,可不依赖于本机的Tomcat,使得开发和上线运行容器环境一致
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>80</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
- 目录结构:src->main->java/resource/webapp
maven的骨架结构,可自建和使用骨架。
2、配置文件
- web.xml
要点:
(1)将springmvc作为默认处理请求的DispatcherServlet
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 指定加载的配置文件 ,通过参数contextConfigLocation加载 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value> <!-- 通过这里将web.xml与springmvc.xml发生关联 -->
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
(2)处理编码问题
<filter>
<filter-name>CharacterEncodingFilter</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>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
(3)采用系统登录的话处理登录问题 (用这种方式感觉有些复杂,还没太搞明白)
- springmvc.xml
开启注解
自动扫描根目录的设置
各种bean的加载(数据库、redis、文件上传、dubbo等) - DemoDao.xml
参考中文官网 - jdbc.properites
放在resource的一级目录下可自动加载,可在Spring中以${value}的形式读取数值 - log4j.properties
log4j日志记录
配置模板:
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=c:\\mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ###
log4j.rootLogger=debug, stdout
- freemarkerDemo.ftl
freemarker使用场景:主要用于经常访问,但不怎么变化的页面(如物品详情页),在添加该商品时提前生成,减少数据库查询频率。
private void generateHtml(String templateName, String htmlPageName, Map dataMap) {
Configuration configuration = freeMarkerConfigurer.getConfiguration();
Writer writer = null;
try {
Template template = configuration.getTemplate(templateName);
writer = new FileWriter(new File(outputpath+"\\"+htmlPageName));
template.process(dataMap,writer);
}catch (Exception ex){
ex.printStackTrace();
}finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3、从前端请求开始
axios.get axios.post是两种常用的请求方式,以json格式发送和接收输入,=>
是ECMAScript6规范。以res接收服务端发过来的response,解析其中的res.data.data。后端配套Result类作为统一的返回数据类,规范交互。
created(){
axios.post("/user/getUsername.do").then(
res=>{
if (res.data.flag){
this.username = res.data.data;
axios.post("/user/getMenuList.do").then(
res => {
if (res.data.flag){
this.menuList = res.data.data;
}
}
);
}
})
}
4、Controller
@RestController ----------Controller与Respondbody的集合体
@RequestMappping(“/user”)
@Reference(dubbo)
@Autowired
@RequestParameter(“id”) ------- 接收单个指定名称的字段
@RequestBody User ----------直接将前台传过来的json转为pojo对象
5、Service
@Service
@Transional 重点:注意事务的捕捉等级(rollbackFor = Exception.class)与异常抛出的等级(RuntimeException、 Exceptio)的对应关系。如下文注释部分。
@Service(interfaceClass = BankService.class)
@Transactional
public class BankServiceImpl implements BankService {
@Autowired
private BankDao bankDao;
@Override
public void transforMoney(String from, String to, int money) throws Exception{
BankUser fromBankUser = bankDao.getBankUserByName(from);
BankUser toBankUser = bankDao.getBankUserByName(to);
Map map = new HashMap();
map.clear();
map.put("id", toBankUser.getId());
map.put("money", toBankUser.getMoneycount() + money);
bankDao.updateCount(map);
if (fromBankUser.getMoneycount() - money < 0) {
bankDao.mistake(1);
//throw new Exception("余额不足");
//注意Spring默认捕捉到RuntimeException和Error的错误才会rollback回退事务,
//所以(1)(2)都行,但是(!3)无法回退事务
//(1)@Transactional 与 throw new RuntimeException("余额不足");
//(2)@Transactional(rollbackFor = Exception.class) 与 throw new Exception("余额不足");
//(!3)@Transactional 与 throw new Exception("余额不足");
}
map.clear();
map.put("id", fromBankUser.getId());
map.put("money", fromBankUser.getMoneycount() - money);
bankDao.updateCount(map);
}
}
6、Dao
只写接口,对应在mybatis的xml文件中描述,main、resource路径是完全对应的,maven install后合并在同一路径下。
7、mybatis
xml文件
select --------(1)如果数据库字段名与pojo字段名不对应时使用 select member_id as “memberId” … 的方式insert --------(1)主键是由数据库自增的,可通过以下配置实现主键id的自返回
<insert id="add" parameterType="cn.cathayinfo.pojo.CheckGroup" useGeneratedKeys="true" keyProperty="id">
insert into t_checkgroup (code,name,helpCode,sex,remark,attention)
values (#{code},#{name},#{helpCode},#{sex},#{remark},#{attention})
</insert>
parameterType 注意多个复杂参数时用map传进去
resultType -------- pojo时指定路径全称
resultMap ------------- 最复杂、多表联合查询,
<select id="findById" resultMap="findByIdResultMap">
select * from t_setmeal where id=#{id}
</select>
<resultMap type="pojo.Setmeal" id="baseResultMap">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="code" property="code"/>
<result column="helpCode" property="helpCode"/>
<result column="sex" property="sex"/>
<result column="age" property="age"/>
<result column="price" property="price"/>
<result column="remark" property="remark"/>
<result column="attention" property="attention"/>
<result column="img" property="img"/>
</resultMap>
<resultMap type="pojo.Setmeal"
id="findByIdResultMap"
extends="baseResultMap">
<collection property="checkGroups"
javaType="ArrayList"
ofType="pojo.CheckGroup"
column="id"
select="findCheckGroupById">
</collection>
</resultMap>
8、utils公共类
DateUtils
POIUtils
QiniuUtilis
SMSUtilis
CalidateCode/Redis/Cookies
9、 分页查询 QueryPageBean / PageResult
@Override
public PageResult pageQuery(QueryPageBean queryPageBean) {
PageHelper.startPage(queryPageBean.getCurrentPage(), queryPageBean.getPageSize());
Page<CheckGroup> checkGroups = checkGroupDao.selectByCondition(queryPageBean.getQueryString());
return new PageResult(checkGroups.getTotal(), checkGroups);
}
10、静态页面Freemarker用在经常访问但不经常变化的页面场景中
http://freemarker.foofun.cn/index.html
11、Reids
12、Echarts
<script type="text/javascript">
// 基于准备好的dom,初始化echarts实例
var myChart1 = echarts.init(document.getElementById('chart1'));
// 使用刚指定的配置项和数据显示图表。
//myChart.setOption(option);
axios.get("/report/getMemberReport.do").then((res)=>{
myChart1.setOption(
{
title: {
text: '会员数量'
},
tooltip: {},
legend: {
data:['会员数量']
},
xAxis: {
data: res.data.data.months
},
yAxis: {
type:'value'
},
series: [{
name: '会员数量',
type: 'line',
data: res.data.data.memberCount
}]
});
});
</script>
13、认证、授权Spring Security
(1)基础表:User、Role、Permission、Menu 及 t_user_role、t_role_permission、t_role_menu。
(2)配置spring-security.xml文件,并引入到springmvc中;
(3)在web中配置权限filter;
(4) 实现@Component public class SpringSecurityUserService implements UserDetailsService
在这个类中实现上述表格的查询,根据用户user查询角色role,根据角色role查询可以看到哪些菜单Menu,根据角色role查询拥有哪些权限Permission。
(5)在每个方法上注解好权限要求
@PreAuthorize("hasAnyAuthority('CHECKITEM_QUERY')")
@RequestMapping("findAll")
public Result findAll() {
try {
List<CheckItem> checkItems = checkItemService.findAll();
return new Result(true, MessageConstant.QUERY_CHECKITEM_SUCCESS,checkItems);
} catch (Exception ex) {
return new Result(false, MessageConstant.QUERY_CHECKITEM_FAIL);
}
}
TODO:后期再找找看看有没有可视化的权限配置平台。现在使用的这个代码维护成本太高。
14、批处理任务Spring Schedule
引入Spring Schedule
cron表达式可从网上查,根据自己需求稍加修改即可
Spring Schedule的功能不够强大,单线程,处理简单问题还可,处理复杂耗时问题可能会产生死锁等待情况,可使用第三方更为强大的定时任务工具(如 Quartz Scheduler)。
@Scheduled(cron = "0 0 12 * * ?")
public void redisScheduledJob() {
System.out.println("执行Redis清理缓存定时任务");
Set<String> diff = jedisPool.getResource().sdiff(RedisConstant.SETMEAL_PIC_RESOURCES, RedisConstant.SETMEAL_PIC_DB_RESOURCES);
if (diff != null && diff.size() > 0) {
for (String fileName : diff) {
QiniuUtils.deleteFromQiniu(fileName);
jedisPool.getResource().srem(RedisConstant.SETMEAL_PIC_RESOURCES, fileName);
}
}
}
参考资料
- java doc : https://docs.oracle.com/javase/8/docs/api/
- Spring:https://springdoc.cn/spring/index.html
- Spring MVC:https://springdoc.cn/spring/web.html#spring-web
- mybatis:https://mybatis.org/mybatis-3/zh_CN/configuration.html
- maven:https://maven.apache.org/pom.html
- freemarker:http://freemarker.foofun.cn/index.html
- echarts:https://echarts.apache.org/examples/zh/index.html
- axios:http://www.axios-js.com/zh-cn/docs/
以上。