Prechádzať zdrojové kódy

Merge remote-tracking branch 'origin/master'

wangwei 7 rokov pred
rodič
commit
fefd980895
25 zmenil súbory, kde vykonal 1211 pridanie a 53 odobranie
  1. 46 0
      CODE_OF_CONDUCT.md
  2. 18 0
      CONTRIBUTING.md
  3. 1 4
      README.md
  4. 58 0
      docs/dev-guide/README.md
  5. 1 0
      docs/dev-guide/autz/README.md
  6. 79 0
      docs/dev-guide/crud/README.md
  7. 0 0
      docs/dev-guide/crud/custom-field.md
  8. 63 0
      docs/dev-guide/logging/README.md
  9. 6 0
      docs/help/submit-code-issues.md
  10. 1 2
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/AuthorizedToken.java
  11. 12 1
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/GeneratedToken.java
  12. 11 0
      hsweb-commons/hsweb-commons-controller/README.md
  13. 7 5
      hsweb-commons/hsweb-commons-dao/hsweb-commons-dao-mybatis/src/main/java/org/hswebframework/web/dao/mybatis/builder/EasyOrmSqlBuilder.java
  14. 47 5
      hsweb-commons/hsweb-commons-service/hsweb-commons-service-simple/src/main/java/org/hswebframework/web/service/EnableCacheGenericEntityService.java
  15. 6 1
      hsweb-concurrent/hsweb-concurrent-cache/pom.xml
  16. 22 0
      hsweb-concurrent/hsweb-concurrent-cache/src/main/java/org/hswebframework/web/cache/FixUseSupperClassAutoConfiguration.java
  17. 22 0
      hsweb-concurrent/hsweb-concurrent-cache/src/main/java/org/hswebframework/web/cache/monitor/MonitorSupportCache.java
  18. 296 0
      hsweb-concurrent/hsweb-concurrent-cache/src/main/java/org/hswebframework/web/cache/spring/fix/FixUseSupperClassAnnotationParser.java
  19. 17 0
      hsweb-concurrent/hsweb-concurrent-cache/src/main/java/org/hswebframework/web/cache/spring/fix/FixUseSupperClassCacheAnnotationParser.java
  20. 193 0
      hsweb-concurrent/hsweb-concurrent-cache/src/main/java/org/hswebframework/web/cache/spring/fix/FixUseSupperClassCacheOperationSource.java
  21. 198 0
      hsweb-concurrent/hsweb-concurrent-cache/src/main/java/org/hswebframework/web/cache/spring/fix/FixUseSupperClassFallbackCacheOperationSource.java
  22. 13 13
      hsweb-starter/hsweb-spring-boot-starter/src/main/java/org/hswebframework/web/starter/SystemInitializeAutoConfiguration.java
  23. 9 0
      hsweb-starter/hsweb-spring-boot-starter/src/main/java/org/hswebframework/web/starter/init/SystemInitialize.java
  24. 1 1
      hsweb-system/hsweb-system-organizational/hsweb-system-organizational-starter/src/main/resources/hsweb-starter.js
  25. 84 21
      pom.xml

+ 46 - 0
CODE_OF_CONDUCT.md

@@ -0,0 +1,46 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at admin@hsweb.me. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
+
+[homepage]: http://contributor-covenant.org
+[version]: http://contributor-covenant.org/version/1/4/

+ 18 - 0
CONTRIBUTING.md

@@ -0,0 +1,18 @@
+# 贡献你的代码
+1. fork 本仓库
+2. 修改,增加代码
+3. 执行`mvn test`通过
+4. 提交`pull request`
+5. 坐等审查
+6. 合并
+
+# BUG
+如果知道导致bug的位置,你可以直接修改后`pull request`,也可以[勾出bug所在位置](docs/help/submit-code-issues.md),然后提交[issues](https://github.com/hs-web/hsweb-framework/issues/new)
+并 `@zhou-hao`,label 选择`bug`.我们会尽快解决.
+
+# 需求&优化
+你可以通过issues提交你希望`hsweb`增加的特性以及功能优化,并可以在 [projects](https://github.com/hs-web/hsweb-framework/projects)中查看`hsweb`的开发进展以及计划.
+
+# 社区&交流
+你可以通过提交`issues`或者加入官方QQ群:[515649185](http://shang.qq.com/wpa/qunwpa?idkey=3d66b5dd14991d7645af694e7649b373f3a9ce1216131094c78cb2348d542c41)
+以及发送邮件和我们取得联系.

+ 1 - 4
README.md

@@ -4,7 +4,7 @@
 [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg?style=flat-square)](https://www.apache.org/licenses/LICENSE-2.0.html)
 [![Insight.io](https://www.insight.io/repoBadge/github.com/hs-web/hsweb-framework)](https://insight.io/github.com/hs-web/hsweb-framework)
 
-[Features](FEATURES.md)
+ [贡献代码](CONTRIBUTING.md)  [开发手册](https://github.com/hs-web/hsweb-framework/wiki/开发手册)
 
 ## 模块简介
 
@@ -22,6 +22,3 @@
 |[hsweb-starter](hsweb-starter)|模块启动器| 90%|
 |[hsweb-system](hsweb-system)|**系统功能**| 40%|
 |[hsweb-tests](hsweb-tests)|测试| 80%|
-
-
-![](./docs/hsweb3.png)

+ 58 - 0
docs/dev-guide/README.md

@@ -0,0 +1,58 @@
+# hsweb 开发手册
+
+## 框架基础设施
+主要为框架提供的常用工具
+1. [增删改查](crud)
+    * [通用增删改查](crud#通用增删改查)
+    * [动态条件](crud#动态条件)
+    * [表关联动态条件](crud#表关联)
+    * [拓展自定义字段](crud/custom-field.md)
+2. 权限控制
+    * 常用API
+    * 使用注解声明权限控制
+    * 自定义声明权限控制
+    * 拓展数据权限控制
+3. [访问日志](logging)
+    * [声明记录访问日志](logging#声明记录访问日志)
+    * [监听访问日志](logging#监听访问日志)
+    * [日志序列化](logging#日志序列化)
+4. 动态数据源
+    * 在配置文件中添加动态数据源
+    * 通过自定义,在数据库或其他地方添加动态数据源
+    * 注解方式切换动态数据源
+    * 编程方式切换动态数据源
+5. 常用并发工具
+    * 锁,分布式锁
+    * 计数器
+    * 异步任务,批量任务,事务
+6. 消息封装
+    * 消息队列
+    * websocket
+7. 其他工具
+    * 智能日期格式化
+    * excel,word操作
+    * 动态脚本引擎
+
+## 系统功能
+主要为框架实现的常用功能
+1. 权限配置
+2. 组织架构
+    * 组织架构数据权限控制
+    * 人员关系
+3. 数据字典
+4. 动态表单
+   * 设计表单
+   * 增删改查
+   * 验证器
+   * 触发器
+5. 文件上传下载
+    * 本地文件上传下载
+    * 文件秒传
+    * 静态文件上传下载
+    * 拓展其他文件上传
+6. 定时调度
+    * 动态脚本编写定时调度任务
+    * 集群下指定固定节点执行任务
+7. 动态脚本
+8. 工作流引擎
+    * flowable工作流设计器

+ 1 - 0
docs/dev-guide/autz/README.md

@@ -0,0 +1 @@
+# 

+ 79 - 0
docs/dev-guide/crud/README.md

@@ -0,0 +1,79 @@
+# 通用增删改查
+hsweb 中提供了一套通用的增删改查封装([hsweb-commons](../../../hsweb-commons)),实现增删改查以及动态查询。
+
+接口约定: 
+1. 实体类需要实现`Entity`接口, 通用实体类继承`GenericEntity<主键>`类.
+2. Dao继承`Dao`接口, 通用增删改查继承`CrudDao<实体类,主键>`.
+3. Service继承`Service`接口,通用增删改查继承`CrudService`.
+4. Controller,通用增删改查实现`SimpleGenericEntityController<实体类,主键,动态查询参数>`
+
+实现约定:
+1. 框架提供的实体都是接口形式,使用`EntityFactory`来创建实例,便于拓展属性. 实际业务中,可能并不需要这么做.
+2. Dao通用增删改查目前提供mybatis实现,可参照[UserMapper.xml](https://github.com/hs-web/hsweb-framework/blob/master/hsweb-system/hsweb-system-authorization/hsweb-system-authorization-dao/hsweb-system-authorization-dao-mybatis/src/main/resources/org/hswebframework/web/dao/mybatis/mappers/authorization/UserMapper.xml#L23-L69)
+   使用xml方式,提供了动态条件的同时保留了灵活性.
+3. Service提供了dsl的方式构造动态条件,继承`GenericEntityService<实体类,主键>`即可.注意:框架未使用dao来生成主键,而是在service中通过IDGenerator来生成.
+
+# 动态条件
+
+### Service中使用dsl进行动态条件
+继承`GenericEntityService`后可获得dsl方式动态条件功能:
+
+```java
+    public void method(){
+       //where name=? or name = ?
+       createQuery().where("name","张三").or("name","李四").list();
+       //set status=? where id = ?
+       createUpdate().set("status",1).where("id",id).exec(); //注意需要调用exec()
+    }
+```
+更多用法,详见:[hsweb-commons-service-simple](https://github.com/hs-web/hsweb-framework/tree/master/hsweb-commons/hsweb-commons-service/hsweb-commons-service-simple)
+
+### 前端传参,动态查询
+目前仅实现了一种动态条件从参数:`QueryParamEntity`,
+因此Controller实现`SimpleGenericEntityController<实体类,主键,QueryParamEntity>`接口. 获得动态查询功能
+
+```bash
+  GET /user?terms[0].column=name&terms[0].termType=like&terms[0].value=张三
+  // 等同于 where name like ?
+```
+更多用法,详见:[hsweb-commons-controller](https://github.com/hs-web/hsweb-framework/tree/master/hsweb-commons/hsweb-commons-controller)
+
+# 表关联
+由于动态条件实现较简单,目前动态条件需要修改mybatis dao实现的的mapper.xml,局部代码如下
+```xml
+ <resultMap id="CardDataResultMap" type="com.zmcsoft.apsp.api.card.entity.SimpleCardDataEntity">
+        <id property="id" column="u_id" javaType="string" jdbcType="VARCHAR"/>
+        <result property="name" column="name" javaType="String" jdbcType="VARCHAR"/>
+        
+        <!--关联表的信息-->
+        <result property="detail.email" column="detail.email" javaType="String" jdbcType="VARCHAR"/>
+        <result property="detail.phoneNumber" column="detail.phone_number" javaType="String" jdbcType="VARCHAR"/>
+</resultMap>
+
+ <select id="query" parameterType="org.hswebframework.web.commons.entity.Entity" resultMap="CardDataResultMap">
+        <include refid="config"/>
+        select
+        <include refid="BasicMapper.buildSelectField"/>
+        from user 
+        left join user_detail detail on detail.user_id = user.id
+        <where>
+            <include refid="BasicMapper.buildWhere"/>
+        </where>
+        <include refid="BasicMapper.buildSortField"/>
+</select>
+
+<select id="count" parameterType="org.hswebframework.web.commons.entity.Entity" resultMap="CardDataResultMap">
+        <include refid="config"/>
+        select count(1) from user 
+        left join user_detail detail on detail.user_id = user.id
+        <where>
+            <include refid="BasicMapper.buildWhere"/>
+        </where>
+        <include refid="BasicMapper.buildSortField"/>
+</select>
+```
+
+然后就可以通过动态查询来查询了
+```java
+createQuery().where("detail.email","admin@hsweb.me").single(); 
+```

+ 0 - 0
docs/dev-guide/crud/custom-field.md


+ 63 - 0
docs/dev-guide/logging/README.md

@@ -0,0 +1,63 @@
+## 声明记录访问日志
+
+1. 如果你是maven工程
+    * 引入私服配置
+    
+            <repositories>
+                <repository>
+                    <id>hsweb-nexus</id>
+                    <name>Nexus Release Repository</name>
+                    <url>http://nexus.hsweb.me/content/groups/public/</url>
+                    <snapshots>
+                        <enabled>true</enabled>
+                    </snapshots>
+                </repository>
+            </repositories>
+    
+    * 直接引入依赖
+
+            <dependency>
+                <groupId>org.hswebframework.web</groupId>
+                <artifactId>hsweb-access-logging-aop</artifactId>
+                <version>3.0-SNAPSHOT</version>
+            </dependency>
+		
+	
+2. 如果你是非maven工程,[请自行去以下地址](http://nexus.hsweb.me/)下载jar包
+		
+## 监听访问日志
+
+1. 开启访问日志
+    * 在启动类中注解@EnableAccessLogger
+    
+            @SpringBootApplication
+            @EnableAccessLogger
+            public class AppApplication {
+                public static void main(String[] args) {
+                    SpringApplication.run(AppApplication.class, args);
+                }	
+            }
+2. 访问日志 API
+
+    * controller类或者方法上,注解 @AccessLogger("功能描述")
+    
+            @AccessLogger("hello")
+            @RequestMapping(value = "/",method = RequestMethod.GET)
+            public String  hello() {
+                return "Hello World ! ";
+            }        
+    
+3. 日志监听
+
+    * 创建类,实现: AccessLoggerListener接口并注入到spring容器, 当有日志产生时,会调用接口方法onLogger,并传入日志信息
+    
+            @Component
+            public class MyLoggingListener implements AccessLoggerListener {
+                @Override
+                public void onLogger(AccessLoggerInfo loggerInfo) {
+                    System.out.println(loggerInfo.toString());
+                }
+            }
+    
+ 
+## 日志序列化

+ 6 - 0
docs/help/submit-code-issues.md

@@ -0,0 +1,6 @@
+# 提交包含代码的 issues
+
+1. 在github中打开代码,点击行号选择代码(按住shift多选);或者使用idea,选中代码段,右键-open on github.
+2. 右侧出现 ... 的图标,点击,选择open new issue按钮. 或者直接复制浏览器地址栏中的地址到issue中.
+3. 描述问题
+4. 提交

+ 1 - 2
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/AuthorizedToken.java

@@ -1,8 +1,7 @@
 package org.hswebframework.web.authorization.basic.web;
 
 /**
- * TODO 完成注释
- *
+ * 已完成认证的令牌,如果返回此令牌,将直接使用{@link this#getUserId()}来绑定用户信息
  * @author zhouhao
  */
 public interface AuthorizedToken extends ParsedToken {

+ 12 - 1
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/GeneratedToken.java

@@ -4,12 +4,23 @@ import java.io.Serializable;
 import java.util.Map;
 
 /**
- * Created by zhouhao on 2017/8/30.
+ * 生成好的令牌信息
+ * @author zhouhao
  */
 public interface GeneratedToken extends Serializable {
+    /**
+     * 要响应的数据,可自定义想要的数据给调用者
+     * @return {@link Map}
+     */
     Map<String,Object> getResponse();
 
+    /**
+     * @return 令牌字符串,令牌具有唯一性,不可逆,不包含敏感信息
+     */
     String getToken();
 
+    /**
+     * @return 令牌有效期(单位毫秒)
+     */
     int getTimeout();
 }

+ 11 - 0
hsweb-commons/hsweb-commons-controller/README.md

@@ -1,6 +1,17 @@
 # 通用Controller
 提供增删改查的restful接口
 
+ 以`RequestMapping("/user)`为例
+ 
+| 功能      | http方法&url    |  响应   |   说明 |
+| ------------- | -------------| ------------- | ----|
+|查询|GET /user|HTTP Status:200 {"status":200,"result":{"data":[],"total":0}} |可进行[动态查询](#动态查询)|
+|获取指定id的数据|GET /user/id|HTTP Status:200 {"status":200,"result":{"name":""} |可进行[动态查询](#动态查询)|
+|新增|POST /user|HTTP Status:201 {"status":201,"result":"{id}"} |contentType='application/json' |
+|更新|PUT /user/{id}|HTTP Status:200 {"status":200} |contentType='application/json'|
+|新增或者更新|PATCH /user|HTTP Status:200 {"status":200,"result":"{id}"} |contentType='application/json' |
+|删除|DELETE /user/{id}|HTTP Status:200 {"status":200} | |
+
 # 动态查询
 
 目前支持动态查询条件类 `QueryParamEntity`:

+ 7 - 5
hsweb-commons/hsweb-commons-dao/hsweb-commons-dao-mybatis/src/main/java/org/hswebframework/web/dao/mybatis/builder/EasyOrmSqlBuilder.java

@@ -52,7 +52,8 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
 /**
- *  使用easyorm 动态构建 sql
+ * 使用easyorm 动态构建 sql
+ *
  * @author zhouhao
  * @since 2.0
  */
@@ -149,10 +150,11 @@ public class EasyOrmSqlBuilder {
                     column.setAlias(resultMapping.getProperty());
                 column.setJavaType(resultMapping.getJavaType());
                 column.setProperty("resultMapping", resultMapping);
-                if (column.getJdbcType() == JDBCType.DATE || column.getJdbcType() == JDBCType.TIME) {
+                if (column.getJdbcType() == JDBCType.DATE) {
+                    column.setValueConverter(new DateTimeConverter("yyyy-MM-dd", column.getJavaType()));
+                } else if (column.getJdbcType() == JDBCType.TIMESTAMP) {
                     column.setValueConverter(new DateTimeConverter("yyyy-MM-dd HH:mm:ss", column.getJavaType()));
-                }
-                if (column.getJdbcType() == JDBCType.NUMERIC) {
+                } else if (column.getJdbcType() == JDBCType.NUMERIC) {
                     column.setValueConverter(new NumberValueConverter(column.getJavaType()));
                 }
                 rdbTableMetaData.addColumn(column);
@@ -227,7 +229,7 @@ public class EasyOrmSqlBuilder {
         }
         if (param.isPaging() && Pager.get() == null) {
             Pager.doPaging(param.getPageIndex(), param.getPageSize());
-        }else{
+        } else {
             Pager.reset();
         }
         RDBTableMetaData tableMetaData = createMeta(tableName, resultMapId);

+ 47 - 5
hsweb-commons/hsweb-commons-service/hsweb-commons-service-simple/src/main/java/org/hswebframework/web/service/EnableCacheGenericEntityService.java

@@ -3,6 +3,7 @@ package org.hswebframework.web.service;
 import org.hswebframework.web.commons.entity.GenericEntity;
 import org.springframework.cache.annotation.CacheEvict;
 import org.springframework.cache.annotation.Cacheable;
+import org.springframework.cache.annotation.Caching;
 
 import java.util.List;
 
@@ -31,33 +32,74 @@ public abstract class EnableCacheGenericEntityService<E extends GenericEntity<PK
     }
 
     @Override
-    @CacheEvict(key = "'id:'+#pk")
+    @Caching(
+            evict = {
+                    @CacheEvict(key = "'id:'+#pk"),
+                    @CacheEvict(key = "'all'"),
+                    @CacheEvict(key = "'count'")
+            }
+    )
     public int updateByPk(PK pk, E entity) {
         return super.updateByPk(pk, entity);
     }
 
     @Override
-    @CacheEvict(key = "'id:'+#entity.id")
+    @Caching(
+            evict = {
+                    @CacheEvict(key = "'id:'+#entity.id"),
+                    @CacheEvict(key = "'all'"),
+                    @CacheEvict(key = "'count'")
+            }
+    )
     protected int updateByPk(E entity) {
         return super.updateByPk(entity);
     }
 
     @Override
-    @CacheEvict(key = "'id:'+#result")
+    @Caching(
+            evict = {
+                    @CacheEvict(key = "'id:'+#result"),
+                    @CacheEvict(key = "'all'"),
+                    @CacheEvict(key = "'count'")
+            }
+    )
     public PK insert(E entity) {
         return super.insert(entity);
     }
 
     @Override
-    @CacheEvict(key = "'id:'+#pk")
+    @Caching(
+            evict = {
+                    @CacheEvict(key = "'id:'+#pk"),
+                    @CacheEvict(key = "'all'"),
+                    @CacheEvict(key = "'count'")
+            }
+    )
     public int deleteByPk(PK pk) {
         return super.deleteByPk(pk);
     }
 
     @Override
-    @CacheEvict(key = "'id:'+#result")
+    @Caching(
+            evict = {
+                    @CacheEvict(key = "'id:'+#result"),
+                    @CacheEvict(key = "'all'"),
+                    @CacheEvict(key = "'count'")
+            }
+    )
     public PK saveOrUpdate(E entity) {
         return super.saveOrUpdate(entity);
     }
 
+    @Override
+    @Cacheable(key = "'all'")
+    public List<E> select() {
+        return super.select();
+    }
+
+    @Override
+    @Cacheable(key = "'count'")
+    public int count() {
+        return super.count();
+    }
 }

+ 6 - 1
hsweb-concurrent/hsweb-concurrent-cache/pom.xml

@@ -28,5 +28,10 @@
 
     <artifactId>hsweb-concurrent-cache</artifactId>
 
-
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context</artifactId>
+        </dependency>
+    </dependencies>
 </project>

+ 22 - 0
hsweb-concurrent/hsweb-concurrent-cache/src/main/java/org/hswebframework/web/cache/FixUseSupperClassAutoConfiguration.java

@@ -0,0 +1,22 @@
+package org.hswebframework.web.cache;
+
+import org.hswebframework.web.cache.spring.fix.FixUseSupperClassAnnotationParser;
+import org.hswebframework.web.cache.spring.fix.FixUseSupperClassCacheOperationSource;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.cache.interceptor.CacheOperationSource;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Role;
+
+/**
+ *
+ * @author zhouhao
+ */
+@Configuration
+public class FixUseSupperClassAutoConfiguration {
+    @Bean
+    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
+    public CacheOperationSource cacheOperationSource() {
+        return new FixUseSupperClassCacheOperationSource(new FixUseSupperClassAnnotationParser());
+    }
+}

+ 22 - 0
hsweb-concurrent/hsweb-concurrent-cache/src/main/java/org/hswebframework/web/cache/monitor/MonitorSupportCache.java

@@ -0,0 +1,22 @@
+package org.hswebframework.web.cache.monitor;
+
+import org.springframework.cache.Cache;
+
+import java.util.Set;
+
+/**
+ * 支持监控的缓存
+ *
+ * @author zhouhao
+ */
+public interface MonitorSupportCache extends Cache {
+    long getTotalTimes();
+
+    long getHitTimes();
+
+    long getUpdateTimes();
+
+    long size();
+
+    Set<Object> keySet();
+}

+ 296 - 0
hsweb-concurrent/hsweb-concurrent-cache/src/main/java/org/hswebframework/web/cache/spring/fix/FixUseSupperClassAnnotationParser.java

@@ -0,0 +1,296 @@
+/*
+ * Copyright 2002-2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hswebframework.web.cache.spring.fix;
+
+import org.springframework.cache.annotation.*;
+import org.springframework.cache.interceptor.CacheEvictOperation;
+import org.springframework.cache.interceptor.CacheOperation;
+import org.springframework.cache.interceptor.CachePutOperation;
+import org.springframework.cache.interceptor.CacheableOperation;
+import org.springframework.core.annotation.AnnotatedElementUtils;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
+
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Strategy implementation for parsing Spring's {@link Caching}, {@link Cacheable},
+ * {@link CacheEvict}, and {@link CachePut} annotations.
+ *
+ * @author Costin Leau
+ * @author Juergen Hoeller
+ * @author Chris Beams
+ * @author Phillip Webb
+ * @author Stephane Nicoll
+ * @author Sam Brannen
+ * @since 3.1
+ */
+@SuppressWarnings("serial")
+public class FixUseSupperClassAnnotationParser implements FixUseSupperClassCacheAnnotationParser, Serializable {
+    @Override
+    public Collection<CacheOperation> parseCacheAnnotations(Class targetClass, Method method) {
+        DefaultCacheConfig defaultConfig = getDefaultCacheConfig(targetClass);
+        return parseCacheAnnotations(defaultConfig, method);
+    }
+
+    @Override
+    public Collection<CacheOperation> parseCacheAnnotations(Class<?> type) {
+        DefaultCacheConfig defaultConfig = getDefaultCacheConfig(type);
+        return parseCacheAnnotations(defaultConfig, type);
+    }
+
+    @Override
+    public Collection<CacheOperation> parseCacheAnnotations(Method method) {
+        DefaultCacheConfig defaultConfig = getDefaultCacheConfig(method.getDeclaringClass());
+        return parseCacheAnnotations(defaultConfig, method);
+    }
+
+    protected Collection<CacheOperation> parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) {
+        Collection<CacheOperation> ops = null;
+
+        Collection<Cacheable> cacheables = AnnotatedElementUtils.getAllMergedAnnotations(ae, Cacheable.class);
+        if (!cacheables.isEmpty()) {
+            ops = lazyInit(ops);
+            for (Cacheable cacheable : cacheables) {
+                ops.add(parseCacheableAnnotation(ae, cachingConfig, cacheable));
+            }
+        }
+        Collection<CacheEvict> evicts = AnnotatedElementUtils.getAllMergedAnnotations(ae, CacheEvict.class);
+        if (!evicts.isEmpty()) {
+            ops = lazyInit(ops);
+            for (CacheEvict evict : evicts) {
+                ops.add(parseEvictAnnotation(ae, cachingConfig, evict));
+            }
+        }
+        Collection<CachePut> puts = AnnotatedElementUtils.getAllMergedAnnotations(ae, CachePut.class);
+        if (!puts.isEmpty()) {
+            ops = lazyInit(ops);
+            for (CachePut put : puts) {
+                ops.add(parsePutAnnotation(ae, cachingConfig, put));
+            }
+        }
+        Collection<Caching> cachings = AnnotatedElementUtils.getAllMergedAnnotations(ae, Caching.class);
+        if (!cachings.isEmpty()) {
+            ops = lazyInit(ops);
+            for (Caching caching : cachings) {
+                Collection<CacheOperation> cachingOps = parseCachingAnnotation(ae, cachingConfig, caching);
+                if (cachingOps != null) {
+                    ops.addAll(cachingOps);
+                }
+            }
+        }
+
+        return ops;
+    }
+
+    private <T extends Annotation> Collection<CacheOperation> lazyInit(Collection<CacheOperation> ops) {
+        return (ops != null ? ops : new ArrayList<CacheOperation>(1));
+    }
+
+    CacheableOperation parseCacheableAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, Cacheable cacheable) {
+        CacheableOperation.Builder builder = new CacheableOperation.Builder();
+
+        builder.setName(ae.toString());
+        builder.setCacheNames(cacheable.cacheNames());
+        builder.setCondition(cacheable.condition());
+        builder.setUnless(cacheable.unless());
+        builder.setKey(cacheable.key());
+        builder.setKeyGenerator(cacheable.keyGenerator());
+        builder.setCacheManager(cacheable.cacheManager());
+        builder.setCacheResolver(cacheable.cacheResolver());
+        builder.setSync(cacheable.sync());
+
+        defaultConfig.applyDefault(builder);
+        CacheableOperation op = builder.build();
+        validateCacheOperation(ae, op);
+
+        return op;
+    }
+
+    CacheEvictOperation parseEvictAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, CacheEvict cacheEvict) {
+        CacheEvictOperation.Builder builder = new CacheEvictOperation.Builder();
+
+        builder.setName(ae.toString());
+        builder.setCacheNames(cacheEvict.cacheNames());
+        builder.setCondition(cacheEvict.condition());
+        builder.setKey(cacheEvict.key());
+        builder.setKeyGenerator(cacheEvict.keyGenerator());
+        builder.setCacheManager(cacheEvict.cacheManager());
+        builder.setCacheResolver(cacheEvict.cacheResolver());
+        builder.setCacheWide(cacheEvict.allEntries());
+        builder.setBeforeInvocation(cacheEvict.beforeInvocation());
+
+        defaultConfig.applyDefault(builder);
+        CacheEvictOperation op = builder.build();
+        validateCacheOperation(ae, op);
+
+        return op;
+    }
+
+    CacheOperation parsePutAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, CachePut cachePut) {
+        CachePutOperation.Builder builder = new CachePutOperation.Builder();
+
+        builder.setName(ae.toString());
+        builder.setCacheNames(cachePut.cacheNames());
+        builder.setCondition(cachePut.condition());
+        builder.setUnless(cachePut.unless());
+        builder.setKey(cachePut.key());
+        builder.setKeyGenerator(cachePut.keyGenerator());
+        builder.setCacheManager(cachePut.cacheManager());
+        builder.setCacheResolver(cachePut.cacheResolver());
+
+        defaultConfig.applyDefault(builder);
+        CachePutOperation op = builder.build();
+        validateCacheOperation(ae, op);
+
+        return op;
+    }
+
+    Collection<CacheOperation> parseCachingAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, Caching caching) {
+        Collection<CacheOperation> ops = null;
+
+        Cacheable[] cacheables = caching.cacheable();
+        if (!ObjectUtils.isEmpty(cacheables)) {
+            ops = lazyInit(ops);
+            for (Cacheable cacheable : cacheables) {
+                ops.add(parseCacheableAnnotation(ae, defaultConfig, cacheable));
+            }
+        }
+        CacheEvict[] cacheEvicts = caching.evict();
+        if (!ObjectUtils.isEmpty(cacheEvicts)) {
+            ops = lazyInit(ops);
+            for (CacheEvict cacheEvict : cacheEvicts) {
+                ops.add(parseEvictAnnotation(ae, defaultConfig, cacheEvict));
+            }
+        }
+        CachePut[] cachePuts = caching.put();
+        if (!ObjectUtils.isEmpty(cachePuts)) {
+            ops = lazyInit(ops);
+            for (CachePut cachePut : cachePuts) {
+                ops.add(parsePutAnnotation(ae, defaultConfig, cachePut));
+            }
+        }
+
+        return ops;
+    }
+
+    /**
+     * Provides the {@link DefaultCacheConfig} instance for the specified {@link Class}.
+     *
+     * @param target the class-level to handle
+     * @return the default config (never {@code null})
+     */
+    DefaultCacheConfig getDefaultCacheConfig(Class<?> target) {
+        CacheConfig annotation = AnnotatedElementUtils.getMergedAnnotation(target, CacheConfig.class);
+        if (annotation != null) {
+            return new DefaultCacheConfig(annotation.cacheNames(), annotation.keyGenerator(),
+                    annotation.cacheManager(), annotation.cacheResolver());
+        }
+        return new DefaultCacheConfig();
+    }
+
+    /**
+     * Validates the specified {@link CacheOperation}.
+     * <p>Throws an {@link IllegalStateException} if the state of the operation is
+     * invalid. As there might be multiple sources for default values, this ensure
+     * that the operation is in a proper state before being returned.
+     *
+     * @param ae        the annotated element of the cache operation
+     * @param operation the {@link CacheOperation} to validate
+     */
+    private void validateCacheOperation(AnnotatedElement ae, CacheOperation operation) {
+        if (StringUtils.hasText(operation.getKey()) && StringUtils.hasText(operation.getKeyGenerator())) {
+            throw new IllegalStateException("Invalid cache annotation configuration on '" +
+                    ae.toString() + "'. Both 'key' and 'keyGenerator' attributes have been set. " +
+                    "These attributes are mutually exclusive: either set the SpEL expression used to" +
+                    "compute the key at runtime or set the name of the KeyGenerator bean to use.");
+        }
+        if (StringUtils.hasText(operation.getCacheManager()) && StringUtils.hasText(operation.getCacheResolver())) {
+            throw new IllegalStateException("Invalid cache annotation configuration on '" +
+                    ae.toString() + "'. Both 'cacheManager' and 'cacheResolver' attributes have been set. " +
+                    "These attributes are mutually exclusive: the cache manager is used to configure a" +
+                    "default cache resolver if none is set. If a cache resolver is set, the cache manager" +
+                    "won't be used.");
+        }
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        return (this == other || other instanceof FixUseSupperClassAnnotationParser);
+    }
+
+    @Override
+    public int hashCode() {
+        return FixUseSupperClassAnnotationParser.class.hashCode();
+    }
+
+
+    /**
+     * Provides default settings for a given set of cache operations.
+     */
+    static class DefaultCacheConfig {
+
+        private final String[] cacheNames;
+
+        private final String keyGenerator;
+
+        private final String cacheManager;
+
+        private final String cacheResolver;
+
+        public DefaultCacheConfig() {
+            this(null, null, null, null);
+        }
+
+        private DefaultCacheConfig(String[] cacheNames, String keyGenerator, String cacheManager, String cacheResolver) {
+            this.cacheNames = cacheNames;
+            this.keyGenerator = keyGenerator;
+            this.cacheManager = cacheManager;
+            this.cacheResolver = cacheResolver;
+        }
+
+        /**
+         * Apply the defaults to the specified {@link CacheOperation.Builder}.
+         *
+         * @param builder the operation builder to update
+         */
+        public void applyDefault(CacheOperation.Builder builder) {
+            if (builder.getCacheNames().isEmpty() && this.cacheNames != null) {
+                builder.setCacheNames(this.cacheNames);
+            }
+            if (!StringUtils.hasText(builder.getKey()) && !StringUtils.hasText(builder.getKeyGenerator()) &&
+                    StringUtils.hasText(this.keyGenerator)) {
+                builder.setKeyGenerator(this.keyGenerator);
+            }
+
+            if (StringUtils.hasText(builder.getCacheManager()) || StringUtils.hasText(builder.getCacheResolver())) {
+                // One of these is set so we should not inherit anything
+            } else if (StringUtils.hasText(this.cacheResolver)) {
+                builder.setCacheResolver(this.cacheResolver);
+            } else if (StringUtils.hasText(this.cacheManager)) {
+                builder.setCacheManager(this.cacheManager);
+            }
+        }
+
+    }
+
+}

+ 17 - 0
hsweb-concurrent/hsweb-concurrent-cache/src/main/java/org/hswebframework/web/cache/spring/fix/FixUseSupperClassCacheAnnotationParser.java

@@ -0,0 +1,17 @@
+package org.hswebframework.web.cache.spring.fix;
+
+import org.springframework.cache.annotation.CacheAnnotationParser;
+import org.springframework.cache.interceptor.CacheOperation;
+
+import java.lang.reflect.Method;
+import java.util.Collection;
+
+/**
+ * TODO 完成注释
+ *
+ * @author zhouhao
+ */
+public interface FixUseSupperClassCacheAnnotationParser extends CacheAnnotationParser {
+
+    Collection<CacheOperation> parseCacheAnnotations(Class targetClass, Method method);
+}

+ 193 - 0
hsweb-concurrent/hsweb-concurrent-cache/src/main/java/org/hswebframework/web/cache/spring/fix/FixUseSupperClassCacheOperationSource.java

@@ -0,0 +1,193 @@
+/*
+ * Copyright 2002-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hswebframework.web.cache.spring.fix;
+
+import org.springframework.cache.annotation.CacheAnnotationParser;
+import org.springframework.cache.interceptor.CacheOperation;
+import org.springframework.util.Assert;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.*;
+
+/**
+ * Implementation of the {@link org.springframework.cache.interceptor.CacheOperationSource
+ * CacheOperationSource} interface for working with caching metadata in annotation format.
+ * <p>
+ * <p>This class reads Spring's {@link org.springframework.cache.annotation.Cacheable}, {@link org.springframework.cache.annotation.CachePut} and {@link org.springframework.cache.annotation.CacheEvict}
+ * annotations and exposes corresponding caching operation definition to Spring's cache
+ * infrastructure. This class may also serve as base class for a custom
+ * {@code CacheOperationSource}.
+ *
+ * @author Costin Leau
+ * @author Juergen Hoeller
+ * @author Stephane Nicoll
+ * @since 3.1
+ */
+@SuppressWarnings("serial")
+public class FixUseSupperClassCacheOperationSource extends FixUseSupperClassFallbackCacheOperationSource implements Serializable {
+
+    private boolean publicMethodsOnly;
+
+    private final Set<FixUseSupperClassCacheAnnotationParser> annotationParsers;
+
+
+    /**
+     * Create a default AnnotationCacheOperationSource, supporting public methods
+     * that carry the {@code Cacheable} and {@code CacheEvict} annotations.
+     */
+    public FixUseSupperClassCacheOperationSource() {
+        this(true);
+    }
+
+    /**
+     * Create a default {@code AnnotationCacheOperationSource}, supporting public methods
+     * that carry the {@code Cacheable} and {@code CacheEvict} annotations.
+     *
+     * @param publicMethodsOnly whether to support only annotated public methods
+     *                          typically for use with proxy-based AOP), or protected/private methods as well
+     *                          (typically used with AspectJ class weaving)
+     */
+    public FixUseSupperClassCacheOperationSource(boolean publicMethodsOnly) {
+        this.publicMethodsOnly = publicMethodsOnly;
+        this.annotationParsers = new LinkedHashSet<>(1);
+        this.annotationParsers.add(new FixUseSupperClassAnnotationParser());
+    }
+
+    /**
+     * Create a custom AnnotationCacheOperationSource.
+     *
+     * @param annotationParser the CacheAnnotationParser to use
+     */
+    public FixUseSupperClassCacheOperationSource(FixUseSupperClassCacheAnnotationParser annotationParser) {
+        this.publicMethodsOnly = true;
+        Assert.notNull(annotationParser, "CacheAnnotationParser must not be null");
+        this.annotationParsers = Collections.singleton(annotationParser);
+    }
+
+    /**
+     * Create a custom AnnotationCacheOperationSource.
+     *
+     * @param annotationParsers the CacheAnnotationParser to use
+     */
+    public FixUseSupperClassCacheOperationSource(FixUseSupperClassCacheAnnotationParser... annotationParsers) {
+        this.publicMethodsOnly = true;
+        Assert.notEmpty(annotationParsers, "At least one CacheAnnotationParser needs to be specified");
+        Set<FixUseSupperClassCacheAnnotationParser> parsers = new LinkedHashSet<>(annotationParsers.length);
+        Collections.addAll(parsers, annotationParsers);
+        this.annotationParsers = parsers;
+    }
+
+    /**
+     * Create a custom AnnotationCacheOperationSource.
+     *
+     * @param annotationParsers the CacheAnnotationParser to use
+     */
+    public FixUseSupperClassCacheOperationSource(Set<FixUseSupperClassCacheAnnotationParser> annotationParsers) {
+        this.publicMethodsOnly = true;
+        Assert.notEmpty(annotationParsers, "At least one CacheAnnotationParser needs to be specified");
+        this.annotationParsers = annotationParsers;
+    }
+
+
+    @Override
+    protected Collection<CacheOperation> findCacheOperations(Class<?> targetClass, Method method) {
+        return determineCacheOperations(parser -> parser.parseCacheAnnotations(targetClass, method));
+    }
+
+    @Override
+    protected Collection<CacheOperation> findCacheOperations(final Class<?> clazz) {
+        return determineCacheOperations(parser -> parser.parseCacheAnnotations(clazz));
+    }
+
+//	@Override
+//	protected Collection<CacheOperation> findCacheOperations(final Method method) {
+//		return determineCacheOperations(parser -> parser.parseCacheAnnotations(method));
+//	}
+
+    /**
+     * Determine the cache operation(s) for the given {@link CacheOperationProvider}.
+     * <p>This implementation delegates to configured
+     * {@link CacheAnnotationParser}s for parsing known annotations into
+     * Spring's metadata attribute class.
+     * <p>Can be overridden to support custom annotations that carry
+     * caching metadata.
+     *
+     * @param provider the cache operation provider to use
+     * @return the configured caching operations, or {@code null} if none found
+     */
+    protected Collection<CacheOperation> determineCacheOperations(CacheOperationProvider provider) {
+        Collection<CacheOperation> ops = null;
+        for (FixUseSupperClassCacheAnnotationParser annotationParser : this.annotationParsers) {
+            Collection<CacheOperation> annOps = provider.getCacheOperations(annotationParser);
+            if (annOps != null) {
+                if (ops == null) {
+                    ops = new ArrayList<>();
+                }
+                ops.addAll(annOps);
+            }
+        }
+        return ops;
+    }
+
+    /**
+     * By default, only public methods can be made cacheable.
+     */
+    @Override
+    protected boolean allowPublicMethodsOnly() {
+        return this.publicMethodsOnly;
+    }
+
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (!(other instanceof FixUseSupperClassCacheOperationSource)) {
+            return false;
+        }
+        FixUseSupperClassCacheOperationSource otherCos = (FixUseSupperClassCacheOperationSource) other;
+        return (this.annotationParsers.equals(otherCos.annotationParsers) &&
+                this.publicMethodsOnly == otherCos.publicMethodsOnly);
+    }
+
+    @Override
+    public int hashCode() {
+        return this.annotationParsers.hashCode();
+    }
+
+    public void setPublicMethodsOnly(boolean publicMethodsOnly) {
+        this.publicMethodsOnly = publicMethodsOnly;
+    }
+
+    /**
+     * Callback interface providing {@link CacheOperation} instance(s) based on
+     * a given {@link CacheAnnotationParser}.
+     */
+    protected interface CacheOperationProvider {
+
+        /**
+         * Return the {@link CacheOperation} instance(s) provided by the specified parser.
+         *
+         * @param parser the parser to use
+         * @return the cache operations, or {@code null} if none found
+         */
+        Collection<CacheOperation> getCacheOperations(FixUseSupperClassCacheAnnotationParser parser);
+    }
+
+}

+ 198 - 0
hsweb-concurrent/hsweb-concurrent-cache/src/main/java/org/hswebframework/web/cache/spring/fix/FixUseSupperClassFallbackCacheOperationSource.java

@@ -0,0 +1,198 @@
+/*
+ * Copyright 2002-2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hswebframework.web.cache.spring.fix;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.cache.interceptor.CacheOperation;
+import org.springframework.cache.interceptor.CacheOperationSource;
+import org.springframework.core.BridgeMethodResolver;
+import org.springframework.core.MethodClassKey;
+import org.springframework.util.ClassUtils;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Abstract implementation of {@link CacheOperation} that caches attributes
+ * for methods and implements a fallback policy: 1. specific target method;
+ * 2. target class; 3. declaring method; 4. declaring class/interface.
+ * <p>
+ * <p>Defaults to using the target class's caching attribute if none is
+ * associated with the target method. Any caching attribute associated with
+ * the target method completely overrides a class caching attribute.
+ * If none found on the target class, the interface that the invoked method
+ * has been called through (in case of a JDK proxy) will be checked.
+ * <p>
+ * <p>This implementation caches attributes by method after they are first
+ * used. If it is ever desirable to allow dynamic changing of cacheable
+ * attributes (which is very unlikely), caching could be made configurable.
+ *
+ * @author Costin Leau
+ * @author Juergen Hoeller
+ * @author zhouhao
+ * @see org.springframework.cache.interceptor.AbstractFallbackCacheOperationSource
+ * @since 3.1
+ */
+public abstract class FixUseSupperClassFallbackCacheOperationSource implements CacheOperationSource {
+
+    /**
+     * Canonical value held in cache to indicate no caching attribute was
+     * found for this method and we don't need to look again.
+     */
+    private final static Collection<CacheOperation> NULL_CACHING_ATTRIBUTE = Collections.emptyList();
+
+
+    /**
+     * Logger available to subclasses.
+     * <p>As this base class is not marked Serializable, the logger will be recreated
+     * after serialization - provided that the concrete subclass is Serializable.
+     */
+    protected final Log logger = LogFactory.getLog(getClass());
+
+    /**
+     * Cache of CacheOperations, keyed by method on a specific target class.
+     * <p>As this base class is not marked Serializable, the cache will be recreated
+     * after serialization - provided that the concrete subclass is Serializable.
+     */
+    private final Map<Object, Collection<CacheOperation>> attributeCache =
+            new ConcurrentHashMap<Object, Collection<CacheOperation>>(1024);
+
+
+    /**
+     * Determine the caching attribute for this method invocation.
+     * <p>Defaults to the class's caching attribute if no method attribute is found.
+     *
+     * @param method      the method for the current invocation (never {@code null})
+     * @param targetClass the target class for this invocation (may be {@code null})
+     * @return {@link CacheOperation} for this method, or {@code null} if the method
+     * is not cacheable
+     */
+    @Override
+    public Collection<CacheOperation> getCacheOperations(Method method, Class<?> targetClass) {
+        if (method.getDeclaringClass() == Object.class) {
+            return null;
+        }
+
+        Object cacheKey = getCacheKey(method, targetClass);
+        Collection<CacheOperation> cached = this.attributeCache.get(cacheKey);
+
+        if (cached != null) {
+            return (cached != NULL_CACHING_ATTRIBUTE ? cached : null);
+        } else {
+            Collection<CacheOperation> cacheOps = computeCacheOperations(method, targetClass);
+            if (cacheOps != null) {
+                if (logger.isDebugEnabled()) {
+                    logger.debug("Adding cacheable method '" + method.getName() + "' with attribute: " + cacheOps);
+                }
+                this.attributeCache.put(cacheKey, cacheOps);
+            } else {
+                this.attributeCache.put(cacheKey, NULL_CACHING_ATTRIBUTE);
+            }
+            return cacheOps;
+        }
+    }
+
+    /**
+     * Determine a cache key for the given method and target class.
+     * <p>Must not produce same key for overloaded methods.
+     * Must produce same key for different instances of the same method.
+     *
+     * @param method      the method (never {@code null})
+     * @param targetClass the target class (may be {@code null})
+     * @return the cache key (never {@code null})
+     */
+    protected Object getCacheKey(Method method, Class<?> targetClass) {
+        return new MethodClassKey(method, targetClass);
+    }
+
+    private Collection<CacheOperation> computeCacheOperations(Method method, Class<?> targetClass) {
+        // Don't allow no-public methods as required.
+        if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
+            return null;
+        }
+
+        // The method may be on an interface, but we need attributes from the target class.
+        // If the target class is null, the method will be unchanged.
+        Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
+        // If we are dealing with method with generic parameters, find the original method.
+        specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
+
+
+        // First try is the method in the target class.
+        // 解决@CacheConfig不能继承的问题
+        Collection<CacheOperation> opDef = findCacheOperations(targetClass, specificMethod);
+        if (opDef != null) {
+            return opDef;
+        }
+
+        // Second try is the caching operation on the target class.
+        opDef = findCacheOperations(specificMethod.getDeclaringClass());
+        if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
+            return opDef;
+        }
+
+        if (specificMethod != method) {
+            // Fallback is to look at the original method.
+            opDef = findCacheOperations(targetClass, method);
+            if (opDef != null) {
+                return opDef;
+            }
+            // Last fallback is the class of the original method.
+            opDef = findCacheOperations(method.getDeclaringClass());
+            if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
+                return opDef;
+            }
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Subclasses need to implement this to return the caching attribute
+     * for the given method, if any.
+     *
+     * @param method the method to retrieve the attribute for
+     * @return all caching attribute associated with this method
+     * (or {@code null} if none)
+     */
+    protected abstract Collection<CacheOperation> findCacheOperations(Class<?> targetClass, Method method);
+
+    /**
+     * Subclasses need to implement this to return the caching attribute
+     * for the given class, if any.
+     *
+     * @param clazz the class to retrieve the attribute for
+     * @return all caching attribute associated with this class
+     * (or {@code null} if none)
+     */
+    protected abstract Collection<CacheOperation> findCacheOperations(Class<?> clazz);
+
+    /**
+     * Should only public methods be allowed to have caching semantics?
+     * <p>The default implementation returns {@code false}.
+     */
+    protected boolean allowPublicMethodsOnly() {
+        return false;
+    }
+
+}

+ 13 - 13
hsweb-starter/hsweb-spring-boot-starter/src/main/java/org/hswebframework/web/starter/SystemInitializeAutoConfiguration.java

@@ -18,7 +18,6 @@
 
 package org.hswebframework.web.starter;
 
-import org.hsweb.ezorm.rdb.RDBDatabase;
 import org.hsweb.ezorm.rdb.executor.SqlExecutor;
 import org.hsweb.ezorm.rdb.meta.RDBDatabaseMetaData;
 import org.hsweb.ezorm.rdb.meta.parser.H2TableMetaParser;
@@ -36,7 +35,6 @@ import org.hswebframework.web.datasource.DatabaseType;
 import org.hswebframework.web.service.Service;
 import org.hswebframework.web.starter.init.SystemInitialize;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.config.BeanPostProcessor;
 import org.springframework.boot.CommandLineRunner;
@@ -69,10 +67,10 @@ public class SystemInitializeAutoConfiguration implements CommandLineRunner, Bea
     private AppProperties appProperties;
 
     @Autowired
-    DataSource dataSource;
+    private DataSource dataSource;
 
     @Autowired
-    SqlExecutor sqlExecutor;
+    private SqlExecutor sqlExecutor;
 
     @Autowired
     private ApplicationContext applicationContext;
@@ -91,12 +89,14 @@ public class SystemInitializeAutoConfiguration implements CommandLineRunner, Bea
         addGlobalVariable("spring", applicationContext);
     }
 
+    @SuppressWarnings("all")
     protected void addGlobalVariable(String var, Object val) {
-        engines.forEach(engine ->{
-            try{
-                engine.addGlobalVariable(Collections.singletonMap(var, val));
-            }catch (Exception ignore){}
-            }
+        engines.forEach(engine -> {
+                    try {
+                        engine.addGlobalVariable(Collections.singletonMap(var, val));
+                    } catch (NullPointerException ignore) {
+                    }
+                }
         );
     }
 
@@ -123,12 +123,12 @@ public class SystemInitializeAutoConfiguration implements CommandLineRunner, Bea
                 metaData.setParser(new MysqlTableMetaParser(sqlExecutor));
                 break;
             default:
-                h2:
                 metaData = new H2RDBDatabaseMetaData();
                 metaData.setParser(new H2TableMetaParser(sqlExecutor));
                 break;
         }
-        RDBDatabase database = new SimpleDatabase(metaData, sqlExecutor);
+        SimpleDatabase database = new SimpleDatabase(metaData, sqlExecutor);
+        database.setAutoParse(true);
         SystemInitialize initialize = new SystemInitialize(sqlExecutor, database, version);
 
         initialize.addScriptContext("db", jdbcUserName);
@@ -139,12 +139,12 @@ public class SystemInitializeAutoConfiguration implements CommandLineRunner, Bea
 
 
     @Override
-    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+    public Object postProcessBeforeInitialization(Object bean, String beanName) {
         return bean;
     }
 
     @Override
-    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+    public Object postProcessAfterInitialization(Object bean, String beanName)  {
         ScriptScope scope;
         if (bean instanceof Service) {
             addGlobalVariable(beanName, bean);

+ 9 - 0
hsweb-starter/hsweb-spring-boot-starter/src/main/java/org/hswebframework/web/starter/init/SystemInitialize.java

@@ -64,6 +64,15 @@ public class SystemInitialize {
         if (installed == null) {
             rdbTable.createInsert().value(targetVersion).exec();
         } else {
+            //合并已安装的依赖
+            //修复如果删掉了依赖,再重启会丢失依赖信息的问题
+            for (SystemVersion.Dependency dependency : installed.getDependencies()) {
+                SystemVersion.Dependency target = targetVersion.getDependency(dependency.getGroupId(), dependency.getArtifactId());
+                if (target == null) {
+                    targetVersion.getDependencies().add(dependency);
+                }
+            }
+
             rdbTable.createUpdate().set(targetVersion).where().sql("1=1").exec();
         }
     }

+ 1 - 1
hsweb-system/hsweb-system-organizational/hsweb-system-organizational-starter/src/main/resources/hsweb-starter.js

@@ -52,7 +52,7 @@ function install(context) {
 
     database.createOrAlter("s_organization")
         .addColumn().name("u_id").alias("id").comment("ID").jdbcType(java.sql.JDBCType.VARCHAR).length(32).primaryKey().commit()
-        .addColumn().name("name").alias("name").comment("名称").jdbcType(java.sql.JDBCType.VARCHAR).length(32).commit()
+        .addColumn().name("name").alias("name").comment("名称").jdbcType(java.sql.JDBCType.VARCHAR).length(256).commit()
         .addColumn().name("full_name").alias("fullName").comment("全称").jdbcType(java.sql.JDBCType.VARCHAR).length(256).commit()
         .addColumn().name("code").alias("code").comment("机构编码").jdbcType(java.sql.JDBCType.VARCHAR).length(32).commit()
         .addColumn().name("district_id").alias("districtId").comment("所在行政区域ID").jdbcType(java.sql.JDBCType.VARCHAR).length(32).commit()

+ 84 - 21
pom.xml

@@ -102,8 +102,56 @@
         <swagger.version>2.6.1</swagger.version>
     </properties>
 
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-release-plugin</artifactId>
+                        <configuration>
+                            <autoVersionSubmodules>true</autoVersionSubmodules>
+                            <useReleaseProfile>false</useReleaseProfile>
+                            <releaseProfiles>release</releaseProfiles>
+                            <goals>deploy</goals>
+                        </configuration>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-gpg-plugin</artifactId>
+                        <version>1.5</version>
+                        <executions>
+                            <execution>
+                                <id>sign-artifacts</id>
+                                <phase>verify</phase>
+                                <goals>
+                                    <goal>sign</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-javadoc-plugin</artifactId>
+                        <version>2.9.1</version>
+                        <executions>
+                            <execution>
+                                <id>attach-javadocs</id>
+                                <goals>
+                                    <goal>jar</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
     <build>
         <plugins>
+
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-scm-plugin</artifactId>
@@ -114,37 +162,51 @@
             </plugin>
 
             <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-source-plugin</artifactId>
                 <version>2.4</version>
-                <configuration>
-                    <attach>true</attach>
-                </configuration>
                 <executions>
                     <execution>
-                        <phase>compile</phase>
+                        <id>attach-sources</id>
                         <goals>
-                            <goal>jar</goal>
+                            <goal>jar-no-fork</goal>
                         </goals>
                     </execution>
                 </executions>
             </plugin>
 
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-javadoc-plugin</artifactId>
-                <version>2.9.1</version>
-                <configuration>
-                    <aggregate>true</aggregate>
-                </configuration>
-                <executions>
-                    <execution>
-                        <phase>deploy</phase>
-                        <goals>
-                            <goal>jar</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
+            <!--<plugin>-->
+                <!--<artifactId>maven-source-plugin</artifactId>-->
+                <!--<version>2.4</version>-->
+                <!--<configuration>-->
+                    <!--<attach>true</attach>-->
+                <!--</configuration>-->
+                <!--<executions>-->
+                    <!--<execution>-->
+                        <!--<phase>compile</phase>-->
+                        <!--<goals>-->
+                            <!--<goal>jar</goal>-->
+                        <!--</goals>-->
+                    <!--</execution>-->
+                <!--</executions>-->
+            <!--</plugin>-->
+
+            <!--<plugin>-->
+                <!--<groupId>org.apache.maven.plugins</groupId>-->
+                <!--<artifactId>maven-javadoc-plugin</artifactId>-->
+                <!--<version>2.9.1</version>-->
+                <!--<configuration>-->
+                    <!--<aggregate>true</aggregate>-->
+                <!--</configuration>-->
+                <!--<executions>-->
+                    <!--<execution>-->
+                        <!--<phase>deploy</phase>-->
+                        <!--<goals>-->
+                            <!--<goal>jar</goal>-->
+                        <!--</goals>-->
+                    <!--</execution>-->
+                <!--</executions>-->
+            <!--</plugin>-->
 
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
@@ -311,6 +373,7 @@
             <url>http://nexus.hsweb.me/content/repositories/snapshots/</url>
         </snapshotRepository>
     </distributionManagement>
+
     <pluginRepositories>
         <pluginRepository>
             <id>hsweb-nexus</id>