声明:
本人提供这个 SpringMVC + Mybatis + Redis 的Demo 本着学习的态度,如果有欠缺和不足的地方,给予指正,并且多多包涵
本人QQ:912554737 也请有相同爱好的朋友fork我的项目,大家一起来搭建一个功能强大且使用简单的项目!无关问题请勿打扰
框架运行环境:
Maven版本:3.3.9
Eclipse版本:MARS.2
JDK版本:1.8
Tomcat版本:8.0.36
框架结构:框架全采用maven管理 所以源码只有180KB左右要是不会Maven就请自行补习
main:一些顶层的封装 包括了
annotation:自定义注解 实现的类似于shiro的权限 但是比较简单不喜勿喷 没shiro强大但是比他简单 然后自己结合RBAC+Redis+ Intercept技术实现的,对于自己目前遇到的权限问题我用这个也完全够用了。至于为什么没用shiro并不是shiro不强大只是不太喜欢那么复杂的用法(虽然已经很简单了) 我是懒得出奇的人 不过还是建议大家多去学习shiro这个权限框架毕竟连Spring都推荐使用Shiro RBAC是一个数据库的设计模型简单理解为:用户-角色-权限-资源 这里不再多说有个网友帖子写得不错 点击打开链接 拦截器什么滴也不多说spring的核心之一
entity:实体类的父类很简单自己看源码就行
log4j:这里重写了log4j的SMTPAppender这个类 首先说说这个类是干嘛的 他是用来发送邮件的当报错时邮件通知管理员,具体的请参考我的另一篇博客,上面有详细介绍点击打开链接
mapper:所有mapper的父类 默认提供了几个常用的方法
message:提示语相关的东西 都不知道咋描述 就是为了代码中不允许有一个中文和硬编码的存在 当然自己也可以改改实现国际化
result:针对返回结果的格式的统一封装 题外话:这个框架完全是为了提供接口而生 所以很多什么页面跳转什么滴都没做只做了返回json这块 连异常都封装成JSon了所以有其他要求的只能自己修改修改了
tools:看名字就知道一些常用的工具类 有什么身份证、经纬度、日期计算、DES和RSA加密、MD5之类的东西 具体请看代码类注解 我觉得我注解已经很多了
redis:这里重写了spring-data-redis里面的RedisCache、RedisCachemanager两个类和封装了一些其他的类目的就是为了实现redis的自动续期和单用户登录功能(一个用户同时只能在一个地方登录)如果需要实现不同平台的单用户登录需要自己小改一下 在缓存中多加个平台标识就行了
butler:这是web项目 名字不必纠结 介绍下包的作用
org.service:系统的一些服务类 这里只有定时任务(Spring Task)和Spring Mail邮件推送服务
org.system:这个看里面的包就知道干嘛用的了 就说说exception和interception这两个
org.system.exception:全局异常处理 这里把所有的程序异常捕捉并用JSON返回,以免其他客户端在服务器报错时出现一些数据无法解析造成的其他问题。至于项目的异常自己也做了配置和封装,在报错的时候可以异步发送邮件给管理员,具体实现请继续往下看
框架的大致结构就介绍到这里 下面说一下配置的xml 有人说多 其实还还好,不喜欢多的可以合一下,两个就能解决了,我分开是为了更明显的区分
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <display-name>butler</display-name> <!-- 声明Spring配置文件所在目录 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath*:applicationContext.xml, classpath*:spring-*.xml </param-value> </context-param> <!-- 声明IntrospectorCleanupListener监听器防止反复加载对象造成内存泄漏 --> <listener> <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class> </listener> <!-- 声明ContextLoaderListener监听器自动装配Spring配置文件信息 --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 全局UTF-8编码过滤器 --> <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> <filter-mapping> <filter-name>CharacterEncoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 配置Spring控制器 --> <servlet> <servlet-name>spring</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- HttpPutFormContentFilter 使put方法是也可以获取表单内的参数 这里我有特殊需求标准的Rest可以去掉--> <filter> <filter-name>HttpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class> </filter> <filter-mapping> <filter-name>HttpMethodFilter</filter-name> <servlet-name>spring</servlet-name> </filter-mapping> <servlet-mapping> <servlet-name>spring</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
<?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" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:c="http://www.springframework.org/schema/c" 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/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.2.xsd"> <!-- 配置需要交给spring扫描管理的包,一般是包括整个项目的java文件的父包(由context提供) --> <context:component-scan base-package="org.system,org.service.task" /> <!-- 属性文件读入,用于加密数据库配置文件 --> <bean id="propertyConfigurer" class="org.system.encrypt.DBConfigurer"> <property name="locations"> <list> <value>classpath:conf.properties</value> </list> </property> </bean> <!-- 配置需要交给spring扫描管理的文件,一般是项目的配置文件(由context提供) --> <context:property-placeholder location="classpath:conf.properties" /> <!-- 配置数据源 --> <bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> <!-- 基本配置 --> <property name="driverClassName" value="${db.driverClassName}" /> <property name="url" value="${db.url}" /> <property name="username" value="${db.user}" /> <property name="password" value="${db.password}" /> <!-- 初始化时建立物理连接的个数 --> <property name="initialSize" value="${db.initialSize}" /> <!-- 最小连接池数 --> <property name="minIdle" value="${db.minIdle}" /> <!-- 最大连接池数量 --> <property name="maxActive" value="${db.maxActive}" /> <!-- 配置获取连接等待超时的时间 --> <property name="maxWait" value="${db.maxWait}" /> </bean> <!-- 配置sqlSessionFactory(由mybatis-spring.jar提供支持) --> <bean name="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 配置数据源 --> <property name="dataSource" ref="dataSource" /> <!-- 配置mybatis的默认选项 --> <property name="configuration" ref="configuration" /> <!-- 配置拦截器用于Mybatis分页和总数查询,只实现Mysql 其他的可以参照 --> <property name="plugins" ref="PaginationInterceptor" /> </bean> <!-- mybatis默认选项配置 这里是因为我返回全是用Map所以如果数据库有字段为空就会使这个属性不显示 为了解决这个问题才需要配置这个--> <bean id="configuration" class="org.apache.ibatis.session.Configuration"> <property name="callSettersOnNulls" value="true"/> </bean> <!-- Mybatis分页拦截器 --> <bean name="PaginationInterceptor" class="org.system.intercept.PaginationInterceptor" /> <!-- Mapper接口所在包名,Spring会自动查找其下的Mapper --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="org.system.mapper..*" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> </bean> <!-- 支持注解事务模式 --> <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" /> <!-- 事务管理 --> <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 配置声明式事务 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="insert*" propagation="REQUIRED" read-only="false" /> <tx:method name="delete*" propagation="REQUIRED" read-only="false" /> <tx:method name="update*" propagation="REQUIRED" read-only="false" /> <tx:method name="get*" propagation="SUPPORTS" /> </tx:attributes> </tx:advice> <!-- 配置该声明式事务规则用于的切入点 步骤: 1.配置切入点 2.应用该声明式事务规则 --> <aop:config proxy-target-class="true" expose-proxy="true"> <!-- 配置切入点 --> <aop:pointcut id="transaction_pointcut" expression="execution(* org.system.service.impl..*.*(..))" /> <!-- 应用该声明式事务规则 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="transaction_pointcut" /> </aop:config> <!-- 异常拦截器 --> <bean id="exceptionHandler" class="org.system.exception.ExceptionResolver" /> <!-- Spring上下文工具类 --> <bean id="springContextUtil" class="org.service.utils.spring.SpringContextUtil" /> </beans>这里说一说有个数据库加密的东西在这里
<!-- 属性文件读入,用于加密数据库配置文件 --> <bean id="propertyConfigurer" class="org.system.encrypt.DBConfigurer"> <property name="locations"> <list> <value>classpath:conf.properties</value> </list> </property> </bean>意思就是用DBConfigurer这个类来解密conf.properties这个文件内的几个配置 就是采用了DES加密解密主要为了防止配置文件泄露数据库的信息暴露 如果不用直接注释掉就可以使用明文了 稍后会把properties相关文件贴出来
接下来是spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:task="http://www.springframework.org/schema/task" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.1.xsd "> <!-- 扫描要自动管理的包 --> <context:component-scan base-package="org.system.controller.impl" /> <!-- 静态资源文件路径设置 --> <mvc:resources location="/api/" mapping="/api/**" /> <!-- 对模型视图名称的解析,在请求时模型视图名称添加前后缀 系统内没有涉及到页面跳转所以基本无用--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/" /> <property name="suffix" value=".html" /> </bean> <!-- 校验拦截器 --> <bean id="validInterceptor" class="org.system.intercept.ValidInterceptor"/> <!-- 配置AOP切点 只拦截Controltroller --> <aop:config> <!--切入点 --> <aop:pointcut id="validPoint" expression="execution(public * org.system.controller.impl..*.*(..))" /> <!--在该切入点使用自定义拦截器 --> <aop:advisor pointcut-ref="validPoint" advice-ref="validInterceptor" /> </aop:config> <!--开启这个配置,spring才能识别@Scheduled注解 --> <task:scheduler id="scheduler" pool-size="10" /> <task:executor id="executor" pool-size="5-10" queue-capacity="200" rejection-policy="CALLER_RUNS" /> <task:annotation-driven executor="executor" scheduler="scheduler" /> <!-- 导入其他配置文件 --> <import resource="/org/system/config/*.xml" /> </beans>
</pre><span style="white-space:pre"></span><pre name="code" class="html"><?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 邮件发送器 --> <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"> <!-- 协议类型 --> <property name="protocol" value="${mail.smtp.type}" /> <!-- 服务器地址 --> <property name="host" value="${mail.smtp.host}" /> <!-- 服务器端口 --> <property name="port" value="${mail.smtp.port}" /> <!--用户名 --> <property name="username" value="${mail.username}" /> <!-- 用户密码 --> <property name="password" value="${mail.password}" /> <!-- 默认编码 --> <property name="defaultEncoding" value="UTF-8"/> <!-- 其他配置 --> <property name="javaMailProperties"> <props> <!--是否验证密码 --> <prop key="mail.smtp.auth">${mail.smtp.auth}</prop> <!-- 超时时间 --> <prop key="mail.smtp.timeout">${mail.smtp.timeout}</prop> <!-- SSL类配置 --> <prop key="mail.smtp.socketFactory.class">javax.net.ssl.SSLSocketFactory</prop> </props> </property> </bean> </beans>
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> <log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'> <!-- 控制台日志配置 --> <appender name="console" class="org.apache.log4j.ConsoleAppender"> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%-d{yyyy-MM-dd HH:mm:ss} [%p] [%c{3}] %m%n"/> </layout> </appender> <!-- info级别日志控制 --> <appender name="info" class="org.apache.log4j.DailyRollingFileAppender"> <!-- 文件路径 --> <param name="File" value="${catalina.home}/logs/${webapp.root}/infrastructure/info.log" /> <!-- 是否追加 --> <param name="Append" value="true" /> <!-- 最低日志级别 --> <param name="Threshold" value="INFO" /> <!-- 回滚日志后缀 --> <param name="datePattern" value="'.'yyyy-MM-dd" /> <!-- 是否启用缓冲 当缓冲区数据达到一定大小再写入文件 默认8K --> <!-- <param name="BufferedIO" value="true"/> <param name="BufferSize" value="8192"/> --> <!-- 日志输出布局 --> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%-d{yyyy-MM-dd HH:mm:ss} [%p] [%c] %m%n" /> </layout> <!--限制输出级别 --> <filter class="org.apache.log4j.varia.LevelRangeFilter"> <!-- 最小级别 --> <param name="LevelMax" value="INFO" /> <!-- 最大级别 --> <param name="LevelMin" value="INFO" /> </filter> </appender> <!-- warn级别日志控制 --> <appender name="warn" class="org.apache.log4j.DailyRollingFileAppender"> <!-- 文件路径 --> <param name="File" value="${catalina.home}/logs/${webapp.root}/infrastructure/warn.log" /> <!-- 是否追加 --> <param name="Append" value="true" /> <!-- 最低日志级别 --> <param name="Threshold" value="WARN" /> <!-- 回滚日志后缀 --> <param name="datePattern" value="'.'yyyy-MM-dd" /> <!-- 是否启用缓冲 当缓冲区数据达到一定大小再写入文件 默认8K --> <!-- <param name="BufferedIO" value="true"/> <param name="BufferSize" value="8192"/> --> <!-- 日志输出布局 --> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%-d{yyyy-MM-dd HH:mm:ss} [%p] [%c] %m%n" /> </layout> <!--限制输出级别 --> <filter class="org.apache.log4j.varia.LevelRangeFilter"> <!-- 最小级别 --> <param name="LevelMax" value="WARN" /> <!-- 最大级别 --> <param name="LevelMin" value="WARN" /> </filter> </appender> <!-- error级别日志控制 --> <appender name="error" class="org.apache.log4j.DailyRollingFileAppender"> <!-- 文件路径 --> <param name="File" value="${catalina.home}/logs/${webapp.root}/infrastructure/error.log" /> <!-- 是否追加 --> <param name="Append" value="true" /> <!-- 最低日志级别 --> <param name="Threshold" value="ERROR" /> <!-- 回滚日志后缀 --> <param name="datePattern" value="'.'yyyy-MM-dd" /> <!-- 是否启用缓冲 当缓冲区数据达到一定大小再写入文件 默认8K --> <!-- <param name="BufferedIO" value="true"/> <param name="BufferSize" value="8192"/> --> <!-- 日志输出布局 --> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%-d{yyyy-MM-dd HH:mm:ss} [%p] [%c] %m%n" /> </layout> <!--限制输出级别 --> <filter class="org.apache.log4j.varia.LevelRangeFilter"> <!-- 最小级别 --> <param name="LevelMax" value="ERROR" /> <!-- 最大级别 --> <param name="LevelMin" value="ERROR" /> </filter> </appender> <!-- 发送日志文件到邮件 --> <appender name="email" class="org.main.log4j.SMTPAppender"> <!-- 最小输出日志级别 --> <param name="Threshold" value="ERROR" /> <!-- 缓冲Event个数默认512 当达到了多少个就覆盖以前的Event 而非网络上所说的缓冲数据大小 不看源码坑出血 --> <param name="BufferSize" value="512" /> <!-- 错误个数默认一个 即出现错误就发送邮件 --> <param name="ErrorSize" value="5" /> <!-- 发送邮件的协议类型 --> <param name="SMTPProtocol" value="smtps" /> <!-- 发送日志的邮箱 --> <param name="From" value="***@****.com" /> <!-- 发送日志邮箱SMTP --> <param name="SMTPHost" value="smtp.qiye.163.com" /> <!-- 发送端口 目前只测试过QQ和163邮箱 其他邮箱不知道行不行可能会有写问题 到时候只能自己解决了 --> <param name="SMTPPort" value="465" /> <!-- 发送日志的邮箱用户名 --> <param name="SMTPUsername" value="***@***.com" /> <!-- 发送日志的邮箱密码 --> <param name="SMTPPassword" value="yVKzhrbaMaNQ2kEE" /> <!-- 日志邮件主题 --> <param name="Subject" value="后台系统框架异常提醒,请尽快处理" /> <!-- 日志邮件接收者 --> <param name="To" value="***@qq.com" /> <!-- 抄送邮件接受者 --> <!-- <param name="Bcc" value="***@qq.com"/> --> <!-- 日志输出布局 --> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%-d{yyyy-MM-dd HH:mm:ss} [%p] [%c] %m%n" /> </layout> </appender> <!-- 异步发送邮件设置 --> <appender name="asyncout" class="org.apache.log4j.AsyncAppender"> <appender-ref ref="email" /> </appender> <!-- 需要特殊处理的日志级别 --> <logger name="org.springframework"> <level value="WARN" /> </logger> <logger name="org.system"> <level value="DEBUG" /> </logger> <!-- 根路径设置 --> <root> <level value="INFO" /> <appender-ref ref="console" /> <appender-ref ref="info" /> <appender-ref ref="warn" /> <appender-ref ref="error" /> <appender-ref ref="asyncout" /> </root> </log4j:configuration>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 指定自己定义的validator --> <mvc:annotation-driven validator="validator"> <mvc:message-converters register-defaults="true"> <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter"> <property name="supportedMediaTypes" value="application/json;charset=UTF-8" /> <property name="features"> <array> <value>WriteMapNullValue</value> <value>WriteNullStringAsEmpty</value> </array> </property> </bean> </mvc:message-converters> </mvc:annotation-driven> <!-- 以下 validator ConversionService 在使用 mvc:annotation-driven 会 自动注册 --> <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"> <property name="providerClass" value="org.hibernate.validator.HibernateValidator" /> <!-- 如果不加默认到 使用classpath下的 ValidationMessages.properties --> <property name="validationMessageSource" ref="messageSource" /> </bean> <!-- 国际化的消息资源文件(本系统中主要用于显示/错误消息定制) --> <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <property name="basenames"> <list> <value>classpath:org/system/config/valid</value> </list> </property> <property name="useCodeAsDefaultMessage" value="false" /> <property name="defaultEncoding" value="UTF-8" /> <property name="cacheSeconds" value="60" /> </bean> <!-- <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**" /> <bean class="org.system.intercept.SecurityInterceptor" /> </mvc:interceptor> </mvc:interceptors> --> </beans>最后是conf.properties
这里加密的几个对应得明文分别是
url:jdbc:mysql://localhsot:3306/butler?useUnicode=true&characterEncoding=utf8&mysqlEncoding=utf8
user:root
password:123456
如果对应的没变化就不需要改 后面提供的框架源码这块都会是错的请谅解
#数据库连接配置 db.driverClassName=com.mysql.jdbc.Driver db.url=VjG9ty54tspjXih4i7GttGhMgOjH9n9fK+PmEKzqTNldvpAhYfSUuRBTf3b++nhyUZERvK3jb0VqFpnhJF0Whf1k7QSvjxxY1FaNlCT+Vz5cwk3kNBUfUHZ5EcLPNLOl db.user=0q87vZtbVbk= db.password=XfSWPx0Kqvg= db.initialSize=2 db.minIdle=2 db.maxActive=10 db.maxWait=6000 #邮件服务器设置 mail.smtp.host=smtps mail.smtp.host=smtp.qiye.163.com mail.smtp.port=465 mail.smtp.auth=true mail.smtp.timeout=25000 mail.username=******@****.com mail.password=******* #Redis缓存配置 redis.minIdle=5 redis.maxIdle=100 redis.maxTotal=300 redis.maxWaitMillis=3000 redis.testOnBorrow=true redis.host=127.0.0.1 redis.port=6379 redis.password=yxt123 redis.database=1
配置文件到这里都差不多了 文件内注解都有应该都能看懂至于框架的详细功能和实现方式就下次有时间在写!
接着上次说这里懒就把readme.rd里面的东西拷出来了
声明: 本人提供这个 SpringMvc + Mybatis + Redis 的Demo 本着学习的态度,如果有欠缺和不足的地方,给予指正,并且多多包涵. 数据库脚本: butler.sql 只提供了基本的权限表和用户表 想要扩展请自行百度RBAC基本上你能想到的权限都可以通过他实现这也是目前比较主流的权限设计 框架使用环境: Maven:3.3.9 Eclipse:MARS.2 JDK:1.8 Tomcat:8.0.36 框架包含内容: 一、权限控制 权限控制起初打算使用Shiro来作为权限控制的组件,研究了下Shiro确实很强大,难怪Spring都放弃自家的组件使用它来作为权限控制,体积小功能也比较强大 ,上手简单。 但是这shiro我赞同他强大但是并不代表我要用它,我只参照了他的两个注解@RequiresPermissions、@RequiresRoles这两个注解都是作用在方法上的 @RequiresRoles:在本系统代表必须具备某角色才能访问该方法 如果满足角色要求将不会继续判断权限 @RequiresPermissions:在本系统代表必须具备某权限才能访问该方法,它在角色之后 若果两个注解都有且不满足角色要求才会查询权限 这里权限和数据库的有一点差异 它对应数据库的method_source这张表 这两个注解可以分开用也可以在一起使用 建议不要都不使用 这样就脱离的控制 虽然我在权限拦截器中控制了如果没这两个权限也不允许访问 但是拦截器大家都知道取决于配置 假如你配置这个拦截器拦截路径为 "/**" 那么是没问题的 但是如果系统分几种用户,什么管理员和用户都在一起的话可能我们会对路径有所规范。管理员访问方法的路径假如为 “/admin/**”,用户访问方法的路径假如为 “/user/**” 这时候可能就对应两个拦截器分别控制 各自的权限,拦截路径就不能是 “/**”了 那么可能 “/other/**”我们就没法掌控了 所以在使用的时候需要注意一些规范或者加一个全路径的拦截器来处理这种情况,我是采用的规范方法路径的方式来解决这问题,一般也要求下面 开发人员一定按照要求定义接口方法 至于实现思路很简单,利用拦截器接管所有请求,然后根据用户携带的token(登录时会返回一个token作为用户的合法令牌)从缓存中获取用户信息并从数据库加载出改用户的角色和权限数据然后根据反射获取访问该方法的注解的值 比较是否满足 满足则继续执行 不满足则提示相关操作无权限之类的,这里我是把权限信息首次加载后放入缓存,系统内的高热点数据建议大家都放入缓存效率高得多,然后有地方修改对应用户的权限时清除改用户的权限缓存,具体 的请参照Spring Cache这里完全把Redis和Spring Cache结合在一起了 完全不需要代码来操作redis(除了业务中需要操作缓存来做什么事情) 数据库采用的是RBAC模型设计,这里不多说,博客中也提到对应的网站 大家不懂得可以去了解了解 二、日志异常提醒 日志采用的是slf4j,为啥配置文件就是log4j.xml大家可以百度一下这两个的关系,要求高一点的可以使用logback,基本的配置没啥技术难度,无非就是集成了网络上很多大咖的建议的综合体。 日志我除了控制台打印的之外,其他对INFO、WARN、ERROR三种日志进行了分类操作 每种日志分类写入并按天分割 然后对于错误级别及以上日志进行了特殊处理,发生错误及以上级别的日志都会进行邮件提醒,这里踩了很多坑,网络上也有教程 但是大多数都是同步发送邮件(SMTP其实超慢滴,同步你就死了)还有就是网络上的配置都是一个错误 发送一封邮件(这要是错误率低的系统还好,要是不成熟的话,每天报个千儿八百的错那你就哭了),你去百度一定会百度到很多说啥配置BufferSize就可以控制了之类的问题,跟你们打包票的说,这都是胡乱转载人家的帖子,人家原文 说的是写入日志的配置是这个意思(xml内我打了注释那几个)但是发送日志源码的意思是缓冲多少个Event事件(一个错一个事件)然后超过这个BufferSize(默认512)设定的值之后覆盖之前的事件,就这个意思而已,人家判断发送邮件 的条件是event的级别是否为error。 所以这里我重写了他的SMTPAppender类,实现了ErrorSize参数,控制当多少个event事件发送一次,具体的请参考源码 三、Spring+Redis整合 这里就简单带过就是,重写了spring-data-redis里面的几个类,目的为了实现redis的访问自动延期,和为单用户登录提供了些方法 四、单用户登录(同一个用户只能在一个地方登录) 这里大家最好看源码,我登录里面调用了,自己跟踪去看看怎实现的(轮询缓存也可以实现,但是呵呵) 五、RSA分段加密 这个为啥要单独提出来呢!目前JDK中提供的最牛的加密算法了,虽然ECC比这更牛,可以JDK没整完,具体类在tools项目的rsa包内,类中提供了密钥对的生成、加密、解密、和转化为.Net格式的密钥等方法,并采用分段加密解密技术 突破了加密内容的长度限制,但是内容越多加密越慢!视场景而使用。 六、Spring Mail 这个网络上都有配置,我只是服装了些实体类简化调用 七、实体类校验 采用的是Hibernate Validator校验,但是它使用都尼玛要在方法中每个都去判断,这对我这类懒得XX的人来说肯定受不了,于是就用拦截器统一处理了,具体参照org.system.intercept.ValidInterceptor.java 八、自动分页 org.system.intercept.PaginationInterceptor.java 不多说拦截的是mybatis,如果设置了page和rows这两个分页参数那么我就和先查询总数放入实体类的total属性中并对接下了的查询limit操作,就这样 九、全局异常处理 org.system.exception.ExceptionResolver.java拦截所以异常并封装成json格式,没做全局页面的配置,拦截器中是根据返回是否是json来判断的,不是json原来改怎么做就怎么做不影响 十、其他 好像没啥可以拿出来说的了,哦,对了,json解析器换成了fastjson,原因嘛百度fastjson和jackson就知道了,这里不是快了一点点,还有也配置了Spring Task 简单的定时任务,org.service.task.ServiceTask.java这里面有使用demo 其他的很多都在源码配置当中有说明,基本没啥工作流啊啥玩意的东西应该都够用了,不够只能自己加了
源码地址:觉得还行回复支持一下让更多人看到!