提交 1ce853e1 编写于 作者: Y yingjun

1.添加Spring AOP支持

2.代码完善
上级 8de9aab4
#优雅的SSM架构(Spring+SpringMVC+Mybatis)
- Maven
- Spring(IOC DI 声明式事务处理)
- Spring(IOC DI AOP 声明式事务处理)
- SpringMVC(支持Restful风格)
- Hibernate Validate(参数校验)
- Mybatis(最少配置方案)
- Quartz时间调度
- Redis缓存(ProtoStuff序列化)
- **[Redis Sentinel主从高可用方案](http://wosyingjun.iteye.com/blog/2289593)**
- **[Druid(数据源配置 sql防注入 sql性能监控)](http://wosyingjun.iteye.com/blog/2306139)**
- [Redis Sentinel主从高可用方案](http://wosyingjun.iteye.com/blog/2289593)
- [Redis Cluster集群高可用方案](http://wosyingjun.iteye.com/blog/2289220)
- [Druid(数据源配置 sql防注入 sql性能监控)](http://wosyingjun.iteye.com/blog/2306139)
- 统一的异常处理
- JSP JSTL
- JSP JSTL JavaScript
- Sping Shiro权限控制(待完善)
###**架构图:**
![](http://i.imgur.com/EvH40td.png)
\ No newline at end of file
......@@ -15,17 +15,6 @@
</sourceRoots>
</configuration>
</facet>
<facet type="Spring" name="Spring">
<configuration>
<fileset id="fileset" name="Spring Application Context" removed="false">
<file>file://$MODULE_DIR$/src/main/resources/spring/spring-service.xml</file>
<file>file://$MODULE_DIR$/src/main/resources/spring/spring-redis.xml</file>
<file>file://$MODULE_DIR$/src/main/resources/spring/spring-web.xml</file>
<file>file://$MODULE_DIR$/src/main/resources/spring/spring-quartz.xml</file>
<file>file://$MODULE_DIR$/src/main/resources/spring/spring-dao.xml</file>
</fileset>
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/target/classes" />
......@@ -50,6 +39,8 @@
<orderEntry type="library" name="Maven: org.quartz-scheduler:quartz:1.8.5" level="project" />
<orderEntry type="library" name="Maven: javax.transaction:jta:1.1" level="project" />
<orderEntry type="library" name="Maven: com.alibaba:fastjson:1.2.8" level="project" />
<orderEntry type="library" name="Maven: org.hibernate:hibernate-validator:4.2.0.Final" level="project" />
<orderEntry type="library" name="Maven: javax.validation:validation-api:1.0.0.GA" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-core:4.2.0.RELEASE" level="project" />
<orderEntry type="library" name="Maven: commons-logging:commons-logging:1.2" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-beans:4.2.0.RELEASE" level="project" />
......
......@@ -71,7 +71,12 @@
<artifactId>fastjson</artifactId>
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.2.0.Final</version>
</dependency>
<!--common end -->
<!--spring start -->
......
package com.yingjun.ssm.aop;
import com.yingjun.ssm.dto.BaseResult;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
/**
* @author yingjun
*
* 采用AOP的方式处理参数问题。
*/
@Component
@Aspect
public class BindingResultAop {
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
@Pointcut("execution(* com.yingjun.ssm.web.*.*(..))")
public void aopMethod(){}
@Around("aopMethod()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable{
LOG.info("before method invoking!");
BindingResult bindingResult = null;
for(Object arg:joinPoint.getArgs()){
if(arg instanceof BindingResult){
bindingResult = (BindingResult) arg;
}
}
if(bindingResult != null){
if(bindingResult.hasErrors()){
String errorInfo="["+bindingResult.getFieldError().getField()+"]"+bindingResult.getFieldError().getDefaultMessage();
return new BaseResult<Object>(false, errorInfo);
}
}
return joinPoint.proceed();
}
}
package com.yingjun.ssm.dto;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.io.Serializable;
/**
......@@ -8,6 +10,7 @@ import java.io.Serializable;
*
* ajax 请求的返回类型封装JSON结果
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public class BaseResult<T> implements Serializable {
......
package com.yingjun.ssm.entity;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.yingjun.ssm.util.CustomDateSerializer;
import com.yingjun.ssm.validator.Not999;
import javax.validation.constraints.Min;
import java.util.Date;
public class Goods {
@Min(900)
@Not999 //这个为自定义的验证标签
private long goodsId;
private String title;
......@@ -14,8 +22,12 @@ public class Goods {
private int number;
//这里展示了jackson封装好的以及自定义的对时间格式的转换方式
//后续对于一些复杂的转换可以自定义转换方式
@JsonFormat(pattern = "yyyy-MM-dd")
private Date createTime;
@JsonSerialize(using = CustomDateSerializer.class)
private Date updateTime;
public long getGoodsId() {
......
......@@ -39,6 +39,7 @@ public class GlobalExceptionResolver implements HandlerExceptionResolver {
writer.write(JSON.toJSONString(result));
writer.flush();
} catch (Exception e) {
LOG.error("Exception:",e);
}
return null;
}
......
package com.yingjun.ssm.service.impl;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.yingjun.ssm.cache.RedisCache;
import com.yingjun.ssm.dao.GoodsDao;
import com.yingjun.ssm.dao.OrderDao;
......@@ -20,6 +9,16 @@ import com.yingjun.ssm.entity.User;
import com.yingjun.ssm.enums.ResultEnum;
import com.yingjun.ssm.exception.BizException;
import com.yingjun.ssm.service.GoodsService;
import org.apache.commons.collections.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class GoodsServiceImpl implements GoodsService {
......@@ -38,14 +37,14 @@ public class GoodsServiceImpl implements GoodsService {
public List<Goods> getGoodsList(int offset, int limit) {
String cache_key = RedisCache.CAHCENAME + "|getGoodsList|" + offset + "|" + limit;
List<Goods> result_cache = cache.getListCache(cache_key, Goods.class);
if (result_cache == null) {
if (result_cache != null) {
LOG.info("get cache with key:" + cache_key);
} else {
// 缓存中没有再去数据库取,并插入缓存(缓存时间为60秒)
result_cache = goodsDao.queryAll(offset, limit);
cache.putListCacheWithExpireTime(cache_key, result_cache, RedisCache.CAHCETIME);
LOG.info("put cache with key:" + cache_key);
return result_cache;
} else {
LOG.info("get cache with key:" + cache_key);
}
return result_cache;
}
......
package com.yingjun.ssm.util;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 自定义返回JSON 数据格中日期格式化处理
*
* @author yingjun
*
*/
public class CustomDateSerializer extends JsonSerializer<Date> {
@Override
public void serialize(Date value, JsonGenerator jsonGenerator, SerializerProvider provider) throws IOException, JsonProcessingException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
jsonGenerator.writeString(sdf.format(value));
}
}
package com.yingjun.ssm.validator;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 自定义返回JSON 数据格中日期格式化处理
*
* @author yingjun
*
*/
public class CustomDateSerializer extends JsonSerializer<Date> {
@Override
public void serialize(Date value, JsonGenerator jsonGenerator, SerializerProvider provider) throws IOException, JsonProcessingException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
jsonGenerator.writeString(sdf.format(value));
}
}
package com.yingjun.ssm.validator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
* 自定义validator标签(和 hibernate validator组合使用)
*
* @author yingjun
*
*/
@Constraint(validatedBy = Not999Validator.class) // 具体的实现
@Target({ java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD })
@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@Documented
public @interface Not999 {
// 提示信息,可以写死,可以填写国际化的key
String message() default "{com.yingjun.ssm.validator.not999}";
// 下面这两个属性必须添加
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
package com.yingjun.ssm.validator;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class Not999Validator implements ConstraintValidator<Not999, Long> {
@Override
public void initialize(Not999 arg0) {
}
@Override
public boolean isValid(Long vaule, ConstraintValidatorContext context) {
if(vaule==999){
return false;
}else{
return true;
}
}
}
......@@ -10,44 +10,54 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.validation.Valid;
import java.util.List;
@Controller
@RequestMapping("/goods")
public class GoodsController {
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
@Autowired
private GoodsService goodsService;
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
@RequestMapping(value = "/list", method = RequestMethod.GET)
public String list(Model model, Integer offset, Integer limit) {
LOG.info("invoke----------/goods/list");
offset = offset == null ? 0 : offset;//默认便宜0
limit = limit == null ? 50 : limit;//默认展示50条
List<Goods> list = goodsService.getGoodsList(offset, limit);
model.addAttribute("goodslist", list);
return "goodslist";
}
@RequestMapping(value = "/{goodsId}/buy", method = RequestMethod.POST, produces = { "application/json;charset=UTF-8" })
@ResponseBody
public BaseResult<Object> buy(@CookieValue(value = "userPhone", required = false) Long userPhone,
@PathVariable("goodsId") Long goodsId){
LOG.info("invoke----------/"+goodsId+"/buy userPhone:"+userPhone);
if (userPhone == null||goodsId==null) {
return new BaseResult<Object>(false, ResultEnum.INVALID_USER.getMsg());
}
try {
goodsService.buyGoods(userPhone, goodsId, false);
}catch (BizException e) {
return new BaseResult<Object>(false, e.getMessage());
}catch (Exception e) {
return new BaseResult<Object>(false, ResultEnum.INNER_ERROR.getMsg());
}
return new BaseResult<Object>(true, null);
}
@Autowired
private GoodsService goodsService;
@RequestMapping(value = "/list", method = RequestMethod.GET)
public String list(Model model, Integer offset, Integer limit) {
LOG.info("invoke----------/goods/list");
offset = offset == null ? 0 : offset;//默认便宜0
limit = limit == null ? 50 : limit;//默认展示50条
List<Goods> list = goodsService.getGoodsList(offset, limit);
model.addAttribute("goodslist", list);
return "goodslist";
}
@RequestMapping(value = "/{goodsId}/buy", method = RequestMethod.GET, produces = {"application/json;charset=UTF-8"})
@ResponseBody
public BaseResult<Object> buy(@CookieValue(value = "userPhone", required = false) Long userPhone,
/*@PathVariable("goodsId") Long goodsId*/ @Valid Goods goods, BindingResult result) {
LOG.info("invoke----------/" + goods.getGoodsId() + "/buy userPhone:" + userPhone);
if (userPhone == null) {
return new BaseResult<Object>(false, ResultEnum.INVALID_USER.getMsg());
}
//Valid 参数验证(这里注释掉,采用AOP的方式验证,见BindingResultAop.java)
//if (result.hasErrors()) {
// String errorInfo = "[" + result.getFieldError().getField() + "]" + result.getFieldError().getDefaultMessage();
// return new BaseResult<Object>(false, errorInfo);
//}
try {
goodsService.buyGoods(userPhone, goods.getGoodsId(), false);
} catch (BizException e) {
return new BaseResult<Object>(false, e.getMessage());
} catch (Exception e) {
return new BaseResult<Object>(false, ResultEnum.INNER_ERROR.getMsg());
}
return new BaseResult<Object>(true, null);
}
}
javax.validation.constraints.AssertFalse.message = \u53EA\u80FD\u4E3Afalse
javax.validation.constraints.AssertTrue.message = \u53EA\u80FD\u4E3Atrue
javax.validation.constraints.DecimalMax.message = \u5FC5\u987B\u5C0F\u4E8E\u6216\u7B49\u4E8E{value}
javax.validation.constraints.DecimalMin.message = \u5FC5\u987B\u5927\u4E8E\u6216\u7B49\u4E8E{value}
javax.validation.constraints.Digits.message = \u6570\u5B57\u7684\u503C\u8D85\u51FA\u4E86\u5141\u8BB8\u8303\u56F4(\u53EA\u5141\u8BB8\u5728{integer}\u4F4D\u6574\u6570\u548C{fraction}\u4F4D\u5C0F\u6570\u8303\u56F4\u5185)
javax.validation.constraints.Future.message = \u9700\u8981\u662F\u4E00\u4E2A\u5C06\u6765\u7684\u65F6\u95F4
javax.validation.constraints.Max.message = \u6700\u5927\u4E0D\u80FD\u8D85\u8FC7{value}
javax.validation.constraints.Min.message = \u6700\u5C0F\u4E0D\u80FD\u5C0F\u4E8E{value}
javax.validation.constraints.NotNull.message = \u4E0D\u80FD\u4E3Anull
javax.validation.constraints.Null.message = \u5FC5\u987B\u4E3Anull
javax.validation.constraints.Past.message = \u9700\u8981\u662F\u4E00\u4E2A\u8FC7\u53BB\u7684\u4E8B\u4EF6
javax.validation.constraints.Pattern.message = \u9700\u8981\u5339\u914D\u6B63\u5219\u8868\u8FBE\u5F0F"{regexp}"
javax.validation.constraints.Size.message = \u4E2A\u6570\u5FC5\u987B\u5728{min}\u548C{max}\u4E4B\u95F4
org.hibernate.validator.constraints.CreditCardNumber.message = \u4E0D\u5408\u6CD5\u7684\u4FE1\u7528\u5361\u53F7\u7801
org.hibernate.validator.constraints.Email.message = \u4E0D\u662F\u4E00\u4E2A\u5408\u6CD5\u7684\u7535\u5B50\u90AE\u4EF6\u5730\u5740
org.hibernate.validator.constraints.Length.message = \u957F\u5EA6\u9700\u8981\u5728{min}\u548C{max}\u4E4B\u95F4
org.hibernate.validator.constraints.NotBlank.message = \u4E0D\u80FD\u4E3A\u7A7A
org.hibernate.validator.constraints.NotEmpty.message = \u4E0D\u80FD\u4E3A\u7A7A
org.hibernate.validator.constraints.Range.message = \u9700\u8981\u5728{min}\u548C{max}\u4E4B\u95F4
org.hibernate.validator.constraints.SafeHtml.message = \u53EF\u80FD\u6709\u4E0D\u5B89\u5168\u7684HTML\u5185\u5BB9
org.hibernate.validator.constraints.ScriptAssert.message = \u6267\u884C\u811A\u672C\u8868\u8FBE\u5F0F"{script}"\u6CA1\u6709\u80FD\u591F\u5F97\u5230true
org.hibernate.validator.constraints.URL.message = \u9700\u8981\u662F\u4E00\u4E2A\u5408\u6CD5\u7684URL
com.yingjun.ssm.validator.not999 =\u4E0D\u80FD\u7B49\u4E8E999
\ No newline at end of file
......@@ -6,13 +6,27 @@ redis.pool.maxWaitMillis=5000
redis.pool.testOnBorrow=true
#redis \u5355\u8282\u70B9\u914D\u7F6E
redis.ip=192.168.11.99
redis.ip=192.168.xx.xxx
redis.port=6379
#redis \u9AD8\u53EF\u7528\u914D\u7F6E
#redis \u9AD8\u53EF\u7528\u914D\u7F6E(\u57FA\u4E8Eredis sentinel)
#sentinel1.ip=192.168.11.100
#sentinel1.port=63791
#sentinel2.ip=192.168.11.101
#sentinel2.port=63792
#sentinel3.ip=192.168.11.102
#sentinel3.port=63792
\ No newline at end of file
#sentinel3.port=63792
#redis \u96C6\u7FA4\u9AD8\u53EF\u7528\u914D\u7F6E\uFF08\u57FA\u4E8Ereids cluster\uFF09
#redis.ip1=192.168.11.100
#redis.port1=7111
#redis.ip2=192.168.11.101
#redis.port2=7112
#redis.ip3=192.168.11.102
#redis.port3=7113
#redis.ip4=192.168.11.103
#redis.port4=7114
#redis.ip5=192.168.11.104
#redis.port5=7115
#redis.ip6=192.168.11.105
#redis.port6=7116
\ No newline at end of file
......@@ -20,6 +20,41 @@
<property name="testOnBorrow" value="${redis.pool.testOnBorrow}" />
</bean>
<!-- JedisCluster 集群高可用配置 -->
<!--<bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
<constructor-arg index="0">
<set>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="${redis.ip1}" />
<constructor-arg index="1" value="${redis.port1}" type="int" />
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="${redis.ip2}" />
<constructor-arg index="1" value="${redis.port2}" type="int" />
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="${redis.ip3}" />
<constructor-arg index="1" value="${redis.port3}" type="int" />
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="${redis.ip4}" />
<constructor-arg index="1" value="${redis.port4}" type="int" />
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="${redis.ip5}" />
<constructor-arg index="1" value="${redis.port5}" type="int" />
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="${redis.ip6}" />
<constructor-arg index="1" value="${redis.port6}" type="int" />
</bean>
</set>
</constructor-arg>
<constructor-arg index="1" value="2000" type="int"></constructor-arg>
<constructor-arg index="2" value="100" type="int"></constructor-arg>
<constructor-arg index="3" ref="jedisPoolConfig"></constructor-arg>
</bean>-->
<!--redis Sentinel主从高可用方案配置 -->
<!-- <bean id="sentinelConfiguration" class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
<property name="master">
......
......@@ -3,39 +3,43 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置springMVC-->
<!--1:开始springMVC注解模式-->
<!-- 激活组件扫描功能,扫描aop的相关组件组件 -->
<context:component-scan base-package="com.yingjun.ssm.aop"/>
<!-- 启动对@AspectJ注解的支持 -->
<aop:aspectj-autoproxy proxy-target-class="true" />
<!--简化配置:
1、自动注册DefaultAnnotationHandlerMapping,AnnotationMethodHandlerAdapter
2、提供一系列:数据绑定,数字和日期的format,@NumberFormat,@DataTimeFormat,xml,json默认读写支持
-->
<mvc:annotation-driven/>
<!--2:静态资源默认servlet配置
<!--静态资源默认servlet配置
1、加入对静态资源的处理:js,css,gif,png
2、允许使用"/"做整体映射
-->
<mvc:default-servlet-handler/>
<!--3:配置JSP 显示ViewResolver-->
<!--配置JSP 显示ViewResolver-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--4:扫描web相关的bean-->
<!--扫描web相关的controller-->
<context:component-scan base-package="com.yingjun.ssm.web"/>
<!-- 全局异常捕捉 -->
<!--全局异常捕捉 -->
<bean class="com.yingjun.ssm.exception.GlobalExceptionResolver" />
</beans>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册