From 128be64772d94d4f5b1f5837c3b244c6f7f944d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Fri, 8 Sep 2017 14:50:02 +0800 Subject: [PATCH 01/44] Revert "note" This reverts commit a21e66db170d70b9387ad4022892020e3e82e436. --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d51f18b..0bb94d9 100644 --- a/README.md +++ b/README.md @@ -14,16 +14,15 @@ * 博客 springboot+security restful权限控制官方推荐(五) 地址:http://blog.csdn.net/u012373815/article/details/59749385 * 博客:spring security 同时只有一个用户在线,session 并发问题 地址: + ##springboot-SpringSecurity3 * 博客 springBoot+springSecurity 动态管理Restful风格权限(三) 地址:http://blog.csdn.net/u012373815/article/details/55225079 - ##springboot-SpringSecurity4 * 实战,项目中正在用 ##springboot-WebSocket - -包含三部分代码,三部分代码有交合: +包含三部分代码: * 第一是 博客 spring boot +WebSocket 广播式(一)地址:http://blog.csdn.net/u012373815/article/details/54375195 中所示代码 @@ -31,10 +30,9 @@ * 第三是 博客 spring boot +WebSocket(三) 点对点式 地址: http://blog.csdn.net/u012373815/article/details/54380476 中所示代码 - +三部分代码有交合。 ##springboot-Cache - 包含两部分代码: * 第一部分是 博客 springboot的缓存技术 地址: http://blog.csdn.net/u012373815/article/details/54564076 From d4444317d7b39ee7ad5d13cb33f93e77c966f212 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Fri, 8 Sep 2017 15:52:06 +0800 Subject: [PATCH 02/44] roll back master --- README.md | 9 +- .../us/example/config/WebSecurityConfig.java | 19 +-- .../example/controller/LoginController.java | 40 +------ .../java/com/us/example/domain/SysUser.java | 1 + .../java/com/us/example/util/SessionUtil.java | 109 ------------------ .../src/main/resources/application.properties | 2 +- 6 files changed, 17 insertions(+), 163 deletions(-) delete mode 100644 springboot-springSecurity2/src/main/java/com/us/example/util/SessionUtil.java diff --git a/README.md b/README.md index 0bb94d9..ee37c45 100644 --- a/README.md +++ b/README.md @@ -12,17 +12,17 @@ ##springboot-SpringSecurity2 * 博客 springboot+security restful权限控制官方推荐(五) 地址:http://blog.csdn.net/u012373815/article/details/59749385 -* 博客:spring security 同时只有一个用户在线,session 并发问题 地址: - ##springboot-SpringSecurity3 * 博客 springBoot+springSecurity 动态管理Restful风格权限(三) 地址:http://blog.csdn.net/u012373815/article/details/55225079 + ##springboot-SpringSecurity4 * 实战,项目中正在用 ##springboot-WebSocket -包含三部分代码: + +包含三部分代码,三部分代码有交合: * 第一是 博客 spring boot +WebSocket 广播式(一)地址:http://blog.csdn.net/u012373815/article/details/54375195 中所示代码 @@ -30,9 +30,10 @@ * 第三是 博客 spring boot +WebSocket(三) 点对点式 地址: http://blog.csdn.net/u012373815/article/details/54380476 中所示代码 -三部分代码有交合。 + ##springboot-Cache + 包含两部分代码: * 第一部分是 博客 springboot的缓存技术 地址: http://blog.csdn.net/u012373815/article/details/54564076 diff --git a/springboot-springSecurity2/src/main/java/com/us/example/config/WebSecurityConfig.java b/springboot-springSecurity2/src/main/java/com/us/example/config/WebSecurityConfig.java index 75a6c38..5e9b09d 100644 --- a/springboot-springSecurity2/src/main/java/com/us/example/config/WebSecurityConfig.java +++ b/springboot-springSecurity2/src/main/java/com/us/example/config/WebSecurityConfig.java @@ -2,8 +2,6 @@ import com.us.example.security.CustomUserService; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.context.embedded.ServletListenerRegistrationBean; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; @@ -11,10 +9,7 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.core.session.SessionRegistry; -import org.springframework.security.core.session.SessionRegistryImpl; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.web.session.HttpSessionEventPublisher; /** * Created by yangyibo on 17/1/18. @@ -26,8 +21,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private CustomUserService customUserService; - @Autowired - SessionRegistry sessionRegistry; + @Autowired protected void configure(AuthenticationManagerBuilder auth) throws Exception { @@ -50,15 +44,12 @@ protected void configure(HttpSecurity http) throws Exception { .antMatchers("/**") .permitAll() .and() - .sessionManagement().maximumSessions(1).sessionRegistry(sessionRegistry); - http.httpBasic(); + .sessionManagement() + .and() + .httpBasic(); } - @Bean - public SessionRegistry getSessionRegistry(){ - SessionRegistry sessionRegistry=new SessionRegistryImpl(); - return sessionRegistry; - } + } diff --git a/springboot-springSecurity2/src/main/java/com/us/example/controller/LoginController.java b/springboot-springSecurity2/src/main/java/com/us/example/controller/LoginController.java index 278733e..bca4d70 100644 --- a/springboot-springSecurity2/src/main/java/com/us/example/controller/LoginController.java +++ b/springboot-springSecurity2/src/main/java/com/us/example/controller/LoginController.java @@ -1,59 +1,29 @@ package com.us.example.controller; import com.us.example.domain.SysUser; -import com.us.example.util.SessionUtil; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.core.Authentication; import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.session.SessionRegistry; -import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; -import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import org.springframework.web.bind.annotation.RestController; /** * Created by yangyibo on 17/3/1. */ -@Controller +@RestController public class LoginController { - @Autowired - private SessionRegistry sessionRegistry; - /** - * http://localhost:8080/login - * http://localhost:8080/logout - * @param loginedUser - * @param logout - * @return - */ + @RequestMapping(value = "/login") @ResponseBody //用户名密码是用base64 加密 原文为 admin:admin 即 用户名:密码 内容是放在request.getHeader 的 "authorization" 中 - public Object login(@AuthenticationPrincipal SysUser loginedUser, @RequestParam(name = "logout", required = false) String logout,HttpServletRequest request) { + public Object login(@AuthenticationPrincipal SysUser loginedUser, @RequestParam(name = "logout", required = false) String logout) { if (logout != null) { - return "logout"; + return null; } if (loginedUser != null) { - SessionUtil.dropPreviousUser(request,sessionRegistry,loginedUser); return loginedUser; } return null; } - -// 此方法未用到 -// @RequestMapping(value="/logout", method = RequestMethod.GET) -// @ResponseBody -// public String logout (HttpServletRequest request, HttpServletResponse response) { -// Authentication auth = SecurityContextHolder.getContext().getAuthentication(); -// if (auth != null){ -// new SecurityContextLogoutHandler().logout(request, response, auth); -// } -// return "logout ok"; -// } } diff --git a/springboot-springSecurity2/src/main/java/com/us/example/domain/SysUser.java b/springboot-springSecurity2/src/main/java/com/us/example/domain/SysUser.java index 23793b8..697cf8a 100644 --- a/springboot-springSecurity2/src/main/java/com/us/example/domain/SysUser.java +++ b/springboot-springSecurity2/src/main/java/com/us/example/domain/SysUser.java @@ -97,4 +97,5 @@ public Collection getAuthorities() { public void setGrantedAuthorities(List authorities) { this.authorities = authorities; } + } diff --git a/springboot-springSecurity2/src/main/java/com/us/example/util/SessionUtil.java b/springboot-springSecurity2/src/main/java/com/us/example/util/SessionUtil.java deleted file mode 100644 index 5127ab4..0000000 --- a/springboot-springSecurity2/src/main/java/com/us/example/util/SessionUtil.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.us.example.util; - -import com.us.example.domain.SysUser; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.session.SessionInformation; -import org.springframework.security.core.session.SessionRegistry; -import org.springframework.security.web.authentication.session.SessionAuthenticationException; - -import javax.servlet.http.HttpServletRequest; -import java.util.List; - -/** - * Created by yangyibo on 8/23/17. - */ -public class SessionUtil { - /** - * 辨别用户是否已经登录 - * - * @param request - * @param sessionRegistry - * @param loginedUser - */ - public static void deleteSameUser(HttpServletRequest request, SessionRegistry sessionRegistry, SysUser loginedUser) { - SecurityContext sc = (SecurityContext) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT"); - List sessionsInfo; - sessionsInfo = sessionRegistry.getAllSessions(sc.getAuthentication().getPrincipal(), true); - String currentSessionId; - if (null != sessionsInfo && sessionsInfo.size() == 0) { - sessionRegistry.registerNewSession(request.getSession().getId(), sc.getAuthentication().getPrincipal()); - sessionsInfo = sessionRegistry.getAllSessions(sc.getAuthentication().getPrincipal(), false); - } - currentSessionId = sessionsInfo.get(0).getSessionId(); - List o = sessionRegistry.getAllPrincipals(); - for (Object principal : o) { - if (principal instanceof SysUser && (loginedUser.getUsername().equals(((SysUser) principal).getUsername()))) { - List oldSessionsInfo = sessionRegistry.getAllSessions(principal, false); - if (null != oldSessionsInfo && oldSessionsInfo.size() > 0 && !oldSessionsInfo.get(0).getSessionId().equals(currentSessionId)) { - for (SessionInformation sessionInformation : sessionsInfo) { - //当前session失效 - sessionInformation.expireNow(); - sc.setAuthentication(null); - sessionRegistry.removeSessionInformation(currentSessionId); - throw new SessionAuthenticationException("当前用户已经在线,登录失败!!!"); - } - } - } - } - } - - /** - * 剔除前一个用户 - * - * @param request - * @param sessionRegistry - * @param loginedUser - */ - public static void dropPreviousUser(HttpServletRequest request, SessionRegistry sessionRegistry, SysUser loginedUser) { - SecurityContext sc = (SecurityContext) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT"); - List sessionsInfo; - sessionsInfo = sessionRegistry.getAllSessions(sc.getAuthentication().getPrincipal(), true); - String currentSessionId; - currentSessionId = sessionsInfo.get(0).getSessionId(); - List o = sessionRegistry.getAllPrincipals(); - for (Object principal : o) { - if (principal instanceof SysUser && (loginedUser.getUsername().equals(((SysUser) principal).getUsername()))) { - List oldSessionsInfo = sessionRegistry.getAllSessions(principal, false); - if (null != oldSessionsInfo && oldSessionsInfo.size() > 0 && !oldSessionsInfo.get(0).getSessionId().equals(currentSessionId)) { - for (SessionInformation sessionInformation : oldSessionsInfo) { - //发送消息通知 - //当前session失效 - sessionInformation.expireNow(); - } - } - } - } - } - - - /** - * session 失效 - * - * @param request - * @param sessionRegistry - */ - public static void expireSession(HttpServletRequest request, SysUser user, SessionRegistry sessionRegistry) { - List sessionsInfo = null; - if (null != user) { - List o = sessionRegistry.getAllPrincipals(); - for (Object principal : o) { - if (principal instanceof SysUser && (user.getUsername().equals(((SysUser) principal).getUsername()))) { - sessionsInfo = sessionRegistry.getAllSessions(principal, false); - } - } - } else if (null != request) { - SecurityContext sc = (SecurityContext) request.getSession().getAttribute("SPRING_SECURITY_CONTEXT"); - if (null != sc.getAuthentication().getPrincipal()) { - sessionsInfo = sessionRegistry.getAllSessions(sc.getAuthentication().getPrincipal(), false); - sc.setAuthentication(null); - } - } - if (null != sessionsInfo && sessionsInfo.size() > 0) { - for (SessionInformation sessionInformation : sessionsInfo) { - //当前session失效 - sessionInformation.expireNow(); - sessionRegistry.removeSessionInformation(sessionInformation.getSessionId()); - } - } - } -} diff --git a/springboot-springSecurity2/src/main/resources/application.properties b/springboot-springSecurity2/src/main/resources/application.properties index 3033022..9db1bfa 100755 --- a/springboot-springSecurity2/src/main/resources/application.properties +++ b/springboot-springSecurity2/src/main/resources/application.properties @@ -3,7 +3,7 @@ ms.db.url=jdbc:mysql://localhost:3306/cache?characterEncoding=utf-8&useSSL=false ms.db.username=root ms.db.password=admin ms.db.maxActive=500 -server.port=8080 +server.port=8099 logging.level.org.springframework.security= INFO spring.thymeleaf.cache=false From 8b463bda1aed3d14d6326985ba55d072e526e201 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Fri, 8 Sep 2017 16:50:24 +0800 Subject: [PATCH 03/44] add md5 encoder --- .../com/us/example/service/UserService.java | 3 +- .../util/BCryptPasswordEncoderTest.java | 18 +++++++- .../java/com/us/example/util/MD5Util.java | 45 +++++++++++++++++++ 3 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 springboot-springSecurity2/src/main/java/com/us/example/util/MD5Util.java diff --git a/springboot-springSecurity2/src/main/java/com/us/example/service/UserService.java b/springboot-springSecurity2/src/main/java/com/us/example/service/UserService.java index 7a75814..d8da056 100644 --- a/springboot-springSecurity2/src/main/java/com/us/example/service/UserService.java +++ b/springboot-springSecurity2/src/main/java/com/us/example/service/UserService.java @@ -2,6 +2,7 @@ import com.us.example.dao.UserDao; import com.us.example.domain.SysUser; +import com.us.example.util.MD5Util; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; @@ -17,7 +18,7 @@ public class UserService { public SysUser create(SysUser sysUser){ //进行加密 BCryptPasswordEncoder encoder =new BCryptPasswordEncoder(); - sysUser.setPassword(encoder.encode(sysUser.getRawPassword().trim())); + sysUser.setPassword(encoder.encode(MD5Util.encode(sysUser.getRawPassword().trim()))); userDao.create(sysUser); return sysUser; } diff --git a/springboot-springSecurity2/src/main/java/com/us/example/util/BCryptPasswordEncoderTest.java b/springboot-springSecurity2/src/main/java/com/us/example/util/BCryptPasswordEncoderTest.java index 5edb03b..0b96976 100644 --- a/springboot-springSecurity2/src/main/java/com/us/example/util/BCryptPasswordEncoderTest.java +++ b/springboot-springSecurity2/src/main/java/com/us/example/util/BCryptPasswordEncoderTest.java @@ -9,7 +9,21 @@ public class BCryptPasswordEncoderTest { public static void main(String[] args) { BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); - System.out.println(encoder.encode("abel")); - System.out.println(encoder.encode("admin")); + System.out.println("encoder: " + encoder.encode("abel")); + System.out.println("encoder: " + encoder.encode("admin")); + + if (encoder.matches("abel", "$2a$10$IAz6WzJ314LH1NXq7Rf.dOYPP2uvzk08g.eAl9l4DRG4YsxavEV4W")) { + System.out.println("encoder: true"); + } + + + System.out.println("------------华丽的分割线-----------------------"); + String Md5Password = MD5Util.encode("abel"); + System.out.println("Md5Password: " + Md5Password); + System.out.println("encoder: " + encoder.encode(Md5Password)); + if (encoder.matches(Md5Password, "$2a$10$37MXEfzlbtC6QSsRTlRhIOmykMRJtO5mU8Y.yiJBjy1x4WYWFR5gG")) { + System.out.println("Md5Password: true"); + } + } } diff --git a/springboot-springSecurity2/src/main/java/com/us/example/util/MD5Util.java b/springboot-springSecurity2/src/main/java/com/us/example/util/MD5Util.java new file mode 100644 index 0000000..da620ec --- /dev/null +++ b/springboot-springSecurity2/src/main/java/com/us/example/util/MD5Util.java @@ -0,0 +1,45 @@ +package com.us.example.util; + +import java.security.MessageDigest; + +/** + * Created by yangyibo on 9/8/17. + */ +public class MD5Util { + private static final String SALT = "salt"; + + public static String encode(String password) { + password = password + SALT; + return processEncode(password); + } + + public static String processEncode(String password) { + MessageDigest md5 = null; + try { + md5 = MessageDigest.getInstance("MD5"); + } catch (Exception e) { + throw new RuntimeException(e); + } + char[] charArray = password.toCharArray(); + byte[] byteArray = new byte[charArray.length]; + + for (int i = 0; i < charArray.length; i++) + byteArray[i] = (byte) charArray[i]; + byte[] md5Bytes = md5.digest(byteArray); + StringBuffer hexValue = new StringBuffer(); + for (int i = 0; i < md5Bytes.length; i++) { + int val = ((int) md5Bytes[i]) & 0xff; + if (val < 16) { + hexValue.append("0"); + } + + hexValue.append(Integer.toHexString(val)); + } + return hexValue.toString(); + } + + public static void main(String[] args) { + System.out.println(MD5Util.encode("abel")); + System.out.println(MD5Util.encode("admin")); + } +} From 38a623a6d0ffe2aeba6b9a9d04156a0b7041ed42 Mon Sep 17 00:00:00 2001 From: abel <527515025@users.noreply.github.com> Date: Wed, 25 Oct 2017 22:48:33 -0500 Subject: [PATCH 04/44] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ee37c45..5bf4513 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -##springboot-SpringSecurity0 +#springboot-SpringSecurity0 + 包含两部分代码: * 第一是 博客 springboot+mybatis+SpringSecurity 实现用户角色数据库管理 地址:http://blog.csdn.net/u012373815/article/details/54632176 From b761810afef97620589528fd2d1c8da8817b4f92 Mon Sep 17 00:00:00 2001 From: abel <527515025@users.noreply.github.com> Date: Wed, 25 Oct 2017 22:50:53 -0500 Subject: [PATCH 05/44] modify readme.md modify readme.md --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 5bf4513..c5d8d95 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -#springboot-SpringSecurity0 +## springboot-SpringSecurity0 包含两部分代码: @@ -6,22 +6,22 @@ * 第二是 博客 springBoot+springSecurity验证密码MD5加密 地址:http://blog.csdn.net/u012373815/article/details/54927070 -##springboot-SpringSecurity1 +## springboot-SpringSecurity1 * 博客 springBoot+springSecurity 数据库动态管理用户、角色、权限(二) 地址:http://blog.csdn.net/u012373815/article/details/54633046 -##springboot-SpringSecurity2 +## springboot-SpringSecurity2 * 博客 springboot+security restful权限控制官方推荐(五) 地址:http://blog.csdn.net/u012373815/article/details/59749385 -##springboot-SpringSecurity3 +## springboot-SpringSecurity3 * 博客 springBoot+springSecurity 动态管理Restful风格权限(三) 地址:http://blog.csdn.net/u012373815/article/details/55225079 -##springboot-SpringSecurity4 +## springboot-SpringSecurity4 * 实战,项目中正在用 -##springboot-WebSocket +## springboot-WebSocket 包含三部分代码,三部分代码有交合: @@ -33,7 +33,7 @@ -##springboot-Cache +## springboot-Cache 包含两部分代码: @@ -41,11 +41,11 @@ * 第二部分是 博客 springboot缓存篇(二)-redis 做缓存 地址:http://blog.csdn.net/u012373815/article/details/54572687 -##springboot-Cache2 +## springboot-Cache2 * 是 博客 springboot缓存 之 GuavaCacheManager 地址:http://blog.csdn.net/u012373815/article/details/60468033 -##springboot-shiro +## springboot-shiro * 是博客 springboot集成shiro 实现权限控制 地址:http://blog.csdn.net/u012373815/article/details/57532292 From 587a95f0146b9f33ae0792a842c00a07f0be2140 Mon Sep 17 00:00:00 2001 From: abel <527515025@qq.com> Date: Wed, 4 Jul 2018 22:19:34 +0800 Subject: [PATCH 06/44] add springboot -springCloud --- springboot-springCloud/config/pom.xml | 28 +++ .../main/java/com/abel/ConfigApplication.java | 21 ++ .../config/src/main/resources/application.yml | 9 + .../config/src/main/resources/bootstrap.yml | 14 ++ .../src/main/resources/config/person.yml | 3 + .../config/src/main/resources/config/some.yml | 2 + .../config/target/classes/application.yml | 9 + .../config/target/classes/bootstrap.yml | 14 ++ .../config/target/classes/config/person.yml | 3 + .../config/target/classes/config/some.yml | 2 + springboot-springCloud/discovery/pom.xml | 26 ++ .../java/com/abel/DiscoveryApplication.java | 17 ++ .../src/main/resources/application.yml | 12 + .../discovery/target/classes/application.yml | 12 + springboot-springCloud/monitor/pom.xml | 52 ++++ .../java/com/abel/MonitorApplication.java | 20 ++ .../src/main/resources/application.yml | 3 + .../monitor/src/main/resources/bootstrap.yml | 10 + .../monitor/target/classes/application.yml | 3 + .../monitor/target/classes/bootstrap.yml | 10 + springboot-springCloud/person/pom.xml | 41 ++++ .../main/java/com/abel/PersonApplication.java | 14 ++ .../src/main/java/com/abel/bean/Person.java | 45 ++++ .../com/abel/controller/PersonController.java | 29 +++ .../java/com/abel/dao/PersonRepository.java | 10 + .../person/src/main/resources/application.yml | 9 + .../person/src/main/resources/bootstrap.yml | 15 ++ .../person/target/classes/application.yml | 9 + .../person/target/classes/bootstrap.yml | 15 ++ springboot-springCloud/pom.xml | 108 +++++++++ springboot-springCloud/some/pom.xml | 29 +++ .../main/java/com/abel/SomeApplication.java | 27 +++ .../some/src/main/resources/application.yml | 2 + .../some/src/main/resources/bootstrap.yml | 15 ++ .../some/target/classes/application.yml | 2 + .../some/target/classes/bootstrap.yml | 15 ++ springboot-springCloud/ui/pom.xml | 63 +++++ .../src/main/java/com/abel/UiApplication.java | 22 ++ .../src/main/java/com/abel/bean/Person.java | 39 +++ .../com/abel/controller/UiController.java | 34 +++ .../abel/service/PersonHystrixService.java | 31 +++ .../java/com/abel/service/PersonService.java | 24 ++ .../com/abel/service/SomeHystrixService.java | 26 ++ .../ui/src/main/resources/application.yml | 6 + .../ui/src/main/resources/bootstrap.yml | 10 + .../main/resources/static/css/application.css | 15 ++ .../fonts/glyphicons-halflings-regular.eot | Bin 0 -> 20335 bytes .../fonts/glyphicons-halflings-regular.svg | 229 ++++++++++++++++++ .../fonts/glyphicons-halflings-regular.ttf | Bin 0 -> 41280 bytes .../fonts/glyphicons-halflings-regular.woff | Bin 0 -> 23320 bytes .../ui/src/main/resources/static/index.html | 36 +++ .../ui/src/main/resources/static/js/app.js | 49 ++++ .../src/main/resources/static/tpl/person.html | 34 +++ .../src/main/resources/static/tpl/some.html | 27 +++ .../ui/target/classes/application.yml | 6 + .../ui/target/classes/bootstrap.yml | 10 + .../target/classes/static/css/application.css | 15 ++ .../fonts/glyphicons-halflings-regular.eot | Bin 0 -> 20335 bytes .../fonts/glyphicons-halflings-regular.svg | 229 ++++++++++++++++++ .../fonts/glyphicons-halflings-regular.ttf | Bin 0 -> 41280 bytes .../fonts/glyphicons-halflings-regular.woff | Bin 0 -> 23320 bytes .../ui/target/classes/static/index.html | 36 +++ .../ui/target/classes/static/js/app.js | 49 ++++ .../ui/target/classes/static/tpl/person.html | 34 +++ .../ui/target/classes/static/tpl/some.html | 27 +++ 65 files changed, 1706 insertions(+) create mode 100644 springboot-springCloud/config/pom.xml create mode 100644 springboot-springCloud/config/src/main/java/com/abel/ConfigApplication.java create mode 100644 springboot-springCloud/config/src/main/resources/application.yml create mode 100644 springboot-springCloud/config/src/main/resources/bootstrap.yml create mode 100644 springboot-springCloud/config/src/main/resources/config/person.yml create mode 100644 springboot-springCloud/config/src/main/resources/config/some.yml create mode 100644 springboot-springCloud/config/target/classes/application.yml create mode 100644 springboot-springCloud/config/target/classes/bootstrap.yml create mode 100644 springboot-springCloud/config/target/classes/config/person.yml create mode 100644 springboot-springCloud/config/target/classes/config/some.yml create mode 100644 springboot-springCloud/discovery/pom.xml create mode 100644 springboot-springCloud/discovery/src/main/java/com/abel/DiscoveryApplication.java create mode 100644 springboot-springCloud/discovery/src/main/resources/application.yml create mode 100644 springboot-springCloud/discovery/target/classes/application.yml create mode 100644 springboot-springCloud/monitor/pom.xml create mode 100644 springboot-springCloud/monitor/src/main/java/com/abel/MonitorApplication.java create mode 100644 springboot-springCloud/monitor/src/main/resources/application.yml create mode 100644 springboot-springCloud/monitor/src/main/resources/bootstrap.yml create mode 100644 springboot-springCloud/monitor/target/classes/application.yml create mode 100644 springboot-springCloud/monitor/target/classes/bootstrap.yml create mode 100644 springboot-springCloud/person/pom.xml create mode 100644 springboot-springCloud/person/src/main/java/com/abel/PersonApplication.java create mode 100644 springboot-springCloud/person/src/main/java/com/abel/bean/Person.java create mode 100644 springboot-springCloud/person/src/main/java/com/abel/controller/PersonController.java create mode 100644 springboot-springCloud/person/src/main/java/com/abel/dao/PersonRepository.java create mode 100644 springboot-springCloud/person/src/main/resources/application.yml create mode 100644 springboot-springCloud/person/src/main/resources/bootstrap.yml create mode 100644 springboot-springCloud/person/target/classes/application.yml create mode 100644 springboot-springCloud/person/target/classes/bootstrap.yml create mode 100644 springboot-springCloud/pom.xml create mode 100644 springboot-springCloud/some/pom.xml create mode 100644 springboot-springCloud/some/src/main/java/com/abel/SomeApplication.java create mode 100644 springboot-springCloud/some/src/main/resources/application.yml create mode 100644 springboot-springCloud/some/src/main/resources/bootstrap.yml create mode 100644 springboot-springCloud/some/target/classes/application.yml create mode 100644 springboot-springCloud/some/target/classes/bootstrap.yml create mode 100644 springboot-springCloud/ui/pom.xml create mode 100644 springboot-springCloud/ui/src/main/java/com/abel/UiApplication.java create mode 100644 springboot-springCloud/ui/src/main/java/com/abel/bean/Person.java create mode 100644 springboot-springCloud/ui/src/main/java/com/abel/controller/UiController.java create mode 100644 springboot-springCloud/ui/src/main/java/com/abel/service/PersonHystrixService.java create mode 100644 springboot-springCloud/ui/src/main/java/com/abel/service/PersonService.java create mode 100644 springboot-springCloud/ui/src/main/java/com/abel/service/SomeHystrixService.java create mode 100644 springboot-springCloud/ui/src/main/resources/application.yml create mode 100644 springboot-springCloud/ui/src/main/resources/bootstrap.yml create mode 100644 springboot-springCloud/ui/src/main/resources/static/css/application.css create mode 100644 springboot-springCloud/ui/src/main/resources/static/fonts/glyphicons-halflings-regular.eot create mode 100644 springboot-springCloud/ui/src/main/resources/static/fonts/glyphicons-halflings-regular.svg create mode 100644 springboot-springCloud/ui/src/main/resources/static/fonts/glyphicons-halflings-regular.ttf create mode 100644 springboot-springCloud/ui/src/main/resources/static/fonts/glyphicons-halflings-regular.woff create mode 100644 springboot-springCloud/ui/src/main/resources/static/index.html create mode 100644 springboot-springCloud/ui/src/main/resources/static/js/app.js create mode 100644 springboot-springCloud/ui/src/main/resources/static/tpl/person.html create mode 100644 springboot-springCloud/ui/src/main/resources/static/tpl/some.html create mode 100644 springboot-springCloud/ui/target/classes/application.yml create mode 100644 springboot-springCloud/ui/target/classes/bootstrap.yml create mode 100644 springboot-springCloud/ui/target/classes/static/css/application.css create mode 100644 springboot-springCloud/ui/target/classes/static/fonts/glyphicons-halflings-regular.eot create mode 100644 springboot-springCloud/ui/target/classes/static/fonts/glyphicons-halflings-regular.svg create mode 100644 springboot-springCloud/ui/target/classes/static/fonts/glyphicons-halflings-regular.ttf create mode 100644 springboot-springCloud/ui/target/classes/static/fonts/glyphicons-halflings-regular.woff create mode 100644 springboot-springCloud/ui/target/classes/static/index.html create mode 100644 springboot-springCloud/ui/target/classes/static/js/app.js create mode 100644 springboot-springCloud/ui/target/classes/static/tpl/person.html create mode 100644 springboot-springCloud/ui/target/classes/static/tpl/some.html diff --git a/springboot-springCloud/config/pom.xml b/springboot-springCloud/config/pom.xml new file mode 100644 index 0000000..7ab688b --- /dev/null +++ b/springboot-springCloud/config/pom.xml @@ -0,0 +1,28 @@ + + + + springCloud + com.abel + 1.0-SNAPSHOT + + 4.0.0 + + config + + + + org.springframework.cloud + spring-cloud-starter + + + org.springframework.cloud + spring-cloud-config-server + + + org.springframework.cloud + spring-cloud-starter-eureka + + + \ No newline at end of file diff --git a/springboot-springCloud/config/src/main/java/com/abel/ConfigApplication.java b/springboot-springCloud/config/src/main/java/com/abel/ConfigApplication.java new file mode 100644 index 0000000..f67e777 --- /dev/null +++ b/springboot-springCloud/config/src/main/java/com/abel/ConfigApplication.java @@ -0,0 +1,21 @@ +package com.abel; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.config.server.EnableConfigServer; +import org.springframework.cloud.netflix.eureka.EnableEurekaClient; + +/** + * Created by yangyibo on 2018/6/27. + * 配置模块 + */ +@SpringBootApplication +//开启配置服务器的支持 +@EnableConfigServer +//开启Eureka server 作为客户端的支持 +@EnableEurekaClient +public class ConfigApplication { + public static void main(String[] args) { + SpringApplication.run(ConfigApplication.class,args); + } +} diff --git a/springboot-springCloud/config/src/main/resources/application.yml b/springboot-springCloud/config/src/main/resources/application.yml new file mode 100644 index 0000000..d603c75 --- /dev/null +++ b/springboot-springCloud/config/src/main/resources/application.yml @@ -0,0 +1,9 @@ +spring: + cloud: + config: + server: + native: + search-locations: classpath:/config #配置其他所需的配置文件的路径 + +server: + port: 8888 diff --git a/springboot-springCloud/config/src/main/resources/bootstrap.yml b/springboot-springCloud/config/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..e1eb80b --- /dev/null +++ b/springboot-springCloud/config/src/main/resources/bootstrap.yml @@ -0,0 +1,14 @@ +spring: + application: + name: config #在Eureka server 注册的服务名为config + profiles: + active: native # 配置服务器使用本地配置(默认为git 配置) + +eureka: + instance: + non-secure-port: ${server.port:8888} #非ssl 端口,若环境变量中server.port有值,则使用环境变量的值,没有则使用8080 + metadata-map: + instanceId: ${spring.application.name}:${random.value} #配置在Eureka server 的实例id + client: + service-url: + defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/ #Eureka 客户端设置的 Eureka server 地址 \ No newline at end of file diff --git a/springboot-springCloud/config/src/main/resources/config/person.yml b/springboot-springCloud/config/src/main/resources/config/person.yml new file mode 100644 index 0000000..cd85094 --- /dev/null +++ b/springboot-springCloud/config/src/main/resources/config/person.yml @@ -0,0 +1,3 @@ +spring: + jpa: + database: HSQL \ No newline at end of file diff --git a/springboot-springCloud/config/src/main/resources/config/some.yml b/springboot-springCloud/config/src/main/resources/config/some.yml new file mode 100644 index 0000000..c95f232 --- /dev/null +++ b/springboot-springCloud/config/src/main/resources/config/some.yml @@ -0,0 +1,2 @@ +my: + message: Message from Development diff --git a/springboot-springCloud/config/target/classes/application.yml b/springboot-springCloud/config/target/classes/application.yml new file mode 100644 index 0000000..d603c75 --- /dev/null +++ b/springboot-springCloud/config/target/classes/application.yml @@ -0,0 +1,9 @@ +spring: + cloud: + config: + server: + native: + search-locations: classpath:/config #配置其他所需的配置文件的路径 + +server: + port: 8888 diff --git a/springboot-springCloud/config/target/classes/bootstrap.yml b/springboot-springCloud/config/target/classes/bootstrap.yml new file mode 100644 index 0000000..e1eb80b --- /dev/null +++ b/springboot-springCloud/config/target/classes/bootstrap.yml @@ -0,0 +1,14 @@ +spring: + application: + name: config #在Eureka server 注册的服务名为config + profiles: + active: native # 配置服务器使用本地配置(默认为git 配置) + +eureka: + instance: + non-secure-port: ${server.port:8888} #非ssl 端口,若环境变量中server.port有值,则使用环境变量的值,没有则使用8080 + metadata-map: + instanceId: ${spring.application.name}:${random.value} #配置在Eureka server 的实例id + client: + service-url: + defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/ #Eureka 客户端设置的 Eureka server 地址 \ No newline at end of file diff --git a/springboot-springCloud/config/target/classes/config/person.yml b/springboot-springCloud/config/target/classes/config/person.yml new file mode 100644 index 0000000..cd85094 --- /dev/null +++ b/springboot-springCloud/config/target/classes/config/person.yml @@ -0,0 +1,3 @@ +spring: + jpa: + database: HSQL \ No newline at end of file diff --git a/springboot-springCloud/config/target/classes/config/some.yml b/springboot-springCloud/config/target/classes/config/some.yml new file mode 100644 index 0000000..c95f232 --- /dev/null +++ b/springboot-springCloud/config/target/classes/config/some.yml @@ -0,0 +1,2 @@ +my: + message: Message from Development diff --git a/springboot-springCloud/discovery/pom.xml b/springboot-springCloud/discovery/pom.xml new file mode 100644 index 0000000..0473b75 --- /dev/null +++ b/springboot-springCloud/discovery/pom.xml @@ -0,0 +1,26 @@ + + + + springCloud + com.abel + 1.0-SNAPSHOT + ../pom.xml + + 4.0.0 + + + discovery + + + + org.springframework.cloud + spring-cloud-starter + + + org.springframework.cloud + spring-cloud-starter-eureka-server + + + \ No newline at end of file diff --git a/springboot-springCloud/discovery/src/main/java/com/abel/DiscoveryApplication.java b/springboot-springCloud/discovery/src/main/java/com/abel/DiscoveryApplication.java new file mode 100644 index 0000000..ac06d6c --- /dev/null +++ b/springboot-springCloud/discovery/src/main/java/com/abel/DiscoveryApplication.java @@ -0,0 +1,17 @@ +package com.abel; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; + +/** + * Created by yangyibo on 2018/6/27. + * 服务发现 一个常规的springboot 项目,只需要通过 @EnableEurekaServer 注解开启对EurekaServer 的支持即可 + */ +@SpringBootApplication +@EnableEurekaServer +public class DiscoveryApplication { + public static void main(String[] args) { + SpringApplication.run(DiscoveryApplication.class,args); + } +} diff --git a/springboot-springCloud/discovery/src/main/resources/application.yml b/springboot-springCloud/discovery/src/main/resources/application.yml new file mode 100644 index 0000000..b2df4c4 --- /dev/null +++ b/springboot-springCloud/discovery/src/main/resources/application.yml @@ -0,0 +1,12 @@ +#云计算环境下习惯使用YAML 配置此处我们也是用YAML 配置 +server: + port: 8761 + +eureka: + instance: + #当前Eureka Server 的hostname 为 localhost + hostname: localhost + #当前服务不需要到Eureka Server 上注册 + client: + register-with-eureka: false + fetch-registry: false diff --git a/springboot-springCloud/discovery/target/classes/application.yml b/springboot-springCloud/discovery/target/classes/application.yml new file mode 100644 index 0000000..b2df4c4 --- /dev/null +++ b/springboot-springCloud/discovery/target/classes/application.yml @@ -0,0 +1,12 @@ +#云计算环境下习惯使用YAML 配置此处我们也是用YAML 配置 +server: + port: 8761 + +eureka: + instance: + #当前Eureka Server 的hostname 为 localhost + hostname: localhost + #当前服务不需要到Eureka Server 上注册 + client: + register-with-eureka: false + fetch-registry: false diff --git a/springboot-springCloud/monitor/pom.xml b/springboot-springCloud/monitor/pom.xml new file mode 100644 index 0000000..f43ea56 --- /dev/null +++ b/springboot-springCloud/monitor/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + monitor + jar + + monitor + Turbine and Hystrix Dashboard + + + springCloud + com.abel + 1.0-SNAPSHOT + ../pom.xml + + + + + org.springframework.cloud + spring-cloud-starter + + + org.springframework.cloud + spring-cloud-starter-hystrix-dashboard + + + org.springframework.cloud + spring-cloud-starter-turbine + + + + + + com.spotify + docker-maven-plugin + + ${project.name}:${project.version} + ${project.basedir}/src/main/docker + false + + + ${project.build.directory} + ${project.build.finalName}.jar + + + + + + + \ No newline at end of file diff --git a/springboot-springCloud/monitor/src/main/java/com/abel/MonitorApplication.java b/springboot-springCloud/monitor/src/main/java/com/abel/MonitorApplication.java new file mode 100644 index 0000000..364680f --- /dev/null +++ b/springboot-springCloud/monitor/src/main/java/com/abel/MonitorApplication.java @@ -0,0 +1,20 @@ +package com.abel; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.EnableEurekaClient; +import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; +import org.springframework.cloud.netflix.turbine.EnableTurbine; +/** + * Created by yangyibo on 2018/6/29. + */ +@SpringBootApplication +@EnableEurekaClient +@EnableHystrixDashboard +@EnableTurbine +public class MonitorApplication { + + public static void main(String[] args) { + SpringApplication.run(MonitorApplication.class, args); + } +} diff --git a/springboot-springCloud/monitor/src/main/resources/application.yml b/springboot-springCloud/monitor/src/main/resources/application.yml new file mode 100644 index 0000000..96307f7 --- /dev/null +++ b/springboot-springCloud/monitor/src/main/resources/application.yml @@ -0,0 +1,3 @@ +server: + port: 8989 + diff --git a/springboot-springCloud/monitor/src/main/resources/bootstrap.yml b/springboot-springCloud/monitor/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..49faa9b --- /dev/null +++ b/springboot-springCloud/monitor/src/main/resources/bootstrap.yml @@ -0,0 +1,10 @@ +spring: + application: + name: monitor + +eureka: + instance: + nonSecurePort: ${server.port:8989} + client: + serviceUrl: + defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/ \ No newline at end of file diff --git a/springboot-springCloud/monitor/target/classes/application.yml b/springboot-springCloud/monitor/target/classes/application.yml new file mode 100644 index 0000000..96307f7 --- /dev/null +++ b/springboot-springCloud/monitor/target/classes/application.yml @@ -0,0 +1,3 @@ +server: + port: 8989 + diff --git a/springboot-springCloud/monitor/target/classes/bootstrap.yml b/springboot-springCloud/monitor/target/classes/bootstrap.yml new file mode 100644 index 0000000..49faa9b --- /dev/null +++ b/springboot-springCloud/monitor/target/classes/bootstrap.yml @@ -0,0 +1,10 @@ +spring: + application: + name: monitor + +eureka: + instance: + nonSecurePort: ${server.port:8989} + client: + serviceUrl: + defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/ \ No newline at end of file diff --git a/springboot-springCloud/person/pom.xml b/springboot-springCloud/person/pom.xml new file mode 100644 index 0000000..19928b0 --- /dev/null +++ b/springboot-springCloud/person/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + + springCloud + com.abel + 1.0-SNAPSHOT + ../pom.xml + + person + + + org.springframework.cloud + spring-cloud-starter + + + org.springframework.cloud + spring-cloud-config-client + + + org.springframework.cloud + spring-cloud-starter-eureka + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.hsqldb + hsqldb + + + postgresql + postgresql + 9.1-901-1.jdbc4 + + + \ No newline at end of file diff --git a/springboot-springCloud/person/src/main/java/com/abel/PersonApplication.java b/springboot-springCloud/person/src/main/java/com/abel/PersonApplication.java new file mode 100644 index 0000000..b11054e --- /dev/null +++ b/springboot-springCloud/person/src/main/java/com/abel/PersonApplication.java @@ -0,0 +1,14 @@ +package com.abel; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * Created by yangyibo on 2018/6/28. + */ +@SpringBootApplication +public class PersonApplication { + public static void main(String[] args) { + SpringApplication.run(PersonApplication.class,args); + } +} diff --git a/springboot-springCloud/person/src/main/java/com/abel/bean/Person.java b/springboot-springCloud/person/src/main/java/com/abel/bean/Person.java new file mode 100644 index 0000000..5ba2a12 --- /dev/null +++ b/springboot-springCloud/person/src/main/java/com/abel/bean/Person.java @@ -0,0 +1,45 @@ +package com.abel.bean; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +/** + * Created by yangyibo on 2018/6/28. + */ +@Entity +public class Person { + @Id + @GeneratedValue + private Long id; + + private String name; + + public Person() { + super(); + } + + public Person(String name) { + super(); + this.name = name; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + + +} diff --git a/springboot-springCloud/person/src/main/java/com/abel/controller/PersonController.java b/springboot-springCloud/person/src/main/java/com/abel/controller/PersonController.java new file mode 100644 index 0000000..18aa081 --- /dev/null +++ b/springboot-springCloud/person/src/main/java/com/abel/controller/PersonController.java @@ -0,0 +1,29 @@ +package com.abel.controller; +import com.abel.bean.Person; +import com.abel.dao.PersonRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * Created by yangyibo on 2018/6/27. + */ + +@RestController +public class PersonController { + @Autowired + PersonRepository personRepository; + + @RequestMapping(value = "/save", method = RequestMethod.POST) + public List savePerson(@RequestBody String personName) { + Person p = new Person(personName); + personRepository.save(p); + List people = personRepository.findAll(new PageRequest(0, 10)).getContent(); + return people; + } +} \ No newline at end of file diff --git a/springboot-springCloud/person/src/main/java/com/abel/dao/PersonRepository.java b/springboot-springCloud/person/src/main/java/com/abel/dao/PersonRepository.java new file mode 100644 index 0000000..7f9def0 --- /dev/null +++ b/springboot-springCloud/person/src/main/java/com/abel/dao/PersonRepository.java @@ -0,0 +1,10 @@ +package com.abel.dao; +import com.abel.bean.Person; +import org.springframework.data.jpa.repository.JpaRepository; + +/** + * Created by yangyibo on 2018/6/28. + */ +public interface PersonRepository extends JpaRepository{ + +} diff --git a/springboot-springCloud/person/src/main/resources/application.yml b/springboot-springCloud/person/src/main/resources/application.yml new file mode 100644 index 0000000..886b4b0 --- /dev/null +++ b/springboot-springCloud/person/src/main/resources/application.yml @@ -0,0 +1,9 @@ +server: + port: 8082 + + +spring: + jpa: + hibernate: + ddl-auto: update + diff --git a/springboot-springCloud/person/src/main/resources/bootstrap.yml b/springboot-springCloud/person/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..4890014 --- /dev/null +++ b/springboot-springCloud/person/src/main/resources/bootstrap.yml @@ -0,0 +1,15 @@ +spring: + application: + name: person + cloud: + config: + enabled: true + discovery: + enabled: true + service-id: CONFIG #1 +eureka: + instance: + non-secure-port: ${server.port:8082} + client: + service-url: + defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/ \ No newline at end of file diff --git a/springboot-springCloud/person/target/classes/application.yml b/springboot-springCloud/person/target/classes/application.yml new file mode 100644 index 0000000..886b4b0 --- /dev/null +++ b/springboot-springCloud/person/target/classes/application.yml @@ -0,0 +1,9 @@ +server: + port: 8082 + + +spring: + jpa: + hibernate: + ddl-auto: update + diff --git a/springboot-springCloud/person/target/classes/bootstrap.yml b/springboot-springCloud/person/target/classes/bootstrap.yml new file mode 100644 index 0000000..4890014 --- /dev/null +++ b/springboot-springCloud/person/target/classes/bootstrap.yml @@ -0,0 +1,15 @@ +spring: + application: + name: person + cloud: + config: + enabled: true + discovery: + enabled: true + service-id: CONFIG #1 +eureka: + instance: + non-secure-port: ${server.port:8082} + client: + service-url: + defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/ \ No newline at end of file diff --git a/springboot-springCloud/pom.xml b/springboot-springCloud/pom.xml new file mode 100644 index 0000000..601c6d6 --- /dev/null +++ b/springboot-springCloud/pom.xml @@ -0,0 +1,108 @@ + + + 4.0.0 + + com.abel + springCloud + pom + 1.0-SNAPSHOT + + + config + discovery + ui + person + some + monitor + + + + UTF-8 + 1.8 + + + + + org.springframework.cloud + spring-cloud-starter-parent + Angel.SR3 + + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + com.spotify + docker-maven-plugin + 0.2.9 + + true + + + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + \ No newline at end of file diff --git a/springboot-springCloud/some/pom.xml b/springboot-springCloud/some/pom.xml new file mode 100644 index 0000000..703a886 --- /dev/null +++ b/springboot-springCloud/some/pom.xml @@ -0,0 +1,29 @@ + + + + springCloud + com.abel + 1.0-SNAPSHOT + + 4.0.0 + + some + + + + org.springframework.cloud + spring-cloud-starter + + + org.springframework.cloud + spring-cloud-config-client + + + org.springframework.cloud + spring-cloud-starter-eureka + + + + \ No newline at end of file diff --git a/springboot-springCloud/some/src/main/java/com/abel/SomeApplication.java b/springboot-springCloud/some/src/main/java/com/abel/SomeApplication.java new file mode 100644 index 0000000..842ab86 --- /dev/null +++ b/springboot-springCloud/some/src/main/java/com/abel/SomeApplication.java @@ -0,0 +1,27 @@ +package com.abel; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * Created by yangyibo on 2018/6/28. + */ +@SpringBootApplication +@EnableDiscoveryClient +@RestController +public class SomeApplication { + @Value("${my.message}") //Value 值来自于config server + private String message; + + @RequestMapping(value = "/getsome") + public String getsome(){ + return message; + } + public static void main(String[] args) { + SpringApplication.run(SomeApplication.class, args); + } +} diff --git a/springboot-springCloud/some/src/main/resources/application.yml b/springboot-springCloud/some/src/main/resources/application.yml new file mode 100644 index 0000000..b67e63e --- /dev/null +++ b/springboot-springCloud/some/src/main/resources/application.yml @@ -0,0 +1,2 @@ +server: + port: 8083 diff --git a/springboot-springCloud/some/src/main/resources/bootstrap.yml b/springboot-springCloud/some/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..28ef2cb --- /dev/null +++ b/springboot-springCloud/some/src/main/resources/bootstrap.yml @@ -0,0 +1,15 @@ +spring: + application: + name: some + cloud: + config: + enabled: true + discovery: + enabled: true + service-id: CONFIG +eureka: + instance: + non-secure-port: ${server.port:8083} + client: + service-url: + defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/ \ No newline at end of file diff --git a/springboot-springCloud/some/target/classes/application.yml b/springboot-springCloud/some/target/classes/application.yml new file mode 100644 index 0000000..b67e63e --- /dev/null +++ b/springboot-springCloud/some/target/classes/application.yml @@ -0,0 +1,2 @@ +server: + port: 8083 diff --git a/springboot-springCloud/some/target/classes/bootstrap.yml b/springboot-springCloud/some/target/classes/bootstrap.yml new file mode 100644 index 0000000..28ef2cb --- /dev/null +++ b/springboot-springCloud/some/target/classes/bootstrap.yml @@ -0,0 +1,15 @@ +spring: + application: + name: some + cloud: + config: + enabled: true + discovery: + enabled: true + service-id: CONFIG +eureka: + instance: + non-secure-port: ${server.port:8083} + client: + service-url: + defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/ \ No newline at end of file diff --git a/springboot-springCloud/ui/pom.xml b/springboot-springCloud/ui/pom.xml new file mode 100644 index 0000000..3a577e7 --- /dev/null +++ b/springboot-springCloud/ui/pom.xml @@ -0,0 +1,63 @@ + + + + springCloud + com.abel + 1.0-SNAPSHOT + + 4.0.0 + + ui + + + + org.springframework.cloud + spring-cloud-starter + + + org.springframework.cloud + spring-cloud-starter-hystrix + + + org.springframework.cloud + spring-cloud-starter-zuul + + + org.springframework.cloud + spring-cloud-config-client + + + org.springframework.cloud + spring-cloud-starter-eureka + + + org.springframework.cloud + spring-cloud-starter-feign + + + org.springframework.cloud + spring-cloud-starter-ribbon + + + + org.webjars + bootstrap + + + org.webjars + angularjs + 1.3.15 + + + org.webjars + angular-ui-router + 0.2.13 + + + org.webjars + jquery + + + \ No newline at end of file diff --git a/springboot-springCloud/ui/src/main/java/com/abel/UiApplication.java b/springboot-springCloud/ui/src/main/java/com/abel/UiApplication.java new file mode 100644 index 0000000..ac0e602 --- /dev/null +++ b/springboot-springCloud/ui/src/main/java/com/abel/UiApplication.java @@ -0,0 +1,22 @@ +package com.abel; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; +import org.springframework.cloud.netflix.eureka.EnableEurekaClient; +import org.springframework.cloud.netflix.feign.EnableFeignClients; +import org.springframework.cloud.netflix.zuul.EnableZuulProxy; + +/** + * Created by yangyibo on 2018/6/29. + */ +@SpringBootApplication +@EnableEurekaClient +@EnableFeignClients //开启fegin 客户端支持 +@EnableCircuitBreaker //开启CircuitBreaker的支持 +@EnableZuulProxy //开启网关代理支持 +public class UiApplication { + public static void main(String[] args) { + SpringApplication.run(UiApplication.class, args); + } +} diff --git a/springboot-springCloud/ui/src/main/java/com/abel/bean/Person.java b/springboot-springCloud/ui/src/main/java/com/abel/bean/Person.java new file mode 100644 index 0000000..0c17e08 --- /dev/null +++ b/springboot-springCloud/ui/src/main/java/com/abel/bean/Person.java @@ -0,0 +1,39 @@ +package com.abel.bean; + +/** + * Created by yangyibo on 2018/6/29. + */ +public class Person { + private Long id; + + private String name; + + + public Person() { + super(); + } + + public Person(String name) { + super(); + this.name = name; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + + +} \ No newline at end of file diff --git a/springboot-springCloud/ui/src/main/java/com/abel/controller/UiController.java b/springboot-springCloud/ui/src/main/java/com/abel/controller/UiController.java new file mode 100644 index 0000000..0c361f7 --- /dev/null +++ b/springboot-springCloud/ui/src/main/java/com/abel/controller/UiController.java @@ -0,0 +1,34 @@ +package com.abel.controller; + +import com.abel.bean.Person; +import com.abel.service.PersonHystrixService; +import com.abel.service.SomeHystrixService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * Created by yangyibo on 2018/6/29. + */ +@RestController +public class UiController { + @Autowired + private SomeHystrixService someHystrixService; + + @Autowired + private PersonHystrixService personHystrixService; + + @RequestMapping("/dispatch") + public List sendMessage(@RequestBody String personName) { + return personHystrixService.save(personName); + } + + @RequestMapping(value = "/getsome",produces={MediaType.TEXT_PLAIN_VALUE}) + public String getSome(){ + return someHystrixService.getSome(); + } +} diff --git a/springboot-springCloud/ui/src/main/java/com/abel/service/PersonHystrixService.java b/springboot-springCloud/ui/src/main/java/com/abel/service/PersonHystrixService.java new file mode 100644 index 0000000..161ecc6 --- /dev/null +++ b/springboot-springCloud/ui/src/main/java/com/abel/service/PersonHystrixService.java @@ -0,0 +1,31 @@ +package com.abel.service; + +import com.abel.bean.Person; +import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +/** + * Created by yangyibo on 2018/6/29. + * 调用person service的断路器 + */ +@Service +public class PersonHystrixService { + + @Autowired + PersonService personService; + + @HystrixCommand(fallbackMethod = "fallbackSave") //使用HystrixCommand的fallbackMethod参数指定,调用失败的时候调用后备方法 fallbackMethod + public List save(String name) { + return personService.save(name); + } + + public List fallbackSave(String name){ + List list = new ArrayList(); + Person p = new Person(name+"没有保存成功,Person Service 故障"); + list.add(p); + return list; + } +} diff --git a/springboot-springCloud/ui/src/main/java/com/abel/service/PersonService.java b/springboot-springCloud/ui/src/main/java/com/abel/service/PersonService.java new file mode 100644 index 0000000..c5989fe --- /dev/null +++ b/springboot-springCloud/ui/src/main/java/com/abel/service/PersonService.java @@ -0,0 +1,24 @@ +package com.abel.service; + +import com.abel.bean.Person; +import org.springframework.cloud.netflix.feign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.util.List; + +/** + * Created by yangyibo on 2018/6/29. + * 使用 Feign 调用person service + * 我们只需要通过简单在接口中声明方法即可调用Person的 Rest服务 + */ +@FeignClient("person") +public interface PersonService { + @RequestMapping(method = RequestMethod.POST, value = "/save", + produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + List save(@RequestBody String name); +} diff --git a/springboot-springCloud/ui/src/main/java/com/abel/service/SomeHystrixService.java b/springboot-springCloud/ui/src/main/java/com/abel/service/SomeHystrixService.java new file mode 100644 index 0000000..6453334 --- /dev/null +++ b/springboot-springCloud/ui/src/main/java/com/abel/service/SomeHystrixService.java @@ -0,0 +1,26 @@ +package com.abel.service; + +import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +/** + * Created by yangyibo on 2018/6/29. + * 使用ribbon 调用some service 并使用断路器 + */ +@Service +public class SomeHystrixService { + + @Autowired + RestTemplate restTemplate; // springboot 下使用ribbon ,只需注入一个 RestTemplate,springboot 已经为我们做好了配置 + + @HystrixCommand(fallbackMethod = "fallbackSome") //HystrixCommand 的参数指定,当调用失败时,使用备用方法fallbackMethod + public String getSome() { + return restTemplate.getForObject("http://some/getsome", String.class); + } + + public String fallbackSome(){ + return "some service模块故障"; + } +} diff --git a/springboot-springCloud/ui/src/main/resources/application.yml b/springboot-springCloud/ui/src/main/resources/application.yml new file mode 100644 index 0000000..b6c3bb3 --- /dev/null +++ b/springboot-springCloud/ui/src/main/resources/application.yml @@ -0,0 +1,6 @@ +server: + port: 80 + + + + diff --git a/springboot-springCloud/ui/src/main/resources/bootstrap.yml b/springboot-springCloud/ui/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..ae4ed53 --- /dev/null +++ b/springboot-springCloud/ui/src/main/resources/bootstrap.yml @@ -0,0 +1,10 @@ +spring: + application: + name: ui + +eureka: + instance: + non-secure-port: ${server.port:80} + client: + service-url: + defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/ \ No newline at end of file diff --git a/springboot-springCloud/ui/src/main/resources/static/css/application.css b/springboot-springCloud/ui/src/main/resources/static/css/application.css new file mode 100644 index 0000000..e587952 --- /dev/null +++ b/springboot-springCloud/ui/src/main/resources/static/css/application.css @@ -0,0 +1,15 @@ +body { + padding-top: 60px; +} + +h1, h2, h3, h4, h5, h6 { + font-family: microsoft yahei; +} +p, div { + font-family: microsoft yahei; + font-size: 12px; +} + +.longtext { + word-break:break-all; +} diff --git a/springboot-springCloud/ui/src/main/resources/static/fonts/glyphicons-halflings-regular.eot b/springboot-springCloud/ui/src/main/resources/static/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 0000000000000000000000000000000000000000..4a4ca865d67e86f961bc6e2ef00bffa4e34bb9ed GIT binary patch literal 20335 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

!oRptb@n+ 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 literal 0 HcmV?d00001 diff --git a/springboot-springCloud/ui/src/main/resources/static/fonts/glyphicons-halflings-regular.svg b/springboot-springCloud/ui/src/main/resources/static/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 0000000..e3e2dc7 --- /dev/null +++ b/springboot-springCloud/ui/src/main/resources/static/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/springboot-springCloud/ui/src/main/resources/static/fonts/glyphicons-halflings-regular.ttf b/springboot-springCloud/ui/src/main/resources/static/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..67fa00bf83801d2fa568546b982c80d27f6ef74e GIT binary patch literal 41280 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

PBK;&Wcg|NX{>QR@W3{K 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 literal 0 HcmV?d00001 diff --git a/springboot-springCloud/ui/src/main/resources/static/fonts/glyphicons-halflings-regular.woff b/springboot-springCloud/ui/src/main/resources/static/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 0000000000000000000000000000000000000000..8c54182aa5d4d1ab3c9171976b615c1dcb1dc187 GIT binary patch literal 23320 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 mX4Rts + + + + 原生云应用 + + + + + + + + + + +

+
+
+
+
+
+ + \ No newline at end of file diff --git a/springboot-springCloud/ui/src/main/resources/static/js/app.js b/springboot-springCloud/ui/src/main/resources/static/js/app.js new file mode 100644 index 0000000..db06c5d --- /dev/null +++ b/springboot-springCloud/ui/src/main/resources/static/js/app.js @@ -0,0 +1,49 @@ +var uiApp = angular.module("uiApp", ["ui.router"]); + +uiApp.config(function ($stateProvider, $urlRouterProvider) { + $urlRouterProvider.otherwise("person"); + + $stateProvider + .state('person', { + url: '/person', + templateUrl: '/tpl/person.html', + controller: 'PersonController' + }) + .state('some',{ + url:'/some', + templateUrl: '/tpl/some.html', + controller: 'SomeController' + }); +}); + + +uiApp.controller("PersonController", function ($scope, $http) { + + $scope.people = ""; + $scope.errorMessage = ""; + + $scope.getMessageResponse = function(personName) { + $http.post('/dispatch', personName).success(function(data){ + $scope.people = data; + $scope.errorMessage = ""; + }).error(function() { + $scope.errorMessage = "错误"; + }); + } + +}); + + +uiApp.controller("SomeController", function ($scope, $http) { + $scope.str = ""; + $scope.errorMessage = ""; + +$scope.getSome = function(){ + $http.get('/getsome').success(function(data){ + $scope.str = data; + $scope.errorMessage = ""; + }).error(function() { + $scope.errorMessage = "错误"; + }); +} +}); \ No newline at end of file diff --git a/springboot-springCloud/ui/src/main/resources/static/tpl/person.html b/springboot-springCloud/ui/src/main/resources/static/tpl/person.html new file mode 100644 index 0000000..3382e34 --- /dev/null +++ b/springboot-springCloud/ui/src/main/resources/static/tpl/person.html @@ -0,0 +1,34 @@ +
+
+
+
+

通过Person Service保存person

+
+
+
+
+
+
+

+ +

+
+
+
+
+
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+
diff --git a/springboot-springCloud/ui/src/main/resources/static/tpl/some.html b/springboot-springCloud/ui/src/main/resources/static/tpl/some.html new file mode 100644 index 0000000..7e20b79 --- /dev/null +++ b/springboot-springCloud/ui/src/main/resources/static/tpl/some.html @@ -0,0 +1,27 @@ +
+
+
+
+

从 Some Service获得字符

+
+
+
+
+
+
+

+ +

+
+
+
+ +
+ +
+ +
+
+
+
+
diff --git a/springboot-springCloud/ui/target/classes/application.yml b/springboot-springCloud/ui/target/classes/application.yml new file mode 100644 index 0000000..b6c3bb3 --- /dev/null +++ b/springboot-springCloud/ui/target/classes/application.yml @@ -0,0 +1,6 @@ +server: + port: 80 + + + + diff --git a/springboot-springCloud/ui/target/classes/bootstrap.yml b/springboot-springCloud/ui/target/classes/bootstrap.yml new file mode 100644 index 0000000..ae4ed53 --- /dev/null +++ b/springboot-springCloud/ui/target/classes/bootstrap.yml @@ -0,0 +1,10 @@ +spring: + application: + name: ui + +eureka: + instance: + non-secure-port: ${server.port:80} + client: + service-url: + defaultZone: http://${eureka.host:localhost}:${eureka.port:8761}/eureka/ \ No newline at end of file diff --git a/springboot-springCloud/ui/target/classes/static/css/application.css b/springboot-springCloud/ui/target/classes/static/css/application.css new file mode 100644 index 0000000..e587952 --- /dev/null +++ b/springboot-springCloud/ui/target/classes/static/css/application.css @@ -0,0 +1,15 @@ +body { + padding-top: 60px; +} + +h1, h2, h3, h4, h5, h6 { + font-family: microsoft yahei; +} +p, div { + font-family: microsoft yahei; + font-size: 12px; +} + +.longtext { + word-break:break-all; +} diff --git a/springboot-springCloud/ui/target/classes/static/fonts/glyphicons-halflings-regular.eot b/springboot-springCloud/ui/target/classes/static/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 0000000000000000000000000000000000000000..4a4ca865d67e86f961bc6e2ef00bffa4e34bb9ed GIT binary patch literal 20335 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

!oRptb@n+ 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 literal 0 HcmV?d00001 diff --git a/springboot-springCloud/ui/target/classes/static/fonts/glyphicons-halflings-regular.svg b/springboot-springCloud/ui/target/classes/static/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 0000000..e3e2dc7 --- /dev/null +++ b/springboot-springCloud/ui/target/classes/static/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/springboot-springCloud/ui/target/classes/static/fonts/glyphicons-halflings-regular.ttf b/springboot-springCloud/ui/target/classes/static/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..67fa00bf83801d2fa568546b982c80d27f6ef74e GIT binary patch literal 41280 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

PBK;&Wcg|NX{>QR@W3{K zY;bp3^^^Hp4EgCcp#a7O7KV(e2E!07sKTguG(W~^?4lZ66!OsI#=Iw^QS(LZUvY)|-*On%Um?5>WA zl?50LJ%&XEbBcfmH}zOz=!^;alP6P=Rtc7q@Q=l%gyhRfi2{4}=YdE4KV#1hzuEkL zQ`e!oCxJ!)KmnXWYrzo%_u;5NbadmMK<}VRv{vp06NK?w7^1Q$Tj1RM!76dG8csvB z!8uB~T2M}Lf-thpE(M7RjA_gX6%1j2BB6X0eI$mNZ8{a1K44Q>^W@3P_G84KehO22 zJG-|8&J9&`rg~weKrl1JkCIVq&`ucl7;DHYw@0%Zyc$6}?KFTU+2;?{&=A`cEfAzN zU!jp_g3S-`18T6M@<#h3A_2$=zd4rj5XfwaD;BKizzZu%((a@Bm!J{db@_d4*S%kS z85)uJ6H=aVdJ9w~XjG@unH$c0h>vFo<4HQ6M~DkI2t|eFJmy!hTnt8Ojt6To$AMXy z%Ec-Z9jL;jXKDjiV*u!Qj44=K))MH9htwFwi|JpZJZ~{M?9ff()c#tpX0uYaf>A6l zaV{Qgbe)MnbW#laMf4`G#PjHlIUp%<3ly2&o*d>RpmOTnmY2VHufF-SoA1<)E?~R( z=WgS$I7Euy4Rm(-QH_=+`sBw1ta=csoM*|uG8xBOE~wUwTAd@51j zuy`QZW4sK^2*CTH5tN8z;Mj{$CxYdT<=Hw1#U3GNO1s#SIAVG`KswTTkWM*}C5vDY4%wW!qp-T+P zjiH`H`Pj08wXN8~6_I0Gp}9bcbE~-^4mD3Jt=O_gbB3QV zH@0hfXH~q;wCr?tu*vs1?)CViBPBqx&5q{6GO8C#^wH0-chR_FWDrbUXgQ%zxOyH_!jd8*jbwmGetZ z>mI90oWQ{QRn`etwI7z}UM6U%>aS8Ge=hn7*WU)BCt>J`RFVl82?Fd<+Sqyf4cQeRYe?3g$5AO038R??pu*~f{I-;y@--*Usl#4Re< zL0XHkkYPBDUr**?V_4F#Mn-@8g*jJTGHZ?Tt9?CpKKr#hdN1F8-^loVTRu^_1Pm+j5TO#%nF7n|JOqvwP95V~0xY6*TP0JMx!rzqf3C;CtWMZ5^~0 zfB$CDI*O00kSYqexd!cwb5wk$FblTdB4HV028U~%vtf*Q%f;rdIV3Y`GsSf4V#7cw zCfk?Lv4)H$nsHSE3V9aY)Liqi7Y81?fbh=cWVC3e2(E;^A(2-yY~Y<$WZLA)Y7gE$ zT8E=mZQ+p1K(^Syah8q-KrYPTrn>-c$%9<8=VNnP74)pTvUR)I5b;omxX3DD3l3;dW|5Dauo)5oQzd4%ke=n%?~M z83VJpFzJdbi5`Mmay@YZ(+%OsARvLo1SC=ifx8=s3|(X#g#d^XKyO?vL1Z#q?Zb;5 zA-fy+dO>$`EsG3s{LwJd8U9DwWodXXebC_2=_AG&D82jX5Lrq30g|WU3-n9;qCyE< z1?eqPcW{p*(2a2s325o|LSc9|Aw45lHu+UfTu(L|)=yFP*VE`$m9;=Po8=Y}R!}aM z;WRW529hmKs7+7^%Bl}03PuiYIM^lC*n;I+XCVHGG6`wTL(U9~xvx*FgS6)E49qQ% zC;{JnAPtIzXtlv-0G~aTPufS%E41M&N2w&e_2F_XBhp*Ps!L~{dD73yyf)TNi=pdT zNP@zwBc%)LA(R5GyG`y`07Vhif3$W;Z9geJw zgy{`K@NafEbUml^`&HpcBusC(FOTyw{RZ@<`_@2y18KsYLzqEybJdUOVAyuJKY9E# zy8nLMKS(N6XIC9}f=p~dGDqksgTh&9$ghkW;;y0tOrSfn>_uvl!!@Z%D(&MWjXlLx z7&NiNe`EN*;PWEA7v?n9Fnd|GPcWzL5Jg4N0^J9*27q z7YoDQg7}`yo;_9#7Azd&p?6FG5Qp_rgBBy82SCT5LYo66_9A;R95{9;5N0pvbL5-- zkqE^(jjVfQ!-e3bgNHXsw1b5N%MmuCoqMP$v;wgoMTy5;j9QS;YtRL7CxS8nfe{!6 zYy=iEL9Hy%fV~2X0 z#O3|xh#tG%Z}*6UDbZ(VN9;Z^B|7ZGd+js^n6tA>CGoYbTiF@3mVJ2J=j|?+o!-zl z880I~AS@(>cJRd&JQ@M$a&ty)hnfb@Dh49Udl4-cqa2@%X3*EDM@yqOtz|8Tu0$~m zYE7Tknnsu6jma2wNo#M$UbG=W7NHtfw2m$aG@p0Bqoy_kFC!^NMs$OLQFh2!z+Ix7 zM>z-tp#eb?{XvR;XdvZpTC?;Pp)|W?cP_uOrPRD)YKOzQ8=6vKS83O-lDU7Vzki5< zI&>8&P1d?OJ+0UY_@_0)6vj2XSd1>}KL?^m6nZ%CJqw$-0WX955Z4na7eyyYccvyX z2oy84(4K}4Hj~9e7zP9&q!4U^wJrfm(Z$@1`9i)Pc3E?Oqwg$s=L%125BqXMlQ&{E z>$jY(Us+x6Y;n8Ureeo6gTdamKflqw7Liabz7AKF^yV>dXPvVae))f8uY5-TK6nmu zLi#@DYYY})m#|SN#)#+QW#bcJM;M=$vf9P1p(+nJjE@pf*Lay0t2mY|j1H`cWbB{< zX62)l?7%1mF)+<>Y}EIuEedwkE&~6dBlb|JM0baj?lBR1Nh1-F@yQZtvKvTG?J+hI z&{0KOurbPhb=|i^@dk$zgzj$L^7yjSm)G5T(>afPdhw-uA6jS0HA&OzL*Xj7Wgb&M zlRrD(WVJ}n+-Y0puDW+gX~U{BZY$ilWW@%sA>;t&rE~??y=UgvhIy`es<9(OlyR{j0uR*$h-@{gKz7%1**%k? zlOYRapLB|@$Dc5IS1`Kn&y01wBjCvqRq&F2I@d%%3V$1Q2;S z`7-d2?uP^NVzR_O+)wXPjNWMt!S-8xyPDp`A$lL)3)O{|74C5YGP5#~nRMds7vZ5&8wZ(r^v{u0f2-j0|9Z zip8kJTaaIQyx-V2iuPB)t&iCs->brSvZGsL<3W8K8wA7Ug?@;aj&AC2jc$%R`qBL| zdSvwOCdpe&d%pIK&4rQpkrkD3LrejN4lxDjC1MIN zbgOuL!KFODppd1J+?pdF&NUDdw~~%f^u#*JCbB^gHccU`=Qh4}PL3Uz9NF=4`(x0F z!4s2d^>O=SPR@_sBD`gcXa1h;e}L-8c74pSj2ky(lN<+{$Yqronrf}kB1{D$72{Sr zg21pec7W=O5Y$8JI+^Eu1%a_gQk46_CW(W;L$pl@_}KW$rQ}4Z&r>0#QMlBVns7F0E8Zllg+cxU*K5-Sf8k)>cByD zR+)FVvn&69**9`M`(WL{B4+Zf|eCMz5v#4M2e_>(&f1matzv>$xLYm+}2ysk)hGhn7C0 z(gTPkq8vJcwj0s41jbqohgBWoUbHHi+8U;|T7+t@X8;ywxom{_xz^qxr&GjB+{7?{ z?)snKaO2OeU$Eex`ugk*=bwFb>&zD)xMb4<4;6Q*3Y|V%e7a3;!|_hJy@6~o6q^?%_}agJ3LmN6ZCOp;R)DbTxD_!`^<3T^{|m{t6j{>eFWHUZf zm^jAN4w)_Frm6I$XQV5vUy8DTjRhK9CUnLm-m&`L$(?y3a^Z#NM#AhO{Xt9h{8?*e z^%*@{9vd3z(Stqc5R0b}Wx?3b;V$q0wde}vW?eScuf6D37=90||J(*bzj%*0#>V?H z=Jx0K8Tas8B2mIGC}KU1@v@<#`+~6f>6ol&u{eSF72$P?(XxpM!b9KMW(*efuT1XT z8dfLf@77nq#YUqP(nh*8r}Q=I(+>R)bpG_uk`0L$)=UkOZjMm&65nC&!Fq&!W5aTZ zcq>1=B5*_zBuv5hn#YexXy!64NHIZGAxJb)(FDv#0PQS*H3Cr^_^>gcu0V`%0IMLy zE3x$VIT~8}zWy5U&60Q~YkJu@^0NMG{lLqJ@4%HW6O9e~_IA+N2Pzw0K?h<+AR-Lf zqCJHCVQm}rU?7eIF)rlQz#;T}S| zkDDU0&~e-a63FN^N1Ke`+yL%j{4?%Uxe?v!#GC0gl^a%%-joSNhi=Hx(eq+U;+S&`Fa@@1PE$UPzM*eQ7r>_r@;&9^T|8jHMYXl7SkT z#`hU~qhNt%N5t;oAIpoW!<3=I-ZFS}+!*19z=J>_5q4xuktJ1&?ts^Gq?H}xCMWxbjzPlxD9Qk_L>0cH`(Z+GzVq^oEQf(Ocfzf3 zl6xVHWb97-J`?UiV^o0OOO>0rPUEfUG^EgwDnsl%$$mrV$^zP~Z z#$5T9V3GbNe~riJGKAiyza=jJi~b1P@E39Iu=*Fa0bA5J&+%W#E97g)nn~JNo`oy{ z9Aq2xNB$~K53phNMSkhAfCbt0{@yiFB-)gTmsV4PVs3&S0q9$Ks$mZp(2I6rax6k$S}jQBXCO;9WV$4Id%HV>U6FP06B+x-ED9c3}wu1qy@_{Yz3EU8f7CQ}8fUNcbR4E(RO5=;LRnx%r@Mm`?QTUg1HYU^S40y) zeeE|*g(uehGat~j*M|NAxqDi#LF4-sfg4U49oeo#ClF8fN zP@m|U-Bp)8eNO5wta21vH;!M$8qw^uTTBw-i#gC)&9mpp#UG zqN%=_@C`&|TOw(~H@Yy6KBy4;8WJ5DK73y6A*M_dC@d%3r!u7&X=>)ShtiWn`~@5t z5ix`gxR?cATtL`4sN*==n}>fEyEuqbxxn|McYeCmyJeI2M?b20eqHG^cSY7$U$Llk zfA=e;nvDxfi!QJJIefP_-CtWO`ImokPU(WZ@t0nzd*G%8msS7dC!Jp^Exe@q$3F^P zI=^J_>-bpD=vd5GC2r0Lr8h!5AzEl&li^1(Q#|I&Po9548x4-*aRC!KaWu+rT-3v< zLcbQ=dFN##|2d0|#&wPl-~6|cOK>fpbL0C^b3z}+ho@HhK#{0peK6wI#`<75H^)na zu|7atu~W5v(~h-2-l;!+%7*KS9c#-w^(Rhfb6us)V0^GYF}{%;YOFXEuL!#Hie*!VMmqEGUdkz?-?<3F`puEwF^~KXmeY~n!P2F|69iS2 zekIN>VohjEi$2q68Bc%4?+C)ba@`v6Ne_%^YPw4@&%OIU9;W`EtA2G`>GoHjxzNho zMlZz1*`F9MYs`pmQ4DR7sjiIXuIP9nhJQZ1lz8YimfESme%sqSS?V@@Gb+MV4oEgS zf?de21|cEuly`zIXbBA6xB^>O;lI+r(sYsj8ryptOYhWQyG_Lree*W`HL-_&EWJa2 zZ5t%B5mWgfbT-O8UBc8-Z!+zF*_u-cy!@&^T?ofd-v&S6{ieKMbjhfdVCfC!dz0YTeul6S!&fa^ zer>Z#fhirCi#LAZ?zb*#TX@lxpSzRJ*dE2Hs+EI#Q!~%Kbye1HGlgq%SI1&6 zVfr$}6FBAB@_zs;Ng#@C0oP*Zl+`&NZ90ZxAzstxfPJR+LP>*A^CLw+6f_zeVL<4h z%S4b|m+zPJy<$2T3Z~)n74y(=B9cqCm}#3`VY1Dg8y%cFrO6$0`IoIxOwpj-=9VO@ ztELg9A2!VzaHk&oYA}$V=k_jJY06c#T)42qEjnc@V-8QPH#Ie6adppR-x`cexurc| zPxjA<48EIQzPAux(B|{U+##!j$!353j9Hh@dYY}gtZnrpCX}G~)NA)!qZeHE#7gJ1 zy6(EBP>n~ncPv>G>$n^u=lJ)9o8))p98j>Ch+Uf{P=pNMft$_1P^~FPmF$uAO|~A$NM^was_1 ze0XYKq)Yu@wc~<2x-Pyrx!C6yhnnn7YgetGm&wdqziKUZChyzV&p2mFYg6v5X&1TJ zg5;d3H4E2K%KPdCYp>oq>*DJ5jg2%-K??!2P=Q5KM8j#qmxZF6W-3{tgBgkjReNi{ zJ>x(B^EX1E)vmfbT&nZCCe6kE=2EM^i}>z+4!6_Sy3fPkYxsLDe{baPNqR5hER~W; zm|>tHUK%md$oN9qW1s5i6P|ZCt2{NejmeJ69~-dakjp*cU`K~KP|LuJL~9D4&ang$ zIPWF0RtP*3G6JC=xB?kq`G`mZB99V${*39#&*?9JF1h0It1eF4ANs}f$xZigqGm#o zscsi*N(I|94V}IW+t8Yxbz4VOZLKAF#>UT%kz3jM;qrR|8!xU++Bw{-!2p_onm6Fp-Xb3Bu9Kb9%gx6GDo^8fi4y zLY6et=YUcNDC>&4q{)@63k=`vpW+|B`M=nA*mv|N$l)`4_Pm%JYcRz=JXjEaIoyt5 zH)PR3dnS=f@mc|_gDS>xzCgjF6dc`>QIlNGLa}jVi$NYG8LUPWL^4QG5R{{;wSv=w z2n*1{5wgi_5o`vNWY3V#H&5sT;T$Z&D5p4`RCsQ2h9xX!s==I`1f`xP(Kb*SxQ zN2Wpz<|LIBLexGyi#{H7W98)~s4&ZjaYmXOG*K+|4rQOE%FFX8Jh0MWV|R8T6d%|q zp`_q4nEHr*4jKDcAcy`+VHuAM@714T(hWPF)1ML_-*LkubnveLPKRD51ob6S*>2dm zfB62LHyQ_s-)M{|X2T0z)TpikG{i~H>2WC2ME4j&uuN(sT5R}f{bz_*V!J3H%!r>S zZk|Ro088`nPlB7G1+o7L}Y=BVO;jg9^4^pcHV{O%VwE=gCLp_f8W7KchluZ*2l<8b)v6HRR$)r$3K zsb$5@mt46#ms@`2B{#2NYlyP+BJ#20zZ1SGUnIRjT9bq{_B@OHo~>saemDHj?4jQi zT=si$7SVdH@VfkCnQK>Y6hN<>E6x@Nf2Tj9?~%g8-w|j1oI+2QQY`DNA63>7PL4(4JfOX|%*2>y`#BTc)D*1fwSL`O* zZ!IBiv`+scFGU0d9kr?c2sZ%Kd9)F*zKnD`XhCy@Vgrp=O-^kC?LEju;L*Y4d;v}c zHX+#r6{+!{3ez4Ti%0;Y>;ouETBsgvYv-eqLUE}$6ePk~31yXBVk_e-Djy-NtTUh! zVtJ*@;9g35O>X4W-kLJiDd!L}-1~}Xjd-KsmN25OTEba^VZ~7A@SU-Clk`-z*Y~Ir z!0}@<<*Fc`y; z50@i3geSZnq2yKRb|azH_-)K0#Q#!`hzDb3Al8`Z$a;jukBC&Flae7u9v4f1>_Qk8 zWA})I8!63k+?|e9Q*PPF)FPmPu@3OqHjIxAnh(#7<&~XaO2D*54JQMZlabJf34ts| z&ICDp?d6wQ3u}4#W&I#=IPor|g~7l0*$nK_ZTQW4o?S%ts6E3=LTRJnWZYd7Ckce$ z_R*ifPw^ksfA!K!L}DTcU%%XtdX!%Pf31_as22Df4|YL{5-1Mt@#8LV?bVH7cSwsM z*%0N$)S`&^gH+Dr%jE1agQ%)dRo7S zi|v9jWROy9wfOsBx;-@9$iwK-WC`&gMy##_vMLX&hgVgDR|hrM%pR=;ZOihsX{`m0 zMa_w@I#Of6vi)c#5)d_lx?HjrN_Ez+txl8@Ao+L*1WkzEb7!BSv|qtK`AvPCk9?C7zt zm-Kg>4ptvvr|Z9yR&ck(*YPc~hZlnW7l1!nQSGRwl0}4M3q-U=b0kx%v&Ci}Q{9}T zytwX+QF^F3hhDWIf*4|yTq1eoGv(pIrb%lt2Vgk(LZbjEW-A$TrU)6H=7xoJe(xt{ zx^GzNHGBQ%`0>8-2KUS@iodSbYmF2xd1Tp5f1NtjTg#qsPMJH!(RnF5ClG#y&0BJ_ zKjy0q_!^n-mL>YPoERrJ}@HYGXmgax&nlYmbhyp{dNo3 zAK-5MLkdvfPfHKAKlD)hp{0M`zyHr8+ke`}zJo)5+P9CNez@)M(m(Cr|EHyg+mNnI zYc!2HmifJCX8 zEEhm2LMf3Z=Vf8WR`=14{{x)g!Qk0xTV#6j7}4-7bu#hkr#i1wTB38ASx_d?BdDvT|Cv($dQ}e z_jca*Vml8TZl4b6LP>J%==^@CQs<|PAwjEaM3)nNYO|tN_i27$8O6}_(>S`E2Z}+y z{*>i$*Z|2-n(N#@@_4--J>_)@TxP%Z*5f)H(khK7Zm7zc#*d#G@PI^A%v zq#&91Tb%WBGpAjcXqTd>W5Ac1GzGL{Y2vERE)hb|WRL>13z<;nu2Nkh4JQi1-yy@} zc_nF~L^q4e)BmEUx@ z9X1dQS|A+fpfF7{2^sIuSxqijEWL;coF^3XG}oqJPEE_G0bmML&#c%SAiJx1D#(+= z0T1b=RL_ramu7OZc!9ZSE+kzdt_uRB4#}Y-{_k`W>_M?8=@j5EGh|s1h|+Y*4(O#x z6%3gaOPq4ZHt?p4RaK8R1@vc@?pl1kJL%dSJagsq!5X9G*(`Nxoo=%NP5r5Uzu6ak z+``rnX)alH`KHzSFIG8O)#X9Qn)|#}qcmbAg3^9Sgw$V0e0!|c0?{m(l6X+P?1NfvW;@SFFc>kFd6%d41Ub*|j8>e9|YV-*{2u+h0(4w($QcifKyoLxB9QCXMrgQiF=7vW{eSGiiVM!6{ z6T45pTwHy_Z}yzKM}LPL*zi^RnEjO(S&Fs1RPmubg*JJx>P@LwW|)EqxS=*-A|uoW zH7qEULGuHVq1sbH1r=-+66DBICqIV5v(%}oBvt$n3C@Ox4=uWW{GCheK57z>ecmA6 zV532g>94=|3h8wdY1Ch#k%E>OsnACB9a(CX=sSgsStne=WTlzlu2yZR7X&g9OYl~W z&D=?v1aH#WUfn*>e1{UcW zIL39L@k5E=2dYPLk|vT@1qSxyfqaY#{Epa%@+g0K5Y6*>;R~oBZ&=!Z(U)b^&t#bT z5Vv{_5jzAbVq_o2gz}T6i-8?d23#(a4?cnE3s+xv`yF?G4kA~z1J$f*NOev-}lMFTj~RP~}vfT;+LWIQ6D!#^cJg zIgN6r<`iMgxQ~k_e?FMSn?D%nkn%ZB((CywpfHYi_WaFSXKrB5V70Y+Rj|J=Z0(R* z+Re;#(I+Ae3CYz_<(jM5X2d!?S&s}rN*1j(wIQF+VfL7t>dek2m&+&1N!et#R0qu- zYt$RE*_#tHoeo>H*XgiiR=9m$cWZ6G)jh)<=$9nqEOjwSs+H`D!)s}IL!eMxu(76d}Ac2|qP#^&`&Hb*EOh*{F6D#;`_CW1~$a(c~n25MQ-Zb!({aOIWG zMvL94$knTvXqKJl()t8TQxM^&xC4<Z*{)9zOH75B7y#I+k=={;-X_P1_+_N=*?;io+w;OJ1Vh4qkqPjg=tRY)al z4mBoFSE9SD=DBqYCu(Pz41G)|=$BJaX#jvE=05yCJqNX}KAw}nYg!h2xb@aU)*IEj zB%csw{AAPZ<1z|>qsA$mhP+whjk;59!wN<88~6Mmck>5hhTgYMwh3GlKp^s{NrvE! zV^k8)*fR39DlS!Ipd$I%u&V`4pgL2OMn;PhiVq+a7J0A77D~74kCx=cKoqGW5EX#I z-ep22d?&WPkzyb01V2c-29718EjeO;7-w7xG4#60)2r z`z=AIs;LU0n5A`B&|Fw?)hHTeKq;h!8dx0+Q!?Gcq@o5WH$9+$ma;mnnT%tCGNv^n zkCPA$5RU(G!^^rLR&H} z*b8yumBjTpQrJ;xBW0NS{bjY^!~G`n%lq>4XIbI(*TJhqKP-iWPElO}yNj3A z(E1^Lwf5=IfATOLp0l}qa>j@{icp}nMQ|!4lWUZHE$!3$X|u@)!ch~7mO(*+&aP@U zR-tRG%1@AE_lUl3=;e3jM3}MM-F0X9Z5^j2^cyX6*!6y2s4nI9G!Fl!dqMsT zo5|hTn5y=(v$|(&>a7W#yTxib^VqOuj%b=SMe$s)Y|hF}XEe>z1$OYCm-Y?Rd%9X$ z+vr!%%dAzzctXF%GK+m8=m|BZ=@$oQCi({&8w2!v`5sw$=)8?*{_VJ6na+;S+JE-i zPc_E#)%Y>`6CsOxKKR zaZnY^tD5-2PsSIAqbN@SWP!6cjaArB%XlyZ(-xJQV7bCS&q=%drQ7d0@4|a-doi(g z*1VV2E1uS?<_^xAwKnnOjQ)Y(*&9||=^U8VzrJtb)Gb%#=1)Ig@_h28+irX5lO1PV zI&bd3d@>Z8dfVL7=FYqHjE=fBr}YQVxZgR1(`PA2!pKtW9@A&)jwemls zPF4=+jvo!d7&Bh<9-)k=fRAyunE43^6@;KdJpq_Zl~8Cb5r#RqWA>S653;(!!5vn| z#Rv2o|L0t9M>s!tU~q@UdGP^u2lg|Oa3VjrWAN;A2lPJ>Q-8e0y+*%}U?- z-*dg~Q}TmMJ{#Y%^KY$Jx^m&fC9OCzIH><|fZ8kZJZh>PNEKAV6bH{etq?r0su6Yv zM27McAdWCH*!LP$Uw8!#E^0Eo{7W5z6N_dOoIRuv16SbX+(xWo)LDpoE1CJF=@&fw zuD}j#NZ>M5a`F+9gY=0{o7OHg`^1jHrJ4B9wq=FXoE6hsrAMs2 z3kMpeFV8m>A1Zu)byLk=kJ93=x5zUV{Q1eD6---lzMCy$W*3U04&~3fbCzZ4GTGNQ z^Wwqzi>map%i?RBzOnz)Pdb(?Rn|6b5+mWZ>VVk-K*DRCHr(pHV_+U0fq=0r2p347 zLrnE7VTVAN7wiV8C=u>WM2UGHe;|mDKM=&{s?Zc}qCQ@OzA;;@=G70YBXAg7IR0g! zdKyTZN01chB1Fk*IFt5?QwC>|&~+=%Iij(at{m;SylNY0+kz!cYbWDUP_#BIa-<36 zh+d#2mnz7or{WTTiy=`c1T%GIsm!(@mzsRQ7gsSuAfF0rDwoYdw%5-$) zYp1O_r)j8oZTF)3aG`xpy=i z!Wf~#8(bv7Y(T?paY2HMR!0TqfmJwave|uJPXL+= zGUae1Z<#7>01QUQ%zdg=!I}W0my}vO3!_Q_PK5zAY;iw*C zohlD;OcH$sS%AAhasq&EIP`_6wq9=2aqGh&9$sNZCZkDtHF(7`g?{ zCQGZr-NefnGhMX`&@q&#^MjIqcu)iZhNtcW+Jx4_SB*$+FR!odrScx=lnZMk z`rsh!YM+mf4h2Q?CoZ86U}EZn!daO2!G|h7W@5TuDnLpQ{zS#t!_CMq&lG)zATyMnU8-xDl+#rz&r|`(V-H@X?Y4CZ)2I zys9li;xI@-NMHVd6wQH&wGX5>vRFn4jv2+>r~ES)7!fB(IHHyr<-52QTOm4mlEz;D z-`eXyd)>Uf5HJuvcD_#7z0_WN@MGGGif7~6JlbAr6R1ipKEk&Q9vN#YHJj)QNeD(+ z4Bt4#!nTa%?gCRFV+>{h$5x4Z$ruBAh`4yDC=(-2;9D7q531ykQ9|RR@4fpKN;f6X zJd#h1%tgZ89(&t3@%CwS)Hr9@lt49X0 z7DMjr$G6be&fa^J+Cn+8UwL;zBTHe^m3NJd+3_vaokx!n*$ltm2<`si_VNT@ zqrGVQ$G10BN9nwyEt=5Y0_w2x*1q>B5qx}W3+Tv_|J%0y!?cY{)Yg%4p4e7)gg4e8 zJa}a07!!bBml!;WTGflJlh6~AEpQ3AcHa4E@}@Ev7|o=zzC-d&a9+NW4xL08ie&h`Aa~I z5b*~+T_@y##U@O>-h40O`Wm2X z2^RBf))4D>$YiqFY%Zq*Ri|7wYe@ek`+_K1Y&N%DenJ0Wkw>)n^o9O_!|JXQFGlJ- zLt!_k+iCNdf2sd`jgR<|&t*=xYRqL+lLLctHO5Lg*_3L87!SmCKrB*dhcUIGPtk8@t`e8gva8;$9z=*K^)S_Vk-9~LQM9dJt2mhw#fJydT zbxkB1Yb31~`auGO4g$D&&T0er%#YS89Bms-iBDT#HxTMZeL&Pin&K6cJZqpbo0i@% zl2QHemW2i6#v{G*es<)3{Yir*&RcNf=SCRxhNW*mW@Bsa*PZw4k6=!X&&R0~&fqy- z=m%I6!EjiSNPRaoEYX_Ly3#z?1@6e_kzMI>19nEwP)r<{)$<6!N5rmj zVwUAdjt-o*yhPjy`7V{p@S&^rTy@o+$@wm$#o=`?oxWe4|G3Nhvzl@;WOgS z8vc++*v&}dvqE3sPp9(|fE?s20i0L}45L|P6JZxC6zt=2$kh(dv1&xszDS{sR4tQ= z%ew9QyHbp*5)+%CLKX4th#Vccf9s_CGcwvg_U6c@!9Sj#K6-aJe^^?d#Zc{TCI^>3L)$eK#};^5lU8(CAQC6Ma{B-xcb+k*q$x?=V9rbiGSl^#y(I zZt;$BH~*ggQ*qTp`rHSGr)Dd$SfpdxIA&Xom>`4lK;Ga$q`PC%207V-{MJFbbp<0B zB|9oTq@|<}fi|J>4cKsC!)EbY($V`5+|Pb8)&}X{&wF(Pf(^xg`cItEt4`LA5h_e> z2O?uZg^y_pB7gugJH|C->w)uLmFRANW2Em@_&_Wi*l>WojrM)+UGZBV{)vwVJx>tN zAx)TO<>a;|>~A7UmLxRu4QvLNSxduFx|#T-l;op*^#VJu8p*t;in;O~6BB zgF{MEDxDjlWkp*MH4@13G(-xxE*Ik2>7=bUq^RHFz)^5~DdOKfJR9-Mu!IY{rMLVM zE(DK#9i3{NS>gX zAp(nzkWt`eT%!WW?&VENB9|}3s5EY+Vfs7Q-K>9#S~lm#>)3`H_2l94Eqq;n_qtoq zKn*9?--v*XCoAy>!1+xs(2}0pmjFdaYGW9UL3-3As#wyPl@*%!;Bny22k>d785cf@ zbhYOz1S&lFD9o#Q8jc*kK%$I3rWQSt%9-ULU@es>@j)Ovv6^c{V2vNLV|g4$ zXL=wf^|IoHCNp$|&YN{7?;a!$6zOR_q5{Bq<-UsgOM?B`Z!MU8y zj`jliV55DYnh1*_*N9Ul=MGS0333MFpb}N#`*69e8WjX#fgk0u!zl{xN5w!d|3UJB zB4SehI`l!Z0gcMow~?np3)TXg5E1%O4|@+Onhwc)6+xC z7FJ=ELh(_N9+Z^lW==8H^Uv41Iqd*an* zlYTYr$}6HiQMbY6R`@AVrtgcT|ra4gKTFlLn zVAm!Jb~VSyD#GKBNO|K=J3_)qLx)5&Zzfsk+;K{)AZYEqU=+2r&`sR@%Q=BQbUEh*&PMN|?wt!2zE?C3FDLAZeVcSO!AG?bVgX{2D zv5~70fgOXL+=2M}A}T8LBD2t22{Y%ZK3+e;K$(nD_{dB3fMltLYW$C=)MGVP5L1^+ zQoZI;8$KQi;DI)Afd4&7)cYmxFSOGGaQR|#T?}1jZ2>{2hDDF@Kmum^Vt$MiD&uOy zph4Z^^YnwbvSRY@DxG&;sW3eED|dVac8o{x$dAa6peKSCP;ldiOmCF1YZ%8FBWg zx5IUpOIEgQJhpR-(&c~AXI361(s8?l^8u}InM!>nh-LVJDQ@qyj5bK?m=kKR7Q^$& z)Fx$LsyREriAJFbdAO7MB|J|DwV*2bQKZv@k>L_!Ggxmdgy1!}rVzf?A*1Yr>}CN3 zB#Ob*ip?uhsD8pOb3xpExZfWM`+w*U?_m8q_=dT*u=Vwu&wBh5g_&(OTlRoI=VFB%wwdS<0=0LouDekb3&R@zi zs2TOYQ||Y;%Ds42M?6jCY~jloeJP;;J-y?&^o^S!BSxyu<9R?d?EDX|{tD&*cmJqt zCHu*ECb}P9eynULRZD0xP&&Slas7bi(8xpZ#!B4eFmWgVA)tUs5KTZCLi_`91$>8d z9v;F#pOoi7pTo0hJWcd0Dc%Osn4|pJz4I$rjiEP_-Ge}sQLKji@j#9c;;Si?KkX01 z5=|{!wgM-`er+t(L{X}U*dJAE4ZDq8ZAd;&AU_$3Rv=-5s3ol12LV@5w~8-NzUA=j zttzja#2KDyQGsqmNbIvCbcOE3J7sI^HG~+6;xJ=;;NcJ(4GkQ603k*(Zz;9_cc9geb$EMrfZuz#kq7AcODK)>DIO4|cL z{v4!JwB4it20Uqt(WVodsz17$4)3N?f0O0`)f`I$128a4%mWyX@CzlfRH8A-AN5l~ z1R(ZC+fMV;i1?@6tT<}Ud&mt$_yL~VP?<% z+}oGh29Ig;wr!~shk*M*R&86eX4@(%nKgNiCwRW=Xx}P5LEh_VPbzIi_S)zik0YFd z^rw+I-jHhg2rim1$LTSKm=h=Ii@`(S`FjiGJpj=C5i^|dZ`6_rDyl;ri^DVhcO9nF+`LLxhAJT@1m+zLeY z0h>b<2zo@Y$|ypIb#oMcOfCn5)R7)849424EK9m(yLIYAoY6@u{RUf?;(p=x9tP@vctQN~Bnjo_K^ z5r()@gjJp!RHq1!tDzN~l%m3^N%I9VSd2gDpU2-n{;>R_d>U4gm~a)3a03SJ^{7=8 zsRBnLWqE^CkY$FMMTK;YdS&op6Ziwh*JQ+c7Xu-x*RMrLRrSI^(Hw9*Xl`^+;14?8 zC)karE>|h2*$^;m@ZQ5eXCb}=Mw;U9Bdx$F(L>(=X@eDb=EwzlUk z|NO7T!PRUk`iSv=Z~6ae?P`Ofy3X)@*98F)Q4tXo*AGDD!+rOA0f{J5gTzwXM6lK% zB7zDS!4DdnrY5n}8f(?0CK^qnX%nj!t+B*9Hcf2DwvOo}*0lNPbexRikBsd&X{Y04 zpwGGYS;fSD{K)Q}ecyBLInQ~|-RIuD_uO;dv)26Q9KCTQW$A`@o*9#zva0VXlVYx1 zZnw?!`Ddd?2HpDEm(7w+#(&i~I2kxGJkzWXgRU9djznBB+k?mknBfebfE5X{Uv@3& zy3-6CappF{*s;H_HS@W~jYmIYiTTfP*0QN~x8nZ70>KC4LKk!5#g9%|@tYenS%TZL zz8ig4;uf3l+66*~-Fxw$gAr%xqs`0|JU+pso4nyrFy<%EZUct4 znC^TGRmWb9?}|=$w^T(6Of5yBs+L4w$-{M-yOwkwbfqL#wYbg%Ye%J~SG8pKT`VjV zUv^7X#&}QDj75*d*FAKw(>=`XYB6mvq5Q@E8`~ZnR{9TXJnqKvdNVl@^LicGU);Yh z?gPxiF<#{DdmCsd7njlhxcyz+_jcR|Hj*h4dmWHoYl=Y|5HP#ZiMzI$lK43(1$WC* ziK2gIIEc78&gVMPY(rU7-X75G?!hQM8w;MI9Zb_tHyQzX`g@&lN8K?y#v#v2<~8|Q z#>#Zc8jrGeJ#Jv^gKo;1G{kM)$bsczcE#}TCS#cBCAwu(5ISr%-ZcAPft)a4+W?II zy+}9ZV`;k?UpF8vwk?L=jcrDc1#UO3}Nd`0|~!PSF%2473qo#;)hPu!i9lvI(_opgQ314DKUxtd&-+%t6S(Dg$Prxd5u zr)*7mf7qW=t5dsEFAq-{o;!T^h_n&)Bi0Cz(~5n=(&jUe5e5D=o{LH9u=h)~T$&W_>(1W$dD{hsItX=NtEW zc53$4?2pD*j(>jqYvZqY;yu$mm7X@w4$qAVD<_$T2?zOy>yp?$ur$nYSPU)Q*ntEwk+q94JoAXcP-z=yo*i(46@M=+0 z(axfq(~G?s-cy>ZkLX*z1YfVe-oGP|8F(S+4mJhPhSEceLnp&Y;rj5A@F$U)$jN9% zv^M&5^ipv~@si>##g|J8N;*saQaZD=x%B-R6*FEcOD&sQcBbt5J>Gkso#~ocKl5by z#PaU)zt7q{>tD0GXaBRJw4%OZzkT+457(5oj~MVo5a6gm;NSqisd){vPV*c$()gsn z6_>d2*w9*un4=4xl5e8!Lci@H>VwR+H+4692K%VTSsNupJ>Ck*G3p6cx_n4I5&BK) zL#)ZJRO-pl1Jp-Cucdz8N_WL<_^su2?cA_oL(z)WU2B?KmbJHa6fJ9S#i-48%-Qb3 zl|c*E^=!5}ah32gg3t0|#H=4$1GaiFbAPGT200J;*F!h?SD`1+1Me}b@ix~MF@z2~ zw%qE#>Q!rzdpVAVBFt8;#tH;AIE&wlTEA$`hi@GZVoOoF384k}D^O+u@~?mg`_*hqO74pFS){^GVg0`rcs^C`0lOU?u&~|U2Lo-Yv0LF-c-zuuGv-f|u^6tOX-BUMM z=3RvSy&Avr8vOn(w7LVS#{O12$LEn}AzIvk_L_ZSSmx}L`|S8_e)+JEJlIPSJOeNc zEXKYFAjRQh07s(z!pdFtBU2|f;QKusr!FxbXop%U7$*`Z@o;{XAc>MBLj==};nL6a z?GBd_*55FxH4UAr>3BexA!8&{vSch~`hOUa69KQZ4t% ze2lxUkuS*t`LcXP?uWykg;FbZvPixvi{)#wL>@FAdZa;?p-X?cG|37$rfiXwvPxD< ztF%eGtdWOgt#nAItdsS!K{iU4d|e)vP4W$SM7}AH%C}^*Jcj?2CuEC!Te{^tvQ@q- z+vG{vF5g3U)b}w^c$e&!r{rn*f$WiIn=9Fe1POnxdoavaldekLd772JvZTzchIIW51CGZ^)7R(>h3$*<&fc|*?0ujMyb z+zv~>%J1a&asge!7v)X)16Cq zNZSZVyK+doa!9*!NV{@K8)uGJ?Z!ab_>ja=;;7viq!Ukxr^Hj@De-*7^AXQSJRk9V z#Pbo)M?4?#e8lq+&rdu*@%+T|6VFdPKk@v;^ApccJU{UQ#0wBFK)e9)0>ldtFF?Ei z@dCsP5HCo)An}643lc9#ydd#{#0wHHNW38NLc|LZCq$eOaYDoi5hp~P5OG4p2@@ww zyTZf^6E94>F!92~3llF)yfE=1#ETFwLc9p^BE*XjFG9Qs@gl^F5HCu+DDk4iixMwN zyeRRa#EUw3O5Q7ZujIXYopMV4EBUYFzmoq-{ww*ftO8zVPujIdy|4RNV`LE=^ zlK)EnEBUYFzmoq-{ww*ftO8zVPujIdy|4RNV`Hv+t&3R&ulK)EnEBUYFzmoq- z{ww*ftO8zVPujIXw_e$O?d9UO>y#F|MkoQX7D|xTvy^{Az-Ya>pA%_o2{ww*f ztO8zVPujIdy|4RNV`LE=^lK)EnV@(LhUh-eben*C^B33F^`zzF+C&yytvzO0{|1%B6xsj) literal 0 HcmV?d00001 diff --git a/springboot-springCloud/ui/target/classes/static/fonts/glyphicons-halflings-regular.woff b/springboot-springCloud/ui/target/classes/static/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 0000000000000000000000000000000000000000..8c54182aa5d4d1ab3c9171976b615c1dcb1dc187 GIT binary patch literal 23320 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 mX4Rts + + + + 原生云应用 + + + + + + + + + + +

+
+
+
+
+
+ + \ No newline at end of file diff --git a/springboot-springCloud/ui/target/classes/static/js/app.js b/springboot-springCloud/ui/target/classes/static/js/app.js new file mode 100644 index 0000000..db06c5d --- /dev/null +++ b/springboot-springCloud/ui/target/classes/static/js/app.js @@ -0,0 +1,49 @@ +var uiApp = angular.module("uiApp", ["ui.router"]); + +uiApp.config(function ($stateProvider, $urlRouterProvider) { + $urlRouterProvider.otherwise("person"); + + $stateProvider + .state('person', { + url: '/person', + templateUrl: '/tpl/person.html', + controller: 'PersonController' + }) + .state('some',{ + url:'/some', + templateUrl: '/tpl/some.html', + controller: 'SomeController' + }); +}); + + +uiApp.controller("PersonController", function ($scope, $http) { + + $scope.people = ""; + $scope.errorMessage = ""; + + $scope.getMessageResponse = function(personName) { + $http.post('/dispatch', personName).success(function(data){ + $scope.people = data; + $scope.errorMessage = ""; + }).error(function() { + $scope.errorMessage = "错误"; + }); + } + +}); + + +uiApp.controller("SomeController", function ($scope, $http) { + $scope.str = ""; + $scope.errorMessage = ""; + +$scope.getSome = function(){ + $http.get('/getsome').success(function(data){ + $scope.str = data; + $scope.errorMessage = ""; + }).error(function() { + $scope.errorMessage = "错误"; + }); +} +}); \ No newline at end of file diff --git a/springboot-springCloud/ui/target/classes/static/tpl/person.html b/springboot-springCloud/ui/target/classes/static/tpl/person.html new file mode 100644 index 0000000..3382e34 --- /dev/null +++ b/springboot-springCloud/ui/target/classes/static/tpl/person.html @@ -0,0 +1,34 @@ +
+
+
+
+

通过Person Service保存person

+
+
+
+
+
+
+

+ +

+
+
+
+
+
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+
diff --git a/springboot-springCloud/ui/target/classes/static/tpl/some.html b/springboot-springCloud/ui/target/classes/static/tpl/some.html new file mode 100644 index 0000000..7e20b79 --- /dev/null +++ b/springboot-springCloud/ui/target/classes/static/tpl/some.html @@ -0,0 +1,27 @@ +
+
+
+
+

从 Some Service获得字符

+
+
+
+
+
+
+

+ +

+
+
+
+ +
+ +
+ +
+
+
+
+
From 3a7ce18a85b8bf93ebc5af61f52fa2308487bb0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Fri, 7 Sep 2018 13:09:06 +0800 Subject: [PATCH 07/44] add swagger api --- springboot-swagger-ui/pom.xml | 82 ++++++++++ .../java/com/abel/example/Application.java | 22 +++ .../main/java/com/abel/example/Swagger2.java | 52 ++++++ .../main/java/com/abel/example/bean/User.java | 149 ++++++++++++++++++ .../com/abel/example/config/DBConfig.java | 35 ++++ .../com/abel/example/config/JpaConfig.java | 58 +++++++ .../example/controller/UserController.java | 131 +++++++++++++++ .../java/com/abel/example/dao/UserJpaDao.java | 34 ++++ .../com/abel/example/service/UserService.java | 55 +++++++ .../example/serviceImpl/UserServiceImpl.java | 64 ++++++++ .../com/abel/example/util/CommonUtil.java | 47 ++++++ .../src/main/resources/application.properties | 7 + 12 files changed, 736 insertions(+) create mode 100644 springboot-swagger-ui/pom.xml create mode 100644 springboot-swagger-ui/src/main/java/com/abel/example/Application.java create mode 100644 springboot-swagger-ui/src/main/java/com/abel/example/Swagger2.java create mode 100644 springboot-swagger-ui/src/main/java/com/abel/example/bean/User.java create mode 100644 springboot-swagger-ui/src/main/java/com/abel/example/config/DBConfig.java create mode 100644 springboot-swagger-ui/src/main/java/com/abel/example/config/JpaConfig.java create mode 100644 springboot-swagger-ui/src/main/java/com/abel/example/controller/UserController.java create mode 100644 springboot-swagger-ui/src/main/java/com/abel/example/dao/UserJpaDao.java create mode 100644 springboot-swagger-ui/src/main/java/com/abel/example/service/UserService.java create mode 100644 springboot-swagger-ui/src/main/java/com/abel/example/serviceImpl/UserServiceImpl.java create mode 100644 springboot-swagger-ui/src/main/java/com/abel/example/util/CommonUtil.java create mode 100755 springboot-swagger-ui/src/main/resources/application.properties diff --git a/springboot-swagger-ui/pom.xml b/springboot-swagger-ui/pom.xml new file mode 100644 index 0000000..dff48e7 --- /dev/null +++ b/springboot-swagger-ui/pom.xml @@ -0,0 +1,82 @@ + + + 4.0.0 + + com.abel + springboot-swagger-ui + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 1.3.0.RELEASE + + + + com.abel.Application + + 1.8 + 1.8 + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-web + + + + + + mysql + mysql-connector-java + 6.0.5 + + + com.mchange + c3p0 + 0.9.5.2 + + + commons-logging + commons-logging + + + + + + + org.apache.commons + commons-lang3 + 3.4 + + + + + + io.springfox + + springfox-swagger2 + + 2.2.2 + + + + + + io.springfox + + springfox-swagger-ui + + 2.2.2 + + + + \ No newline at end of file diff --git a/springboot-swagger-ui/src/main/java/com/abel/example/Application.java b/springboot-swagger-ui/src/main/java/com/abel/example/Application.java new file mode 100644 index 0000000..33cef8b --- /dev/null +++ b/springboot-swagger-ui/src/main/java/com/abel/example/Application.java @@ -0,0 +1,22 @@ +package com.abel.example; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.ComponentScan; + +import static org.springframework.boot.SpringApplication.run; + +/** + * Created by yangyibo on 17/1/17. + */ + +@ComponentScan(basePackages ="com.abel.example") +@SpringBootApplication +@EnableCaching +public class Application { + public static void main(String[] args) { + ConfigurableApplicationContext run = run(Application.class, args); + } + +} \ No newline at end of file diff --git a/springboot-swagger-ui/src/main/java/com/abel/example/Swagger2.java b/springboot-swagger-ui/src/main/java/com/abel/example/Swagger2.java new file mode 100644 index 0000000..37e38a9 --- /dev/null +++ b/springboot-swagger-ui/src/main/java/com/abel/example/Swagger2.java @@ -0,0 +1,52 @@ +package com.abel.example; + +import org.springframework.context.annotation.Bean; + +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2; + +/** + * Created by yangyibo on 2018/9/7. + */ +@Configuration +@EnableSwagger2 +public class Swagger2 { + /** + * 创建API应用 + * apiInfo() 增加API相关信息 + * 通过select()函数返回一个ApiSelectorBuilder实例,用来控制哪些接口暴露给Swagger来展现, + * 本例采用指定扫描的包路径来定义指定要建立API的目录。 + * + * @return + */ + @Bean + public Docket createRestApi() { + return new Docket(DocumentationType.SWAGGER_2) + .apiInfo(apiInfo()) + .select() + .apis(RequestHandlerSelectors.basePackage("com.abel.example.controller")) + .paths(PathSelectors.any()) + .build(); + } + + /** + * 创建该API的基本信息(这些基本信息会展现在文档页面中) + * 访问地址:http://项目实际地址/swagger-ui.html + * @return + */ + private ApiInfo apiInfo() { + return new ApiInfoBuilder() + .title("Spring Boot中使用Swagger2构建RESTful APIs") + .description("更多请关注https://blog.csdn.net/u012373815") + .termsOfServiceUrl("https://blog.csdn.net/u012373815") + .contact("abel") + .version("1.0") + .build(); + } +} diff --git a/springboot-swagger-ui/src/main/java/com/abel/example/bean/User.java b/springboot-swagger-ui/src/main/java/com/abel/example/bean/User.java new file mode 100644 index 0000000..7d2431f --- /dev/null +++ b/springboot-swagger-ui/src/main/java/com/abel/example/bean/User.java @@ -0,0 +1,149 @@ +package com.abel.example.bean; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +@Entity +@Table(name = "sec_user") +public class User { + @Id + @GeneratedValue + private Long id; + @Column(name = "name") + private String name; + @Column(name = "password") + private String password; + @Column(name = "username") + private String username; + @Column(name = "email") + private String email; + @Column(name = "mobilephone") + private String mobilephone; + @Column(name = "telephone") + private String telephone; + @Column(name = "user_type") + private Integer userType; + @Column(name = "create_by") + private String createBy; + @Column(name = "create_time") + private Date createTime; + @Column(name = "update_by") + private String updateBy; + @Column(name = "update_time") + private Date updateTime; + @Column(name = "disabled") + private Integer disabled; + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @JsonIgnore + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getMobilephone() { + return mobilephone; + } + + public void setMobilephone(String mobilephone) { + this.mobilephone = mobilephone; + } + + public String getTelephone() { + return telephone; + } + + public void setTelephone(String telephone) { + this.telephone = telephone; + } + + public Integer getUserType() { + return userType; + } + + public void setUserType(Integer userType) { + this.userType = userType; + } + + public String getCreateBy() { + return createBy; + } + + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public String getUpdateBy() { + return updateBy; + } + + public void setUpdateBy(String updateBy) { + this.updateBy = updateBy; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + public Integer getDisabled() { + return disabled; + } + + public void setDisabled(Integer disabled) { + this.disabled = disabled; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } +} \ No newline at end of file diff --git a/springboot-swagger-ui/src/main/java/com/abel/example/config/DBConfig.java b/springboot-swagger-ui/src/main/java/com/abel/example/config/DBConfig.java new file mode 100644 index 0000000..65207c3 --- /dev/null +++ b/springboot-swagger-ui/src/main/java/com/abel/example/config/DBConfig.java @@ -0,0 +1,35 @@ +package com.abel.example.config; + +import java.beans.PropertyVetoException; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +import com.mchange.v2.c3p0.ComboPooledDataSource; + +@Configuration +public class DBConfig { + @Autowired + private Environment env; + + + @Bean(name="dataSource") + public ComboPooledDataSource dataSource() throws PropertyVetoException { + ComboPooledDataSource dataSource = new ComboPooledDataSource(); + dataSource.setDriverClass(env.getProperty("ms.db.driverClassName")); + dataSource.setJdbcUrl(env.getProperty("ms.db.url")); + dataSource.setUser(env.getProperty("ms.db.username")); + dataSource.setPassword(env.getProperty("ms.db.password")); + dataSource.setMaxPoolSize(20); + dataSource.setMinPoolSize(5); + dataSource.setInitialPoolSize(10); + dataSource.setMaxIdleTime(300); + dataSource.setAcquireIncrement(5); + dataSource.setIdleConnectionTestPeriod(60); + + return dataSource; + } + +} \ No newline at end of file diff --git a/springboot-swagger-ui/src/main/java/com/abel/example/config/JpaConfig.java b/springboot-swagger-ui/src/main/java/com/abel/example/config/JpaConfig.java new file mode 100644 index 0000000..5e90ca3 --- /dev/null +++ b/springboot-swagger-ui/src/main/java/com/abel/example/config/JpaConfig.java @@ -0,0 +1,58 @@ +package com.abel.example.config; + +import java.util.HashMap; +import java.util.Map; + +import javax.persistence.EntityManagerFactory; +import javax.sql.DataSource; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +@Configuration +@EnableJpaRepositories("com.abel.example.dao") +@EnableTransactionManagement +@ComponentScan +public class JpaConfig { + + @Autowired + private DataSource dataSource; + + @Bean + public EntityManagerFactory entityManagerFactory() { + HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter(); + //vendorAdapter.setShowSql(true); + //vendorAdapter.setGenerateDdl(true); + + LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean(); + factory.setJpaVendorAdapter(vendorAdapter); + factory.setPackagesToScan("com.abel.example.bean"); + factory.setDataSource(dataSource); + + + Map jpaProperties = new HashMap(); + jpaProperties.put("hibernate.ejb.naming_strategy","org.hibernate.cfg.ImprovedNamingStrategy"); + jpaProperties.put("hibernate.jdbc.batch_size",50); + //jpaProperties.put("hibernate.show_sql",true); + + factory.setJpaPropertyMap(jpaProperties); + factory.afterPropertiesSet(); + return factory.getObject(); + } + + @Bean + public PlatformTransactionManager transactionManager() { + + JpaTransactionManager txManager = new JpaTransactionManager(); + txManager.setEntityManagerFactory(entityManagerFactory()); + return txManager; + } +} \ No newline at end of file diff --git a/springboot-swagger-ui/src/main/java/com/abel/example/controller/UserController.java b/springboot-swagger-ui/src/main/java/com/abel/example/controller/UserController.java new file mode 100644 index 0000000..f65f578 --- /dev/null +++ b/springboot-swagger-ui/src/main/java/com/abel/example/controller/UserController.java @@ -0,0 +1,131 @@ +package com.abel.example.controller; + +import javax.servlet.http.HttpServletRequest; +import java.util.Map; + +import com.abel.example.bean.User; +import io.swagger.annotations.*; +import org.apache.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.*; + + +import com.abel.example.service.UserService; +import com.abel.example.util.CommonUtil; + + +@Controller +@RequestMapping(value = "/users") +@Api(value = "用户的增删改查") +public class UserController { + + @Autowired + private UserService userService; + + + /** + * 查询所有的用户 + * api :localhost:8099/users + * @return + */ + @RequestMapping(method = RequestMethod.GET) + @ResponseBody + @ApiOperation(value = "获取用户列表,目前没有分页") + public ResponseEntity findAll() { + return new ResponseEntity<>(userService.listUsers(), HttpStatus.OK); + } + + /** + * 通过id 查找用户 + * api :localhost:8099/users/1 + * @param id + * @return + */ + @RequestMapping(value = "/{id}", method = RequestMethod.GET) + @ResponseBody + @ApiOperation(value = "通过id获取用户信息", notes="返回用户信息") + public ResponseEntity getUserById(@PathVariable Integer id) { + return new ResponseEntity<>(userService.getUserById(Long.valueOf(id)), HttpStatus.OK); + } + + + /** + * 通过spring data jpa 调用方法 + * api :localhost:8099/users/byname?username=xxx + * 通过用户名查找用户 + * @param request + * @return + */ + @RequestMapping(value = "/byname", method = RequestMethod.GET) + @ResponseBody + @ApiImplicitParam(paramType = "query",name= "username" ,value = "用户名",dataType = "string") + @ApiOperation(value = "通过用户名获取用户信息", notes="返回用户信息") + public ResponseEntity getUserByUserName(HttpServletRequest request) { + Map map = CommonUtil.getParameterMap(request); + String username = (String) map.get("username"); + return new ResponseEntity<>(userService.getUserByUserName(username), HttpStatus.OK); + } + + /** + * 通过spring data jpa 调用方法 + * api :localhost:8099/users/byUserNameContain?username=xxx + * 通过用户名模糊查询 + * @param request + * @return + */ + @RequestMapping(value = "/byUserNameContain", method = RequestMethod.GET) + @ResponseBody + @ApiImplicitParam(paramType = "query",name= "username" ,value = "用户名",dataType = "string") + @ApiOperation(value = "通过用户名模糊搜索用户信息", notes="返回用户信息") + public ResponseEntity getUsers(HttpServletRequest request) { + Map map = CommonUtil.getParameterMap(request); + String username = (String) map.get("username"); + return new ResponseEntity<>(userService.getByUsernameContaining(username), HttpStatus.OK); + } + + + /** + * 添加用户啊 + * api :localhost:8099/users + * + * @param user + * @return + */ + @RequestMapping(method = RequestMethod.POST) + @ResponseBody + @ApiModelProperty(value="user",notes = "用户信息的json串") + @ApiOperation(value = "新增用户", notes="返回新增的用户信息") + public ResponseEntity saveUser(@RequestBody User user) { + return new ResponseEntity<>(userService.saveUser(user), HttpStatus.OK); + } + + /** + * 修改用户信息 + * api :localhost:8099/users + * @param user + * @return + */ + @RequestMapping(method = RequestMethod.PUT) + @ResponseBody + @ApiModelProperty(value="user",notes = "修改后用户信息的json串") + @ApiOperation(value = "新增用户", notes="返回新增的用户信息") + public ResponseEntity updateUser(@RequestBody User user) { + return new ResponseEntity<>(userService.updateUser(user), HttpStatus.OK); + } + + /** + * 同伙ID删除用户 + * api :localhost:8099/users/2 + * @param id + * @return + */ + @RequestMapping(value = "/{id}", method = RequestMethod.DELETE) + @ResponseBody + @ApiOperation(value = "通过id删除用户信息", notes="返回删除状态1 成功 0 失败") + public ResponseEntity deleteUser(@PathVariable Integer id) { + return new ResponseEntity<>(userService.removeUser(id.longValue()), HttpStatus.OK); + } +} \ No newline at end of file diff --git a/springboot-swagger-ui/src/main/java/com/abel/example/dao/UserJpaDao.java b/springboot-swagger-ui/src/main/java/com/abel/example/dao/UserJpaDao.java new file mode 100644 index 0000000..9868bb9 --- /dev/null +++ b/springboot-swagger-ui/src/main/java/com/abel/example/dao/UserJpaDao.java @@ -0,0 +1,34 @@ +package com.abel.example.dao; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.abel.example.bean.User; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; + +/** + * The Interface UserJpaDao. + * + * @author abel + */ +public interface UserJpaDao extends JpaRepository { + + /** + * Find by name. + * + * @param name the name + * @return the user + */ + User findByName(String name); + + User getOne(Long id); + + List findByUsernameContaining(String username); + + User getByUsernameIs(String username); + + @Query("select s from User s where name like CONCAT('%',:name,'%')") + List findByNameLike(@Param("name") String name); +} \ No newline at end of file diff --git a/springboot-swagger-ui/src/main/java/com/abel/example/service/UserService.java b/springboot-swagger-ui/src/main/java/com/abel/example/service/UserService.java new file mode 100644 index 0000000..9f84083 --- /dev/null +++ b/springboot-swagger-ui/src/main/java/com/abel/example/service/UserService.java @@ -0,0 +1,55 @@ +package com.abel.example.service; + +import java.util.List; + +import com.abel.example.bean.User; + +/** + * The Interface UserService. + */ +public interface UserService { + + /** + * Gets the user by name. + * + * @param username the user name + * @return the user by name + */ + public User getUserByUserName(String username); + + public List getByUsernameContaining(String username); + + /** + * 增加学生 + * @param user + * @return + */ + User saveUser(User user); + + /** + * 删除单个学生 + * @param id + * @return + */ + int removeUser(Long id); + + /** + * 更新学生信息 + * @param user + * @return + */ + User updateUser(User user); + + /** + * 根据id获取学生信息 + * @param id + * @return + */ + User getUserById(Long id); + + /** + * 获取学生列表 + * @return + */ + List listUsers(); +} diff --git a/springboot-swagger-ui/src/main/java/com/abel/example/serviceImpl/UserServiceImpl.java b/springboot-swagger-ui/src/main/java/com/abel/example/serviceImpl/UserServiceImpl.java new file mode 100644 index 0000000..dbaf112 --- /dev/null +++ b/springboot-swagger-ui/src/main/java/com/abel/example/serviceImpl/UserServiceImpl.java @@ -0,0 +1,64 @@ +package com.abel.example.serviceImpl; + +import com.abel.example.bean.User; +import com.abel.example.dao.UserJpaDao; +import com.abel.example.service.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * @author abel + * @ClassName UserServiceImpl + * @date 2016年11月10日 + */ +@Service +public class UserServiceImpl implements UserService { + @Autowired + private UserJpaDao userJpaDao; + + /** + * @param username + * @return + */ + @Override + public User getUserByUserName(String username) { + return userJpaDao.getByUsernameIs(username); + } + + @Override + public List getByUsernameContaining(String username) { + return userJpaDao.findByUsernameContaining(username); + } + + @Override + public User saveUser(User user) { + return userJpaDao.save(user); + } + + @Override + public int removeUser(Long id) { + try { + userJpaDao.delete(id); + }catch (Exception e) { + return 0; + } + return 1; + } + + @Override + public User updateUser(User user) { + return userJpaDao.save(user); + } + + @Override + public User getUserById(Long id) { + return userJpaDao.findOne(id); + } + + @Override + public List listUsers() { + return userJpaDao.findAll(); + } +} diff --git a/springboot-swagger-ui/src/main/java/com/abel/example/util/CommonUtil.java b/springboot-swagger-ui/src/main/java/com/abel/example/util/CommonUtil.java new file mode 100644 index 0000000..5fe5eab --- /dev/null +++ b/springboot-swagger-ui/src/main/java/com/abel/example/util/CommonUtil.java @@ -0,0 +1,47 @@ +package com.abel.example.util; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.lang3.StringUtils; + +public class CommonUtil { + + /** + * 从request中获得参数Map,并返回可读的Map. + * + * @param request the request + * @return the parameter map + */ + public static Map getParameterMap(HttpServletRequest request) { + // 参数Map + Map properties = request.getParameterMap(); + //返回值Map + Map returnMap = new HashMap(); + Set keySet = properties.keySet(); + for(String key: keySet){ + String[] values = properties.get(key); + String value = ""; + if(values != null && (values.length==1&&StringUtils.isNotBlank(values[0]))?true:false){ + for(int i=0;i Date: Thu, 13 Sep 2018 12:28:57 +0800 Subject: [PATCH 08/44] modify note --- .../main/java/com/abel/example/controller/UserController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/springboot-swagger-ui/src/main/java/com/abel/example/controller/UserController.java b/springboot-swagger-ui/src/main/java/com/abel/example/controller/UserController.java index f65f578..83c2a80 100644 --- a/springboot-swagger-ui/src/main/java/com/abel/example/controller/UserController.java +++ b/springboot-swagger-ui/src/main/java/com/abel/example/controller/UserController.java @@ -117,7 +117,7 @@ public ResponseEntity updateUser(@RequestBody User user) { } /** - * 同伙ID删除用户 + * 通过ID删除用户 * api :localhost:8099/users/2 * @param id * @return From 8dd2d28319c6287d64162678f65e403b8173b043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Thu, 13 Sep 2018 12:32:02 +0800 Subject: [PATCH 09/44] modify readme add springboot - swagger-ui --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index c5d8d95..5275bb9 100644 --- a/README.md +++ b/README.md @@ -49,4 +49,8 @@ * 是博客 springboot集成shiro 实现权限控制 地址:http://blog.csdn.net/u012373815/article/details/57532292 +## springboot-swagger-ui +* 博客 spring boot +Swagger-ui 自动生成API文档 地址: https://blog.csdn.net/u012373815/article/details/82685962 + + ##未完待续。。。 From ceb9a46e94fe2a7c4d9c90a8c3f95dad79006ec0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Fri, 1 Feb 2019 14:29:40 +0800 Subject: [PATCH 10/44] add spring boot quartz --- springboot-Quartz/pom.xml | 72 ++++++ .../java/com/abel/quartz/Application.java | 18 ++ .../config/InvokingJobDetailFactory.java | 57 +++++ .../com/abel/quartz/config/QuartzConfig.java | 225 ++++++++++++++++++ .../java/com/abel/quartz/job/ExecuteJob.java | 21 ++ .../src/main/resources/application.properties | 47 ++++ .../src/main/resources/banner.txt | 5 + .../src/main/resources/logback-spring.xml | 41 ++++ 8 files changed, 486 insertions(+) create mode 100644 springboot-Quartz/pom.xml create mode 100644 springboot-Quartz/src/main/java/com/abel/quartz/Application.java create mode 100644 springboot-Quartz/src/main/java/com/abel/quartz/config/InvokingJobDetailFactory.java create mode 100644 springboot-Quartz/src/main/java/com/abel/quartz/config/QuartzConfig.java create mode 100644 springboot-Quartz/src/main/java/com/abel/quartz/job/ExecuteJob.java create mode 100644 springboot-Quartz/src/main/resources/application.properties create mode 100644 springboot-Quartz/src/main/resources/banner.txt create mode 100644 springboot-Quartz/src/main/resources/logback-spring.xml diff --git a/springboot-Quartz/pom.xml b/springboot-Quartz/pom.xml new file mode 100644 index 0000000..b8a0b87 --- /dev/null +++ b/springboot-Quartz/pom.xml @@ -0,0 +1,72 @@ + + + 4.0.0 + + com.abel.quartz + springboot-Quartz + 1.0-SNAPSHOT + + org.springframework.boot + spring-boot-starter-parent + 2.0.4.RELEASE + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + + + org.quartz-scheduler + quartz + 2.2.1 + + + + org.springframework.boot + spring-boot-starter-mail + + + + mysql + mysql-connector-java + + + + com.zaxxer + HikariCP + 3.2.0 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + 2.18.1 + + true + + + + + \ No newline at end of file diff --git a/springboot-Quartz/src/main/java/com/abel/quartz/Application.java b/springboot-Quartz/src/main/java/com/abel/quartz/Application.java new file mode 100644 index 0000000..38d1a0a --- /dev/null +++ b/springboot-Quartz/src/main/java/com/abel/quartz/Application.java @@ -0,0 +1,18 @@ +package com.abel.quartz; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ImportResource; + +/** + * Created by yangyibo on 2019/2/1. + */ +@SpringBootApplication +@ImportResource("classpath*:META-INF/spring/*.xml") +public class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + + +} diff --git a/springboot-Quartz/src/main/java/com/abel/quartz/config/InvokingJobDetailFactory.java b/springboot-Quartz/src/main/java/com/abel/quartz/config/InvokingJobDetailFactory.java new file mode 100644 index 0000000..c8211b9 --- /dev/null +++ b/springboot-Quartz/src/main/java/com/abel/quartz/config/InvokingJobDetailFactory.java @@ -0,0 +1,57 @@ +package com.abel.quartz.config; + +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.springframework.context.ApplicationContext; +import org.springframework.scheduling.quartz.QuartzJobBean; +import java.lang.reflect.Method; + + +/** + * Created by yangyibo on 2019/2/1. + */ +public class InvokingJobDetailFactory extends QuartzJobBean { + + /** + * 计划任务所在类 + */ + private String targetObject; + + /** + * 具体需要执行的计划任务 + */ + private String targetMethod; + + private ApplicationContext ctx; + + @Override + protected void executeInternal(JobExecutionContext context) throws JobExecutionException { + try { + Object obj = ctx.getBean(targetObject); + Method m = null; + try { + m = obj.getClass().getMethod(targetMethod); + //调用被代理对象的方法 + m.invoke(obj); + } catch (SecurityException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } + } catch (Exception e) { + throw new JobExecutionException(e); + } + } + + public void setApplicationContext(ApplicationContext applicationContext) { + this.ctx = applicationContext; + } + + public void setTargetObject(String targetObject) { + this.targetObject = targetObject; + } + + public void setTargetMethod(String targetMethod) { + this.targetMethod = targetMethod; + } +} diff --git a/springboot-Quartz/src/main/java/com/abel/quartz/config/QuartzConfig.java b/springboot-Quartz/src/main/java/com/abel/quartz/config/QuartzConfig.java new file mode 100644 index 0000000..2dbef04 --- /dev/null +++ b/springboot-Quartz/src/main/java/com/abel/quartz/config/QuartzConfig.java @@ -0,0 +1,225 @@ +package com.abel.quartz.config; + +import java.beans.PropertyVetoException; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import com.zaxxer.hikari.HikariDataSource; +import org.quartz.Job; +import org.quartz.JobDetail; +import org.quartz.Trigger; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.quartz.CronTriggerFactoryBean; +import org.springframework.scheduling.quartz.JobDetailFactoryBean; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; + +/** + * Created by yangyibo on 2019/1/16. + */ +@Configuration +public class QuartzConfig { + + /** + * 1.通过name+group获取唯一的jobKey;2.通过groupname来获取其下的所有jobkey + */ + final static String GROUP_NAME = "QuartzJobGroups"; + + @Value("${quartz.scheduler.instanceName}") + private String quartzInstanceName; + + @Value("${spring.datasource.driverClassName}") + private String myDSDriver; + + @Value("${spring.datasource.url}") + private String myDSUrl; + + @Value("${spring.datasource.username}") + private String myDSUser; + + @Value("${spring.datasource.password}") + private String myDSPassword; + + @Value("${org.quartz.dataSource.myDS.maxConnections}") + private int myDSMaxConnections; + + /** + * 设置属性 + * + * @return + * @throws IOException + */ + private Properties quartzProperties() throws IOException { + Properties prop = new Properties(); + // 调度标识名 集群中每一个实例都必须使用相同的名称 + prop.put("quartz.scheduler.instanceName", quartzInstanceName); + // ID设置为自动获取 每一个必须不同 + prop.put("org.quartz.scheduler.instanceId", "AUTO"); + // 禁用quartz软件更新 + prop.put("org.quartz.scheduler.skipUpdateCheck", "true"); + prop.put("org.quartz.scheduler.jmx.export", "true"); + + + // 数据库代理类,一般org.quartz.impl.jdbcjobstore.StdJDBCDelegate可以满足大部分数据库 + prop.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.StdJDBCDelegate"); + // 数据保存方式为数据库持久化 + prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX"); + // 数据库别名 随便取 + prop.put("org.quartz.jobStore.dataSource", "quartzDataSource"); + //prop.put("org.quartz.jobStore.dataSource", "myDS"); + // 表的前缀,默认QRTZ_ + prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_"); + // 是否加入集群 + prop.put("org.quartz.jobStore.isClustered", "true"); + + // 调度实例失效的检查时间间隔 + prop.put("org.quartz.jobStore.clusterCheckinInterval", "20000"); + prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1"); + // 信息保存时间 ms 默认值60秒 + prop.put("org.quartz.jobStore.misfireThreshold", "120000"); + prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true"); + prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS WHERE LOCK_NAME = ? FOR UPDATE"); + + // 程池的实现类(一般使用SimpleThreadPool即可满足几乎所有用户的需求) + prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool"); + // 定线程数,至少为1(无默认值)(一般设置为1-100之间的整数合适) + prop.put("org.quartz.threadPool.threadCount", "10"); + // 设置线程的优先级(最大为java.lang.Thread.MAX_PRIORITY 10,最小为Thread.MIN_PRIORITY 1,默认为5) + prop.put("org.quartz.threadPool.threadPriority", "5"); + prop.put("org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread", "true"); + + prop.put("org.quartz.plugin.triggHistory.class", "org.quartz.plugins.history.LoggingJobHistoryPlugin"); + prop.put("org.quartz.plugin.shutdownhook.class", "org.quartz.plugins.management.ShutdownHookPlugin"); + prop.put("org.quartz.plugin.shutdownhook.cleanShutdown", "true"); + + //#自定义连接池 + //org.quartz.dataSource.myDS.connectionProvider.class=com.poly.pay.schedule.DruidConnectionProvider + + return prop; + } + + /** + * 数据源 + * + * @return + * @throws PropertyVetoException + */ + @Bean + public HikariDataSource createDataSource() throws PropertyVetoException { + HikariDataSource dataSource = new HikariDataSource(); + dataSource.setJdbcUrl(myDSUrl); + dataSource.setDriverClassName(myDSDriver); + dataSource.setUsername(myDSUser); + dataSource.setPassword(myDSPassword); + dataSource.setMaximumPoolSize(myDSMaxConnections); + return dataSource; + } + + + /** + * 创建触发器工厂 + * + * @param jobDetail + * @param cronExpression + * @return + */ + private static CronTriggerFactoryBean cronTriggerFactoryBean(JobDetail jobDetail, String cronExpression) { + CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean(); + factoryBean.setJobDetail(jobDetail); + factoryBean.setCronExpression(cronExpression); + return factoryBean; + } + + +/****************************************************以下配置需要注意******************************************************/ + + + /** + * 调度工厂 + * 此处配置需要调度的触发器 例如 executeJobTrigger + * + * @param executeJobTrigger + * @return + * @throws IOException + * @throws PropertyVetoException + */ + @Bean + public SchedulerFactoryBean schedulerFactoryBean(@Qualifier("executeJobTrigger") Trigger executeJobTrigger) throws IOException, PropertyVetoException { + SchedulerFactoryBean factory = new SchedulerFactoryBean(); + // this allows to update triggers in DB when updating settings in config file: + //用于quartz集群,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 + factory.setOverwriteExistingJobs(true); + //用于quartz集群,加载quartz数据源 + //factory.setDataSource(dataSource); + //QuartzScheduler 延时启动,应用启动完10秒后 QuartzScheduler 再启动 + //factory.setStartupDelay(10); + //用于quartz集群,加载quartz数据源配置 + factory.setAutoStartup(true); + factory.setQuartzProperties(quartzProperties()); + factory.setApplicationContextSchedulerContextKey("applicationContext"); + factory.setDataSource(createDataSource()); + //注册触发器 + Trigger[] triggers = {executeJobTrigger}; + factory.setTriggers(triggers); + + return factory; + } + + + /** + * 加载触发器 + * + * 新建触发器进行job 的调度 例如 executeJobDetail + * @param jobDetail + * @return + */ + @Bean(name = "executeJobTrigger") + public CronTriggerFactoryBean executeJobTrigger(@Qualifier("executeJobDetail") JobDetail jobDetail) { + //每天凌晨3点执行 + return cronTriggerFactoryBean(jobDetail, "0 1 0 * * ? "); + } + + + /** + * 加载job + * + * 新建job 类用来代理 + * + * + * @return + */ + @Bean + public JobDetailFactoryBean executeJobDetail() { + return createJobDetail(InvokingJobDetailFactory.class, GROUP_NAME, "executeJob"); + } + + + /** + * 执行规则job工厂 + * + * 配置job 类中需要定时执行的 方法 execute + * @param jobClass + * @param groupName + * @param targetObject + * @return + */ + private static JobDetailFactoryBean createJobDetail(Class jobClass, + String groupName, + String targetObject) { + JobDetailFactoryBean factoryBean = new JobDetailFactoryBean(); + factoryBean.setJobClass(jobClass); + factoryBean.setDurability(true); + factoryBean.setRequestsRecovery(true); + factoryBean.setGroup(groupName); + Map map = new HashMap<>(); + map.put("targetMethod", "execute"); + map.put("targetObject", targetObject); + factoryBean.setJobDataAsMap(map); + return factoryBean; + } + +} \ No newline at end of file diff --git a/springboot-Quartz/src/main/java/com/abel/quartz/job/ExecuteJob.java b/springboot-Quartz/src/main/java/com/abel/quartz/job/ExecuteJob.java new file mode 100644 index 0000000..205b451 --- /dev/null +++ b/springboot-Quartz/src/main/java/com/abel/quartz/job/ExecuteJob.java @@ -0,0 +1,21 @@ +package com.abel.quartz.job; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +/** + * Created by yangyibo on 2019/2/1. + */ +@Service +public class ExecuteJob { + private static final Logger logger = LoggerFactory.getLogger(ExecuteJob.class); + + /** + * 方法名在quartz定义 + */ + public void execute() { + System.out.println("定时任务执行了。。。。。"); + + } +} diff --git a/springboot-Quartz/src/main/resources/application.properties b/springboot-Quartz/src/main/resources/application.properties new file mode 100644 index 0000000..aadea30 --- /dev/null +++ b/springboot-Quartz/src/main/resources/application.properties @@ -0,0 +1,47 @@ +## tomcat\u914D\u7F6E +server.port=8090 +#server.tomcat.maxHttpHeaderSize=8192 +server.tomcat.uri-encoding=UTF-8 +spring.http.encoding.charset=UTF-8 +spring.http.encoding.enabled=true +spring.http.encoding.force=true +spring.messages.encoding=UTF-8 +# tomcat\u6700\u5927\u7EBF\u7A0B\u6570\uFF0C\u9ED8\u8BA4\u4E3A200 +server.tomcat.max-threads=800 +# session\u6700\u5927\u8D85\u65F6\u65F6\u95F4(\u5206\u949F)\uFF0C\u9ED8\u8BA4\u4E3A30 +server.session-timeout=60 + +## spring \u914D\u7F6E +spring.application.name=springboot-Quartz +application.main=com.abel.quartz.Application + + +## \u4E3B\u6570\u636E\u6E90\uFF0C\u9ED8\u8BA4\u7684 +spring.datasource.url=jdbc:mysql://localhost:3306/quart?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&useSSL=false +spring.datasource.username=root +spring.datasource.password=admin +spring.datasource.driverClassName=com.mysql.jdbc.Driver + +## \u8FDE\u63A5\u6C60\u914D\u7F6E +spring.datasource.type=com.zaxxer.hikari.HikariDataSource +#\u6700\u5C0F\u7A7A\u95F2\u8FDE\u63A5 +spring.datasource.hikari.minimum-idle=10 +#\u8FDE\u63A5\u6C60\u4E2D\u5141\u8BB8\u7684\u6700\u5927\u8FDE\u63A5\u6570\u3002\u7F3A\u7701\u503C\uFF1A10\uFF1B\u63A8\u8350\u7684\u516C\u5F0F\uFF1A((core_count * 2) + effective_spindle_count) +spring.datasource.hikari.maximum-pool-size=30 +#spring.datasource.hikari.auto-commit=true +#\u4E00\u4E2A\u8FDE\u63A5idle\u72B6\u6001\u7684\u6700\u5927\u65F6\u957F\uFF08\u6BEB\u79D2\uFF09\uFF0C\u8D85\u65F6\u5219\u88AB\u91CA\u653E\uFF08retired\uFF09\uFF0C\u7F3A\u7701:10\u5206\u949F\u3002minimumIdle + + middle_message + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%method - %msg%n + + + + + + ${LOG_FILE} + + + ${LOG_FILE}.%d{yyyy-MM-dd} + + 60 + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%method - %msg%n + + + + + + + + + + + + + + + + + + \ No newline at end of file From 13be5bcd3c6bf6a062034875f68a107ebaf09214 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Tue, 12 Feb 2019 16:03:48 +0800 Subject: [PATCH 11/44] add dependency --- springboot-Quartz/pom.xml | 5 +++++ springboot-Quartz/src/main/resources/application.properties | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/springboot-Quartz/pom.xml b/springboot-Quartz/pom.xml index b8a0b87..4a96830 100644 --- a/springboot-Quartz/pom.xml +++ b/springboot-Quartz/pom.xml @@ -47,6 +47,11 @@ HikariCP 3.2.0 + + + org.springframework + spring-jdbc + diff --git a/springboot-Quartz/src/main/resources/application.properties b/springboot-Quartz/src/main/resources/application.properties index aadea30..c21a4a1 100644 --- a/springboot-Quartz/src/main/resources/application.properties +++ b/springboot-Quartz/src/main/resources/application.properties @@ -17,7 +17,7 @@ application.main=com.abel.quartz.Application ## \u4E3B\u6570\u636E\u6E90\uFF0C\u9ED8\u8BA4\u7684 -spring.datasource.url=jdbc:mysql://localhost:3306/quart?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&useSSL=false +spring.datasource.url=jdbc:mysql://localhost:3306/quartz?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&useSSL=false spring.datasource.username=root spring.datasource.password=admin spring.datasource.driverClassName=com.mysql.jdbc.Driver From bbe8e3710b1e71833681da5e4cfbce9dab9654f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Wed, 27 Mar 2019 19:03:33 +0800 Subject: [PATCH 12/44] add springboot-dynamicDataSource --- springboot-dynamicDataSource/pom.xml | 57 +++++++++++++ .../src/main/java/cn/abel/Application.java | 16 ++++ .../src/main/java/cn/abel/bean/News.java | 50 +++++++++++ .../src/main/java/cn/abel/bean/User.java | 70 +++++++++++++++ .../cn/abel/config/DynamicDataSource.java | 15 ++++ .../abel/config/DynamicDataSourceConfig.java | 83 ++++++++++++++++++ .../DynamicDataSourceContextHolder.java | 23 +++++ .../java/cn/abel/config/HikariConfig.java | 81 ++++++++++++++++++ .../src/main/java/cn/abel/dao/NewsDao.java | 23 +++++ .../src/main/java/cn/abel/dao/UserDao.java | 23 +++++ .../java/cn/abel/enums/DatabaseTypeEnum.java | 32 +++++++ .../java/cn/abel/service/NewsService.java | 47 ++++++++++ .../java/cn/abel/service/UserService.java | 46 ++++++++++ .../resources/local/application.properties | 60 +++++++++++++ .../src/main/resources/local/banner.txt | 5 ++ .../main/resources/local/logback-spring.xml | 41 +++++++++ .../main/resources/mapper/NewsDaoMapper.xml | 75 ++++++++++++++++ .../main/resources/mapper/UserDaoMapper.xml | 85 +++++++++++++++++++ .../src/test/java/cn/abel/BaseTest.java | 32 +++++++ .../java/cn/abel/service/ServiceTest.java | 29 +++++++ 20 files changed, 893 insertions(+) create mode 100644 springboot-dynamicDataSource/pom.xml create mode 100644 springboot-dynamicDataSource/src/main/java/cn/abel/Application.java create mode 100644 springboot-dynamicDataSource/src/main/java/cn/abel/bean/News.java create mode 100644 springboot-dynamicDataSource/src/main/java/cn/abel/bean/User.java create mode 100644 springboot-dynamicDataSource/src/main/java/cn/abel/config/DynamicDataSource.java create mode 100644 springboot-dynamicDataSource/src/main/java/cn/abel/config/DynamicDataSourceConfig.java create mode 100644 springboot-dynamicDataSource/src/main/java/cn/abel/config/DynamicDataSourceContextHolder.java create mode 100644 springboot-dynamicDataSource/src/main/java/cn/abel/config/HikariConfig.java create mode 100644 springboot-dynamicDataSource/src/main/java/cn/abel/dao/NewsDao.java create mode 100644 springboot-dynamicDataSource/src/main/java/cn/abel/dao/UserDao.java create mode 100644 springboot-dynamicDataSource/src/main/java/cn/abel/enums/DatabaseTypeEnum.java create mode 100644 springboot-dynamicDataSource/src/main/java/cn/abel/service/NewsService.java create mode 100644 springboot-dynamicDataSource/src/main/java/cn/abel/service/UserService.java create mode 100644 springboot-dynamicDataSource/src/main/resources/local/application.properties create mode 100644 springboot-dynamicDataSource/src/main/resources/local/banner.txt create mode 100644 springboot-dynamicDataSource/src/main/resources/local/logback-spring.xml create mode 100644 springboot-dynamicDataSource/src/main/resources/mapper/NewsDaoMapper.xml create mode 100644 springboot-dynamicDataSource/src/main/resources/mapper/UserDaoMapper.xml create mode 100644 springboot-dynamicDataSource/src/test/java/cn/abel/BaseTest.java create mode 100644 springboot-dynamicDataSource/src/test/java/cn/abel/service/ServiceTest.java diff --git a/springboot-dynamicDataSource/pom.xml b/springboot-dynamicDataSource/pom.xml new file mode 100644 index 0000000..9992b8b --- /dev/null +++ b/springboot-dynamicDataSource/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + + cn.abel + springboot-dynamicDataSource + 1.0-SNAPSHOT + + org.springframework.boot + spring-boot-starter-parent + 2.0.4.RELEASE + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-starter-freemarker + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 1.3.2 + + + com.github.pagehelper + pagehelper-spring-boot-starter + 1.2.5 + + + mysql + mysql-connector-java + 5.1.40 + + + + org.apache.commons + commons-lang3 + 3.4 + + + + \ No newline at end of file diff --git a/springboot-dynamicDataSource/src/main/java/cn/abel/Application.java b/springboot-dynamicDataSource/src/main/java/cn/abel/Application.java new file mode 100644 index 0000000..4c77f67 --- /dev/null +++ b/springboot-dynamicDataSource/src/main/java/cn/abel/Application.java @@ -0,0 +1,16 @@ +package cn.abel; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author yyb + * @time 2019/3/26 + */ +@SpringBootApplication +public class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/springboot-dynamicDataSource/src/main/java/cn/abel/bean/News.java b/springboot-dynamicDataSource/src/main/java/cn/abel/bean/News.java new file mode 100644 index 0000000..5462104 --- /dev/null +++ b/springboot-dynamicDataSource/src/main/java/cn/abel/bean/News.java @@ -0,0 +1,50 @@ +package cn.abel.bean; + + +public class News { + private Integer id; + private String title; + private String content; + private String imagePath; + private Integer readSum; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getImagePath() { + return imagePath; + } + + public void setImagePath(String imagePath) { + this.imagePath = imagePath; + } + + public Integer getReadSum() { + return readSum; + } + + public void setReadSum(Integer readSum) { + this.readSum = readSum; + } +} \ No newline at end of file diff --git a/springboot-dynamicDataSource/src/main/java/cn/abel/bean/User.java b/springboot-dynamicDataSource/src/main/java/cn/abel/bean/User.java new file mode 100644 index 0000000..58acdf2 --- /dev/null +++ b/springboot-dynamicDataSource/src/main/java/cn/abel/bean/User.java @@ -0,0 +1,70 @@ +package cn.abel.bean; + +import java.util.Date; + + +public class User { + private Integer id; + private String name; + private String address; + private String mobile; + private String email; + private Date createTime; + private Integer role; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getMobile() { + return mobile; + } + + public void setMobile(String mobile) { + this.mobile = mobile; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Integer getRole() { + return role; + } + + public void setRole(Integer role) { + this.role = role; + } +} \ No newline at end of file diff --git a/springboot-dynamicDataSource/src/main/java/cn/abel/config/DynamicDataSource.java b/springboot-dynamicDataSource/src/main/java/cn/abel/config/DynamicDataSource.java new file mode 100644 index 0000000..99e3af5 --- /dev/null +++ b/springboot-dynamicDataSource/src/main/java/cn/abel/config/DynamicDataSource.java @@ -0,0 +1,15 @@ +package cn.abel.config; + +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +/** + * @author yyb + * @time 2019/3/27 + */ +public class DynamicDataSource extends AbstractRoutingDataSource { + @Override + protected Object determineCurrentLookupKey() { + return DynamicDataSourceContextHolder.getDatabaseType(); + } +} + diff --git a/springboot-dynamicDataSource/src/main/java/cn/abel/config/DynamicDataSourceConfig.java b/springboot-dynamicDataSource/src/main/java/cn/abel/config/DynamicDataSourceConfig.java new file mode 100644 index 0000000..f89fd9d --- /dev/null +++ b/springboot-dynamicDataSource/src/main/java/cn/abel/config/DynamicDataSourceConfig.java @@ -0,0 +1,83 @@ +package cn.abel.config; + +import cn.abel.enums.DatabaseTypeEnum; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionFactoryBean; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; + +import javax.sql.DataSource; +import java.util.HashMap; +import java.util.Map; + +/** + * @author yyb + * @time 2019/3/27 + */ +@Configuration +@MapperScan(basePackages = "cn.abel.dao") +public class DynamicDataSourceConfig { + @Value("${spring.datasource.primary.url}") + private String primaryUrl; + @Value("${spring.datasource.user.url}") + private String userUrl; + @Value("${mybatis.mapper-locations}") + private String resources; + //当两个数据库连接账号密码不一样时 +// @Value("${spring.datasource.user.username}") +// private String userName; +// @Value("${spring.datasource.user.password}") +// private String password; + + @Autowired + private HikariConfig hikariConfig; + + @Primary + @Bean(name = "primaryDataSource") + public DataSource getPrimaryDataSource() { + return hikariConfig.getHikariDataSource(primaryUrl); + } + + @Bean(name = "userDataSource") + public DataSource getUserDataSource() { + return hikariConfig.getHikariDataSource(userUrl); + } + + + //当两个数据库连接账号密码不一样时使用 +// @Bean(name = "userDataSource") +// public DataSource getUserDataSource() { +// return hikariConfig.getHikariDataSource(userUrl, userName, password); +// } + + + @Bean("dynamicDataSource") + public DynamicDataSource dynamicDataSource(@Qualifier("primaryDataSource") DataSource primaryDataSource, + @Qualifier("userDataSource") DataSource miaoMoreDataSource) { + Map targetDataSources = new HashMap<>(); + targetDataSources.put(DatabaseTypeEnum.PRIMARY, primaryDataSource); + targetDataSources.put(DatabaseTypeEnum.USER, miaoMoreDataSource); + + DynamicDataSource dataSource = new DynamicDataSource(); + dataSource.setTargetDataSources(targetDataSources);// 该方法是AbstractRoutingDataSource的方法 + dataSource.setDefaultTargetDataSource(primaryDataSource);// 默认的datasource设置为myTestDbDataSource + return dataSource; + } + + /** + * 根据数据源创建SqlSessionFactory + */ + @Bean + public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DynamicDataSource dynamicDataSource) throws Exception { + SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); + bean.setDataSource(dynamicDataSource); + bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(resources)); + return bean.getObject(); + } +} \ No newline at end of file diff --git a/springboot-dynamicDataSource/src/main/java/cn/abel/config/DynamicDataSourceContextHolder.java b/springboot-dynamicDataSource/src/main/java/cn/abel/config/DynamicDataSourceContextHolder.java new file mode 100644 index 0000000..701602e --- /dev/null +++ b/springboot-dynamicDataSource/src/main/java/cn/abel/config/DynamicDataSourceContextHolder.java @@ -0,0 +1,23 @@ +package cn.abel.config; + +import cn.abel.enums.DatabaseTypeEnum; + +/** + * @author yyb + * @time 2019/3/27 + */ +public class DynamicDataSourceContextHolder { + private static final ThreadLocal contextHolder = new ThreadLocal<>(); + + public static void setDatabaseType(DatabaseTypeEnum type){ + contextHolder.set(type); + } + + public static DatabaseTypeEnum getDatabaseType(){ + return contextHolder.get(); + } + + public static void resetDatabaseType() { + contextHolder.set(DatabaseTypeEnum.PRIMARY); + } +} diff --git a/springboot-dynamicDataSource/src/main/java/cn/abel/config/HikariConfig.java b/springboot-dynamicDataSource/src/main/java/cn/abel/config/HikariConfig.java new file mode 100644 index 0000000..2cd63eb --- /dev/null +++ b/springboot-dynamicDataSource/src/main/java/cn/abel/config/HikariConfig.java @@ -0,0 +1,81 @@ +package cn.abel.config; + +import com.zaxxer.hikari.HikariDataSource; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +/** + * @author yyb + * @time 2019/3/27 + */ +@Service +public class HikariConfig { + @Value("${spring.datasource.username}") + private String userName; + @Value("${spring.datasource.password}") + private String password; + @Value("${spring.datasource.driver-class-name}") + private String driverClassName; + @Value("${spring.datasource.min-idle}") + private Integer minIdle; + @Value("${spring.datasource.max-active}") + private Integer maxActive; + @Value("${spring.datasource.validation-query}") + private String validationQuery; + @Value("${spring.datasource.connection-timeout}") + private Integer connectionTimeout; + @Value("${spring.datasource.max-left-time}") + private Integer maxLeftTime; + @Value("${spring.datasource.validation-time-out}") + private Integer validationTimeout; + @Value("${spring.datasource.idle-time-out}") + private Integer idleTimeout; + @Value("${spring.datasource.connection-init-sql}") + private String connectionInitSql; + + /** + * 配置Hikari数据连接池 + * + * @param url 数据源 + * @return 连接池对象 + */ + public HikariDataSource getHikariDataSource(String url) { + com.zaxxer.hikari.HikariConfig config = new com.zaxxer.hikari.HikariConfig(); + config.setMinimumIdle(minIdle); + config.setMaximumPoolSize(maxActive); + config.setConnectionTestQuery(validationQuery); + config.setJdbcUrl(url); + config.setUsername(userName); + config.setPassword(password); + config.setDriverClassName(driverClassName); + config.setConnectionTimeout(connectionTimeout); + config.setMaxLifetime(maxLeftTime); + config.setValidationTimeout(validationTimeout); + config.setConnectionInitSql(connectionInitSql); + return new HikariDataSource(config); + } + + /** + * 用于两个库账号密码不一样的时候 + * + * @param url + * @param userName + * @param passwod + * @return + */ + public HikariDataSource getHikariDataSource(String url, String userName, String passwod) { + com.zaxxer.hikari.HikariConfig config = new com.zaxxer.hikari.HikariConfig(); + config.setMinimumIdle(minIdle); + config.setMaximumPoolSize(maxActive); + config.setConnectionTestQuery(validationQuery); + config.setJdbcUrl(url); + config.setUsername(userName); + config.setPassword(passwod); + config.setConnectionTimeout(connectionTimeout); + config.setDriverClassName(driverClassName); + config.setMaxLifetime(maxLeftTime); + config.setValidationTimeout(validationTimeout); + config.setConnectionInitSql(connectionInitSql); + return new HikariDataSource(config); + } +} diff --git a/springboot-dynamicDataSource/src/main/java/cn/abel/dao/NewsDao.java b/springboot-dynamicDataSource/src/main/java/cn/abel/dao/NewsDao.java new file mode 100644 index 0000000..b32f0f2 --- /dev/null +++ b/springboot-dynamicDataSource/src/main/java/cn/abel/dao/NewsDao.java @@ -0,0 +1,23 @@ +package cn.abel.dao; + +import java.util.List; +import java.util.Map; + +import cn.abel.bean.News; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; + +@Repository +@Mapper +public interface NewsDao { + + List getByMap(Map map); + + News getById(Integer id); + + Integer create(News news); + + int update(News news); + + int delete(Integer id); +} \ No newline at end of file diff --git a/springboot-dynamicDataSource/src/main/java/cn/abel/dao/UserDao.java b/springboot-dynamicDataSource/src/main/java/cn/abel/dao/UserDao.java new file mode 100644 index 0000000..077d1f5 --- /dev/null +++ b/springboot-dynamicDataSource/src/main/java/cn/abel/dao/UserDao.java @@ -0,0 +1,23 @@ +package cn.abel.dao; + +import java.util.List; +import java.util.Map; + +import cn.abel.bean.User; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; + +@Repository +@Mapper +public interface UserDao { + + List getByMap(Map map); + + User getById(Integer id); + + Integer create(User user); + + int update(User user); + + int delete(Integer id); +} \ No newline at end of file diff --git a/springboot-dynamicDataSource/src/main/java/cn/abel/enums/DatabaseTypeEnum.java b/springboot-dynamicDataSource/src/main/java/cn/abel/enums/DatabaseTypeEnum.java new file mode 100644 index 0000000..03d86b1 --- /dev/null +++ b/springboot-dynamicDataSource/src/main/java/cn/abel/enums/DatabaseTypeEnum.java @@ -0,0 +1,32 @@ +package cn.abel.enums; + +/** + * @author yyb + * @time 2019/3/27 + */ +public enum DatabaseTypeEnum { + PRIMARY("1"), USER("2"); + + private String code; + + DatabaseTypeEnum(String code) { + this.code = code; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public static DatabaseTypeEnum getDatabaseTypeEnum(String code) { + for (DatabaseTypeEnum databaseTypeEnum : DatabaseTypeEnum.values()) { + if (databaseTypeEnum.getCode().equals(code)) { + return databaseTypeEnum; + } + } + return null; + } +} diff --git a/springboot-dynamicDataSource/src/main/java/cn/abel/service/NewsService.java b/springboot-dynamicDataSource/src/main/java/cn/abel/service/NewsService.java new file mode 100644 index 0000000..1c6f1ee --- /dev/null +++ b/springboot-dynamicDataSource/src/main/java/cn/abel/service/NewsService.java @@ -0,0 +1,47 @@ +package cn.abel.service; + +import java.util.List; +import java.util.Map; + +import cn.abel.config.DynamicDataSource; +import cn.abel.config.DynamicDataSourceContextHolder; +import cn.abel.enums.DatabaseTypeEnum; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import cn.abel.dao.NewsDao; +import cn.abel.bean.News; + +@Service +public class NewsService { + @Autowired + private NewsDao newsDao; + + public List getByMap(Map map) { + DynamicDataSourceContextHolder.resetDatabaseType(); + return newsDao.getByMap(map); + } + + public News getById(Integer id) { + DynamicDataSourceContextHolder.resetDatabaseType(); + return newsDao.getById(id); + } + + public News create(News news) { + DynamicDataSourceContextHolder.resetDatabaseType(); + newsDao.create(news); + return news; + } + + public News update(News news) { + DynamicDataSourceContextHolder.resetDatabaseType(); + newsDao.update(news); + return news; + } + + public int delete(Integer id) { + DynamicDataSourceContextHolder.resetDatabaseType(); + return newsDao.delete(id); + } + +} \ No newline at end of file diff --git a/springboot-dynamicDataSource/src/main/java/cn/abel/service/UserService.java b/springboot-dynamicDataSource/src/main/java/cn/abel/service/UserService.java new file mode 100644 index 0000000..4766a40 --- /dev/null +++ b/springboot-dynamicDataSource/src/main/java/cn/abel/service/UserService.java @@ -0,0 +1,46 @@ +package cn.abel.service; + +import java.util.List; +import java.util.Map; + +import cn.abel.config.DynamicDataSourceContextHolder; +import cn.abel.enums.DatabaseTypeEnum; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import cn.abel.dao.UserDao; +import cn.abel.bean.User; + +@Service +public class UserService { + @Autowired + private UserDao userDao; + + public List getByMap(Map map){ + DynamicDataSourceContextHolder.setDatabaseType(DatabaseTypeEnum.USER); + return userDao.getByMap(map); + } + + public User getById(Integer id){ + DynamicDataSourceContextHolder.setDatabaseType(DatabaseTypeEnum.USER); + return userDao.getById(id); + } + + public User create(User user){ + DynamicDataSourceContextHolder.setDatabaseType(DatabaseTypeEnum.USER); + userDao.create(user); + return user; + } + + public User update(User user){ + DynamicDataSourceContextHolder.setDatabaseType(DatabaseTypeEnum.USER); + userDao.update(user); + return user; + } + + public int delete(Integer id){ + DynamicDataSourceContextHolder.setDatabaseType(DatabaseTypeEnum.USER); + return userDao.delete(id); + } + +} \ No newline at end of file diff --git a/springboot-dynamicDataSource/src/main/resources/local/application.properties b/springboot-dynamicDataSource/src/main/resources/local/application.properties new file mode 100644 index 0000000..e5981f3 --- /dev/null +++ b/springboot-dynamicDataSource/src/main/resources/local/application.properties @@ -0,0 +1,60 @@ +## tomcat\u914D\u7F6E +server.port=8009 +#server.tomcat.maxHttpHeaderSize=8192 +server.tomcat.uri-encoding=UTF-8 +spring.http.encoding.charset=UTF-8 +spring.http.encoding.enabled=true +spring.http.encoding.force=true +spring.risk.encoding=UTF-8 +# tomcat\u6700\u5927\u7EBF\u7A0B\u6570\uFF0C\u9ED8\u8BA4\u4E3A200 +server.tomcat.max-threads=800 +# session\u6700\u5927\u8D85\u65F6\u65F6\u95F4(\u5206\u949F)\uFF0C\u9ED8\u8BA4\u4E3A30 +server.session-timeout=60 + +## spring \u914D\u7F6E +spring.application.name=springboot-dynamicDataSource +application.main=cn.abel.Application + +## LOG +logging.file=./logs/springboot-dynamicDataSource.log + + +## \u4E3B\u6570\u636E\u6E90\uFF0C\u9ED8\u8BA4\u7684 +spring.datasource.primary.url=jdbc:mysql://localhost:3306/oldMan?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&useSSL=false +spring.datasource.user.url=jdbc:mysql://localhost:3306/rules?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&useSSL=false + +spring.datasource.username=root +spring.datasource.password=admin +spring.datasource.driverClassName=com.mysql.jdbc.Driver + +spring.datasource.type=com.zaxxer.hikari.HikariDataSource +#\u6700\u5C0F\u7A7A\u95F2\u8FDE\u63A5 +spring.datasource.min-idle=5 +#\u6700\u5927\u8FDE\u63A5\u6570\u91CF +spring.datasource.max-active=100 +#\u68C0\u6D4B\u6570\u636E\u5E93\u7684\u67E5\u8BE2\u8BED\u53E5 +spring.datasource.validation-query=select 1 from dual +#\u7B49\u5F85\u8FDE\u63A5\u6C60\u5206\u914D\u8FDE\u63A5\u7684\u6700\u5927\u65F6\u957F\uFF08\u6BEB\u79D2\uFF09 +spring.datasource.connection-timeout=60000 +#\u4E00\u4E2A\u8FDE\u63A5\u7684\u751F\u547D\u65F6\u957F\uFF08\u6BEB\u79D2\uFF09 +spring.datasource.max-left-time=60000 +#\u751F\u6548\u8D85\u65F6 +spring.datasource.validation-time-out=3000 +#\u4E00\u4E2A\u8FDE\u63A5idle\u72B6\u6001\u7684\u6700\u5927\u65F6\u957F\uFF08\u6BEB\u79D2\uFF09 +spring.datasource.idle-time-out=60000 +#\u8BBE\u7F6E\u9ED8\u8BA4\u5B57\u7B26\u96C6 +spring.datasource.connection-init-sql= set names utf8mb4 + +logging.level.cn.abel.dao=debug +#Mapper.xml\u6240\u5728\u7684\u4F4D\u7F6E +mybatis.mapper-locations=classpath*:mapper/*Mapper.xml +smybatis.type-aliases-package=cn.abel.bean +#Mapper.xml\u6240\u5728\u7684\u4F4D\u7F6E + +## pagehelper +pagehelper.helperDialect=mysql +pagehelper.reasonable=true +pagehelper.supportMethodsArguments=true +pagehelper.params=count=countSql + + diff --git a/springboot-dynamicDataSource/src/main/resources/local/banner.txt b/springboot-dynamicDataSource/src/main/resources/local/banner.txt new file mode 100644 index 0000000..42f7c59 --- /dev/null +++ b/springboot-dynamicDataSource/src/main/resources/local/banner.txt @@ -0,0 +1,5 @@ +######################################################## +# # +# local # +# # +######################################################## \ No newline at end of file diff --git a/springboot-dynamicDataSource/src/main/resources/local/logback-spring.xml b/springboot-dynamicDataSource/src/main/resources/local/logback-spring.xml new file mode 100644 index 0000000..0505336 --- /dev/null +++ b/springboot-dynamicDataSource/src/main/resources/local/logback-spring.xml @@ -0,0 +1,41 @@ + + + middle_message + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%method - %msg%n + + + + + + ${LOG_FILE} + + + ${LOG_FILE}.%d{yyyy-MM-dd} + + 60 + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%method - %msg%n + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/springboot-dynamicDataSource/src/main/resources/mapper/NewsDaoMapper.xml b/springboot-dynamicDataSource/src/main/resources/mapper/NewsDaoMapper.xml new file mode 100644 index 0000000..b4eeb93 --- /dev/null +++ b/springboot-dynamicDataSource/src/main/resources/mapper/NewsDaoMapper.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + and id = #{id} + + + and title = #{title} + + + and content = #{content} + + + and image_path = #{imagePath} + + + and read_sum = #{readSum} + + + + + + + + + + + + SELECT LAST_INSERT_ID() + + INSERT INTO news( + id, + title, + content, + image_path, + read_sum + )VALUES( + #{id}, + #{title}, + #{content}, + #{imagePath}, + #{readSum} + ) + + + + UPDATE news SET + title = #{title}, + content = #{content}, + image_path = #{imagePath}, + read_sum = #{readSum} + WHERE id = #{id} + + + + DELETE FROM news WHERE id = #{id} + + + \ No newline at end of file diff --git a/springboot-dynamicDataSource/src/main/resources/mapper/UserDaoMapper.xml b/springboot-dynamicDataSource/src/main/resources/mapper/UserDaoMapper.xml new file mode 100644 index 0000000..ec2ce2a --- /dev/null +++ b/springboot-dynamicDataSource/src/main/resources/mapper/UserDaoMapper.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + and id = #{id} + + + and name = #{name} + + + and address = #{address} + + + and mobile = #{mobile} + + + and email = #{email} + + + and role = #{role} + + + + + + + + + + + + SELECT LAST_INSERT_ID() + + INSERT INTO user( + id, + name, + address, + mobile, + email, + create_time, + role + )VALUES( + #{id}, + #{name}, + #{address}, + #{mobile}, + #{email}, + NOW(), + #{role} + ) + + + + UPDATE user SET + name = #{name}, + address = #{address}, + mobile = #{mobile}, + email = #{email}, + role = #{role} + WHERE id = #{id} + + + + DELETE FROM user WHERE id = #{id} + + + \ No newline at end of file diff --git a/springboot-dynamicDataSource/src/test/java/cn/abel/BaseTest.java b/springboot-dynamicDataSource/src/test/java/cn/abel/BaseTest.java new file mode 100644 index 0000000..2cb1d62 --- /dev/null +++ b/springboot-dynamicDataSource/src/test/java/cn/abel/BaseTest.java @@ -0,0 +1,32 @@ +package cn.abel; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * @author yyb + * @time 2019/3/27 + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = Application.class) +@TestPropertySource("/application.properties") +public class BaseTest { + + @Configuration + @ComponentScan(basePackages = {"cn.abel"}, excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Configuration.class)) + static class ComponentScanConfig { + + } + + + @Test + public void contextLoads() { + } + +} \ No newline at end of file diff --git a/springboot-dynamicDataSource/src/test/java/cn/abel/service/ServiceTest.java b/springboot-dynamicDataSource/src/test/java/cn/abel/service/ServiceTest.java new file mode 100644 index 0000000..a4ddd52 --- /dev/null +++ b/springboot-dynamicDataSource/src/test/java/cn/abel/service/ServiceTest.java @@ -0,0 +1,29 @@ +package cn.abel.service; + +import cn.abel.BaseTest; +import cn.abel.bean.News; +import cn.abel.bean.User; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; + +/** + * @author yyb + * @time 2019/3/27 + */ +public class ServiceTest extends BaseTest { + + @Autowired + private NewsService newsService; + @Autowired + private UserService userService; + + @Test + public void dynamicDataSourceTest() throws Exception { + List newsList = newsService.getByMap(null); + List userList = userService.getByMap(null); + System.out.println("newsList----" + newsList.size() + "--userList---" + userList.size()); + + } +} From d41368581911e4c003ec1cb4622824f50c9c15dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Tue, 2 Apr 2019 14:42:47 +0800 Subject: [PATCH 13/44] add mybatis2 --- springboot-mybatis2/pom.xml | 57 +++++++++++++ .../src/main/java/cn/abel/Application.java | 16 ++++ .../src/main/java/cn/abel/bean/News.java | 50 +++++++++++ .../src/main/java/cn/abel/bean/User.java | 70 +++++++++++++++ .../src/main/java/cn/abel/dao/NewsDao.java | 23 +++++ .../src/main/java/cn/abel/dao/UserDao.java | 23 +++++ .../java/cn/abel/service/NewsService.java | 40 +++++++++ .../java/cn/abel/service/UserService.java | 39 +++++++++ .../resources/local/application.properties | 58 +++++++++++++ .../src/main/resources/local/banner.txt | 5 ++ .../main/resources/local/logback-spring.xml | 41 +++++++++ .../main/resources/mapper/NewsDaoMapper.xml | 75 ++++++++++++++++ .../main/resources/mapper/UserDaoMapper.xml | 85 +++++++++++++++++++ .../src/test/java/cn/abel/BaseTest.java | 32 +++++++ .../java/cn/abel/service/ServiceTest.java | 29 +++++++ 15 files changed, 643 insertions(+) create mode 100644 springboot-mybatis2/pom.xml create mode 100644 springboot-mybatis2/src/main/java/cn/abel/Application.java create mode 100644 springboot-mybatis2/src/main/java/cn/abel/bean/News.java create mode 100644 springboot-mybatis2/src/main/java/cn/abel/bean/User.java create mode 100644 springboot-mybatis2/src/main/java/cn/abel/dao/NewsDao.java create mode 100644 springboot-mybatis2/src/main/java/cn/abel/dao/UserDao.java create mode 100644 springboot-mybatis2/src/main/java/cn/abel/service/NewsService.java create mode 100644 springboot-mybatis2/src/main/java/cn/abel/service/UserService.java create mode 100644 springboot-mybatis2/src/main/resources/local/application.properties create mode 100644 springboot-mybatis2/src/main/resources/local/banner.txt create mode 100644 springboot-mybatis2/src/main/resources/local/logback-spring.xml create mode 100644 springboot-mybatis2/src/main/resources/mapper/NewsDaoMapper.xml create mode 100644 springboot-mybatis2/src/main/resources/mapper/UserDaoMapper.xml create mode 100644 springboot-mybatis2/src/test/java/cn/abel/BaseTest.java create mode 100644 springboot-mybatis2/src/test/java/cn/abel/service/ServiceTest.java diff --git a/springboot-mybatis2/pom.xml b/springboot-mybatis2/pom.xml new file mode 100644 index 0000000..9aac0cf --- /dev/null +++ b/springboot-mybatis2/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + + cn.abel + springboot-mybatis2 + 1.0-SNAPSHOT + + org.springframework.boot + spring-boot-starter-parent + 2.0.4.RELEASE + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-starter-freemarker + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 1.3.2 + + + com.github.pagehelper + pagehelper-spring-boot-starter + 1.2.5 + + + mysql + mysql-connector-java + 5.1.40 + + + + org.apache.commons + commons-lang3 + 3.4 + + + + \ No newline at end of file diff --git a/springboot-mybatis2/src/main/java/cn/abel/Application.java b/springboot-mybatis2/src/main/java/cn/abel/Application.java new file mode 100644 index 0000000..4c77f67 --- /dev/null +++ b/springboot-mybatis2/src/main/java/cn/abel/Application.java @@ -0,0 +1,16 @@ +package cn.abel; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author yyb + * @time 2019/3/26 + */ +@SpringBootApplication +public class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/springboot-mybatis2/src/main/java/cn/abel/bean/News.java b/springboot-mybatis2/src/main/java/cn/abel/bean/News.java new file mode 100644 index 0000000..5462104 --- /dev/null +++ b/springboot-mybatis2/src/main/java/cn/abel/bean/News.java @@ -0,0 +1,50 @@ +package cn.abel.bean; + + +public class News { + private Integer id; + private String title; + private String content; + private String imagePath; + private Integer readSum; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getImagePath() { + return imagePath; + } + + public void setImagePath(String imagePath) { + this.imagePath = imagePath; + } + + public Integer getReadSum() { + return readSum; + } + + public void setReadSum(Integer readSum) { + this.readSum = readSum; + } +} \ No newline at end of file diff --git a/springboot-mybatis2/src/main/java/cn/abel/bean/User.java b/springboot-mybatis2/src/main/java/cn/abel/bean/User.java new file mode 100644 index 0000000..58acdf2 --- /dev/null +++ b/springboot-mybatis2/src/main/java/cn/abel/bean/User.java @@ -0,0 +1,70 @@ +package cn.abel.bean; + +import java.util.Date; + + +public class User { + private Integer id; + private String name; + private String address; + private String mobile; + private String email; + private Date createTime; + private Integer role; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getMobile() { + return mobile; + } + + public void setMobile(String mobile) { + this.mobile = mobile; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Integer getRole() { + return role; + } + + public void setRole(Integer role) { + this.role = role; + } +} \ No newline at end of file diff --git a/springboot-mybatis2/src/main/java/cn/abel/dao/NewsDao.java b/springboot-mybatis2/src/main/java/cn/abel/dao/NewsDao.java new file mode 100644 index 0000000..b32f0f2 --- /dev/null +++ b/springboot-mybatis2/src/main/java/cn/abel/dao/NewsDao.java @@ -0,0 +1,23 @@ +package cn.abel.dao; + +import java.util.List; +import java.util.Map; + +import cn.abel.bean.News; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; + +@Repository +@Mapper +public interface NewsDao { + + List getByMap(Map map); + + News getById(Integer id); + + Integer create(News news); + + int update(News news); + + int delete(Integer id); +} \ No newline at end of file diff --git a/springboot-mybatis2/src/main/java/cn/abel/dao/UserDao.java b/springboot-mybatis2/src/main/java/cn/abel/dao/UserDao.java new file mode 100644 index 0000000..077d1f5 --- /dev/null +++ b/springboot-mybatis2/src/main/java/cn/abel/dao/UserDao.java @@ -0,0 +1,23 @@ +package cn.abel.dao; + +import java.util.List; +import java.util.Map; + +import cn.abel.bean.User; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; + +@Repository +@Mapper +public interface UserDao { + + List getByMap(Map map); + + User getById(Integer id); + + Integer create(User user); + + int update(User user); + + int delete(Integer id); +} \ No newline at end of file diff --git a/springboot-mybatis2/src/main/java/cn/abel/service/NewsService.java b/springboot-mybatis2/src/main/java/cn/abel/service/NewsService.java new file mode 100644 index 0000000..e5145ce --- /dev/null +++ b/springboot-mybatis2/src/main/java/cn/abel/service/NewsService.java @@ -0,0 +1,40 @@ +package cn.abel.service; + +import java.util.List; +import java.util.Map; + + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import cn.abel.dao.NewsDao; +import cn.abel.bean.News; + +@Service +public class NewsService { + @Autowired + private NewsDao newsDao; + + public List getByMap(Map map) { + return newsDao.getByMap(map); + } + + public News getById(Integer id) { + return newsDao.getById(id); + } + + public News create(News news) { + newsDao.create(news); + return news; + } + + public News update(News news) { + newsDao.update(news); + return news; + } + + public int delete(Integer id) { + return newsDao.delete(id); + } + +} \ No newline at end of file diff --git a/springboot-mybatis2/src/main/java/cn/abel/service/UserService.java b/springboot-mybatis2/src/main/java/cn/abel/service/UserService.java new file mode 100644 index 0000000..e21f61c --- /dev/null +++ b/springboot-mybatis2/src/main/java/cn/abel/service/UserService.java @@ -0,0 +1,39 @@ +package cn.abel.service; + +import java.util.List; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import cn.abel.dao.UserDao; +import cn.abel.bean.User; + +@Service +public class UserService { + @Autowired + private UserDao userDao; + + public List getByMap(Map map){ + return userDao.getByMap(map); + } + + public User getById(Integer id){ + return userDao.getById(id); + } + + public User create(User user){ + userDao.create(user); + return user; + } + + public User update(User user){ + userDao.update(user); + return user; + } + + public int delete(Integer id){ + return userDao.delete(id); + } + +} \ No newline at end of file diff --git a/springboot-mybatis2/src/main/resources/local/application.properties b/springboot-mybatis2/src/main/resources/local/application.properties new file mode 100644 index 0000000..3fc81af --- /dev/null +++ b/springboot-mybatis2/src/main/resources/local/application.properties @@ -0,0 +1,58 @@ +## tomcat\u914D\u7F6E +server.port=8009 +#server.tomcat.maxHttpHeaderSize=8192 +server.tomcat.uri-encoding=UTF-8 +spring.http.encoding.charset=UTF-8 +spring.http.encoding.enabled=true +spring.http.encoding.force=true +spring.risk.encoding=UTF-8 +# tomcat\u6700\u5927\u7EBF\u7A0B\u6570\uFF0C\u9ED8\u8BA4\u4E3A200 +server.tomcat.max-threads=800 +# session\u6700\u5927\u8D85\u65F6\u65F6\u95F4(\u5206\u949F)\uFF0C\u9ED8\u8BA4\u4E3A30 +server.session-timeout=60 + +## spring \u914D\u7F6E +spring.application.name=springboot-dynamicDataSource +application.main=cn.abel.Application + +## LOG +logging.file=./logs/springboot-dynamicDataSource.log + + +## \u4E3B\u6570\u636E\u6E90\uFF0C\u9ED8\u8BA4\u7684 +spring.datasource.url=jdbc:mysql://localhost:3306/oldMan?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&useSSL=false +spring.datasource.username=root +spring.datasource.password=admin +spring.datasource.driverClassName=com.mysql.jdbc.Driver + +spring.datasource.type=com.zaxxer.hikari.HikariDataSource +#\u6700\u5C0F\u7A7A\u95F2\u8FDE\u63A5 +spring.datasource.min-idle=5 +#\u6700\u5927\u8FDE\u63A5\u6570\u91CF +spring.datasource.max-active=100 +#\u68C0\u6D4B\u6570\u636E\u5E93\u7684\u67E5\u8BE2\u8BED\u53E5 +spring.datasource.validation-query=select 1 from dual +#\u7B49\u5F85\u8FDE\u63A5\u6C60\u5206\u914D\u8FDE\u63A5\u7684\u6700\u5927\u65F6\u957F\uFF08\u6BEB\u79D2\uFF09 +spring.datasource.connection-timeout=60000 +#\u4E00\u4E2A\u8FDE\u63A5\u7684\u751F\u547D\u65F6\u957F\uFF08\u6BEB\u79D2\uFF09 +spring.datasource.max-left-time=60000 +#\u751F\u6548\u8D85\u65F6 +spring.datasource.validation-time-out=3000 +#\u4E00\u4E2A\u8FDE\u63A5idle\u72B6\u6001\u7684\u6700\u5927\u65F6\u957F\uFF08\u6BEB\u79D2\uFF09 +spring.datasource.idle-time-out=60000 +#\u8BBE\u7F6E\u9ED8\u8BA4\u5B57\u7B26\u96C6 +spring.datasource.connection-init-sql= set names utf8mb4 + +logging.level.cn.abel.dao=debug +#Mapper.xml\u6240\u5728\u7684\u4F4D\u7F6E +mybatis.mapper-locations=classpath*:mapper/*Mapper.xml +smybatis.type-aliases-package=cn.abel.bean +#Mapper.xml\u6240\u5728\u7684\u4F4D\u7F6E + +## pagehelper +pagehelper.helperDialect=mysql +pagehelper.reasonable=true +pagehelper.supportMethodsArguments=true +pagehelper.params=count=countSql + + diff --git a/springboot-mybatis2/src/main/resources/local/banner.txt b/springboot-mybatis2/src/main/resources/local/banner.txt new file mode 100644 index 0000000..42f7c59 --- /dev/null +++ b/springboot-mybatis2/src/main/resources/local/banner.txt @@ -0,0 +1,5 @@ +######################################################## +# # +# local # +# # +######################################################## \ No newline at end of file diff --git a/springboot-mybatis2/src/main/resources/local/logback-spring.xml b/springboot-mybatis2/src/main/resources/local/logback-spring.xml new file mode 100644 index 0000000..0505336 --- /dev/null +++ b/springboot-mybatis2/src/main/resources/local/logback-spring.xml @@ -0,0 +1,41 @@ + + + middle_message + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%method - %msg%n + + + + + + ${LOG_FILE} + + + ${LOG_FILE}.%d{yyyy-MM-dd} + + 60 + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%method - %msg%n + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/springboot-mybatis2/src/main/resources/mapper/NewsDaoMapper.xml b/springboot-mybatis2/src/main/resources/mapper/NewsDaoMapper.xml new file mode 100644 index 0000000..b4eeb93 --- /dev/null +++ b/springboot-mybatis2/src/main/resources/mapper/NewsDaoMapper.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + and id = #{id} + + + and title = #{title} + + + and content = #{content} + + + and image_path = #{imagePath} + + + and read_sum = #{readSum} + + + + + + + + + + + + SELECT LAST_INSERT_ID() + + INSERT INTO news( + id, + title, + content, + image_path, + read_sum + )VALUES( + #{id}, + #{title}, + #{content}, + #{imagePath}, + #{readSum} + ) + + + + UPDATE news SET + title = #{title}, + content = #{content}, + image_path = #{imagePath}, + read_sum = #{readSum} + WHERE id = #{id} + + + + DELETE FROM news WHERE id = #{id} + + + \ No newline at end of file diff --git a/springboot-mybatis2/src/main/resources/mapper/UserDaoMapper.xml b/springboot-mybatis2/src/main/resources/mapper/UserDaoMapper.xml new file mode 100644 index 0000000..ec2ce2a --- /dev/null +++ b/springboot-mybatis2/src/main/resources/mapper/UserDaoMapper.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + and id = #{id} + + + and name = #{name} + + + and address = #{address} + + + and mobile = #{mobile} + + + and email = #{email} + + + and role = #{role} + + + + + + + + + + + + SELECT LAST_INSERT_ID() + + INSERT INTO user( + id, + name, + address, + mobile, + email, + create_time, + role + )VALUES( + #{id}, + #{name}, + #{address}, + #{mobile}, + #{email}, + NOW(), + #{role} + ) + + + + UPDATE user SET + name = #{name}, + address = #{address}, + mobile = #{mobile}, + email = #{email}, + role = #{role} + WHERE id = #{id} + + + + DELETE FROM user WHERE id = #{id} + + + \ No newline at end of file diff --git a/springboot-mybatis2/src/test/java/cn/abel/BaseTest.java b/springboot-mybatis2/src/test/java/cn/abel/BaseTest.java new file mode 100644 index 0000000..2cb1d62 --- /dev/null +++ b/springboot-mybatis2/src/test/java/cn/abel/BaseTest.java @@ -0,0 +1,32 @@ +package cn.abel; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * @author yyb + * @time 2019/3/27 + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = Application.class) +@TestPropertySource("/application.properties") +public class BaseTest { + + @Configuration + @ComponentScan(basePackages = {"cn.abel"}, excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Configuration.class)) + static class ComponentScanConfig { + + } + + + @Test + public void contextLoads() { + } + +} \ No newline at end of file diff --git a/springboot-mybatis2/src/test/java/cn/abel/service/ServiceTest.java b/springboot-mybatis2/src/test/java/cn/abel/service/ServiceTest.java new file mode 100644 index 0000000..a4ddd52 --- /dev/null +++ b/springboot-mybatis2/src/test/java/cn/abel/service/ServiceTest.java @@ -0,0 +1,29 @@ +package cn.abel.service; + +import cn.abel.BaseTest; +import cn.abel.bean.News; +import cn.abel.bean.User; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; + +/** + * @author yyb + * @time 2019/3/27 + */ +public class ServiceTest extends BaseTest { + + @Autowired + private NewsService newsService; + @Autowired + private UserService userService; + + @Test + public void dynamicDataSourceTest() throws Exception { + List newsList = newsService.getByMap(null); + List userList = userService.getByMap(null); + System.out.println("newsList----" + newsList.size() + "--userList---" + userList.size()); + + } +} From dc4e9d85e63274099971b3cc61eaece6a0f4fffe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Tue, 2 Apr 2019 14:44:40 +0800 Subject: [PATCH 14/44] add es --- springboot-elasticsearch/pom.xml | 57 +++++++++++++ .../src/main/java/cn/abel/Application.java | 16 ++++ .../src/main/java/cn/abel/bean/News.java | 50 +++++++++++ .../src/main/java/cn/abel/bean/User.java | 70 +++++++++++++++ .../src/main/java/cn/abel/dao/NewsDao.java | 23 +++++ .../src/main/java/cn/abel/dao/UserDao.java | 23 +++++ .../java/cn/abel/service/NewsService.java | 47 ++++++++++ .../java/cn/abel/service/UserService.java | 46 ++++++++++ .../resources/local/application.properties | 59 +++++++++++++ .../src/main/resources/local/banner.txt | 5 ++ .../main/resources/local/logback-spring.xml | 41 +++++++++ .../main/resources/mapper/NewsDaoMapper.xml | 75 ++++++++++++++++ .../main/resources/mapper/UserDaoMapper.xml | 85 +++++++++++++++++++ .../src/test/java/cn/abel/BaseTest.java | 32 +++++++ .../java/cn/abel/service/ServiceTest.java | 29 +++++++ 15 files changed, 658 insertions(+) create mode 100644 springboot-elasticsearch/pom.xml create mode 100644 springboot-elasticsearch/src/main/java/cn/abel/Application.java create mode 100644 springboot-elasticsearch/src/main/java/cn/abel/bean/News.java create mode 100644 springboot-elasticsearch/src/main/java/cn/abel/bean/User.java create mode 100644 springboot-elasticsearch/src/main/java/cn/abel/dao/NewsDao.java create mode 100644 springboot-elasticsearch/src/main/java/cn/abel/dao/UserDao.java create mode 100644 springboot-elasticsearch/src/main/java/cn/abel/service/NewsService.java create mode 100644 springboot-elasticsearch/src/main/java/cn/abel/service/UserService.java create mode 100644 springboot-elasticsearch/src/main/resources/local/application.properties create mode 100644 springboot-elasticsearch/src/main/resources/local/banner.txt create mode 100644 springboot-elasticsearch/src/main/resources/local/logback-spring.xml create mode 100644 springboot-elasticsearch/src/main/resources/mapper/NewsDaoMapper.xml create mode 100644 springboot-elasticsearch/src/main/resources/mapper/UserDaoMapper.xml create mode 100644 springboot-elasticsearch/src/test/java/cn/abel/BaseTest.java create mode 100644 springboot-elasticsearch/src/test/java/cn/abel/service/ServiceTest.java diff --git a/springboot-elasticsearch/pom.xml b/springboot-elasticsearch/pom.xml new file mode 100644 index 0000000..8fdc7a4 --- /dev/null +++ b/springboot-elasticsearch/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + + cn.abel + springboot-elasticserach + 1.0-SNAPSHOT + + org.springframework.boot + spring-boot-starter-parent + 2.0.4.RELEASE + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-starter-freemarker + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 1.3.2 + + + com.github.pagehelper + pagehelper-spring-boot-starter + 1.2.5 + + + mysql + mysql-connector-java + 5.1.40 + + + + org.apache.commons + commons-lang3 + 3.4 + + + + \ No newline at end of file diff --git a/springboot-elasticsearch/src/main/java/cn/abel/Application.java b/springboot-elasticsearch/src/main/java/cn/abel/Application.java new file mode 100644 index 0000000..4c77f67 --- /dev/null +++ b/springboot-elasticsearch/src/main/java/cn/abel/Application.java @@ -0,0 +1,16 @@ +package cn.abel; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author yyb + * @time 2019/3/26 + */ +@SpringBootApplication +public class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/springboot-elasticsearch/src/main/java/cn/abel/bean/News.java b/springboot-elasticsearch/src/main/java/cn/abel/bean/News.java new file mode 100644 index 0000000..5462104 --- /dev/null +++ b/springboot-elasticsearch/src/main/java/cn/abel/bean/News.java @@ -0,0 +1,50 @@ +package cn.abel.bean; + + +public class News { + private Integer id; + private String title; + private String content; + private String imagePath; + private Integer readSum; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getImagePath() { + return imagePath; + } + + public void setImagePath(String imagePath) { + this.imagePath = imagePath; + } + + public Integer getReadSum() { + return readSum; + } + + public void setReadSum(Integer readSum) { + this.readSum = readSum; + } +} \ No newline at end of file diff --git a/springboot-elasticsearch/src/main/java/cn/abel/bean/User.java b/springboot-elasticsearch/src/main/java/cn/abel/bean/User.java new file mode 100644 index 0000000..58acdf2 --- /dev/null +++ b/springboot-elasticsearch/src/main/java/cn/abel/bean/User.java @@ -0,0 +1,70 @@ +package cn.abel.bean; + +import java.util.Date; + + +public class User { + private Integer id; + private String name; + private String address; + private String mobile; + private String email; + private Date createTime; + private Integer role; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getMobile() { + return mobile; + } + + public void setMobile(String mobile) { + this.mobile = mobile; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Integer getRole() { + return role; + } + + public void setRole(Integer role) { + this.role = role; + } +} \ No newline at end of file diff --git a/springboot-elasticsearch/src/main/java/cn/abel/dao/NewsDao.java b/springboot-elasticsearch/src/main/java/cn/abel/dao/NewsDao.java new file mode 100644 index 0000000..b32f0f2 --- /dev/null +++ b/springboot-elasticsearch/src/main/java/cn/abel/dao/NewsDao.java @@ -0,0 +1,23 @@ +package cn.abel.dao; + +import java.util.List; +import java.util.Map; + +import cn.abel.bean.News; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; + +@Repository +@Mapper +public interface NewsDao { + + List getByMap(Map map); + + News getById(Integer id); + + Integer create(News news); + + int update(News news); + + int delete(Integer id); +} \ No newline at end of file diff --git a/springboot-elasticsearch/src/main/java/cn/abel/dao/UserDao.java b/springboot-elasticsearch/src/main/java/cn/abel/dao/UserDao.java new file mode 100644 index 0000000..077d1f5 --- /dev/null +++ b/springboot-elasticsearch/src/main/java/cn/abel/dao/UserDao.java @@ -0,0 +1,23 @@ +package cn.abel.dao; + +import java.util.List; +import java.util.Map; + +import cn.abel.bean.User; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; + +@Repository +@Mapper +public interface UserDao { + + List getByMap(Map map); + + User getById(Integer id); + + Integer create(User user); + + int update(User user); + + int delete(Integer id); +} \ No newline at end of file diff --git a/springboot-elasticsearch/src/main/java/cn/abel/service/NewsService.java b/springboot-elasticsearch/src/main/java/cn/abel/service/NewsService.java new file mode 100644 index 0000000..1c6f1ee --- /dev/null +++ b/springboot-elasticsearch/src/main/java/cn/abel/service/NewsService.java @@ -0,0 +1,47 @@ +package cn.abel.service; + +import java.util.List; +import java.util.Map; + +import cn.abel.config.DynamicDataSource; +import cn.abel.config.DynamicDataSourceContextHolder; +import cn.abel.enums.DatabaseTypeEnum; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import cn.abel.dao.NewsDao; +import cn.abel.bean.News; + +@Service +public class NewsService { + @Autowired + private NewsDao newsDao; + + public List getByMap(Map map) { + DynamicDataSourceContextHolder.resetDatabaseType(); + return newsDao.getByMap(map); + } + + public News getById(Integer id) { + DynamicDataSourceContextHolder.resetDatabaseType(); + return newsDao.getById(id); + } + + public News create(News news) { + DynamicDataSourceContextHolder.resetDatabaseType(); + newsDao.create(news); + return news; + } + + public News update(News news) { + DynamicDataSourceContextHolder.resetDatabaseType(); + newsDao.update(news); + return news; + } + + public int delete(Integer id) { + DynamicDataSourceContextHolder.resetDatabaseType(); + return newsDao.delete(id); + } + +} \ No newline at end of file diff --git a/springboot-elasticsearch/src/main/java/cn/abel/service/UserService.java b/springboot-elasticsearch/src/main/java/cn/abel/service/UserService.java new file mode 100644 index 0000000..4766a40 --- /dev/null +++ b/springboot-elasticsearch/src/main/java/cn/abel/service/UserService.java @@ -0,0 +1,46 @@ +package cn.abel.service; + +import java.util.List; +import java.util.Map; + +import cn.abel.config.DynamicDataSourceContextHolder; +import cn.abel.enums.DatabaseTypeEnum; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import cn.abel.dao.UserDao; +import cn.abel.bean.User; + +@Service +public class UserService { + @Autowired + private UserDao userDao; + + public List getByMap(Map map){ + DynamicDataSourceContextHolder.setDatabaseType(DatabaseTypeEnum.USER); + return userDao.getByMap(map); + } + + public User getById(Integer id){ + DynamicDataSourceContextHolder.setDatabaseType(DatabaseTypeEnum.USER); + return userDao.getById(id); + } + + public User create(User user){ + DynamicDataSourceContextHolder.setDatabaseType(DatabaseTypeEnum.USER); + userDao.create(user); + return user; + } + + public User update(User user){ + DynamicDataSourceContextHolder.setDatabaseType(DatabaseTypeEnum.USER); + userDao.update(user); + return user; + } + + public int delete(Integer id){ + DynamicDataSourceContextHolder.setDatabaseType(DatabaseTypeEnum.USER); + return userDao.delete(id); + } + +} \ No newline at end of file diff --git a/springboot-elasticsearch/src/main/resources/local/application.properties b/springboot-elasticsearch/src/main/resources/local/application.properties new file mode 100644 index 0000000..1c2323b --- /dev/null +++ b/springboot-elasticsearch/src/main/resources/local/application.properties @@ -0,0 +1,59 @@ +## tomcat\u914D\u7F6E +server.port=8009 +#server.tomcat.maxHttpHeaderSize=8192 +server.tomcat.uri-encoding=UTF-8 +spring.http.encoding.charset=UTF-8 +spring.http.encoding.enabled=true +spring.http.encoding.force=true +spring.risk.encoding=UTF-8 +# tomcat\u6700\u5927\u7EBF\u7A0B\u6570\uFF0C\u9ED8\u8BA4\u4E3A200 +server.tomcat.max-threads=800 +# session\u6700\u5927\u8D85\u65F6\u65F6\u95F4(\u5206\u949F)\uFF0C\u9ED8\u8BA4\u4E3A30 +server.session-timeout=60 + +## spring \u914D\u7F6E +spring.application.name=springboot-elasticsearch +application.main=cn.abel.Application + +## LOG +logging.file=./logs/springboot-elasticsearch.log + + +## \u4E3B\u6570\u636E\u6E90\uFF0C\u9ED8\u8BA4\u7684 +spring.datasource.url=jdbc:mysql://localhost:3306/oldMan?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&useSSL=false + +spring.datasource.username=root +spring.datasource.password=admin +spring.datasource.driverClassName=com.mysql.jdbc.Driver + +spring.datasource.type=com.zaxxer.hikari.HikariDataSource +#\u6700\u5C0F\u7A7A\u95F2\u8FDE\u63A5 +spring.datasource.min-idle=5 +#\u6700\u5927\u8FDE\u63A5\u6570\u91CF +spring.datasource.max-active=100 +#\u68C0\u6D4B\u6570\u636E\u5E93\u7684\u67E5\u8BE2\u8BED\u53E5 +spring.datasource.validation-query=select 1 from dual +#\u7B49\u5F85\u8FDE\u63A5\u6C60\u5206\u914D\u8FDE\u63A5\u7684\u6700\u5927\u65F6\u957F\uFF08\u6BEB\u79D2\uFF09 +spring.datasource.connection-timeout=60000 +#\u4E00\u4E2A\u8FDE\u63A5\u7684\u751F\u547D\u65F6\u957F\uFF08\u6BEB\u79D2\uFF09 +spring.datasource.max-left-time=60000 +#\u751F\u6548\u8D85\u65F6 +spring.datasource.validation-time-out=3000 +#\u4E00\u4E2A\u8FDE\u63A5idle\u72B6\u6001\u7684\u6700\u5927\u65F6\u957F\uFF08\u6BEB\u79D2\uFF09 +spring.datasource.idle-time-out=60000 +#\u8BBE\u7F6E\u9ED8\u8BA4\u5B57\u7B26\u96C6 +spring.datasource.connection-init-sql= set names utf8mb4 + +logging.level.cn.abel.dao=debug +#Mapper.xml\u6240\u5728\u7684\u4F4D\u7F6E +mybatis.mapper-locations=classpath*:mapper/*Mapper.xml +smybatis.type-aliases-package=cn.abel.bean +#Mapper.xml\u6240\u5728\u7684\u4F4D\u7F6E + +## pagehelper +pagehelper.helperDialect=mysql +pagehelper.reasonable=true +pagehelper.supportMethodsArguments=true +pagehelper.params=count=countSql + + diff --git a/springboot-elasticsearch/src/main/resources/local/banner.txt b/springboot-elasticsearch/src/main/resources/local/banner.txt new file mode 100644 index 0000000..42f7c59 --- /dev/null +++ b/springboot-elasticsearch/src/main/resources/local/banner.txt @@ -0,0 +1,5 @@ +######################################################## +# # +# local # +# # +######################################################## \ No newline at end of file diff --git a/springboot-elasticsearch/src/main/resources/local/logback-spring.xml b/springboot-elasticsearch/src/main/resources/local/logback-spring.xml new file mode 100644 index 0000000..0505336 --- /dev/null +++ b/springboot-elasticsearch/src/main/resources/local/logback-spring.xml @@ -0,0 +1,41 @@ + + + middle_message + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%method - %msg%n + + + + + + ${LOG_FILE} + + + ${LOG_FILE}.%d{yyyy-MM-dd} + + 60 + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%method - %msg%n + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/springboot-elasticsearch/src/main/resources/mapper/NewsDaoMapper.xml b/springboot-elasticsearch/src/main/resources/mapper/NewsDaoMapper.xml new file mode 100644 index 0000000..b4eeb93 --- /dev/null +++ b/springboot-elasticsearch/src/main/resources/mapper/NewsDaoMapper.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + and id = #{id} + + + and title = #{title} + + + and content = #{content} + + + and image_path = #{imagePath} + + + and read_sum = #{readSum} + + + + + + + + + + + + SELECT LAST_INSERT_ID() + + INSERT INTO news( + id, + title, + content, + image_path, + read_sum + )VALUES( + #{id}, + #{title}, + #{content}, + #{imagePath}, + #{readSum} + ) + + + + UPDATE news SET + title = #{title}, + content = #{content}, + image_path = #{imagePath}, + read_sum = #{readSum} + WHERE id = #{id} + + + + DELETE FROM news WHERE id = #{id} + + + \ No newline at end of file diff --git a/springboot-elasticsearch/src/main/resources/mapper/UserDaoMapper.xml b/springboot-elasticsearch/src/main/resources/mapper/UserDaoMapper.xml new file mode 100644 index 0000000..ec2ce2a --- /dev/null +++ b/springboot-elasticsearch/src/main/resources/mapper/UserDaoMapper.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + and id = #{id} + + + and name = #{name} + + + and address = #{address} + + + and mobile = #{mobile} + + + and email = #{email} + + + and role = #{role} + + + + + + + + + + + + SELECT LAST_INSERT_ID() + + INSERT INTO user( + id, + name, + address, + mobile, + email, + create_time, + role + )VALUES( + #{id}, + #{name}, + #{address}, + #{mobile}, + #{email}, + NOW(), + #{role} + ) + + + + UPDATE user SET + name = #{name}, + address = #{address}, + mobile = #{mobile}, + email = #{email}, + role = #{role} + WHERE id = #{id} + + + + DELETE FROM user WHERE id = #{id} + + + \ No newline at end of file diff --git a/springboot-elasticsearch/src/test/java/cn/abel/BaseTest.java b/springboot-elasticsearch/src/test/java/cn/abel/BaseTest.java new file mode 100644 index 0000000..2cb1d62 --- /dev/null +++ b/springboot-elasticsearch/src/test/java/cn/abel/BaseTest.java @@ -0,0 +1,32 @@ +package cn.abel; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * @author yyb + * @time 2019/3/27 + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = Application.class) +@TestPropertySource("/application.properties") +public class BaseTest { + + @Configuration + @ComponentScan(basePackages = {"cn.abel"}, excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Configuration.class)) + static class ComponentScanConfig { + + } + + + @Test + public void contextLoads() { + } + +} \ No newline at end of file diff --git a/springboot-elasticsearch/src/test/java/cn/abel/service/ServiceTest.java b/springboot-elasticsearch/src/test/java/cn/abel/service/ServiceTest.java new file mode 100644 index 0000000..a4ddd52 --- /dev/null +++ b/springboot-elasticsearch/src/test/java/cn/abel/service/ServiceTest.java @@ -0,0 +1,29 @@ +package cn.abel.service; + +import cn.abel.BaseTest; +import cn.abel.bean.News; +import cn.abel.bean.User; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; + +/** + * @author yyb + * @time 2019/3/27 + */ +public class ServiceTest extends BaseTest { + + @Autowired + private NewsService newsService; + @Autowired + private UserService userService; + + @Test + public void dynamicDataSourceTest() throws Exception { + List newsList = newsService.getByMap(null); + List userList = userService.getByMap(null); + System.out.println("newsList----" + newsList.size() + "--userList---" + userList.size()); + + } +} From 2fa422e4beb9c1adf0ae30b8a1800234d2813701 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Tue, 2 Apr 2019 14:44:59 +0800 Subject: [PATCH 15/44] modify peizhi --- .../src/main/resources/local/application.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/springboot-mybatis2/src/main/resources/local/application.properties b/springboot-mybatis2/src/main/resources/local/application.properties index 3fc81af..831a031 100644 --- a/springboot-mybatis2/src/main/resources/local/application.properties +++ b/springboot-mybatis2/src/main/resources/local/application.properties @@ -12,11 +12,11 @@ server.tomcat.max-threads=800 server.session-timeout=60 ## spring \u914D\u7F6E -spring.application.name=springboot-dynamicDataSource +spring.application.name=springboot-mybatis2 application.main=cn.abel.Application ## LOG -logging.file=./logs/springboot-dynamicDataSource.log +logging.file=./logs/springboot-mybatis2.log ## \u4E3B\u6570\u636E\u6E90\uFF0C\u9ED8\u8BA4\u7684 From 2449a6e1b41b2e1fb1bb17715d04910bc5cbd6cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Tue, 2 Apr 2019 14:51:38 +0800 Subject: [PATCH 16/44] =?UTF-8?q?=E6=96=B0=E5=A2=9Ees=20=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- springboot-elasticsearch/pom.xml | 20 ++++++++- .../cn/abel/config/ESRestClientConfig.java | 45 +++++++++++++++++++ .../resources/local/application.properties | 6 ++- 3 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 springboot-elasticsearch/src/main/java/cn/abel/config/ESRestClientConfig.java diff --git a/springboot-elasticsearch/pom.xml b/springboot-elasticsearch/pom.xml index 8fdc7a4..9983a63 100644 --- a/springboot-elasticsearch/pom.xml +++ b/springboot-elasticsearch/pom.xml @@ -46,12 +46,30 @@ mysql-connector-java 5.1.40 - org.apache.commons commons-lang3 3.4 + + + com.sun.jna + jna + 3.0.9 + + + + org.elasticsearch.client + rest + 5.5.3 + + + + com.alibaba + fastjson + 1.2.40 + + \ No newline at end of file diff --git a/springboot-elasticsearch/src/main/java/cn/abel/config/ESRestClientConfig.java b/springboot-elasticsearch/src/main/java/cn/abel/config/ESRestClientConfig.java new file mode 100644 index 0000000..af260fe --- /dev/null +++ b/springboot-elasticsearch/src/main/java/cn/abel/config/ESRestClientConfig.java @@ -0,0 +1,45 @@ +package cn.abel.config; + +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; +import org.elasticsearch.client.RestClient; +import org.elasticsearch.client.RestClientBuilder; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author yangyibo + * @time 2019/4/2 + */ +@Configuration +public class ESRestClientConfig { + @Value("${elasticsearch.userName}") + private String userName; + @Value("${elasticsearch.password}") + private String password; + @Value("${elasticsearch.rest.hostNames}") + private String hostName; + @Value("${elasticsearch.rest.port}") + private Integer port; + + @Bean(destroyMethod = "close") + public RestClient getRestClient() { + final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(AuthScope.ANY, + new UsernamePasswordCredentials(userName, password)); + RestClientBuilder restClientBuilder = RestClient.builder(new HttpHost(hostName, port)); + //配置身份验证 + restClientBuilder.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() { + @Override + public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) { + return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); + } + }); + return restClientBuilder.build(); + } +} diff --git a/springboot-elasticsearch/src/main/resources/local/application.properties b/springboot-elasticsearch/src/main/resources/local/application.properties index 1c2323b..b520e3b 100644 --- a/springboot-elasticsearch/src/main/resources/local/application.properties +++ b/springboot-elasticsearch/src/main/resources/local/application.properties @@ -56,4 +56,8 @@ pagehelper.reasonable=true pagehelper.supportMethodsArguments=true pagehelper.params=count=countSql - +#es\u670D\u52A1\u5668\u5730\u5740\u914D\u7F6E +elasticsearch.new.userName= +elasticsearch.new.password= +elasticsearch.rest.new.hostNames=127.0.0.1 +elasticsearch.rest.new.port=9200 \ No newline at end of file From 0e2aa3167b36622904ae819c831df2b17e005674 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Tue, 2 Apr 2019 15:22:13 +0800 Subject: [PATCH 17/44] =?UTF-8?q?=E5=88=A0=E9=99=A4news?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/cn/abel/bean/News.java | 50 ------------------- .../src/main/java/cn/abel/dao/NewsDao.java | 23 --------- .../java/cn/abel/service/NewsService.java | 40 --------------- .../java/cn/abel/service/ServiceTest.java | 7 +-- 4 files changed, 2 insertions(+), 118 deletions(-) delete mode 100644 springboot-mybatis2/src/main/java/cn/abel/bean/News.java delete mode 100644 springboot-mybatis2/src/main/java/cn/abel/dao/NewsDao.java delete mode 100644 springboot-mybatis2/src/main/java/cn/abel/service/NewsService.java diff --git a/springboot-mybatis2/src/main/java/cn/abel/bean/News.java b/springboot-mybatis2/src/main/java/cn/abel/bean/News.java deleted file mode 100644 index 5462104..0000000 --- a/springboot-mybatis2/src/main/java/cn/abel/bean/News.java +++ /dev/null @@ -1,50 +0,0 @@ -package cn.abel.bean; - - -public class News { - private Integer id; - private String title; - private String content; - private String imagePath; - private Integer readSum; - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getContent() { - return content; - } - - public void setContent(String content) { - this.content = content; - } - - public String getImagePath() { - return imagePath; - } - - public void setImagePath(String imagePath) { - this.imagePath = imagePath; - } - - public Integer getReadSum() { - return readSum; - } - - public void setReadSum(Integer readSum) { - this.readSum = readSum; - } -} \ No newline at end of file diff --git a/springboot-mybatis2/src/main/java/cn/abel/dao/NewsDao.java b/springboot-mybatis2/src/main/java/cn/abel/dao/NewsDao.java deleted file mode 100644 index b32f0f2..0000000 --- a/springboot-mybatis2/src/main/java/cn/abel/dao/NewsDao.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.abel.dao; - -import java.util.List; -import java.util.Map; - -import cn.abel.bean.News; -import org.apache.ibatis.annotations.Mapper; -import org.springframework.stereotype.Repository; - -@Repository -@Mapper -public interface NewsDao { - - List getByMap(Map map); - - News getById(Integer id); - - Integer create(News news); - - int update(News news); - - int delete(Integer id); -} \ No newline at end of file diff --git a/springboot-mybatis2/src/main/java/cn/abel/service/NewsService.java b/springboot-mybatis2/src/main/java/cn/abel/service/NewsService.java deleted file mode 100644 index e5145ce..0000000 --- a/springboot-mybatis2/src/main/java/cn/abel/service/NewsService.java +++ /dev/null @@ -1,40 +0,0 @@ -package cn.abel.service; - -import java.util.List; -import java.util.Map; - - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import cn.abel.dao.NewsDao; -import cn.abel.bean.News; - -@Service -public class NewsService { - @Autowired - private NewsDao newsDao; - - public List getByMap(Map map) { - return newsDao.getByMap(map); - } - - public News getById(Integer id) { - return newsDao.getById(id); - } - - public News create(News news) { - newsDao.create(news); - return news; - } - - public News update(News news) { - newsDao.update(news); - return news; - } - - public int delete(Integer id) { - return newsDao.delete(id); - } - -} \ No newline at end of file diff --git a/springboot-mybatis2/src/test/java/cn/abel/service/ServiceTest.java b/springboot-mybatis2/src/test/java/cn/abel/service/ServiceTest.java index a4ddd52..f1d4046 100644 --- a/springboot-mybatis2/src/test/java/cn/abel/service/ServiceTest.java +++ b/springboot-mybatis2/src/test/java/cn/abel/service/ServiceTest.java @@ -1,7 +1,6 @@ package cn.abel.service; import cn.abel.BaseTest; -import cn.abel.bean.News; import cn.abel.bean.User; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -14,16 +13,14 @@ */ public class ServiceTest extends BaseTest { - @Autowired - private NewsService newsService; + @Autowired private UserService userService; @Test public void dynamicDataSourceTest() throws Exception { - List newsList = newsService.getByMap(null); List userList = userService.getByMap(null); - System.out.println("newsList----" + newsList.size() + "--userList---" + userList.size()); + System.out.println("--userList---" + userList.size()); } } From 60996e3b545180e87e109318db3bd069d56c529b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Tue, 2 Apr 2019 15:31:32 +0800 Subject: [PATCH 18/44] =?UTF-8?q?=E6=B7=BB=E5=8A=A0sql=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- springboot-mybatis2/sql/user.sql | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 springboot-mybatis2/sql/user.sql diff --git a/springboot-mybatis2/sql/user.sql b/springboot-mybatis2/sql/user.sql new file mode 100644 index 0000000..a42941a --- /dev/null +++ b/springboot-mybatis2/sql/user.sql @@ -0,0 +1,35 @@ +/* + Navicat Premium Data Transfer + + Source Server : localhost + Source Server Type : MySQL + Source Server Version : 50719 + Source Host : 127.0.0.1 + Source Database : oldMan + + Target Server Type : MySQL + Target Server Version : 50719 + File Encoding : utf-8 + + Date: 04/02/2019 15:27:32 PM +*/ + +SET NAMES utf8; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for `user` +-- ---------------------------- +DROP TABLE IF EXISTS `user`; +CREATE TABLE `user` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(100) DEFAULT NULL, + `address` varchar(200) DEFAULT NULL, + `mobile` varchar(50) DEFAULT NULL, + `email` varchar(100) DEFAULT NULL, + `create_time` datetime DEFAULT NULL, + `role` int(11) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4; + +SET FOREIGN_KEY_CHECKS = 1; From 2522e4d331c8efd86d2ce5166ad1abac6e20cefe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Tue, 2 Apr 2019 15:39:40 +0800 Subject: [PATCH 19/44] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- springboot-dynamicDataSource/sql/news.sql | 33 +++++++++++++++++++++ springboot-dynamicDataSource/sql/user.sql | 35 +++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 springboot-dynamicDataSource/sql/news.sql create mode 100644 springboot-dynamicDataSource/sql/user.sql diff --git a/springboot-dynamicDataSource/sql/news.sql b/springboot-dynamicDataSource/sql/news.sql new file mode 100644 index 0000000..82fd1e2 --- /dev/null +++ b/springboot-dynamicDataSource/sql/news.sql @@ -0,0 +1,33 @@ +/* + Navicat Premium Data Transfer + + Source Server : localhost + Source Server Type : MySQL + Source Server Version : 50719 + Source Host : 127.0.0.1 + Source Database : oldMan + + Target Server Type : MySQL + Target Server Version : 50719 + File Encoding : utf-8 + + Date: 04/02/2019 15:38:58 PM +*/ + +SET NAMES utf8; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for `news` +-- ---------------------------- +DROP TABLE IF EXISTS `news`; +CREATE TABLE `news` ( + `id` int(11) NOT NULL, + `title` varchar(100) DEFAULT NULL, + `content` text, + `image_path` varchar(100) DEFAULT NULL, + `read_sum` int(11) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/springboot-dynamicDataSource/sql/user.sql b/springboot-dynamicDataSource/sql/user.sql new file mode 100644 index 0000000..a42941a --- /dev/null +++ b/springboot-dynamicDataSource/sql/user.sql @@ -0,0 +1,35 @@ +/* + Navicat Premium Data Transfer + + Source Server : localhost + Source Server Type : MySQL + Source Server Version : 50719 + Source Host : 127.0.0.1 + Source Database : oldMan + + Target Server Type : MySQL + Target Server Version : 50719 + File Encoding : utf-8 + + Date: 04/02/2019 15:27:32 PM +*/ + +SET NAMES utf8; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for `user` +-- ---------------------------- +DROP TABLE IF EXISTS `user`; +CREATE TABLE `user` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(100) DEFAULT NULL, + `address` varchar(200) DEFAULT NULL, + `mobile` varchar(50) DEFAULT NULL, + `email` varchar(100) DEFAULT NULL, + `create_time` datetime DEFAULT NULL, + `role` int(11) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4; + +SET FOREIGN_KEY_CHECKS = 1; From 14c45d2e80d7b43d96b58d424b8f48cf9c651428 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Wed, 3 Apr 2019 17:22:14 +0800 Subject: [PATCH 20/44] =?UTF-8?q?=E6=96=B0=E5=A2=9Ees=20=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?query?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/abel/constants/Constants.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 springboot-elasticsearch/src/main/java/cn/abel/constants/Constants.java diff --git a/springboot-elasticsearch/src/main/java/cn/abel/constants/Constants.java b/springboot-elasticsearch/src/main/java/cn/abel/constants/Constants.java new file mode 100644 index 0000000..401df39 --- /dev/null +++ b/springboot-elasticsearch/src/main/java/cn/abel/constants/Constants.java @@ -0,0 +1,25 @@ +package cn.abel.constants; + +/** + * @author yangyibo + * @time 2019/4/3 + */ +public class Constants { + + /** + * es 查询query 拼接 前缀 + */ + public static final String ES_QUERY_JSON_PREFIX = "{\n" + + " \"query\": {\n" + + " \"term\": {\n" + + " \"profileId\": {\n" + + " \"value\": \""; + /** + * es 查询query 拼接后缀 + */ + public static final String ES_QUERY_JSON_SUFFIX = "\"\n" + + " }\n" + + " }\n" + + " }\n" + + "}\n"; +} From 0fc34ec981f9487002e760a66ab74bd7f17a1aeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Wed, 3 Apr 2019 17:24:06 +0800 Subject: [PATCH 21/44] delete note --- .../src/main/java/cn/abel/service/NewsService.java | 8 -------- .../src/main/java/cn/abel/service/UserService.java | 7 ------- 2 files changed, 15 deletions(-) diff --git a/springboot-elasticsearch/src/main/java/cn/abel/service/NewsService.java b/springboot-elasticsearch/src/main/java/cn/abel/service/NewsService.java index 1c6f1ee..cf7bfc5 100644 --- a/springboot-elasticsearch/src/main/java/cn/abel/service/NewsService.java +++ b/springboot-elasticsearch/src/main/java/cn/abel/service/NewsService.java @@ -3,9 +3,6 @@ import java.util.List; import java.util.Map; -import cn.abel.config.DynamicDataSource; -import cn.abel.config.DynamicDataSourceContextHolder; -import cn.abel.enums.DatabaseTypeEnum; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -18,29 +15,24 @@ public class NewsService { private NewsDao newsDao; public List getByMap(Map map) { - DynamicDataSourceContextHolder.resetDatabaseType(); return newsDao.getByMap(map); } public News getById(Integer id) { - DynamicDataSourceContextHolder.resetDatabaseType(); return newsDao.getById(id); } public News create(News news) { - DynamicDataSourceContextHolder.resetDatabaseType(); newsDao.create(news); return news; } public News update(News news) { - DynamicDataSourceContextHolder.resetDatabaseType(); newsDao.update(news); return news; } public int delete(Integer id) { - DynamicDataSourceContextHolder.resetDatabaseType(); return newsDao.delete(id); } diff --git a/springboot-elasticsearch/src/main/java/cn/abel/service/UserService.java b/springboot-elasticsearch/src/main/java/cn/abel/service/UserService.java index 4766a40..e21f61c 100644 --- a/springboot-elasticsearch/src/main/java/cn/abel/service/UserService.java +++ b/springboot-elasticsearch/src/main/java/cn/abel/service/UserService.java @@ -3,8 +3,6 @@ import java.util.List; import java.util.Map; -import cn.abel.config.DynamicDataSourceContextHolder; -import cn.abel.enums.DatabaseTypeEnum; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -17,29 +15,24 @@ public class UserService { private UserDao userDao; public List getByMap(Map map){ - DynamicDataSourceContextHolder.setDatabaseType(DatabaseTypeEnum.USER); return userDao.getByMap(map); } public User getById(Integer id){ - DynamicDataSourceContextHolder.setDatabaseType(DatabaseTypeEnum.USER); return userDao.getById(id); } public User create(User user){ - DynamicDataSourceContextHolder.setDatabaseType(DatabaseTypeEnum.USER); userDao.create(user); return user; } public User update(User user){ - DynamicDataSourceContextHolder.setDatabaseType(DatabaseTypeEnum.USER); userDao.update(user); return user; } public int delete(Integer id){ - DynamicDataSourceContextHolder.setDatabaseType(DatabaseTypeEnum.USER); return userDao.delete(id); } From 8af2a4ebcda4ad794ac49e2cf4d859567b60084b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Wed, 3 Apr 2019 17:46:46 +0800 Subject: [PATCH 22/44] delete news --- .../src/main/java/cn/abel/bean/News.java | 50 ------------- .../src/main/java/cn/abel/dao/NewsDao.java | 23 ------ .../java/cn/abel/service/NewsService.java | 39 ---------- .../main/resources/mapper/NewsDaoMapper.xml | 75 ------------------- 4 files changed, 187 deletions(-) delete mode 100644 springboot-elasticsearch/src/main/java/cn/abel/bean/News.java delete mode 100644 springboot-elasticsearch/src/main/java/cn/abel/dao/NewsDao.java delete mode 100644 springboot-elasticsearch/src/main/java/cn/abel/service/NewsService.java delete mode 100644 springboot-elasticsearch/src/main/resources/mapper/NewsDaoMapper.xml diff --git a/springboot-elasticsearch/src/main/java/cn/abel/bean/News.java b/springboot-elasticsearch/src/main/java/cn/abel/bean/News.java deleted file mode 100644 index 5462104..0000000 --- a/springboot-elasticsearch/src/main/java/cn/abel/bean/News.java +++ /dev/null @@ -1,50 +0,0 @@ -package cn.abel.bean; - - -public class News { - private Integer id; - private String title; - private String content; - private String imagePath; - private Integer readSum; - - public Integer getId() { - return id; - } - - public void setId(Integer id) { - this.id = id; - } - - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getContent() { - return content; - } - - public void setContent(String content) { - this.content = content; - } - - public String getImagePath() { - return imagePath; - } - - public void setImagePath(String imagePath) { - this.imagePath = imagePath; - } - - public Integer getReadSum() { - return readSum; - } - - public void setReadSum(Integer readSum) { - this.readSum = readSum; - } -} \ No newline at end of file diff --git a/springboot-elasticsearch/src/main/java/cn/abel/dao/NewsDao.java b/springboot-elasticsearch/src/main/java/cn/abel/dao/NewsDao.java deleted file mode 100644 index b32f0f2..0000000 --- a/springboot-elasticsearch/src/main/java/cn/abel/dao/NewsDao.java +++ /dev/null @@ -1,23 +0,0 @@ -package cn.abel.dao; - -import java.util.List; -import java.util.Map; - -import cn.abel.bean.News; -import org.apache.ibatis.annotations.Mapper; -import org.springframework.stereotype.Repository; - -@Repository -@Mapper -public interface NewsDao { - - List getByMap(Map map); - - News getById(Integer id); - - Integer create(News news); - - int update(News news); - - int delete(Integer id); -} \ No newline at end of file diff --git a/springboot-elasticsearch/src/main/java/cn/abel/service/NewsService.java b/springboot-elasticsearch/src/main/java/cn/abel/service/NewsService.java deleted file mode 100644 index cf7bfc5..0000000 --- a/springboot-elasticsearch/src/main/java/cn/abel/service/NewsService.java +++ /dev/null @@ -1,39 +0,0 @@ -package cn.abel.service; - -import java.util.List; -import java.util.Map; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import cn.abel.dao.NewsDao; -import cn.abel.bean.News; - -@Service -public class NewsService { - @Autowired - private NewsDao newsDao; - - public List getByMap(Map map) { - return newsDao.getByMap(map); - } - - public News getById(Integer id) { - return newsDao.getById(id); - } - - public News create(News news) { - newsDao.create(news); - return news; - } - - public News update(News news) { - newsDao.update(news); - return news; - } - - public int delete(Integer id) { - return newsDao.delete(id); - } - -} \ No newline at end of file diff --git a/springboot-elasticsearch/src/main/resources/mapper/NewsDaoMapper.xml b/springboot-elasticsearch/src/main/resources/mapper/NewsDaoMapper.xml deleted file mode 100644 index b4eeb93..0000000 --- a/springboot-elasticsearch/src/main/resources/mapper/NewsDaoMapper.xml +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - and id = #{id} - - - and title = #{title} - - - and content = #{content} - - - and image_path = #{imagePath} - - - and read_sum = #{readSum} - - - - - - - - - - - - SELECT LAST_INSERT_ID() - - INSERT INTO news( - id, - title, - content, - image_path, - read_sum - )VALUES( - #{id}, - #{title}, - #{content}, - #{imagePath}, - #{readSum} - ) - - - - UPDATE news SET - title = #{title}, - content = #{content}, - image_path = #{imagePath}, - read_sum = #{readSum} - WHERE id = #{id} - - - - DELETE FROM news WHERE id = #{id} - - - \ No newline at end of file From 79ce2c75cd2cdfab791ae80d2d0710569f6597c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Wed, 3 Apr 2019 17:47:34 +0800 Subject: [PATCH 23/44] es query ok --- .../java/cn/abel/constants/Constants.java | 10 ++- .../java/cn/abel/service/UserService.java | 67 ++++++++++++------- .../resources/local/application.properties | 8 +-- .../java/cn/abel/service/ServiceTest.java | 10 +-- 4 files changed, 59 insertions(+), 36 deletions(-) diff --git a/springboot-elasticsearch/src/main/java/cn/abel/constants/Constants.java b/springboot-elasticsearch/src/main/java/cn/abel/constants/Constants.java index 401df39..d3b38be 100644 --- a/springboot-elasticsearch/src/main/java/cn/abel/constants/Constants.java +++ b/springboot-elasticsearch/src/main/java/cn/abel/constants/Constants.java @@ -12,7 +12,7 @@ public class Constants { public static final String ES_QUERY_JSON_PREFIX = "{\n" + " \"query\": {\n" + " \"term\": {\n" + - " \"profileId\": {\n" + + " \"userId\": {\n" + " \"value\": \""; /** * es 查询query 拼接后缀 @@ -22,4 +22,12 @@ public class Constants { " }\n" + " }\n" + "}\n"; + + /** + * 需要查询的es的 index + */ + public static final String ES_INDEX_PATH = "/logstash_indes/_search"; + } + + diff --git a/springboot-elasticsearch/src/main/java/cn/abel/service/UserService.java b/springboot-elasticsearch/src/main/java/cn/abel/service/UserService.java index e21f61c..99f3c4b 100644 --- a/springboot-elasticsearch/src/main/java/cn/abel/service/UserService.java +++ b/springboot-elasticsearch/src/main/java/cn/abel/service/UserService.java @@ -1,8 +1,18 @@ package cn.abel.service; +import java.util.Collections; import java.util.List; import java.util.Map; +import cn.abel.constants.Constants; +import com.alibaba.fastjson.JSONObject; +import org.apache.http.HttpEntity; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.entity.ContentType; +import org.apache.http.nio.entity.NStringEntity; +import org.apache.http.util.EntityUtils; +import org.elasticsearch.client.Response; +import org.elasticsearch.client.RestClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -12,28 +22,37 @@ @Service public class UserService { @Autowired - private UserDao userDao; - - public List getByMap(Map map){ - return userDao.getByMap(map); - } - - public User getById(Integer id){ - return userDao.getById(id); - } - - public User create(User user){ - userDao.create(user); - return user; - } - - public User update(User user){ - userDao.update(user); - return user; - } - - public int delete(Integer id){ - return userDao.delete(id); - } - + private UserDao userDao; + @Autowired + private RestClient restClient; + + public List getByMap(Map map) { + return userDao.getByMap(map); + } + + public User getById(Integer id) throws Exception { + //拼接es 的查询query + String json = Constants.ES_QUERY_JSON_PREFIX + id + Constants.ES_QUERY_JSON_SUFFIX; + HttpEntity entry = new NStringEntity(json, ContentType.APPLICATION_JSON); + Response response = restClient.performRequest(HttpGet.METHOD_NAME, Constants.ES_INDEX_PATH, Collections.emptyMap(), entry); + JSONObject responseJson = JSONObject.parseObject(EntityUtils.toString(response.getEntity())); + //es 中查询的结果 + System.out.println(responseJson); + return userDao.getById(id); + } + + public User create(User user) { + userDao.create(user); + return user; + } + + public User update(User user) { + userDao.update(user); + return user; + } + + public int delete(Integer id) { + return userDao.delete(id); + } + } \ No newline at end of file diff --git a/springboot-elasticsearch/src/main/resources/local/application.properties b/springboot-elasticsearch/src/main/resources/local/application.properties index b520e3b..d3234b6 100644 --- a/springboot-elasticsearch/src/main/resources/local/application.properties +++ b/springboot-elasticsearch/src/main/resources/local/application.properties @@ -57,7 +57,7 @@ pagehelper.supportMethodsArguments=true pagehelper.params=count=countSql #es\u670D\u52A1\u5668\u5730\u5740\u914D\u7F6E -elasticsearch.new.userName= -elasticsearch.new.password= -elasticsearch.rest.new.hostNames=127.0.0.1 -elasticsearch.rest.new.port=9200 \ No newline at end of file +elasticsearch.userName= +elasticsearch.password= +elasticsearch.rest.hostNames=127.0.0.1 +elasticsearch.rest.port=9200 \ No newline at end of file diff --git a/springboot-elasticsearch/src/test/java/cn/abel/service/ServiceTest.java b/springboot-elasticsearch/src/test/java/cn/abel/service/ServiceTest.java index a4ddd52..b9ded5a 100644 --- a/springboot-elasticsearch/src/test/java/cn/abel/service/ServiceTest.java +++ b/springboot-elasticsearch/src/test/java/cn/abel/service/ServiceTest.java @@ -1,12 +1,10 @@ package cn.abel.service; import cn.abel.BaseTest; -import cn.abel.bean.News; import cn.abel.bean.User; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; -import java.util.List; /** * @author yyb @@ -14,16 +12,14 @@ */ public class ServiceTest extends BaseTest { - @Autowired - private NewsService newsService; + @Autowired private UserService userService; @Test public void dynamicDataSourceTest() throws Exception { - List newsList = newsService.getByMap(null); - List userList = userService.getByMap(null); - System.out.println("newsList----" + newsList.size() + "--userList---" + userList.size()); + User user = userService.getById(1); + System.out.println("--userList---" + user.toString()); } } From d8a520367e117754267ec07292d7e2712597eb9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Thu, 11 Apr 2019 16:05:38 +0800 Subject: [PATCH 24/44] add springboot - kafka --- springboot-kafka/pom.xml | 63 +++++++++++++ .../src/main/java/cn/abel/Application.java | 15 ++++ .../main/java/cn/abel/config/KafkaConfig.java | 88 +++++++++++++++++++ .../prehandle/KafkaConsumerService.java | 28 ++++++ .../abel/service/prehandle/SplitService.java | 26 ++++++ .../resources/local/application.properties | 18 ++++ .../src/main/resources/local/banner.txt | 5 ++ .../main/resources/local/logback-spring.xml | 35 ++++++++ 8 files changed, 278 insertions(+) create mode 100644 springboot-kafka/pom.xml create mode 100644 springboot-kafka/src/main/java/cn/abel/Application.java create mode 100644 springboot-kafka/src/main/java/cn/abel/config/KafkaConfig.java create mode 100644 springboot-kafka/src/main/java/cn/abel/service/prehandle/KafkaConsumerService.java create mode 100644 springboot-kafka/src/main/java/cn/abel/service/prehandle/SplitService.java create mode 100644 springboot-kafka/src/main/resources/local/application.properties create mode 100644 springboot-kafka/src/main/resources/local/banner.txt create mode 100644 springboot-kafka/src/main/resources/local/logback-spring.xml diff --git a/springboot-kafka/pom.xml b/springboot-kafka/pom.xml new file mode 100644 index 0000000..1ca1a2a --- /dev/null +++ b/springboot-kafka/pom.xml @@ -0,0 +1,63 @@ + + + 4.0.0 + + + cn.abel + springboot-kafka + 1.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.0.4.RELEASE + + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.kafka + spring-kafka + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.mortbay.jetty + jetty-util + 6.1.26 + + + com.alibaba + fastjson + 1.2.40 + + + + + org.projectlombok + lombok + true + + + + + org.apache.commons + commons-lang3 + 3.7 + + + \ No newline at end of file diff --git a/springboot-kafka/src/main/java/cn/abel/Application.java b/springboot-kafka/src/main/java/cn/abel/Application.java new file mode 100644 index 0000000..f2e2b4c --- /dev/null +++ b/springboot-kafka/src/main/java/cn/abel/Application.java @@ -0,0 +1,15 @@ +package cn.abel; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration; + +/** + * Created by yyb on 2018/12/12. + */ +@SpringBootApplication(exclude = KafkaAutoConfiguration.class) +public class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/springboot-kafka/src/main/java/cn/abel/config/KafkaConfig.java b/springboot-kafka/src/main/java/cn/abel/config/KafkaConfig.java new file mode 100644 index 0000000..f68bd15 --- /dev/null +++ b/springboot-kafka/src/main/java/cn/abel/config/KafkaConfig.java @@ -0,0 +1,88 @@ +package cn.abel.config; + +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.common.serialization.StringDeserializer; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.kafka.annotation.EnableKafka; +import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; +import org.springframework.kafka.config.KafkaListenerContainerFactory; +import org.springframework.kafka.core.ConsumerFactory; +import org.springframework.kafka.core.DefaultKafkaConsumerFactory; + +import java.util.HashMap; +import java.util.Map; + +/** + * Created by yyb on 2018/12/12. + */ +@Configuration +@EnableKafka +public class KafkaConfig { + + /** + * kafka地址 + */ + @Value("${spring.kafka.bootstrap-servers}") + private String bootstrapServers; + /** + * 默认组 + */ + @Value("${spring.kafka.consumer.group-id}") + private String groupId; + /** + * 自动消费设定 + */ + @Value("${spring.kafka.consumer.auto-offset-reset}") + private String autoOffsetReset; + /** + * 一次批量处理的数据量 + */ + @Value("${spring.kafka.consumer.max-poll-records}") + private int maxPollRecords; + /** + * 一次批量处理的时间间隔 + */ + @Value("${spring.kafka.consumer.max-poll-interval-ms}") + private int maxPollIntervalMs; + + @Value("${spring.kafka.listener.concurrency}") + private int cocurrency; + + @Value("${spring.kafka.consumer.auto-commit-interval}") + private Integer commitInterval; + + @Bean(name = "kafkaListenerContainerFactory") + public KafkaListenerContainerFactory batchFactory() { + ConcurrentKafkaListenerContainerFactory factory = + new ConcurrentKafkaListenerContainerFactory<>(); + factory.setConsumerFactory(consumerFactory()); + factory.setConcurrency(cocurrency); + //设置为批量消费,每个批次数量在Kafka配置参数中设置ConsumerConfig.MAX_POLL_RECORDS_CONFIG + factory.setBatchListener(true); + return factory; + } + + @Bean + public ConsumerFactory consumerFactory() { + return new DefaultKafkaConsumerFactory<>(consumerConfigs()); + } + @Bean + public Map consumerConfigs() { + Map props = new HashMap<>(); + props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers); + props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false); + props.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, commitInterval); + props.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "15000"); + props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); + props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId); + //每一批数量 + props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, maxPollRecords); + props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, autoOffsetReset); + return props; + } + + +} diff --git a/springboot-kafka/src/main/java/cn/abel/service/prehandle/KafkaConsumerService.java b/springboot-kafka/src/main/java/cn/abel/service/prehandle/KafkaConsumerService.java new file mode 100644 index 0000000..c454557 --- /dev/null +++ b/springboot-kafka/src/main/java/cn/abel/service/prehandle/KafkaConsumerService.java @@ -0,0 +1,28 @@ +package cn.abel.service.prehandle; + +import org.apache.kafka.clients.consumer.ConsumerRecord; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * Created by yyb on 2018/12/10. + */ + +@Component +public class KafkaConsumerService { + + @Autowired + private SplitService splitService; + + @KafkaListener(topics = "${log.statistical.kafka.topic}", containerFactory = "kafkaListenerContainerFactory") + public void processMessage(List> records) { + + for (ConsumerRecord record : records) { + String message = (String) record.value(); + splitService.saveAndSplitLog(message); + } + } +} diff --git a/springboot-kafka/src/main/java/cn/abel/service/prehandle/SplitService.java b/springboot-kafka/src/main/java/cn/abel/service/prehandle/SplitService.java new file mode 100644 index 0000000..7529274 --- /dev/null +++ b/springboot-kafka/src/main/java/cn/abel/service/prehandle/SplitService.java @@ -0,0 +1,26 @@ +package cn.abel.service.prehandle; + + +import com.alibaba.fastjson.JSONObject; +import lombok.extern.slf4j.Slf4j; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +/** + * Created by yyb on 2018/12/10. + */ +@Service +@Slf4j +public class SplitService { + + private static final Logger logger = LoggerFactory.getLogger(SplitService.class); + + public void saveAndSplitLog(String message) { + JSONObject jsonObject = JSONObject.parseObject(message); + // 从kafka 中获取到数据 + String content = jsonObject.getString("message"); + } + +} diff --git a/springboot-kafka/src/main/resources/local/application.properties b/springboot-kafka/src/main/resources/local/application.properties new file mode 100644 index 0000000..af51498 --- /dev/null +++ b/springboot-kafka/src/main/resources/local/application.properties @@ -0,0 +1,18 @@ +server.port=8090 +spring.kafka.bootstrap-servers=127.0.0.1:9092 +spring.kafka.consumer.group-id=springboot-kafka +spring.kafka.consumer.auto-offset-reset=latest +spring.kafka.consumer.enable-auto-commit=true +spring.kafka.consumer.auto-commit-interval=2000 +spring.kafka.listener.concurrency= 1 +spring.kafka.consumer.max-poll-records=50 +spring.kafka.consumer.max-poll-interval-ms=4000 +# topic +log.statistical.kafka.topic=nginx_log + + +logging.file=./logs/springboot-kafka.log + + + + diff --git a/springboot-kafka/src/main/resources/local/banner.txt b/springboot-kafka/src/main/resources/local/banner.txt new file mode 100644 index 0000000..42f7c59 --- /dev/null +++ b/springboot-kafka/src/main/resources/local/banner.txt @@ -0,0 +1,5 @@ +######################################################## +# # +# local # +# # +######################################################## \ No newline at end of file diff --git a/springboot-kafka/src/main/resources/local/logback-spring.xml b/springboot-kafka/src/main/resources/local/logback-spring.xml new file mode 100644 index 0000000..67ff2b8 --- /dev/null +++ b/springboot-kafka/src/main/resources/local/logback-spring.xml @@ -0,0 +1,35 @@ + + + middle_nginxlog_storage + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + ${LOG_FILE} + + + ${LOG_FILE}.%d{yyyy-MM-dd} + + 30 + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + \ No newline at end of file From 46e4e161a9af6c4386ad3ef497d23b7fc66b259f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Fri, 12 Apr 2019 17:07:23 +0800 Subject: [PATCH 25/44] modify pom --- springboot-kafka/pom.xml | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/springboot-kafka/pom.xml b/springboot-kafka/pom.xml index 1ca1a2a..8778b54 100644 --- a/springboot-kafka/pom.xml +++ b/springboot-kafka/pom.xml @@ -25,21 +25,12 @@ org.springframework.boot spring-boot-starter-web - - org.springframework.kafka - spring-kafka - org.springframework.boot spring-boot-starter-test test - - org.mortbay.jetty - jetty-util - 6.1.26 - com.alibaba fastjson @@ -48,16 +39,14 @@ - org.projectlombok - lombok - true + org.springframework.kafka + spring-kafka - - org.apache.commons - commons-lang3 - 3.7 + org.projectlombok + lombok + true \ No newline at end of file From 0db55bdae9e0e8bb0501316ea3f73a9b1ed75b33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Thu, 4 Jul 2019 15:58:46 +0800 Subject: [PATCH 26/44] modify name --- springboot-Quartz/src/main/resources/application.properties | 2 +- springboot-Quartz/src/main/resources/logback-spring.xml | 2 +- .../src/main/resources/local/application.properties | 2 +- .../src/main/resources/local/logback-spring.xml | 2 +- .../src/main/resources/local/application.properties | 2 +- .../src/main/resources/local/logback-spring.xml | 2 +- springboot-kafka/src/main/resources/local/logback-spring.xml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/springboot-Quartz/src/main/resources/application.properties b/springboot-Quartz/src/main/resources/application.properties index c21a4a1..096d108 100644 --- a/springboot-Quartz/src/main/resources/application.properties +++ b/springboot-Quartz/src/main/resources/application.properties @@ -5,7 +5,7 @@ server.tomcat.uri-encoding=UTF-8 spring.http.encoding.charset=UTF-8 spring.http.encoding.enabled=true spring.http.encoding.force=true -spring.messages.encoding=UTF-8 +spring.abel.encoding=UTF-8 # tomcat\u6700\u5927\u7EBF\u7A0B\u6570\uFF0C\u9ED8\u8BA4\u4E3A200 server.tomcat.max-threads=800 # session\u6700\u5927\u8D85\u65F6\u65F6\u95F4(\u5206\u949F)\uFF0C\u9ED8\u8BA4\u4E3A30 diff --git a/springboot-Quartz/src/main/resources/logback-spring.xml b/springboot-Quartz/src/main/resources/logback-spring.xml index 0505336..d41b140 100644 --- a/springboot-Quartz/src/main/resources/logback-spring.xml +++ b/springboot-Quartz/src/main/resources/logback-spring.xml @@ -1,6 +1,6 @@ - middle_message + springboot-quartz diff --git a/springboot-dynamicDataSource/src/main/resources/local/application.properties b/springboot-dynamicDataSource/src/main/resources/local/application.properties index e5981f3..28ba7a9 100644 --- a/springboot-dynamicDataSource/src/main/resources/local/application.properties +++ b/springboot-dynamicDataSource/src/main/resources/local/application.properties @@ -5,7 +5,7 @@ server.tomcat.uri-encoding=UTF-8 spring.http.encoding.charset=UTF-8 spring.http.encoding.enabled=true spring.http.encoding.force=true -spring.risk.encoding=UTF-8 +spring.abel.encoding=UTF-8 # tomcat\u6700\u5927\u7EBF\u7A0B\u6570\uFF0C\u9ED8\u8BA4\u4E3A200 server.tomcat.max-threads=800 # session\u6700\u5927\u8D85\u65F6\u65F6\u95F4(\u5206\u949F)\uFF0C\u9ED8\u8BA4\u4E3A30 diff --git a/springboot-dynamicDataSource/src/main/resources/local/logback-spring.xml b/springboot-dynamicDataSource/src/main/resources/local/logback-spring.xml index 0505336..d600f43 100644 --- a/springboot-dynamicDataSource/src/main/resources/local/logback-spring.xml +++ b/springboot-dynamicDataSource/src/main/resources/local/logback-spring.xml @@ -1,6 +1,6 @@ - middle_message + springboot-dataSource diff --git a/springboot-elasticsearch/src/main/resources/local/application.properties b/springboot-elasticsearch/src/main/resources/local/application.properties index d3234b6..5808183 100644 --- a/springboot-elasticsearch/src/main/resources/local/application.properties +++ b/springboot-elasticsearch/src/main/resources/local/application.properties @@ -5,7 +5,7 @@ server.tomcat.uri-encoding=UTF-8 spring.http.encoding.charset=UTF-8 spring.http.encoding.enabled=true spring.http.encoding.force=true -spring.risk.encoding=UTF-8 +spring.es.encoding=UTF-8 # tomcat\u6700\u5927\u7EBF\u7A0B\u6570\uFF0C\u9ED8\u8BA4\u4E3A200 server.tomcat.max-threads=800 # session\u6700\u5927\u8D85\u65F6\u65F6\u95F4(\u5206\u949F)\uFF0C\u9ED8\u8BA4\u4E3A30 diff --git a/springboot-elasticsearch/src/main/resources/local/logback-spring.xml b/springboot-elasticsearch/src/main/resources/local/logback-spring.xml index 0505336..18df5a2 100644 --- a/springboot-elasticsearch/src/main/resources/local/logback-spring.xml +++ b/springboot-elasticsearch/src/main/resources/local/logback-spring.xml @@ -1,6 +1,6 @@ - middle_message + springboot-es diff --git a/springboot-kafka/src/main/resources/local/logback-spring.xml b/springboot-kafka/src/main/resources/local/logback-spring.xml index 67ff2b8..9a98e4a 100644 --- a/springboot-kafka/src/main/resources/local/logback-spring.xml +++ b/springboot-kafka/src/main/resources/local/logback-spring.xml @@ -1,6 +1,6 @@ - middle_nginxlog_storage + springboot-kafka From 2472cd3cc5ae41921ab8913bddbf442c11040e89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Wed, 14 Aug 2019 10:34:11 +0800 Subject: [PATCH 27/44] add springRedis queue --- springboot-redis-queue/pom.xml | 70 ++++++++ .../main/java/cn/abel/queue/Application.java | 16 ++ .../cn/abel/queue/config/RedisConfig.java | 161 ++++++++++++++++++ .../queue/controller/PublisherController.java | 35 ++++ .../abel/queue/service/ProducerService.java | 29 ++++ .../abel/queue/service/ReceiverService.java | 21 +++ .../resources/local/application.properties | 52 ++++++ .../src/main/resources/local/banner.txt | 5 + .../main/resources/local/logback-spring.xml | 33 ++++ 9 files changed, 422 insertions(+) create mode 100644 springboot-redis-queue/pom.xml create mode 100644 springboot-redis-queue/src/main/java/cn/abel/queue/Application.java create mode 100644 springboot-redis-queue/src/main/java/cn/abel/queue/config/RedisConfig.java create mode 100644 springboot-redis-queue/src/main/java/cn/abel/queue/controller/PublisherController.java create mode 100644 springboot-redis-queue/src/main/java/cn/abel/queue/service/ProducerService.java create mode 100644 springboot-redis-queue/src/main/java/cn/abel/queue/service/ReceiverService.java create mode 100644 springboot-redis-queue/src/main/resources/local/application.properties create mode 100644 springboot-redis-queue/src/main/resources/local/banner.txt create mode 100644 springboot-redis-queue/src/main/resources/local/logback-spring.xml diff --git a/springboot-redis-queue/pom.xml b/springboot-redis-queue/pom.xml new file mode 100644 index 0000000..e7b6b0b --- /dev/null +++ b/springboot-redis-queue/pom.xml @@ -0,0 +1,70 @@ + + + 4.0.0 + + cn.abel + springboot-redis-queue + 1.0-SNAPSHOT + + org.springframework.boot + spring-boot-starter-parent + 2.0.4.RELEASE + + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-starter-data-redis + + + org.apache.commons + commons-pool2 + + + org.springframework.boot + spring-boot-autoconfigure + + + + redis.clients + jedis + 3.0.1 + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + 2.18.1 + + true + + + + + \ No newline at end of file diff --git a/springboot-redis-queue/src/main/java/cn/abel/queue/Application.java b/springboot-redis-queue/src/main/java/cn/abel/queue/Application.java new file mode 100644 index 0000000..287bdd7 --- /dev/null +++ b/springboot-redis-queue/src/main/java/cn/abel/queue/Application.java @@ -0,0 +1,16 @@ +package cn.abel.queue; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author yyb + * @time 2019/8/13 + */ +@SpringBootApplication +public class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/springboot-redis-queue/src/main/java/cn/abel/queue/config/RedisConfig.java b/springboot-redis-queue/src/main/java/cn/abel/queue/config/RedisConfig.java new file mode 100644 index 0000000..371a1e9 --- /dev/null +++ b/springboot-redis-queue/src/main/java/cn/abel/queue/config/RedisConfig.java @@ -0,0 +1,161 @@ +package cn.abel.queue.config; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.interceptor.KeyGenerator; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisPassword; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import java.time.Duration; + +/** + * @author yyb + * @time 2019/8/13 + */ +@Configuration +@EnableCaching +public class RedisConfig extends CachingConfigurerSupport { + /** + * logger + */ + private static final Logger logger = LoggerFactory.getLogger(RedisConfig.class); + @Value("${spring.redis.database}") + private Integer database; + @Value("${spring.redis.host}") + private String host; + @Value("${spring.redis.port}") + private Integer port; + @Value("${spring.redis.password}") + private String password; + @Value("${spring.redis.lettuce.pool.max-active}") + private Integer maxActive; + @Value("${spring.redis.lettuce.pool.max-wait}") + private Integer maxWait; + @Value("${spring.redis.lettuce.pool.max-idle}") + private Integer maxIdle; + @Value("${spring.redis.lettuce.pool.min-idle}") + private Integer minIdle; + @Value("${spring.redis.lettuce.shutdown-timeout}") + private Integer timeout; + + /** + * 在使用@Cacheable时,如果不指定key,则使用这个默认的key生成器生成的key + * + * @return + */ + @Override + @Bean + public KeyGenerator keyGenerator() { + return (target, method, params) -> { + StringBuilder sb = new StringBuilder(); + sb.append(target.getClass().getName()); + sb.append(method.getName()); + for (Object obj : params) { + sb.append(obj.toString()); + } + return sb.toString(); + }; + } + + @Bean(name = "redisTemplate") + public RedisTemplate redisTemplate() { + return getTemplate(redisConnectionFactory()); + } + + private RedisConnectionFactory redisConnectionFactory() { + return connectionFactory(maxActive, maxIdle, minIdle, maxWait, host, password, timeout, port, database); + } + + + /** + * 创建连接 + * + * @param maxActive + * @param maxIdle + * @param minIdle + * @param maxWait + * @param host + * @param password + * @param timeout + * @param port + * @param database + * @return + */ + private RedisConnectionFactory connectionFactory(Integer maxActive, + Integer maxIdle, + Integer minIdle, + Integer maxWait, + String host, + String password, + Integer timeout, + Integer port, + Integer database) { + RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(); + redisStandaloneConfiguration.setHostName(host); + redisStandaloneConfiguration.setPort(port); + redisStandaloneConfiguration.setDatabase(database); + redisStandaloneConfiguration.setPassword(RedisPassword.of(password)); + + GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); + poolConfig.setMaxTotal(maxActive); + poolConfig.setMinIdle(minIdle); + poolConfig.setMaxIdle(maxIdle); + poolConfig.setMaxWaitMillis(maxWait); + LettuceClientConfiguration lettucePoolingConfig = LettucePoolingClientConfiguration.builder() + .poolConfig(poolConfig).shutdownTimeout(Duration.ofMillis(timeout)).build(); + LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(redisStandaloneConfiguration, + lettucePoolingConfig); + connectionFactory.afterPropertiesSet(); + + return connectionFactory; + } + + /** + * 创建 RedisTemplate 连接类型,此处为hash + * + * @param factory + * @return + */ + private RedisTemplate getTemplate(RedisConnectionFactory factory) { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(factory); + template.setValueSerializer(jackson2JsonRedisSerializer(new ObjectMapper())); + template.setKeySerializer(new StringRedisSerializer()); + template.setHashKeySerializer(new StringRedisSerializer()); + template.setHashValueSerializer(jackson2JsonRedisSerializer(new ObjectMapper())); + + template.afterPropertiesSet(); + return template; + } + + /** + * 对value 进行序列化 + * + * @param objectMapper + * @return + */ + private Jackson2JsonRedisSerializer jackson2JsonRedisSerializer(ObjectMapper objectMapper) { + Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = + new Jackson2JsonRedisSerializer<>(Object.class); + jackson2JsonRedisSerializer.setObjectMapper(objectMapper); + objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); + objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + return jackson2JsonRedisSerializer; + } +} diff --git a/springboot-redis-queue/src/main/java/cn/abel/queue/controller/PublisherController.java b/springboot-redis-queue/src/main/java/cn/abel/queue/controller/PublisherController.java new file mode 100644 index 0000000..8246841 --- /dev/null +++ b/springboot-redis-queue/src/main/java/cn/abel/queue/controller/PublisherController.java @@ -0,0 +1,35 @@ +package cn.abel.queue.controller; + +import cn.abel.queue.service.ProducerService; +import cn.abel.queue.service.ReceiverService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author yyb + * @time 2019/8/13 + */ + +@RestController +@RequestMapping("/publisher") +public class PublisherController { + @Autowired + private ProducerService producerService; + @Autowired + private ReceiverService receiverService; + + @RequestMapping(value = "{name}",method = RequestMethod.GET) + public String sendMessage(@PathVariable("name") String name) { + return producerService.sendMessage(name); + } + + + @RequestMapping(value = "/get",method =RequestMethod.GET ) + public String getMessage() { + return receiverService.getMessage(); + } + +} diff --git a/springboot-redis-queue/src/main/java/cn/abel/queue/service/ProducerService.java b/springboot-redis-queue/src/main/java/cn/abel/queue/service/ProducerService.java new file mode 100644 index 0000000..469f3bb --- /dev/null +++ b/springboot-redis-queue/src/main/java/cn/abel/queue/service/ProducerService.java @@ -0,0 +1,29 @@ +package cn.abel.queue.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Service; + + +/** + * @author yyb + * @time 2019/8/13 + */ + +@Service +public class ProducerService { + @Autowired + private StringRedisTemplate redisTemplate; + + public String sendMessage(String name) { + try { + redisTemplate.opsForList().leftPush("quere", name); + return "消息发送成功了"; + + } catch (Exception e) { + e.printStackTrace(); + return "消息发送失败了"; + } + } + +} \ No newline at end of file diff --git a/springboot-redis-queue/src/main/java/cn/abel/queue/service/ReceiverService.java b/springboot-redis-queue/src/main/java/cn/abel/queue/service/ReceiverService.java new file mode 100644 index 0000000..07fb207 --- /dev/null +++ b/springboot-redis-queue/src/main/java/cn/abel/queue/service/ReceiverService.java @@ -0,0 +1,21 @@ +package cn.abel.queue.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Service; + +/** + * @author yyb + * @time 2019/8/13 + */ +@Service +public class ReceiverService { + @Autowired + private StringRedisTemplate redisTemplate; + + public String getMessage() { + String value = redisTemplate.opsForList().rightPop("quere"); + return value; + } +} + diff --git a/springboot-redis-queue/src/main/resources/local/application.properties b/springboot-redis-queue/src/main/resources/local/application.properties new file mode 100644 index 0000000..286c930 --- /dev/null +++ b/springboot-redis-queue/src/main/resources/local/application.properties @@ -0,0 +1,52 @@ +## tomcat\u914D\u7F6E +server.port=9666 +#server.tomcat.maxHttpHeaderSize=8192 +server.tomcat.uri-encoding=UTF-8 +spring.http.encoding.charset=UTF-8 +spring.http.encoding.enabled=true +spring.http.encoding.force=true +spring.messages.encoding=UTF-8 +# tomcat\u6700\u5927\u7EBF\u7A0B\u6570\uFF0C\u9ED8\u8BA4\u4E3A200 +server.tomcat.max-threads=800 +# session\u6700\u5927\u8D85\u65F6\u65F6\u95F4(\u5206\u949F)\uFF0C\u9ED8\u8BA4\u4E3A30 +server.session-timeout=60 + +## spring \u914D\u7F6E +spring.application.name=redis-queue +application.main=cn.abel.queue.Application + +## LOG +logging.file=./logs/redis-queue.log + + +## spring cache +#\u7F13\u5B58\u7684\u540D\u79F0\u96C6\u5408\uFF0C\u591A\u4E2A\u91C7\u7528\u9017\u53F7\u5206\u5272 +#spring.cache.cache-names=admin,role +#\u7F13\u5B58\u7684\u7C7B\u578B\uFF0C\u5B98\u65B9\u63D0\u4F9B\u4E86\u5F88\u591A\uFF0C\u8FD9\u91CC\u6211\u4EEC\u586B\u5199redis +spring.cache.type=redis +#\u662F\u5426\u7F13\u5B58null\u6570\u636E\uFF0C\u9ED8\u8BA4\u662Ffalse +#spring.cache.redis.cache-null-values=false +#redis\u4E2D\u7F13\u5B58\u8D85\u65F6\u7684\u65F6\u95F4\uFF0C\u9ED8\u8BA460000ms +#spring.cache.redis.time-to-live=60000 +#\u7F13\u5B58\u6570\u636Ekey\u662F\u5426\u4F7F\u7528\u524D\u7F00\uFF0C\u9ED8\u8BA4\u662Ftrue +#spring.cache.redis.use-key-prefix=true +#\u7F13\u5B58\u6570\u636Ekey\u7684\u524D\u7F00\uFF0C\u5728\u4E0A\u9762\u7684\u914D\u7F6E\u4E3Atrue\u65F6\u6709\u6548\uFF0C + +# Redis\u6570\u636E\u5E93\u7D22\u5F15\uFF08\u9ED8\u8BA4\u5E93\uFF09 +spring.redis.database=2 +# Redis\u670D\u52A1\u5668\u5730\u5740 +spring.redis.host=127.0.0.1 +# Redis\u670D\u52A1\u5668\u8FDE\u63A5\u7AEF\u53E3 +spring.redis.port=6379 +# Redis\u670D\u52A1\u5668\u8FDE\u63A5\u5BC6\u7801\uFF08\u9ED8\u8BA4\u4E3A\u7A7A\uFF09 +spring.redis.password= +# \u8FDE\u63A5\u6C60\u6700\u5927\u8FDE\u63A5\u6570\uFF08\u4F7F\u7528\u8D1F\u503C\u8868\u793A\u6CA1\u6709\u9650\u5236\uFF09 +spring.redis.lettuce.pool.max-active=100 +# \u8FDE\u63A5\u6C60\u6700\u5927\u963B\u585E\u7B49\u5F85\u65F6\u95F4\uFF08\u4F7F\u7528\u8D1F\u503C\u8868\u793A\u6CA1\u6709\u9650\u5236\uFF09 +spring.redis.lettuce.pool.max-wait=1000 +# \u8FDE\u63A5\u6C60\u4E2D\u7684\u6700\u5927\u7A7A\u95F2\u8FDE\u63A5 +spring.redis.lettuce.pool.max-idle=50 +# \u8FDE\u63A5\u6C60\u4E2D\u7684\u6700\u5C0F\u7A7A\u95F2\u8FDE\u63A5 +spring.redis.lettuce.pool.min-idle=0 +# \u8FDE\u63A5\u8D85\u65F6\u65F6\u95F4\uFF08\u6BEB\u79D2\uFF09 +spring.redis.lettuce.shutdown-timeout=0 \ No newline at end of file diff --git a/springboot-redis-queue/src/main/resources/local/banner.txt b/springboot-redis-queue/src/main/resources/local/banner.txt new file mode 100644 index 0000000..42f7c59 --- /dev/null +++ b/springboot-redis-queue/src/main/resources/local/banner.txt @@ -0,0 +1,5 @@ +######################################################## +# # +# local # +# # +######################################################## \ No newline at end of file diff --git a/springboot-redis-queue/src/main/resources/local/logback-spring.xml b/springboot-redis-queue/src/main/resources/local/logback-spring.xml new file mode 100644 index 0000000..2e2778f --- /dev/null +++ b/springboot-redis-queue/src/main/resources/local/logback-spring.xml @@ -0,0 +1,33 @@ + + + demo + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + ${LOG_FILE} + + + ${LOG_FILE}.%d{yyyy-MM-dd} + + 60 + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + \ No newline at end of file From c37c003b772aede5e7f8cf6f9013906dd6e29e1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Fri, 23 Aug 2019 10:17:42 +0800 Subject: [PATCH 28/44] modify readme --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index 5275bb9..285b1f8 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +## 致歉 +由于自己懒以及身体对issuse 解决的不及时。请大家以后提issuse 的时候写清楚 模块名 比如“springboot-SpringSecurity4” 和问题,我会抽时间抓紧解决。 + ## springboot-SpringSecurity0 包含两部分代码: @@ -45,6 +48,8 @@ * 是 博客 springboot缓存 之 GuavaCacheManager 地址:http://blog.csdn.net/u012373815/article/details/60468033 + + ## springboot-shiro * 是博客 springboot集成shiro 实现权限控制 地址:http://blog.csdn.net/u012373815/article/details/57532292 @@ -52,5 +57,20 @@ ## springboot-swagger-ui * 博客 spring boot +Swagger-ui 自动生成API文档 地址: https://blog.csdn.net/u012373815/article/details/82685962 +## springBoot-Quartz +* 博客 springBoot-Quartz 定时任务 地址: https://abelyang.blog.csdn.net/article/details/86740625 + +## springboot 整合mybatis2 +* springboot 整合mybatis2 更简便的整合方式地址: https://abelyang.blog.csdn.net/article/details/89296273 + +## springboot+Kafka +* kafka 与 sprigboot 的结合,springboot 从Kafka中读取数据的小例子地址: https://abelyang.blog.csdn.net/article/details/89296305 + +## springboot+es +* Elasticsearch 与 sprigboot 的结合,springboot 操作es的小例子地址: https://abelyang.blog.csdn.net/article/details/89296320 + +## Springboot多数据源切换 +* springboot 多个数据源的配置, 一个springboot 项目操作多个数据库的数据:https://abelyang.blog.csdn.net/article/details/89296341 + ##未完待续。。。 From 8d6e7cbcd02c67b2b138b076f6dd8609ee14354a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Tue, 26 Nov 2019 15:48:02 +0800 Subject: [PATCH 29/44] add rocketmq --- springboot-rocketmq/pom.xml | 72 +++++++++++++++++++ .../main/java/cn/abel/queue/Application.java | 16 +++++ .../java/cn/abel/queue/config/JmsConfig.java | 16 +++++ .../queue/controller/PublisherController.java | 57 +++++++++++++++ .../abel/queue/service/ProducerService.java | 50 +++++++++++++ .../abel/queue/service/ReceiverService.java | 61 ++++++++++++++++ .../resources/local/application.properties | 21 ++++++ .../src/main/resources/local/banner.txt | 5 ++ .../main/resources/local/logback-spring.xml | 33 +++++++++ 9 files changed, 331 insertions(+) create mode 100644 springboot-rocketmq/pom.xml create mode 100644 springboot-rocketmq/src/main/java/cn/abel/queue/Application.java create mode 100644 springboot-rocketmq/src/main/java/cn/abel/queue/config/JmsConfig.java create mode 100644 springboot-rocketmq/src/main/java/cn/abel/queue/controller/PublisherController.java create mode 100644 springboot-rocketmq/src/main/java/cn/abel/queue/service/ProducerService.java create mode 100644 springboot-rocketmq/src/main/java/cn/abel/queue/service/ReceiverService.java create mode 100644 springboot-rocketmq/src/main/resources/local/application.properties create mode 100644 springboot-rocketmq/src/main/resources/local/banner.txt create mode 100644 springboot-rocketmq/src/main/resources/local/logback-spring.xml diff --git a/springboot-rocketmq/pom.xml b/springboot-rocketmq/pom.xml new file mode 100644 index 0000000..af5a513 --- /dev/null +++ b/springboot-rocketmq/pom.xml @@ -0,0 +1,72 @@ + + + 4.0.0 + + cn.abel + springboot-rocketmq + 1.0-SNAPSHOT + + org.springframework.boot + spring-boot-starter-parent + 2.0.4.RELEASE + + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + org.apache.commons + commons-pool2 + + + org.springframework.boot + spring-boot-autoconfigure + + + + org.apache.rocketmq + rocketmq-client + 4.3.0 + + + org.codehaus.groovy + groovy + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + 2.18.1 + + true + + + + + \ No newline at end of file diff --git a/springboot-rocketmq/src/main/java/cn/abel/queue/Application.java b/springboot-rocketmq/src/main/java/cn/abel/queue/Application.java new file mode 100644 index 0000000..287bdd7 --- /dev/null +++ b/springboot-rocketmq/src/main/java/cn/abel/queue/Application.java @@ -0,0 +1,16 @@ +package cn.abel.queue; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author yyb + * @time 2019/8/13 + */ +@SpringBootApplication +public class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/springboot-rocketmq/src/main/java/cn/abel/queue/config/JmsConfig.java b/springboot-rocketmq/src/main/java/cn/abel/queue/config/JmsConfig.java new file mode 100644 index 0000000..d5d4695 --- /dev/null +++ b/springboot-rocketmq/src/main/java/cn/abel/queue/config/JmsConfig.java @@ -0,0 +1,16 @@ +package cn.abel.queue.config; + +/** + * @author yyb + * @time 2019/11/26 + */ +public class JmsConfig { + /** + * Name Server 地址,因为是集群部署 所以有多个用 分号 隔开 + */ + public static final String NAME_SERVER = "10.33.20.223:9876"; + /** + * 主题名称 主题一般是服务器设置好 而不能在代码里去新建topic( 如果没有创建好,生产者往该主题发送消息 会报找不到topic错误) + */ + public static final String TOPIC = "stock_change"; +} diff --git a/springboot-rocketmq/src/main/java/cn/abel/queue/controller/PublisherController.java b/springboot-rocketmq/src/main/java/cn/abel/queue/controller/PublisherController.java new file mode 100644 index 0000000..3731621 --- /dev/null +++ b/springboot-rocketmq/src/main/java/cn/abel/queue/controller/PublisherController.java @@ -0,0 +1,57 @@ +package cn.abel.queue.controller; + +import cn.abel.queue.config.JmsConfig; +import cn.abel.queue.service.ProducerService; +import cn.abel.queue.service.ReceiverService; +import org.apache.rocketmq.client.producer.SendResult; +import org.apache.rocketmq.common.message.Message; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author yyb + * @time 2019/8/13 + */ + +@RestController +@RequestMapping("/publisher") +public class PublisherController { + @Autowired + private ProducerService producerService; + @Autowired + private ReceiverService receiverService; + + private List mesList; + + /** + * 初始化消息 + */ + public PublisherController() { + mesList = new ArrayList<>(); + mesList.add("小小"); + mesList.add("爸爸"); + mesList.add("妈妈"); + mesList.add("爷爷"); + mesList.add("奶奶"); + + } + + @RequestMapping("/text/rocketmq") + public Object callback() throws Exception { + //总共发送五次消息 + for (String s : mesList) { + //创建生产信息 + Message message = new Message(JmsConfig.TOPIC, "testtag", ("小小一家人的称谓:" + s).getBytes()); + //发送 + SendResult sendResult = producerService.getProducer().send(message); + System.out.println(String.format("输出生产者信息={ %s }",sendResult)); + } + return "成功"; + } +} diff --git a/springboot-rocketmq/src/main/java/cn/abel/queue/service/ProducerService.java b/springboot-rocketmq/src/main/java/cn/abel/queue/service/ProducerService.java new file mode 100644 index 0000000..a7a69dc --- /dev/null +++ b/springboot-rocketmq/src/main/java/cn/abel/queue/service/ProducerService.java @@ -0,0 +1,50 @@ +package cn.abel.queue.service; + +import cn.abel.queue.config.JmsConfig; +import org.apache.rocketmq.client.exception.MQClientException; +import org.apache.rocketmq.client.producer.DefaultMQProducer; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + + +/** + * @author yyb + * @time 2019/8/13 + */ + +@Service +@Component +public class ProducerService { + private String producerGroup = "test_producer"; + private DefaultMQProducer producer; + + public ProducerService(){ + //示例生产者 + producer = new DefaultMQProducer(producerGroup); + //不开启vip通道 开通口端口会减2 + producer.setVipChannelEnabled(false); + //绑定name server + producer.setNamesrvAddr(JmsConfig.NAME_SERVER); + start(); + } + /** + * 对象在使用之前必须要调用一次,只能初始化一次 + */ + public void start(){ + try { + this.producer.start(); + } catch (MQClientException e) { + e.printStackTrace(); + } + } + + public DefaultMQProducer getProducer(){ + return this.producer; + } + /** + * 一般在应用上下文,使用上下文监听器,进行关闭 + */ + public void shutdown(){ + this.producer.shutdown(); + } +} \ No newline at end of file diff --git a/springboot-rocketmq/src/main/java/cn/abel/queue/service/ReceiverService.java b/springboot-rocketmq/src/main/java/cn/abel/queue/service/ReceiverService.java new file mode 100644 index 0000000..3e6095e --- /dev/null +++ b/springboot-rocketmq/src/main/java/cn/abel/queue/service/ReceiverService.java @@ -0,0 +1,61 @@ +package cn.abel.queue.service; + +import cn.abel.queue.config.JmsConfig; +import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; +import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; +import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently; +import org.apache.rocketmq.client.exception.MQClientException; +import org.apache.rocketmq.common.consumer.ConsumeFromWhere; +import org.apache.rocketmq.common.message.Message; +import org.springframework.stereotype.Service; + +import java.io.UnsupportedEncodingException; + +/** + * @author yyb + * @time 2019/8/13 + */ +@Service +public class ReceiverService { + /** + * 消费者实体对象 + */ + private DefaultMQPushConsumer consumer; + /** + * 消费者组 + */ + public static final String CONSUMER_GROUP = "test_consumer"; + /** + * 通过构造函数 实例化对象 + */ + public ReceiverService() throws MQClientException { + + consumer = new DefaultMQPushConsumer(CONSUMER_GROUP); + consumer.setNamesrvAddr(JmsConfig.NAME_SERVER); + //消费模式:一个新的订阅组第一次启动从队列的最后位置开始消费 后续再启动接着上次消费的进度开始消费 + consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET); + //订阅主题和 标签( * 代表所有标签)下信息 + consumer.subscribe(JmsConfig.TOPIC, "*"); + // //注册消费的监听 并在此监听中消费信息,并返回消费的状态信息 + consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> { + // msgs中只收集同一个topic,同一个tag,并且key相同的message + // 会把不同的消息分别放置到不同的队列中 + try { + for (Message msg : msgs) { + + //消费者获取消息 这里只输出 不做后面逻辑处理 + String body = new String(msg.getBody(), "utf-8"); + System.out.println(String.format("Consumer-获取消息-主题topic为={ %s }, 消费消息为={ %s }", msg.getTopic(), body)); + } + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + return ConsumeConcurrentlyStatus.RECONSUME_LATER; + } + return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; + }); + + consumer.start(); + System.out.println("消费者 启动成功======="); + } +} + diff --git a/springboot-rocketmq/src/main/resources/local/application.properties b/springboot-rocketmq/src/main/resources/local/application.properties new file mode 100644 index 0000000..45a7d43 --- /dev/null +++ b/springboot-rocketmq/src/main/resources/local/application.properties @@ -0,0 +1,21 @@ +## tomcat\u914D\u7F6E +server.port=9666 +#server.tomcat.maxHttpHeaderSize=8192 +server.tomcat.uri-encoding=UTF-8 +spring.http.encoding.charset=UTF-8 +spring.http.encoding.enabled=true +spring.http.encoding.force=true +spring.messages.encoding=UTF-8 +# tomcat\u6700\u5927\u7EBF\u7A0B\u6570\uFF0C\u9ED8\u8BA4\u4E3A200 +server.tomcat.max-threads=800 +# session\u6700\u5927\u8D85\u65F6\u65F6\u95F4(\u5206\u949F)\uFF0C\u9ED8\u8BA4\u4E3A30 +server.session-timeout=60 + +## spring \u914D\u7F6E +spring.application.name=rocketmq-queue +application.main=cn.abel.queue.Application + +## LOG +logging.file=./logs/rocketmq-queue.log + + diff --git a/springboot-rocketmq/src/main/resources/local/banner.txt b/springboot-rocketmq/src/main/resources/local/banner.txt new file mode 100644 index 0000000..42f7c59 --- /dev/null +++ b/springboot-rocketmq/src/main/resources/local/banner.txt @@ -0,0 +1,5 @@ +######################################################## +# # +# local # +# # +######################################################## \ No newline at end of file diff --git a/springboot-rocketmq/src/main/resources/local/logback-spring.xml b/springboot-rocketmq/src/main/resources/local/logback-spring.xml new file mode 100644 index 0000000..2e2778f --- /dev/null +++ b/springboot-rocketmq/src/main/resources/local/logback-spring.xml @@ -0,0 +1,33 @@ + + + demo + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + ${LOG_FILE} + + + ${LOG_FILE}.%d{yyyy-MM-dd} + + 60 + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + \ No newline at end of file From f9ddcf6b46d6337e6bec74015189bcbfeae140be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Tue, 26 Nov 2019 16:48:28 +0800 Subject: [PATCH 30/44] modify --- springboot-jpa/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/springboot-jpa/pom.xml b/springboot-jpa/pom.xml index 0f1cc9a..6e54fd3 100644 --- a/springboot-jpa/pom.xml +++ b/springboot-jpa/pom.xml @@ -42,7 +42,7 @@ com.mchange c3p0 - 0.9.5.2 + 0.9.5.4 commons-logging From 290ba9f66a3fe7a34d48ff8d8bb34c46948a58b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Thu, 19 Mar 2020 15:27:52 +0800 Subject: [PATCH 31/44] modify pom --- springboot-SpringSecurity0/pom.xml | 2 +- springboot-SpringSecurity1/pom.xml | 2 +- springboot-shiro/pom.xml | 4 ++-- springboot-springSecurity2/pom.xml | 2 +- springboot-springSecurity3/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/springboot-SpringSecurity0/pom.xml b/springboot-SpringSecurity0/pom.xml index 6245cd1..ff6894e 100644 --- a/springboot-SpringSecurity0/pom.xml +++ b/springboot-SpringSecurity0/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.us - springboot-security + springboot-security0 1.0-SNAPSHOT diff --git a/springboot-SpringSecurity1/pom.xml b/springboot-SpringSecurity1/pom.xml index b1072e5..88ccf01 100644 --- a/springboot-SpringSecurity1/pom.xml +++ b/springboot-SpringSecurity1/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.us - springboot-security + springboot-security1 1.0-SNAPSHOT diff --git a/springboot-shiro/pom.xml b/springboot-shiro/pom.xml index f48819e..0f01d61 100644 --- a/springboot-shiro/pom.xml +++ b/springboot-shiro/pom.xml @@ -4,8 +4,8 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - com.us - shiro + com.abel-shiro + springboot-shiro 1.0-SNAPSHOT diff --git a/springboot-springSecurity2/pom.xml b/springboot-springSecurity2/pom.xml index 65f18a9..041860b 100644 --- a/springboot-springSecurity2/pom.xml +++ b/springboot-springSecurity2/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.us - springboot-security + springboot-security2 1.0-SNAPSHOT diff --git a/springboot-springSecurity3/pom.xml b/springboot-springSecurity3/pom.xml index 1b5ef59..29dbd84 100644 --- a/springboot-springSecurity3/pom.xml +++ b/springboot-springSecurity3/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.us - springboot-security + springboot-security3 1.0-SNAPSHOT From 484c4ccefe1ba4d7cb1dbff155e960eaeb924cb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Thu, 19 Mar 2020 15:53:40 +0800 Subject: [PATCH 32/44] xiugai mingzi --- .../src/main/java/cn/abel/config/DynamicDataSourceConfig.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/springboot-dynamicDataSource/src/main/java/cn/abel/config/DynamicDataSourceConfig.java b/springboot-dynamicDataSource/src/main/java/cn/abel/config/DynamicDataSourceConfig.java index f89fd9d..3cbc3cd 100644 --- a/springboot-dynamicDataSource/src/main/java/cn/abel/config/DynamicDataSourceConfig.java +++ b/springboot-dynamicDataSource/src/main/java/cn/abel/config/DynamicDataSourceConfig.java @@ -59,10 +59,10 @@ public DataSource getUserDataSource() { @Bean("dynamicDataSource") public DynamicDataSource dynamicDataSource(@Qualifier("primaryDataSource") DataSource primaryDataSource, - @Qualifier("userDataSource") DataSource miaoMoreDataSource) { + @Qualifier("userDataSource") DataSource userDataSource) { Map targetDataSources = new HashMap<>(); targetDataSources.put(DatabaseTypeEnum.PRIMARY, primaryDataSource); - targetDataSources.put(DatabaseTypeEnum.USER, miaoMoreDataSource); + targetDataSources.put(DatabaseTypeEnum.USER, userDataSource); DynamicDataSource dataSource = new DynamicDataSource(); dataSource.setTargetDataSources(targetDataSources);// 该方法是AbstractRoutingDataSource的方法 From 974255cca3ed74ea66226d1d882fe962507d4a40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Fri, 20 Mar 2020 11:20:23 +0800 Subject: [PATCH 33/44] add dubbo --- abel-parent/pom.xml | 378 ++++++++++++++++++ abel-util/pom.xml | 61 +++ .../src/main/java/cn/abel/code/InfoCode.java | 62 +++ .../abel/exception/AppRuntimeException.java | 33 ++ .../java/cn/abel/exception/HttpExeption.java | 21 + .../cn/abel/exception/ServiceException.java | 60 +++ .../java/cn/abel/response/ResponseEntity.java | 72 ++++ .../java/cn/abel/utils/DateTimeUtils.java | 283 +++++++++++++ springboot-dubbo/abel-user-api/pom.xml | 50 +++ .../java/cn/abel/user/models/Permission.java | 65 +++ .../main/java/cn/abel/user/models/Role.java | 55 +++ .../main/java/cn/abel/user/models/User.java | 88 ++++ .../abel/user/service/PermissionService.java | 21 + .../cn/abel/user/service/RoleService.java | 22 + .../cn/abel/user/service/UserService.java | 21 + .../abel-user-provider/doc/user.sql | 83 ++++ springboot-dubbo/abel-user-provider/pom.xml | 177 ++++++++ .../cn/abel/user/UserProviderApplication.java | 14 + .../cn/abel/user/constants/Constants.java | 7 + .../java/cn/abel/user/dao/PermissionDao.java | 29 ++ .../main/java/cn/abel/user/dao/RoleDao.java | 20 + .../main/java/cn/abel/user/dao/UserDao.java | 20 + .../user/exception/JsonExceptionMapper.java | 31 ++ .../user/exception/ReaderExceptionMapper.java | 31 ++ .../user/exception/RestExceptionMapper.java | 45 +++ .../exception/ServiceExceptionMapper.java | 29 ++ .../exception/ValidationExceptionMapper.java | 36 ++ .../java/cn/abel/user/filter/RestFilter.java | 71 ++++ .../cn/abel/user/filter/RestInterceptor.java | 88 ++++ .../service/impl/PermissionServiceImpl.java | 44 ++ .../user/service/impl/RoleServiceImpl.java | 40 ++ .../user/service/impl/UserServiceImpl.java | 49 +++ .../java/cn/abel/user/utils/CommentUtils.java | 28 ++ .../resources/META-INF/spring/provider.xml | 26 ++ .../main/resources/dev/application.properties | 53 +++ .../src/main/resources/dev/banner.txt | 5 + .../src/main/resources/dev/logback-spring.xml | 40 ++ .../resources/local/application.properties | 53 +++ .../src/main/resources/local/banner.txt | 5 + .../main/resources/local/logback-spring.xml | 41 ++ .../resources/mapper/PermissionDaoMapper.xml | 89 +++++ .../main/resources/mapper/RoleDaoMapper.xml | 68 ++++ .../main/resources/mapper/UserDaoMapper.xml | 100 +++++ .../src/main/test/cn/abel/user/BaseTest.java | 33 ++ .../impl/PermissionServiceImplTest.java | 55 +++ 45 files changed, 2702 insertions(+) create mode 100644 abel-parent/pom.xml create mode 100644 abel-util/pom.xml create mode 100644 abel-util/src/main/java/cn/abel/code/InfoCode.java create mode 100644 abel-util/src/main/java/cn/abel/exception/AppRuntimeException.java create mode 100644 abel-util/src/main/java/cn/abel/exception/HttpExeption.java create mode 100644 abel-util/src/main/java/cn/abel/exception/ServiceException.java create mode 100644 abel-util/src/main/java/cn/abel/response/ResponseEntity.java create mode 100644 abel-util/src/main/java/cn/abel/utils/DateTimeUtils.java create mode 100644 springboot-dubbo/abel-user-api/pom.xml create mode 100644 springboot-dubbo/abel-user-api/src/main/java/cn/abel/user/models/Permission.java create mode 100644 springboot-dubbo/abel-user-api/src/main/java/cn/abel/user/models/Role.java create mode 100644 springboot-dubbo/abel-user-api/src/main/java/cn/abel/user/models/User.java create mode 100644 springboot-dubbo/abel-user-api/src/main/java/cn/abel/user/service/PermissionService.java create mode 100644 springboot-dubbo/abel-user-api/src/main/java/cn/abel/user/service/RoleService.java create mode 100644 springboot-dubbo/abel-user-api/src/main/java/cn/abel/user/service/UserService.java create mode 100644 springboot-dubbo/abel-user-provider/doc/user.sql create mode 100644 springboot-dubbo/abel-user-provider/pom.xml create mode 100644 springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/UserProviderApplication.java create mode 100644 springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/constants/Constants.java create mode 100644 springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/dao/PermissionDao.java create mode 100644 springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/dao/RoleDao.java create mode 100644 springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/dao/UserDao.java create mode 100644 springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/exception/JsonExceptionMapper.java create mode 100644 springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/exception/ReaderExceptionMapper.java create mode 100644 springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/exception/RestExceptionMapper.java create mode 100644 springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/exception/ServiceExceptionMapper.java create mode 100644 springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/exception/ValidationExceptionMapper.java create mode 100644 springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/filter/RestFilter.java create mode 100644 springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/filter/RestInterceptor.java create mode 100644 springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/service/impl/PermissionServiceImpl.java create mode 100644 springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/service/impl/RoleServiceImpl.java create mode 100644 springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/service/impl/UserServiceImpl.java create mode 100644 springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/utils/CommentUtils.java create mode 100644 springboot-dubbo/abel-user-provider/src/main/resources/META-INF/spring/provider.xml create mode 100644 springboot-dubbo/abel-user-provider/src/main/resources/dev/application.properties create mode 100644 springboot-dubbo/abel-user-provider/src/main/resources/dev/banner.txt create mode 100644 springboot-dubbo/abel-user-provider/src/main/resources/dev/logback-spring.xml create mode 100644 springboot-dubbo/abel-user-provider/src/main/resources/local/application.properties create mode 100644 springboot-dubbo/abel-user-provider/src/main/resources/local/banner.txt create mode 100644 springboot-dubbo/abel-user-provider/src/main/resources/local/logback-spring.xml create mode 100644 springboot-dubbo/abel-user-provider/src/main/resources/mapper/PermissionDaoMapper.xml create mode 100644 springboot-dubbo/abel-user-provider/src/main/resources/mapper/RoleDaoMapper.xml create mode 100644 springboot-dubbo/abel-user-provider/src/main/resources/mapper/UserDaoMapper.xml create mode 100644 springboot-dubbo/abel-user-provider/src/main/test/cn/abel/user/BaseTest.java create mode 100644 springboot-dubbo/abel-user-provider/src/main/test/cn/abel/user/service/impl/PermissionServiceImplTest.java diff --git a/abel-parent/pom.xml b/abel-parent/pom.xml new file mode 100644 index 0000000..d87f059 --- /dev/null +++ b/abel-parent/pom.xml @@ -0,0 +1,378 @@ + + + 4.0.0 + + cn.abel + abel-parent + pom + 1.0.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-parent + 2.0.4.RELEASE + + + + + UTF-8 + UTF-8 + 1.4.0 + 5.1.47 + + + + + + + + + org.apache.commons + commons-lang3 + 3.7 + + + org.apache.commons + commons-collections4 + 4.2 + + + org.apache.commons + commons-pool2 + 2.6.0 + + + + dom4j + dom4j + 1.6.1 + + + + mysql + mysql-connector-java + ${mysql.version} + runtime + + + + com.alibaba + druid + 1.1.10 + + + + org.apache.shiro + shiro-spring + ${shiro.version} + + + org.apache.shiro + shiro-ehcache + ${shiro.version} + + + org.apache.shiro + shiro-quartz + ${shiro.version} + + + org.opensymphony.quartz + quartz + + + + + net.mingsoft + shiro-freemarker-tags + 1.0.0 + + + + org.hibernate + hibernate-core + 5.3.4.Final + + + + com.fasterxml.jackson.core + jackson-core + 2.9.7 + + + com.fasterxml.jackson.core + jackson-databind + 2.9.9.1 + + + + com.alibaba + fastjson + 1.2.58 + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 1.3.2 + + + com.github.pagehelper + pagehelper-spring-boot-starter + 1.2.5 + + + + org.quartz-scheduler + quartz + 2.2.1 + + + + + com.alibaba + dubbo + 2.8.4 + + + log4j + log4j + + + + + org.slf4j + log4j-over-slf4j + 1.7.25 + + + org.apache.zookeeper + zookeeper + 3.4.13 + + + org.slf4j + slf4j-log4j12 + + + log4j + log4j + + + + + + + org.jboss.resteasy + resteasy-jaxrs + 3.0.26.Final + + + org.jboss.resteasy + resteasy-client + 3.0.26.Final + + + org.jboss.resteasy + resteasy-jackson-provider + 3.0.26.Final + + + javax.validation + validation-api + 2.0.1.Final + + + org.hibernate.validator + hibernate-validator + 6.0.13.Final + + + com.101tec + zkclient + 0.10 + + + + + com.google.code.gson + gson + 2.8.5 + + + org.apache.httpcomponents + httpcore + 4.4.10 + + + org.apache.httpcomponents + httpcore-nio + 4.4.10 + + + org.apache.httpcomponents + httpclient + 4.5.6 + + + org.apache.httpcomponents + httpasyncclient + 4.1.4 + + + + com.aliyun.oss + aliyun-sdk-oss + 3.3.0 + + + com.aliyun + aliyun-java-sdk-sts + 3.0.0 + + + + com.aliyun + aliyun-java-sdk-core + 4.2.0 + + + com.aliyun + aliyun-java-sdk-dysmsapi + 1.1.0 + + + com.aliyun.mns + aliyun-sdk-mns + 1.1.8.6 + + + + + + org.mapstruct + mapstruct-jdk8 + 1.1.0.Final + + + + + + + Releases + http://maven.aliyun.com/nexus/content/groups/public/releases + + + Snapshots + http://maven.aliyun.com/nexus/content/groups/public/snapshots + + + + + + local + + local + + + true + + + + dev + + dev + + + + online + + online + + + + test + + test + + + + test-ali + + test-ali + + + + + + ${project.artifactId}-${project.version} + + + src/main/resources/${package.environment} + + *.* + + + + src/main/resources + + local/** + dev/** + test/** + test-ali/** + online/** + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + true + + true + + true + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.6.0 + + UTF-8 + 1.8 + 1.8 + + + org.mapstruct + mapstruct-processor + 1.1.0.Final + + + + + + + + + diff --git a/abel-util/pom.xml b/abel-util/pom.xml new file mode 100644 index 0000000..faea57f --- /dev/null +++ b/abel-util/pom.xml @@ -0,0 +1,61 @@ + + + 4.0.0 + + cn.abel + abel-util + 1.0.0-SNAPSHOT + jar + + cn.abel + abel-parent + 1.0.0-SNAPSHOT + + + UTF-8 + + + + org.apache.commons + commons-lang3 + + + org.apache.commons + commons-collections4 + + + org.springframework + spring-web + 5.1.3.RELEASE + + + org.springframework.boot + spring-boot-starter-web + 2.0.4.RELEASE + + + com.fasterxml.jackson.core + jackson-databind + 2.9.8 + + + com.alibaba + fastjson + + + javax.servlet + javax.servlet-api + 4.0.1 + provided + + + junit + junit + 4.12 + test + + + + \ No newline at end of file diff --git a/abel-util/src/main/java/cn/abel/code/InfoCode.java b/abel-util/src/main/java/cn/abel/code/InfoCode.java new file mode 100644 index 0000000..a2a6c39 --- /dev/null +++ b/abel-util/src/main/java/cn/abel/code/InfoCode.java @@ -0,0 +1,62 @@ +package cn.abel.code; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 返回响应 Created by liyunfeng + */ +public enum InfoCode { + + //系统错误从200~10000 + SUCCESS(200, "OK"), + TIME_OUT(300, "超时"), + + //业务相关从10000自增 + // 10000开始 + INVALID_REQUEST(10000, "不支持的请求方式"), + + //用户相关(20000自增) + REQUEST_PARAM_ERROR(20000, "传入参数错误"), + REGISTER_TYPE_ERROR(20001, "不支持的注册类型"), + INVALID_SIGN(20002, "签名无效"), + USER_LOGIN_EXIST(20003, "用户已存在"), + USER_LOGIN_NOT_EXIST(20008, "用户不存在"), + PASSWORD_ERROR(20009, "用户名或密码错误"), + INVALID_TOKEN(20010, "无效token"), + INVALID_LOGIN(20011, "登录失效,重新登录"), + PASSWORD_SAME(20017, "密码重复"), + OLD_PASSWORD_ERROR(20018, "密码错误,不能修改"), + LOGIN_FAIL(20024, "登录失败"), + VISITOR_REGISTER_FAIL(20025, "游客注册失败"), + TOKEN_EXIST(20033, "token重复"), + LOGIN_TYPE_EXIST(20034, "登录方式已存在"), + USER_PROFILE_SEARCH_ERROR(20035, "用户查询失败"), + USER_PROFILE_LOCK(20036, "用户已锁定"), + ACCOUNT_SEARCH_FAIL(20037, "账号查询失败"), + LOGIN_NAME_TYPE_ERROR(20038, "登录名不为空时,登录方式不能为空"), + PASSWORD_SIMPLE_ERROR(20042, "密码错误,长度应为6-18,包含字母、数字和特殊字符"), + SERVICE_UNAVAILABLE(50000, "操作失败,请重试"); + + private int status; + + private String msg; + + private InfoCode(int status, String msg) { + this.status = status; + this.msg = msg; + } + + public int getStatus() { + return status; + } + + public String getMsg() { + return msg; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); + } +} diff --git a/abel-util/src/main/java/cn/abel/exception/AppRuntimeException.java b/abel-util/src/main/java/cn/abel/exception/AppRuntimeException.java new file mode 100644 index 0000000..aa0173e --- /dev/null +++ b/abel-util/src/main/java/cn/abel/exception/AppRuntimeException.java @@ -0,0 +1,33 @@ +package cn.abel.exception; + + +import cn.abel.code.InfoCode; + +/** + * 接口服务抛出的异常 + * + */ +public class AppRuntimeException extends RuntimeException { + + private static final long serialVersionUID = 2963760410777899525L; + private InfoCode infoCode; + + public AppRuntimeException() { + infoCode = InfoCode.SERVICE_UNAVAILABLE; + + } + + public AppRuntimeException(InfoCode infoCode) { + this.infoCode = infoCode; + } + + public AppRuntimeException(InfoCode infoCode, String message) { + super(message); + this.infoCode = infoCode; + } + + public InfoCode getInfoCode() { + return infoCode; + } + +} diff --git a/abel-util/src/main/java/cn/abel/exception/HttpExeption.java b/abel-util/src/main/java/cn/abel/exception/HttpExeption.java new file mode 100644 index 0000000..3e5cca8 --- /dev/null +++ b/abel-util/src/main/java/cn/abel/exception/HttpExeption.java @@ -0,0 +1,21 @@ +package cn.abel.exception; + +public class HttpExeption extends Exception { + + private static final long serialVersionUID = -4707210535385221192L; + + public HttpExeption() { + } + + public HttpExeption(String msg) { + super(msg); + } + + public HttpExeption(String msg, Throwable e) { + super(msg, e); + } + + public HttpExeption(Throwable e) { + super(e); + } +} \ No newline at end of file diff --git a/abel-util/src/main/java/cn/abel/exception/ServiceException.java b/abel-util/src/main/java/cn/abel/exception/ServiceException.java new file mode 100644 index 0000000..267a197 --- /dev/null +++ b/abel-util/src/main/java/cn/abel/exception/ServiceException.java @@ -0,0 +1,60 @@ +package cn.abel.exception; + +import cn.abel.code.*; + +/** + * 服务异常类 + * + */ +@SuppressWarnings("serial") +public class ServiceException extends Exception { + + //默认的错误码 + private int errorCode = 500; + + private String errorCodes; + + public ServiceException() { + } + + public ServiceException(Throwable e) { + super(e); + } + + public ServiceException(int errorCode) { + this.errorCode = errorCode; + } + + public int getErrorCode() { + return errorCode; + } + + public String getErrorCodes() { + return errorCodes; + } + + public ServiceException(InfoCode infoCode){ + super(infoCode.getMsg()); + this.errorCode = infoCode.getStatus(); + } + + public ServiceException(int errorCode, String msg) { + super(msg); + this.errorCode = errorCode; + } + + public ServiceException(String errorCode, String msg) { + super(msg); + this.errorCodes = errorCode; + } + + public ServiceException(int errorCode, String msg, Throwable e) { + super(msg, e); + this.errorCode = errorCode; + } + + public ServiceException(int errorCode, Throwable e) { + super(e); + this.errorCode = errorCode; + } +} diff --git a/abel-util/src/main/java/cn/abel/response/ResponseEntity.java b/abel-util/src/main/java/cn/abel/response/ResponseEntity.java new file mode 100644 index 0000000..b9a5de5 --- /dev/null +++ b/abel-util/src/main/java/cn/abel/response/ResponseEntity.java @@ -0,0 +1,72 @@ +package cn.abel.response; + + +import cn.abel.code.*; + +import java.util.HashMap; + +/** + * Created by guoshan on 2018/3/21. + *

+ * 服务端返回给前端或客户端使用 + */ +public class ResponseEntity { + int status = 200; + + String message = "ok"; + + Object data = new HashMap(); + + + private ResponseEntity() { + } + + public static ResponseEntity ok(Object data) { + ResponseEntity ResponseEntity = new ResponseEntity(); + ResponseEntity.setData(data); + return ResponseEntity; + } + + public static ResponseEntity error(InfoCode infoCode) { + ResponseEntity ResponseEntity = new ResponseEntity(); + ResponseEntity.setStatus(infoCode.getStatus()); + ResponseEntity.setMessage(infoCode.getMsg()); + return ResponseEntity; + } + + public static ResponseEntity ok() { + ResponseEntity ResponseEntity = new ResponseEntity(); + return ResponseEntity; + } + + public static ResponseEntity error(int code, String msg) { + ResponseEntity ResponseEntity = new ResponseEntity(); + ResponseEntity.setStatus(code); + ResponseEntity.setMessage(msg); + return ResponseEntity; + } + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public Object getData() { + return data == null ? new HashMap() : data; + } + + public void setData(Object data) { + this.data = data; + } +} diff --git a/abel-util/src/main/java/cn/abel/utils/DateTimeUtils.java b/abel-util/src/main/java/cn/abel/utils/DateTimeUtils.java new file mode 100644 index 0000000..6ceae80 --- /dev/null +++ b/abel-util/src/main/java/cn/abel/utils/DateTimeUtils.java @@ -0,0 +1,283 @@ +package cn.abel.utils; + +import cn.abel.exception.ServiceException; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.DateFormatUtils; +import org.apache.commons.lang3.time.DateUtils; + +import java.text.ParseException; +import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.Calendar; +import java.util.Date; + +/** + * 日期util。 + */ +public class DateTimeUtils { + + /** + * 一分钟。 + */ + public static final int ONE_MINUTE = 60 * 1000; + + /** + * 一小时。 + */ + public static final int ONE_HOUR = 60 * ONE_MINUTE; + + /** + * 一天。 + */ + public static final int ONE_DAY = 24 * ONE_HOUR; + + + public static final String DEFAULT_DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss"; + public static final String DEFAULT_DATE_PATTERN = "yyyy-MM-dd"; + private static final int ARG_ERROR_CODE = 400; + private static final String ARG_ERROR = "输入的参数有误"; + + + /** + * 根据给定的格式将时间转为字符串。 + * + * @param dateTime + * @param pattern + * @return + */ + public static String format(Date dateTime, String pattern) { + if (dateTime == null || pattern == null) { + return StringUtils.EMPTY; + } + + return DateFormatUtils.format(dateTime, pattern); + } + + /** + * 将时间转为默认格式的字符串。 + * + * @param dateTime + * @return + */ + public static String format(Date dateTime) { + return format(dateTime, DEFAULT_DATETIME_PATTERN); + } + + /** + * 根据给定的格式获取当前日期的字符串。 + * + * @param pattern + * @return + */ + public static String formatCurrent(String pattern) { + return format(new Date(), pattern); + } + + /** + * 获取当前日期的字符串。 + * + * @return + */ + public static String formatCurrent() { + return format(new Date(), DEFAULT_DATE_PATTERN); + } + + /** + * 去除时间中的时分秒毫秒,只保留日期部分。 + * + * @param dateTime + * @return + */ + public static Date removeTime(Date dateTime) { + if (dateTime == null) { + return null; + } + Calendar calendar = Calendar.getInstance(); + calendar.setTime(dateTime); + calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DATE), 0, 0, 0); + calendar.set(Calendar.MILLISECOND, 0); + return calendar.getTime(); + } + + /** + * 获取明天的日期。 + * + * @return + */ + public static Date getTomorrowDate() { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(new Date()); + calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DATE), 0, 0, 0); + calendar.set(Calendar.MILLISECOND, 0); + calendar.add(Calendar.DATE, 1); + return calendar.getTime(); + } + + /** + * 获取昨天的日期。 + * + * @return + */ + public static Date getYesterdayDate() { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(new Date()); + calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DATE), 0, 0, 0); + calendar.set(Calendar.MILLISECOND, 0); + calendar.add(Calendar.DATE, -1); + return calendar.getTime(); + } + + /** + * 根据给定的格式将时间字符串转化成{@link Date}对象。 + * + * @param dateTimeStr + * @param pattern + * @return + * @throws ServiceException + */ + public static Date parse(String dateTimeStr, String pattern) throws ServiceException { + if (dateTimeStr == null || pattern == null) { + return null; + } + try { + return DateUtils.parseDate(dateTimeStr, pattern); + } catch (ParseException e) { + throw new ServiceException(ARG_ERROR_CODE, ARG_ERROR); + } + } + + /** + * 将时间字符串根据默认格式转换成{@link Date}对象。 + * + * @param dateTimeStr + * @return + * @throws ServiceException + */ + public static Date parse(String dateTimeStr) throws ServiceException { + return parse(dateTimeStr, DEFAULT_DATETIME_PATTERN); + } + + /** + * 获取日期中的月份。 + * + * @param date + * @return + */ + public static int getMonth(Date date) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return calendar.get(Calendar.MONTH) + 1; + } + + /** + * 获取日期中的年份。 + * + * @param date + * @return + */ + public static int getYear(Date date) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return calendar.get(Calendar.YEAR); + } + + /** + * 增加日期。 + * + * @param date + * @param days + * @return + * @throws ServiceException + */ + public static Date dateAdd(Date date, int days) throws ServiceException { + if (date == null) { + throw new ServiceException(ARG_ERROR_CODE, ARG_ERROR); + } + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.add(Calendar.DATE, days); + return calendar.getTime(); + } + + /** + * 增加月份。 + * + * @param date + * @param months + * @return + * @throws ServiceException + */ + public static Date monthAdd(Date date, int months) throws ServiceException { + if (date == null) { + throw new ServiceException(ARG_ERROR_CODE, ARG_ERROR); + } + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.add(Calendar.MONTH, months); + return calendar.getTime(); + } + + /** + * 获取两个日期之间的差值。 + * + * @param one + * @param two + * @return + * @throws ServiceException + */ + public static int dateDiff(Date one, Date two) throws ServiceException { + if (one == null || two == null) { + throw new ServiceException(ARG_ERROR_CODE, ARG_ERROR); + } + long diff = Math.abs((one.getTime() - two.getTime()) / (1000 * 3600 * 24)); + return new Long(diff).intValue(); + } + + + /** + * 计算几天前的时间 + * + * @param date 当前时间 + * @param day 几天前 + * @return + */ + public static Date getDateBefore(Date date, int day) { + Calendar now = Calendar.getInstance(); + now.setTime(date); + now.set(Calendar.DATE, now.get(Calendar.DATE) - day); + return now.getTime(); + } + + /** + * 获取当前月第一天 + * + * @return Date + * @throws ParseException + */ + public static Date getFirstAndLastOfMonth() { + LocalDate today = LocalDate.now(); + LocalDate firstDay = LocalDate.of(today.getYear(),today.getMonth(),1); + ZoneId zone = ZoneId.systemDefault(); + Instant instant = firstDay.atStartOfDay().atZone(zone).toInstant(); + return Date.from(instant); + } + + /** + * 获取当前周第一天 + * + * @return Date + * @throws ParseException + */ + public static Date getWeekFirstDate(){ + Calendar cal = Calendar.getInstance(); + cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY); + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + Date date = cal.getTime(); + return date; + } + +} diff --git a/springboot-dubbo/abel-user-api/pom.xml b/springboot-dubbo/abel-user-api/pom.xml new file mode 100644 index 0000000..650cd03 --- /dev/null +++ b/springboot-dubbo/abel-user-api/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + cn.abel + abel-parent + 1.0.0-SNAPSHOT + + + cn.abel + abel-user-api + 1.0.0-SNAPSHOT + jar + + + + com.github.pagehelper + pagehelper-spring-boot-starter + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-databind + + + + + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + + + + + \ No newline at end of file diff --git a/springboot-dubbo/abel-user-api/src/main/java/cn/abel/user/models/Permission.java b/springboot-dubbo/abel-user-api/src/main/java/cn/abel/user/models/Permission.java new file mode 100644 index 0000000..e5059e7 --- /dev/null +++ b/springboot-dubbo/abel-user-api/src/main/java/cn/abel/user/models/Permission.java @@ -0,0 +1,65 @@ +package cn.abel.user.models; + + +import java.io.Serializable; + +public class Permission implements Serializable { + + private static final long serialVersionUID = 2079120477173696231L; + private Integer id; + private String name; + private String permissionUrl; + private String method; + private String description; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPermissionUrl() { + return permissionUrl; + } + + public void setPermissionUrl(String permissionUrl) { + this.permissionUrl = permissionUrl; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @Override + public String toString() { + return "Permission{" + + "id=" + id + + ", name=" + name + + ", permissionUrl=" + permissionUrl + + ", method=" + method + + ", description=" + description + + '}'; + } +} \ No newline at end of file diff --git a/springboot-dubbo/abel-user-api/src/main/java/cn/abel/user/models/Role.java b/springboot-dubbo/abel-user-api/src/main/java/cn/abel/user/models/Role.java new file mode 100644 index 0000000..6a55c5b --- /dev/null +++ b/springboot-dubbo/abel-user-api/src/main/java/cn/abel/user/models/Role.java @@ -0,0 +1,55 @@ +package cn.abel.user.models; + + +import java.io.Serializable; + +public class Role implements Serializable { + + private static final long serialVersionUID = -4873217276773065821L; + private Integer id; + private String name; + private Integer roleLevel; + private String description; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getRoleLevel() { + return roleLevel; + } + + public void setRoleLevel(Integer roleLevel) { + this.roleLevel = roleLevel; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @Override + public String toString() { + return "Role{" + + "id=" + id + + ", name=" + name + + ", roleLevel=" + roleLevel + + ", description=" + description + + '}'; + } +} \ No newline at end of file diff --git a/springboot-dubbo/abel-user-api/src/main/java/cn/abel/user/models/User.java b/springboot-dubbo/abel-user-api/src/main/java/cn/abel/user/models/User.java new file mode 100644 index 0000000..07f8ab6 --- /dev/null +++ b/springboot-dubbo/abel-user-api/src/main/java/cn/abel/user/models/User.java @@ -0,0 +1,88 @@ +package cn.abel.user.models; + + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import java.io.Serializable; +import java.util.List; + +public class User implements Serializable { + + private static final long serialVersionUID = -4523695542426439365L; + private Integer id; + private String cnname; + private String username; + + @JsonIgnore + private String password; + private String email; + private String mobilePhone; + + + private List roles; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getCnname() { + return cnname; + } + + public void setCnname(String cnname) { + this.cnname = cnname; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + + public String getMobilePhone() { + return mobilePhone; + } + + public void setMobilePhone(String mobilePhone) { + this.mobilePhone = mobilePhone; + } + + public List getRoles() { + return roles; + } + + public void setRoles(List roles) { + this.roles = roles; + } + + @Override + public String toString() { + return "User{" + + "id=" + id + + ", cnname=" + cnname + + ", username=" + username + + ", password=" + password + + ", email=" + email + + ", mobilePhone=" + mobilePhone + + '}'; + } +} \ No newline at end of file diff --git a/springboot-dubbo/abel-user-api/src/main/java/cn/abel/user/service/PermissionService.java b/springboot-dubbo/abel-user-api/src/main/java/cn/abel/user/service/PermissionService.java new file mode 100644 index 0000000..5b03f89 --- /dev/null +++ b/springboot-dubbo/abel-user-api/src/main/java/cn/abel/user/service/PermissionService.java @@ -0,0 +1,21 @@ +package cn.abel.user.service; + +import cn.abel.user.models.Permission; + +import java.util.List; +import java.util.Map; + +public interface PermissionService { + + List getByMap(Map map); + + Permission getById(Integer id); + + Permission create(Permission permission); + + + Permission update(Permission permission); + + int delete(Integer id); + +} \ No newline at end of file diff --git a/springboot-dubbo/abel-user-api/src/main/java/cn/abel/user/service/RoleService.java b/springboot-dubbo/abel-user-api/src/main/java/cn/abel/user/service/RoleService.java new file mode 100644 index 0000000..7c3cc0d --- /dev/null +++ b/springboot-dubbo/abel-user-api/src/main/java/cn/abel/user/service/RoleService.java @@ -0,0 +1,22 @@ +package cn.abel.user.service; + + +import cn.abel.user.models.Role; + +import java.util.List; +import java.util.Map; + +public interface RoleService { + + + List getByMap(Map map); + + Role getById(Integer id); + + Role create(Role role); + + Role update(Role role); + + int delete(Integer id); + +} \ No newline at end of file diff --git a/springboot-dubbo/abel-user-api/src/main/java/cn/abel/user/service/UserService.java b/springboot-dubbo/abel-user-api/src/main/java/cn/abel/user/service/UserService.java new file mode 100644 index 0000000..c19eed5 --- /dev/null +++ b/springboot-dubbo/abel-user-api/src/main/java/cn/abel/user/service/UserService.java @@ -0,0 +1,21 @@ +package cn.abel.user.service; + +import cn.abel.user.models.User; + +import java.util.List; +import java.util.Map; + +public interface UserService { + + List getByMap(Map map); + + User getById(Integer id); + + User create(User user); + + User update(User user); + + int delete(Integer id); + + User getByUserName(String userName); +} \ No newline at end of file diff --git a/springboot-dubbo/abel-user-provider/doc/user.sql b/springboot-dubbo/abel-user-provider/doc/user.sql new file mode 100644 index 0000000..107c5ca --- /dev/null +++ b/springboot-dubbo/abel-user-provider/doc/user.sql @@ -0,0 +1,83 @@ +/* + Navicat Premium Data Transfer + + Source Server : localhost + Source Server Type : MySQL + Source Server Version : 50719 + Source Host : 127.0.0.1:3306 + Source Schema : shiro + + Target Server Type : MySQL + Target Server Version : 50719 + File Encoding : 65001 + + Date: 20/03/2020 10:51:00 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for permission +-- ---------------------------- +DROP TABLE IF EXISTS `permission`; +CREATE TABLE `permission` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(100) COLLATE utf8_bin NOT NULL, + `permission_url` varchar(100) COLLATE utf8_bin DEFAULT NULL, + `method` varchar(100) COLLATE utf8_bin DEFAULT NULL, + `description` varchar(1000) COLLATE utf8_bin DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_bin; + +-- ---------------------------- +-- Table structure for role +-- ---------------------------- +DROP TABLE IF EXISTS `role`; +CREATE TABLE `role` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(100) COLLATE utf8_bin NOT NULL, + `role_level` int(1) DEFAULT NULL, + `description` varchar(1000) COLLATE utf8_bin DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_bin; + +-- ---------------------------- +-- Table structure for role_permission +-- ---------------------------- +DROP TABLE IF EXISTS `role_permission`; +CREATE TABLE `role_permission` ( + `role_id` int(11) NOT NULL, + `permission_id` int(11) NOT NULL, + PRIMARY KEY (`role_id`,`permission_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; + +-- ---------------------------- +-- Table structure for user +-- ---------------------------- +DROP TABLE IF EXISTS `user`; +CREATE TABLE `user` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `cnname` varchar(50) COLLATE utf8_bin DEFAULT NULL, + `username` varchar(50) COLLATE utf8_bin NOT NULL, + `password` varchar(100) COLLATE utf8_bin NOT NULL, + `email` varchar(100) COLLATE utf8_bin DEFAULT NULL, + `mobile_phone` varchar(50) COLLATE utf8_bin DEFAULT NULL, + `status` int(11) DEFAULT '4' COMMENT '用户登录状态,1 : 在线 2 离开 3 忙碌 4 离线', + PRIMARY KEY (`id`), + UNIQUE KEY `username` (`username`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_bin; + +-- ---------------------------- +-- Table structure for user_role +-- ---------------------------- +DROP TABLE IF EXISTS `user_role`; +CREATE TABLE `user_role` ( + `user_id` int(11) NOT NULL, + `role_id` int(11) NOT NULL, + PRIMARY KEY (`user_id`,`role_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/springboot-dubbo/abel-user-provider/pom.xml b/springboot-dubbo/abel-user-provider/pom.xml new file mode 100644 index 0000000..8c87478 --- /dev/null +++ b/springboot-dubbo/abel-user-provider/pom.xml @@ -0,0 +1,177 @@ + + + 4.0.0 + + cn.abel + abel-parent + 1.0.0-SNAPSHOT + + + cn.abel + abel-user-provider + 1.0.0 + jar + + + + + cn.abel + abel-user-api + 1.0.0-SNAPSHOT + + + cn.abel + abel-util + 1.0.0-SNAPSHOT + + + org.apache.commons + commons-pool2 + + + + com.alibaba + fastjson + 1.2.58 + + + + + org.apache.commons + commons-collections4 + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-redis + + + org.springframework.boot + spring-boot-starter-test + test + + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-databind + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + + + com.github.pagehelper + pagehelper-spring-boot-starter + + + mysql + mysql-connector-java + + + + + com.alibaba + dubbo + + + org.slf4j + log4j-over-slf4j + + + org.apache.zookeeper + zookeeper + + + org.jboss.resteasy + resteasy-jaxrs + + + org.jboss.resteasy + resteasy-client + + + org.jboss.resteasy + resteasy-jackson-provider + + + javax.validation + validation-api + + + org.hibernate.validator + hibernate-validator + + + com.101tec + zkclient + + + org.apache.httpcomponents + httpcore + + + org.apache.httpcomponents + httpcore-nio + + + org.apache.httpcomponents + httpclient + + + org.apache.httpcomponents + httpasyncclient + + + it.uniroma1.dis.wsngroup.gexf4j + gexf4j + 1.0.0 + + + + io.sentry + sentry-logback + 1.7.3 + + + + com.fasterxml.jackson.core + jackson-databind + + + + org.apache.curator + curator-framework + 2.5.0 + + + org.javassist + javassist + 3.15.0-GA + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + + + + \ No newline at end of file diff --git a/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/UserProviderApplication.java b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/UserProviderApplication.java new file mode 100644 index 0000000..3a148b4 --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/UserProviderApplication.java @@ -0,0 +1,14 @@ +package cn.abel.user; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ImportResource; + +@SpringBootApplication +@ImportResource("classpath*:META-INF/spring/*.xml") +public class UserProviderApplication { + + public static void main(String[] args) { + SpringApplication.run(UserProviderApplication.class, args); + } +} diff --git a/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/constants/Constants.java b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/constants/Constants.java new file mode 100644 index 0000000..a614bc1 --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/constants/Constants.java @@ -0,0 +1,7 @@ +package cn.abel.user.constants; + +/** + * 公用的常量类。 + */ +public interface Constants { +} diff --git a/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/dao/PermissionDao.java b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/dao/PermissionDao.java new file mode 100644 index 0000000..4542b7d --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/dao/PermissionDao.java @@ -0,0 +1,29 @@ +package cn.abel.user.dao; + + +import cn.abel.user.models.Permission; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Map; + +@Repository +@Mapper +public interface PermissionDao { + + List getByMap(Map map); + + Permission getById(Integer id); + + Integer create(Permission permission); + + int update(Permission permission); + + int delete(Integer id); + + List getList(); + + List getByUserId(Integer userId); + +} \ No newline at end of file diff --git a/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/dao/RoleDao.java b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/dao/RoleDao.java new file mode 100644 index 0000000..20f3c92 --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/dao/RoleDao.java @@ -0,0 +1,20 @@ +package cn.abel.user.dao; + + + +import cn.abel.user.models.Role; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Map; +@Repository +@Mapper +public interface RoleDao { + + List getByMap(Map map); + Role getById(Integer id); + Integer create(Role role); + int update(Role role); + int delete(Integer id); +} \ No newline at end of file diff --git a/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/dao/UserDao.java b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/dao/UserDao.java new file mode 100644 index 0000000..9303a55 --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/dao/UserDao.java @@ -0,0 +1,20 @@ +package cn.abel.user.dao; + + +import cn.abel.user.models.User; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Map; +@Repository +@Mapper +public interface UserDao { + + List getByMap(Map map); + User getById(Integer id); + Integer create(User user); + int update(User user); + int delete(Integer id); + User getByUserName(String userName); +} \ No newline at end of file diff --git a/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/exception/JsonExceptionMapper.java b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/exception/JsonExceptionMapper.java new file mode 100644 index 0000000..93b8ef0 --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/exception/JsonExceptionMapper.java @@ -0,0 +1,31 @@ +package cn.abel.user.exception; + +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; + +import cn.abel.code.InfoCode; +import cn.abel.response.ResponseEntity; +import org.codehaus.jackson.JsonProcessingException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * json转换错误 + * + * @author ye + * @date 2018/11/02 14:20 + */ +public class JsonExceptionMapper implements ExceptionMapper { + + /** logger */ + private static final Logger logger = LoggerFactory.getLogger(JsonExceptionMapper.class); + + @Override + public Response toResponse(JsonProcessingException exception) { + ResponseEntity entity = ResponseEntity.error(InfoCode.REQUEST_PARAM_ERROR); + logger.info("{}", InfoCode.REQUEST_PARAM_ERROR.getMsg()); + return Response.ok(entity, MediaType.APPLICATION_JSON).build(); + } + +} diff --git a/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/exception/ReaderExceptionMapper.java b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/exception/ReaderExceptionMapper.java new file mode 100644 index 0000000..1b20cc8 --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/exception/ReaderExceptionMapper.java @@ -0,0 +1,31 @@ +package cn.abel.user.exception; + +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; + +import cn.abel.code.InfoCode; +import cn.abel.response.ResponseEntity; +import org.jboss.resteasy.spi.ReaderException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * json转换错误 + * + * @author ye + * @date 2018/11/02 14:20 + */ +public class ReaderExceptionMapper implements ExceptionMapper { + + /** logger */ + private static final Logger logger = LoggerFactory.getLogger(ReaderExceptionMapper.class); + + @Override + public Response toResponse(ReaderException exception) { + ResponseEntity entity = ResponseEntity.error(InfoCode.REQUEST_PARAM_ERROR); + logger.info("{}", InfoCode.REQUEST_PARAM_ERROR.getMsg()); + return Response.ok(entity, MediaType.APPLICATION_JSON).build(); + } + +} diff --git a/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/exception/RestExceptionMapper.java b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/exception/RestExceptionMapper.java new file mode 100644 index 0000000..311e3bb --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/exception/RestExceptionMapper.java @@ -0,0 +1,45 @@ +package cn.abel.user.exception; + +import javax.ws.rs.NotAllowedException; +import javax.ws.rs.NotFoundException; +import javax.ws.rs.NotSupportedException; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * http调用错误 + * + * @author ye + * @date 2018/11/02 14:20 + */ +public class RestExceptionMapper implements ExceptionMapper { + + /** logger */ + private static final Logger logger = LoggerFactory.getLogger(WebApplicationException.class); + + @Override + public Response toResponse(WebApplicationException e) { + if (e instanceof NotAllowedException) { + logger.info("method错误。{}", e.getMessage()); + return null; + } + + //415 + if (e instanceof NotSupportedException) { + logger.info("content-type错误。{}", e.getMessage()); + return null; + } + + if (e instanceof NotFoundException) { + logger.info("url错误。{}", e.getMessage()); + return null; + } + + return null; + } + +} diff --git a/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/exception/ServiceExceptionMapper.java b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/exception/ServiceExceptionMapper.java new file mode 100644 index 0000000..6f76d12 --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/exception/ServiceExceptionMapper.java @@ -0,0 +1,29 @@ +package cn.abel.user.exception; + +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; + +import cn.abel.exception.ServiceException; +import cn.abel.response.ResponseEntity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * ServiceExceptionMapper + * + * @author ye + * @date 2018/11/02 14:20 + */ +public class ServiceExceptionMapper implements ExceptionMapper { + + /** logger */ + private static final Logger logger = LoggerFactory.getLogger(ServiceException.class); + + @Override + public Response toResponse(ServiceException exception) { + ResponseEntity entity = ResponseEntity.error(exception.getErrorCode(), exception.getMessage()); + logger.error("{},{}", exception.getErrorCode(), exception.getMessage()); + return Response.ok(entity, MediaType.APPLICATION_JSON).build(); + } +} diff --git a/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/exception/ValidationExceptionMapper.java b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/exception/ValidationExceptionMapper.java new file mode 100644 index 0000000..a464d1b --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/exception/ValidationExceptionMapper.java @@ -0,0 +1,36 @@ +package cn.abel.user.exception; + +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.ws.rs.core.Response; + +import cn.abel.code.InfoCode; +import cn.abel.response.ResponseEntity; +import com.alibaba.dubbo.rpc.protocol.rest.RpcExceptionMapper; +import com.alibaba.dubbo.rpc.protocol.rest.support.ContentType; + +/** + * 参数校验异常 + * + * @author ye + * @date 2018/11/04 16:53 + */ +public class ValidationExceptionMapper extends RpcExceptionMapper { + + @Override + protected Response handleConstraintViolationException(ConstraintViolationException cve) { + //ViolationReport report = new ViolationReport(); + String msg = ""; + for (ConstraintViolation cv : cve.getConstraintViolations()) { + //report.addConstraintViolation(new RestConstraintViolation(cv.getPropertyPath().toString(), + // cv.getMessage(), cv.getInvalidValue() == null ? "null" : cv.getInvalidValue().toString())); + msg = cv.getPropertyPath().toString() + " " + cv.getMessage(); + } + + ResponseEntity entity = ResponseEntity.error(InfoCode.REQUEST_PARAM_ERROR.getStatus(), + InfoCode.REQUEST_PARAM_ERROR.getMsg() + "(" + msg + ")"); + + // 采用json输出代替xml输出 + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(entity).type(ContentType.APPLICATION_JSON_UTF_8).build(); + } +} diff --git a/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/filter/RestFilter.java b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/filter/RestFilter.java new file mode 100644 index 0000000..617b7c9 --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/filter/RestFilter.java @@ -0,0 +1,71 @@ +package cn.abel.user.filter; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import javax.annotation.Priority; +import javax.ws.rs.client.ClientRequestContext; +import javax.ws.rs.client.ClientRequestFilter; +import javax.ws.rs.client.ClientResponseContext; +import javax.ws.rs.client.ClientResponseFilter; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.ContainerResponseFilter; +import javax.ws.rs.container.PreMatching; +import javax.ws.rs.core.MultivaluedMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 日志-复制并替代com.alibaba.dubbo.rpc.protocol.rest.support.LoggingFilter + * + * @author ye + * @date 2018/11/02 15:20 + */ +@Priority(900) +@PreMatching +public class RestFilter implements ContainerRequestFilter, ClientRequestFilter, ContainerResponseFilter, + ClientResponseFilter { + + /** logger */ + private static final Logger logger = LoggerFactory.getLogger(RestFilter.class); + + @Override + public void filter(ClientRequestContext context) throws IOException { + logHttpHeaders(context.getStringHeaders()); + } + + @Override + public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException { + logHttpHeaders(responseContext.getHeaders()); + } + + @Override + public void filter(ContainerRequestContext context) throws IOException { + logHttpHeaders(context.getHeaders()); + } + + @Override + public void filter(ContainerRequestContext requestContext, + ContainerResponseContext responseContext) throws IOException { + logHttpHeaders(responseContext.getStringHeaders()); + } + + protected void logHttpHeaders(MultivaluedMap headers) { + StringBuilder msg = new StringBuilder("The HTTP headers are: \n"); + for (Map.Entry> entry : headers.entrySet()) { + msg.append(entry.getKey()).append(": "); + for (int i = 0; i < entry.getValue().size(); i++) { + msg.append(entry.getValue().get(i)); + if (i < entry.getValue().size() - 1) { + msg.append(", "); + } + } + msg.append("\n"); + } + logger.info(msg.toString()); + } +} diff --git a/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/filter/RestInterceptor.java b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/filter/RestInterceptor.java new file mode 100644 index 0000000..75170c2 --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/filter/RestInterceptor.java @@ -0,0 +1,88 @@ +package cn.abel.user.filter; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.ext.ReaderInterceptor; +import javax.ws.rs.ext.ReaderInterceptorContext; +import javax.ws.rs.ext.WriterInterceptor; +import javax.ws.rs.ext.WriterInterceptorContext; + +import org.apache.commons.io.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 日志-复制并替代com.alibaba.dubbo.rpc.protocol.rest.support.LoggingFilter + * + * @author ye + * @date 2018/11/04 16:34 + */ +public class RestInterceptor implements WriterInterceptor, ReaderInterceptor { + + /** logger */ + private static final Logger logger = LoggerFactory.getLogger(RestInterceptor.class); + + @Override + public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException { + byte[] buffer = IOUtils.toByteArray(context.getInputStream()); + logger.info("The contents of request body is: \n" + new String(buffer, "UTF-8") + "\n"); + context.setInputStream(new ByteArrayInputStream(buffer)); + return context.proceed(); + } + + @Override + public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException { + RestInterceptor.OutputStreamWrapper wrapper = + new RestInterceptor.OutputStreamWrapper(context.getOutputStream()); + context.setOutputStream(wrapper); + context.proceed(); + logger.info("The contents of response body is: \n" + new String(wrapper.getBytes(), "UTF-8") + "\n"); + } + + protected static class OutputStreamWrapper extends OutputStream { + + private final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + private final OutputStream output; + + private OutputStreamWrapper(OutputStream output) { + this.output = output; + } + + @Override + public void write(int i) throws IOException { + buffer.write(i); + output.write(i); + } + + @Override + public void write(byte[] b) throws IOException { + buffer.write(b); + output.write(b); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + buffer.write(b, off, len); + output.write(b, off, len); + } + + @Override + public void flush() throws IOException { + output.flush(); + } + + @Override + public void close() throws IOException { + output.close(); + } + + public byte[] getBytes() { + return buffer.toByteArray(); + } + } + +} diff --git a/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/service/impl/PermissionServiceImpl.java b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/service/impl/PermissionServiceImpl.java new file mode 100644 index 0000000..69531f9 --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/service/impl/PermissionServiceImpl.java @@ -0,0 +1,44 @@ +package cn.abel.user.service.impl; + +import cn.abel.user.dao.PermissionDao; +import cn.abel.user.models.Permission; +import cn.abel.user.service.PermissionService; +import com.alibaba.dubbo.config.annotation.Service; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; +import java.util.Map; + +@Service(protocol = {"dubbo"}, validation = "false") +public class PermissionServiceImpl implements PermissionService { + @Autowired + private PermissionDao permissionDao; + + @Override + public List getByMap(Map map) { + return permissionDao.getByMap(map); + } + + @Override + public Permission getById(Integer id) { + return permissionDao.getById(id); + } + + @Override + public Permission create(Permission permission) { + permissionDao.create(permission); + return permission; + } + + @Override + public Permission update(Permission permission) { + permissionDao.update(permission); + return permission; + } + + @Override + public int delete(Integer id) { + return permissionDao.delete(id); + } + +} \ No newline at end of file diff --git a/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/service/impl/RoleServiceImpl.java b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/service/impl/RoleServiceImpl.java new file mode 100644 index 0000000..d24eec1 --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/service/impl/RoleServiceImpl.java @@ -0,0 +1,40 @@ +package cn.abel.user.service.impl; + + +import cn.abel.user.dao.RoleDao; +import cn.abel.user.models.Role; +import cn.abel.user.service.RoleService; +import com.alibaba.dubbo.config.annotation.Service; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; +import java.util.Map; + +@Service(protocol = {"dubbo"}, validation = "false") +public class RoleServiceImpl implements RoleService{ + @Autowired + private RoleDao roleDao; + @Override + public List getByMap(Map map) { + return roleDao.getByMap(map); + } + @Override + public Role getById(Integer id) { + return roleDao.getById(id); + } + @Override + public Role create(Role role) { + roleDao.create(role); + return role; + } + @Override + public Role update(Role role) { + roleDao.update(role); + return role; + } + @Override + public int delete(Integer id) { + return roleDao.delete(id); + } + +} \ No newline at end of file diff --git a/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/service/impl/UserServiceImpl.java b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..3d2ab10 --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/service/impl/UserServiceImpl.java @@ -0,0 +1,49 @@ +package cn.abel.user.service.impl; + +import cn.abel.user.dao.UserDao; +import cn.abel.user.models.User; +import cn.abel.user.service.UserService; +import com.alibaba.dubbo.config.annotation.Service; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; +import java.util.Map; + +@Service(protocol = {"dubbo"}, validation = "false") +public class UserServiceImpl implements UserService { + @Autowired + private UserDao userDao; + + @Override + public List getByMap(Map map) { + return userDao.getByMap(map); + } + + @Override + public User getById(Integer id) { + return userDao.getById(id); + } + + @Override + public User create(User user) { + userDao.create(user); + return user; + } + + @Override + public User update(User user) { + userDao.update(user); + return user; + } + + @Override + public int delete(Integer id) { + return userDao.delete(id); + } + + @Override + public User getByUserName(String userName) { + return userDao.getByUserName(userName); + } + +} \ No newline at end of file diff --git a/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/utils/CommentUtils.java b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/utils/CommentUtils.java new file mode 100644 index 0000000..6ffa68a --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/java/cn/abel/user/utils/CommentUtils.java @@ -0,0 +1,28 @@ +package cn.abel.user.utils; + +import com.alibaba.fastjson.JSON; +import org.apache.commons.collections4.CollectionUtils; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author yyb + * @time 2020/3/10 + */ +public class CommentUtils { + + /** + * + * @param list + * @param + * @return + */ + public static List copyList(List list) { + if (CollectionUtils.isEmpty(list)) { + return new ArrayList(); + } + return JSON.parseArray(JSON.toJSONString(list), list.get(0).getClass()); + } + +} diff --git a/springboot-dubbo/abel-user-provider/src/main/resources/META-INF/spring/provider.xml b/springboot-dubbo/abel-user-provider/src/main/resources/META-INF/spring/provider.xml new file mode 100644 index 0000000..1c090b5 --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/resources/META-INF/spring/provider.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/springboot-dubbo/abel-user-provider/src/main/resources/dev/application.properties b/springboot-dubbo/abel-user-provider/src/main/resources/dev/application.properties new file mode 100644 index 0000000..f2da32a --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/resources/dev/application.properties @@ -0,0 +1,53 @@ +#application +spring.application.name=abel-user-provider +application.main=cn.abel.user.UserProviderApplication +server.port=9658 +server.tomcat.uri-encoding=UTF-8 +spring.http.encoding.charset=UTF-8 +spring.http.encoding.enabled=true +spring.http.encoding.force=true +spring.messages.encoding=UTF-8 +server.tomcat.max-threads=200 +server.session-timeout=60 +log4j.logger.java.sql.Connection=DEBUG + + +#dubbo +dubbo.application.name=abel-user-provider +dubbo.registry.group=abel +dubbo.registry.address=127.0.0.1:2181 +dubbo.registry.version=1.0.0 +dubbo.protocol.port=12885 +dubbo.protocol.rest.port=12886 +dubbo.annotation.package=cn.abel.user +dubbo.log.file=/logs/abel-user-provider/abel-user-provider-dubbo.log + +#db +spring.datasource.url=jdbc:mysql://127.0.0.1:3306/shiro?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&useSSL=false +spring.datasource.username=root +spring.datasource.password=admin +spring.datasource.driverClassName=com.mysql.jdbc.Driver + +#DataSource +spring.datasource.type=com.zaxxer.hikari.HikariDataSource +spring.datasource.hikari.minimum-idle=10 +spring.datasource.hikari.maximum-pool-size=30 +spring.datasource.hikari.idle-timeout=120000 +spring.datasource.hikari.max-lifetime=1800000 +spring.datasource.hikari.connection-timeout=30000 +spring.datasource.hikari.validation-timeout=3000 +spring.datasource.hikari.connection-test-query=SELECT 1 + + +#mybatis +logging.level.cn.abel.user.dao=info +mybatis.type-aliases-package=cn.abel.user.model +mybatis.mapper-locations=classpath*:mapper/*Mapper.xml + +#PageHelper +pagehelper.helperDialect=mysql +pagehelper.reasonable=true +pagehelper.supportMethodsArguments=true +pagehelper.params=count=countSql + +logging.file=/logs/abel-user-provider/abel-user-provider.log diff --git a/springboot-dubbo/abel-user-provider/src/main/resources/dev/banner.txt b/springboot-dubbo/abel-user-provider/src/main/resources/dev/banner.txt new file mode 100644 index 0000000..07d3606 --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/resources/dev/banner.txt @@ -0,0 +1,5 @@ +######################################################## +# # +# dev # +# # +######################################################## \ No newline at end of file diff --git a/springboot-dubbo/abel-user-provider/src/main/resources/dev/logback-spring.xml b/springboot-dubbo/abel-user-provider/src/main/resources/dev/logback-spring.xml new file mode 100644 index 0000000..4b3099c --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/resources/dev/logback-spring.xml @@ -0,0 +1,40 @@ + + + middle_metadata + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%method - %msg%n + + + + + + ${LOG_FILE} + + + ${LOG_FILE}.%d{yyyy-MM-dd} + + 60 + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%method - %msg%n + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/springboot-dubbo/abel-user-provider/src/main/resources/local/application.properties b/springboot-dubbo/abel-user-provider/src/main/resources/local/application.properties new file mode 100644 index 0000000..9c996b1 --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/resources/local/application.properties @@ -0,0 +1,53 @@ +#application +spring.application.name=abel-user-provider +application.main=cn.abel.user.UserProviderApplication +server.port=9658 +server.tomcat.uri-encoding=UTF-8 +spring.http.encoding.charset=UTF-8 +spring.http.encoding.enabled=true +spring.http.encoding.force=true +spring.messages.encoding=UTF-8 +server.tomcat.max-threads=200 +server.session-timeout=60 +log4j.logger.java.sql.Connection=DEBUG + + +#dubbo +dubbo.application.name=abel-user-provider +dubbo.registry.group=abel +dubbo.registry.address=127.0.0.1:2181 +dubbo.registry.version=1.0.0 +dubbo.protocol.port=12885 +dubbo.protocol.rest.port=12886 +dubbo.annotation.package=cn.abel.user +dubbo.log.file=./logs/abel-user-provider/abel-user-provider-dubbo.log + +#db +spring.datasource.url=jdbc:mysql://127.0.0.1:3306/shiro?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&useSSL=false +spring.datasource.username=root +spring.datasource.password=admin +spring.datasource.driverClassName=com.mysql.jdbc.Driver + +#DataSource +spring.datasource.type=com.zaxxer.hikari.HikariDataSource +spring.datasource.hikari.minimum-idle=10 +spring.datasource.hikari.maximum-pool-size=30 +spring.datasource.hikari.idle-timeout=120000 +spring.datasource.hikari.max-lifetime=1800000 +spring.datasource.hikari.connection-timeout=30000 +spring.datasource.hikari.validation-timeout=3000 +spring.datasource.hikari.connection-test-query=SELECT 1 + + +#mybatis +logging.level.cn.abel.user.dao=info +mybatis.type-aliases-package=cn.abel.user.model +mybatis.mapper-locations=classpath*:mapper/*Mapper.xml + +#PageHelper +pagehelper.helperDialect=mysql +pagehelper.reasonable=true +pagehelper.supportMethodsArguments=true +pagehelper.params=count=countSql + +logging.file=./logs/abel-user-provider/abel-user-provider.log diff --git a/springboot-dubbo/abel-user-provider/src/main/resources/local/banner.txt b/springboot-dubbo/abel-user-provider/src/main/resources/local/banner.txt new file mode 100644 index 0000000..42f7c59 --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/resources/local/banner.txt @@ -0,0 +1,5 @@ +######################################################## +# # +# local # +# # +######################################################## \ No newline at end of file diff --git a/springboot-dubbo/abel-user-provider/src/main/resources/local/logback-spring.xml b/springboot-dubbo/abel-user-provider/src/main/resources/local/logback-spring.xml new file mode 100644 index 0000000..854e3ae --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/resources/local/logback-spring.xml @@ -0,0 +1,41 @@ + + + middle_metadata + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%method - %msg%n + + + + + + ${LOG_FILE} + + + ${LOG_FILE}.%d{yyyy-MM-dd} + + 60 + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%method - %msg%n + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/springboot-dubbo/abel-user-provider/src/main/resources/mapper/PermissionDaoMapper.xml b/springboot-dubbo/abel-user-provider/src/main/resources/mapper/PermissionDaoMapper.xml new file mode 100644 index 0000000..1f0c2b5 --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/resources/mapper/PermissionDaoMapper.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + and id = #{id} + + + and name = #{name} + + + and permission_url = #{permissionUrl} + + + and method = #{method} + + + and description = #{description} + + + + + + + + + + + + + + + + SELECT LAST_INSERT_ID() + + INSERT INTO permission( + id, + name, + permission_url, + method, + description + )VALUES( + #{id}, + #{name}, + #{permissionUrl}, + #{method}, + #{description} + ) + + + + UPDATE permission SET + name = #{name}, + permission_url = #{permissionUrl}, + method = #{method}, + description = #{description} + WHERE id = #{id} + + + + DELETE FROM permission WHERE id = #{id} + + + \ No newline at end of file diff --git a/springboot-dubbo/abel-user-provider/src/main/resources/mapper/RoleDaoMapper.xml b/springboot-dubbo/abel-user-provider/src/main/resources/mapper/RoleDaoMapper.xml new file mode 100644 index 0000000..34242c2 --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/resources/mapper/RoleDaoMapper.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + and id = #{id} + + + and name = #{name} + + + and role_level = #{roleLevel} + + + and description = #{description} + + + + + + + + + + + + SELECT LAST_INSERT_ID() + + INSERT INTO role( + id, + name, + role_level, + description + )VALUES( + #{id}, + #{name}, + #{roleLevel}, + #{description} + ) + + + + UPDATE role SET + name = #{name}, + role_level = #{roleLevel}, + description = #{description} + WHERE id = #{id} + + + + DELETE FROM role WHERE id = #{id} + + + \ No newline at end of file diff --git a/springboot-dubbo/abel-user-provider/src/main/resources/mapper/UserDaoMapper.xml b/springboot-dubbo/abel-user-provider/src/main/resources/mapper/UserDaoMapper.xml new file mode 100644 index 0000000..d7cb24c --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/resources/mapper/UserDaoMapper.xml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + and id = #{id} + + + and cnname = #{cnname} + + + and username = #{username} + + + and password = #{password} + + + and email = #{email} + + + and mobile_phone = #{mobilePhone} + + + and ( + cnname like CONCAT('%', #{keywords},'%') + OR username like CONCAT('%', #{keywords},'%') + OR mobile_phone like CONCAT('%', #{keywords},'%') + OR email like CONCAT('%', #{keywords},'%') + ) + + + + + + + + + + + + + SELECT LAST_INSERT_ID() + + INSERT INTO user( + id, + cnname, + username, + password, + email, + mobile_phone + )VALUES( + #{id}, + #{cnname}, + #{username}, + #{password}, + #{email}, + #{mobilePhone} + ) + + + + UPDATE user SET + cnname = #{cnname}, + username = #{username}, + password = #{password}, + email = #{email}, + mobile_phone = #{mobilePhone} + WHERE id = #{id} + + + + DELETE FROM user WHERE id = #{id} + + + \ No newline at end of file diff --git a/springboot-dubbo/abel-user-provider/src/main/test/cn/abel/user/BaseTest.java b/springboot-dubbo/abel-user-provider/src/main/test/cn/abel/user/BaseTest.java new file mode 100644 index 0000000..27a7973 --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/test/cn/abel/user/BaseTest.java @@ -0,0 +1,33 @@ +package cn.abel.user; + +/** + * @author yyb + * @time 2020/3/9 + */ + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = UserProviderApplication.class) +@TestPropertySource("/application.properties") +public class BaseTest { + + @Configuration + @ComponentScan(basePackages = {"cn.abel.user"}, excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Configuration.class)) + static class ComponentScanConfig { + + } + + + @Test + public void contextLoads() { + } + +} \ No newline at end of file diff --git a/springboot-dubbo/abel-user-provider/src/main/test/cn/abel/user/service/impl/PermissionServiceImplTest.java b/springboot-dubbo/abel-user-provider/src/main/test/cn/abel/user/service/impl/PermissionServiceImplTest.java new file mode 100644 index 0000000..5513a6c --- /dev/null +++ b/springboot-dubbo/abel-user-provider/src/main/test/cn/abel/user/service/impl/PermissionServiceImplTest.java @@ -0,0 +1,55 @@ +package cn.abel.user.service.impl; + +import cn.abel.user.BaseTest; +import cn.abel.user.models.Permission; +import cn.abel.user.service.PermissionService; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import static org.junit.Assert.*; + +/** + * @author yyb + * @time 2020/3/20 + */ +public class PermissionServiceImplTest extends BaseTest { + @Autowired + private PermissionService permissionService; + @Test + public void getByMap() throws Exception { + permissionService.getByMap(null); + } + + @Test + public void getById() throws Exception { + Permission permission = permissionService.getById(1); + System.out.println(); + } + + @Test + public void create() throws Exception { + Permission permission = new Permission(); + permission.setDescription("234234"); + permission.setName("新增"); + permission.setMethod("nicai"); + permission.setPermissionUrl("zsdfsf_sf"); + permissionService.create(permission); + } + + @Test + public void update() throws Exception { + Permission permission = new Permission(); + permission.setId(1); + permission.setDescription("234234"); + permission.setName("新增"); + permission.setMethod("nicai"); + permission.setPermissionUrl("zsdfsf_sf"); + permissionService.update(permission); + } + + @Test + public void delete() throws Exception { + permissionService.delete(1); + } + +} \ No newline at end of file From cd637c502a928f0faa319e5e4b3f9f371894a09a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Fri, 20 Mar 2020 11:25:07 +0800 Subject: [PATCH 34/44] modify name --- .../src/main/resources/META-INF/spring/provider.xml | 2 +- .../src/main/resources/dev/logback-spring.xml | 2 +- .../src/main/resources/local/logback-spring.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/springboot-dubbo/abel-user-provider/src/main/resources/META-INF/spring/provider.xml b/springboot-dubbo/abel-user-provider/src/main/resources/META-INF/spring/provider.xml index 1c090b5..c65bacf 100644 --- a/springboot-dubbo/abel-user-provider/src/main/resources/META-INF/spring/provider.xml +++ b/springboot-dubbo/abel-user-provider/src/main/resources/META-INF/spring/provider.xml @@ -3,7 +3,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"> - + diff --git a/springboot-dubbo/abel-user-provider/src/main/resources/dev/logback-spring.xml b/springboot-dubbo/abel-user-provider/src/main/resources/dev/logback-spring.xml index 4b3099c..78f9a29 100644 --- a/springboot-dubbo/abel-user-provider/src/main/resources/dev/logback-spring.xml +++ b/springboot-dubbo/abel-user-provider/src/main/resources/dev/logback-spring.xml @@ -1,6 +1,6 @@ - middle_metadata + abel_user diff --git a/springboot-dubbo/abel-user-provider/src/main/resources/local/logback-spring.xml b/springboot-dubbo/abel-user-provider/src/main/resources/local/logback-spring.xml index 854e3ae..29daaef 100644 --- a/springboot-dubbo/abel-user-provider/src/main/resources/local/logback-spring.xml +++ b/springboot-dubbo/abel-user-provider/src/main/resources/local/logback-spring.xml @@ -1,6 +1,6 @@ - middle_metadata + abel_user From 13236134f2cecdad90a242552601587687cd8423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Fri, 20 Mar 2020 11:26:08 +0800 Subject: [PATCH 35/44] modify name --- springboot-mybatis2/src/main/resources/local/logback-spring.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/springboot-mybatis2/src/main/resources/local/logback-spring.xml b/springboot-mybatis2/src/main/resources/local/logback-spring.xml index 0505336..52e1a99 100644 --- a/springboot-mybatis2/src/main/resources/local/logback-spring.xml +++ b/springboot-mybatis2/src/main/resources/local/logback-spring.xml @@ -1,6 +1,6 @@ - middle_message + abel_user From f1886c8eec8a1baf44e29430f38115bd02917669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Fri, 20 Mar 2020 18:07:02 +0800 Subject: [PATCH 36/44] =?UTF-8?q?=E6=B7=BB=E5=8A=A0dubbo=E6=B6=88=E8=B4=B9?= =?UTF-8?q?=E8=80=85=E7=A4=BA=E4=BE=8B=EF=BC=8Cshiro2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 12 + springboot-dubbo/README.md | 10 + springboot-shiro2/README.md | 6 + springboot-shiro2/pom.xml | 142 +++++++++ .../cn/abel/rest/ShiroRestApplication.java | 19 ++ .../java/cn/abel/rest/config/RedisConfig.java | 199 ++++++++++++ .../cn/abel/rest/constants/Constants.java | 19 ++ .../abel/rest/controller/LoginController.java | 82 +++++ .../exception/DefaultErrorController.java | 59 ++++ .../exception/DefaultExceptionHandler.java | 142 +++++++++ .../rest/freemarker/CustomFreeMarkerView.java | 29 ++ .../rest/freemarker/FreeMarkerConfig.java | 38 +++ .../rest/shiro/HttpHeaderSessionManager.java | 27 ++ .../cn/abel/rest/shiro/RedisSessionDao.java | 99 ++++++ .../java/cn/abel/rest/shiro/ShiroConfig.java | 299 ++++++++++++++++++ .../cn/abel/rest/shiro/ShiroProperty.java | 55 ++++ .../java/cn/abel/rest/shiro/ShiroRealm.java | 145 +++++++++ .../rest/shiro/ShiroRedisCacheManager.java | 126 ++++++++ .../java/cn/abel/rest/shiro/ShiroUser.java | 102 ++++++ .../rest/shiro/UuidSessionIdGenerator.java | 20 ++ .../shiro/credentials/PasswordHelper.java | 27 ++ .../RetryLimitHashedCredentialsMatcher.java | 118 +++++++ .../credentials/ThirdPartySupportedToken.java | 92 ++++++ .../shiro/ext/QuartzSessionValidationJob.java | 63 ++++ .../ext/QuartzSessionValidationScheduler.java | 156 +++++++++ .../filter/ShiroFormAuthenticationFilter.java | 160 ++++++++++ .../rest/shiro/filter/ShiroLogoutFilter.java | 26 ++ .../java/cn/abel/rest/utils/ServletKit.java | 36 +++ .../cn/abel/rest/utils/SpringContextKit.java | 60 ++++ .../resources/META-INF/spring/consumer.xml | 22 ++ .../main/resources/dev/application.properties | 94 ++++++ .../src/main/resources/dev/banner.txt | 5 + .../src/main/resources/dev/logback-spring.xml | 41 +++ .../resources/local/application.properties | 94 ++++++ .../src/main/resources/local/banner.txt | 5 + .../main/resources/local/logback-spring.xml | 41 +++ 36 files changed, 2670 insertions(+) create mode 100644 springboot-dubbo/README.md create mode 100644 springboot-shiro2/README.md create mode 100644 springboot-shiro2/pom.xml create mode 100644 springboot-shiro2/src/main/java/cn/abel/rest/ShiroRestApplication.java create mode 100644 springboot-shiro2/src/main/java/cn/abel/rest/config/RedisConfig.java create mode 100644 springboot-shiro2/src/main/java/cn/abel/rest/constants/Constants.java create mode 100644 springboot-shiro2/src/main/java/cn/abel/rest/controller/LoginController.java create mode 100644 springboot-shiro2/src/main/java/cn/abel/rest/exception/DefaultErrorController.java create mode 100644 springboot-shiro2/src/main/java/cn/abel/rest/exception/DefaultExceptionHandler.java create mode 100644 springboot-shiro2/src/main/java/cn/abel/rest/freemarker/CustomFreeMarkerView.java create mode 100644 springboot-shiro2/src/main/java/cn/abel/rest/freemarker/FreeMarkerConfig.java create mode 100644 springboot-shiro2/src/main/java/cn/abel/rest/shiro/HttpHeaderSessionManager.java create mode 100644 springboot-shiro2/src/main/java/cn/abel/rest/shiro/RedisSessionDao.java create mode 100644 springboot-shiro2/src/main/java/cn/abel/rest/shiro/ShiroConfig.java create mode 100644 springboot-shiro2/src/main/java/cn/abel/rest/shiro/ShiroProperty.java create mode 100644 springboot-shiro2/src/main/java/cn/abel/rest/shiro/ShiroRealm.java create mode 100644 springboot-shiro2/src/main/java/cn/abel/rest/shiro/ShiroRedisCacheManager.java create mode 100644 springboot-shiro2/src/main/java/cn/abel/rest/shiro/ShiroUser.java create mode 100644 springboot-shiro2/src/main/java/cn/abel/rest/shiro/UuidSessionIdGenerator.java create mode 100644 springboot-shiro2/src/main/java/cn/abel/rest/shiro/credentials/PasswordHelper.java create mode 100644 springboot-shiro2/src/main/java/cn/abel/rest/shiro/credentials/RetryLimitHashedCredentialsMatcher.java create mode 100644 springboot-shiro2/src/main/java/cn/abel/rest/shiro/credentials/ThirdPartySupportedToken.java create mode 100644 springboot-shiro2/src/main/java/cn/abel/rest/shiro/ext/QuartzSessionValidationJob.java create mode 100644 springboot-shiro2/src/main/java/cn/abel/rest/shiro/ext/QuartzSessionValidationScheduler.java create mode 100644 springboot-shiro2/src/main/java/cn/abel/rest/shiro/filter/ShiroFormAuthenticationFilter.java create mode 100644 springboot-shiro2/src/main/java/cn/abel/rest/shiro/filter/ShiroLogoutFilter.java create mode 100644 springboot-shiro2/src/main/java/cn/abel/rest/utils/ServletKit.java create mode 100644 springboot-shiro2/src/main/java/cn/abel/rest/utils/SpringContextKit.java create mode 100644 springboot-shiro2/src/main/resources/META-INF/spring/consumer.xml create mode 100644 springboot-shiro2/src/main/resources/dev/application.properties create mode 100644 springboot-shiro2/src/main/resources/dev/banner.txt create mode 100644 springboot-shiro2/src/main/resources/dev/logback-spring.xml create mode 100644 springboot-shiro2/src/main/resources/local/application.properties create mode 100644 springboot-shiro2/src/main/resources/local/banner.txt create mode 100644 springboot-shiro2/src/main/resources/local/logback-spring.xml diff --git a/README.md b/README.md index 285b1f8..905c149 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,12 @@ * 是博客 springboot集成shiro 实现权限控制 地址:http://blog.csdn.net/u012373815/article/details/57532292 +##springboot-shior2 + +是使用shior 框架调取用户权限服务,进行登录权限验证的例子,其中的用户权限服务没有写,都是用TODO 标示出来了,使用时可以根据各自的用户权限服务进行编码替换 + +springboot-shiro2 也是和dubbo 的结合例子是 消费者的示例。 + ## springboot-swagger-ui * 博客 spring boot +Swagger-ui 自动生成API文档 地址: https://blog.csdn.net/u012373815/article/details/82685962 @@ -72,5 +78,11 @@ ## Springboot多数据源切换 * springboot 多个数据源的配置, 一个springboot 项目操作多个数据库的数据:https://abelyang.blog.csdn.net/article/details/89296341 +##springboot-dubbo + +该项目是Springboot 和 dubbo 结合的例子,是provider 的示例,提供服务。简单的写了一些用户和权限的接口没有写的很完整,主要是为了提现dubbo 服务 +Springboot-shiro2 也是和dubbo 的结合例子是 消费者的示例。 + + ##未完待续。。。 diff --git a/springboot-dubbo/README.md b/springboot-dubbo/README.md new file mode 100644 index 0000000..16441b5 --- /dev/null +++ b/springboot-dubbo/README.md @@ -0,0 +1,10 @@ +##springboot-dubbo + +该项目是Springboot 和 dubbo 结合的例子,是provider 的示例,提供服务。简单的写了一些用户和权限的接口没有写的很完整,主要是为了提现dubbo 服务 +Springboot-shiro2 也是和dubbo 的结合例子是 消费者的示例。 + +### abel-user-api +该模块是让消费者 引用的 api 模块,主要用来定义接口和数据传输实体 + +### abel-user-provider +该模块是真正的微服务提供者 diff --git a/springboot-shiro2/README.md b/springboot-shiro2/README.md new file mode 100644 index 0000000..8fe9437 --- /dev/null +++ b/springboot-shiro2/README.md @@ -0,0 +1,6 @@ +##springboot-shior2 + +是使用shior 框架调取用户权限服务,进行登录权限验证的例子,其中的用户权限服务没有写,都是用TODO 标示出来了,使用时可以根据各自的用户权限服务进行编码替换 + +springboot-shiro2 也是和dubbo 的结合例子是 消费者的示例。 +引入了 abel-user-api 模块 \ No newline at end of file diff --git a/springboot-shiro2/pom.xml b/springboot-shiro2/pom.xml new file mode 100644 index 0000000..8a3a9eb --- /dev/null +++ b/springboot-shiro2/pom.xml @@ -0,0 +1,142 @@ + + + 4.0.0 + + cn.abel + abel-parent + 1.0.0-SNAPSHOT + + + cn.abel + springboot-shior2 + 1.0.0 + jar + Spring Boot + + + + cn.abel + abel-util + 1.0.0-SNAPSHOT + + + + cn.abel + abel-user-api + 1.0.0-SNAPSHOT + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-quartz + + + org.springframework.boot + spring-boot-starter-data-redis + + + org.springframework.boot + spring-boot-starter-freemarker + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.apache.commons + commons-pool2 + + + org.apache.commons + commons-collections4 + + + + com.alibaba + fastjson + + + + com.alibaba + dubbo + + + org.slf4j + log4j-over-slf4j + + + org.apache.zookeeper + zookeeper + + + + + org.apache.shiro + shiro-spring + + + org.apache.shiro + shiro-ehcache + + + org.apache.shiro + shiro-quartz + + + org.apache.shiro + shiro-ehcache + + + net.mingsoft + shiro-freemarker-tags + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.core + jackson-annotations + + + 2.9.9.1 + + + + com.fasterxml.jackson.core + jackson-core + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + + + + \ No newline at end of file diff --git a/springboot-shiro2/src/main/java/cn/abel/rest/ShiroRestApplication.java b/springboot-shiro2/src/main/java/cn/abel/rest/ShiroRestApplication.java new file mode 100644 index 0000000..0f1102c --- /dev/null +++ b/springboot-shiro2/src/main/java/cn/abel/rest/ShiroRestApplication.java @@ -0,0 +1,19 @@ +package cn.abel.rest; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.context.annotation.ImportResource; + +/** + * 无数据库运行 + * + */ +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) +@ImportResource("classpath*:META-INF/spring/*.xml") +public class ShiroRestApplication { + + public static void main(String[] args) { + SpringApplication.run(ShiroRestApplication.class, args); + } +} diff --git a/springboot-shiro2/src/main/java/cn/abel/rest/config/RedisConfig.java b/springboot-shiro2/src/main/java/cn/abel/rest/config/RedisConfig.java new file mode 100644 index 0000000..821e775 --- /dev/null +++ b/springboot-shiro2/src/main/java/cn/abel/rest/config/RedisConfig.java @@ -0,0 +1,199 @@ +package cn.abel.rest.config; + +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; + +import cn.abel.rest.constants.Constants; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.interceptor.KeyGenerator; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.cache.RedisCacheWriter; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisPassword; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializationContext; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +/** + * redis缓存配置 + * + */ +@Configuration +@EnableCaching +public class RedisConfig extends CachingConfigurerSupport { + + /** logger */ + private static final Logger logger = LoggerFactory.getLogger(RedisConfig.class); + /** + * serializer + */ + private static final StringRedisSerializer STRING_SERIALIZER = new StringRedisSerializer(); + + @Value("${spring.redis.database}") + private Integer database; + @Value("${spring.redis.host}") + private String host; + @Value("${spring.redis.port}") + private Integer port; + @Value("${spring.redis.password}") + private String password; + @Value("${spring.redis.lettuce.pool.max-active}") + private Integer maxActive; + @Value("${spring.redis.lettuce.pool.max-wait}") + private Integer maxWait; + @Value("${spring.redis.lettuce.pool.max-idle}") + private Integer maxIdle; + @Value("${spring.redis.lettuce.pool.min-idle}") + private Integer minIdle; + @Value("${spring.redis.lettuce.shutdown-timeout}") + private Integer timeout; + + /** + * 在使用@Cacheable时,如果不指定key,则使用这个默认的key生成器生成的key + * + * @return + */ + @Override + @Bean + public KeyGenerator keyGenerator() { + return (target, method, params) -> { + StringBuilder sb = new StringBuilder(); + sb.append(target.getClass().getName()); + sb.append(method.getName()); + for (Object obj : params) { + sb.append(obj.toString()); + } + return sb.toString(); + }; + } + + /** + * 声明缓存管理器 + */ + @Override + @Bean + public CacheManager cacheManager() { + //1. entryTtl: 定义默认的cache time-to-live. + //2. disableCachingNullValues: 禁止缓存Null对象. 视需求而定. + //3. computePrefixWith: 此处定义了cache key的前缀, 避免公司不同项目之间的key名称冲突. + //4. serializeKeysWith, serializeValuesWith: 定义key和value的序列化协议, 同时的hash key和hash value也被定义. + + // 缓存配置 + RedisCacheConfiguration cacheConfiguration = + RedisCacheConfiguration.defaultCacheConfig().disableCachingNullValues().computePrefixWith(cacheName -> Constants.REDIS_KEY_PRE.concat(":").concat(cacheName).concat(":")).entryTtl(Duration.ofDays(Constants.REDIS_TIMEOUT)); + + Map redisCacheConfigurationMap = new HashMap<>(2); + //redisCacheConfigurationMap.put(Constants.CACHE_NAME_PERMISSION, permissionCacheConfiguration); + + //初始化一个RedisCacheWriter + RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory()); + + RedisSerializationContext.SerializationPair valuePair = + RedisSerializationContext.SerializationPair.fromSerializer(STRING_SERIALIZER); + //RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer(new ObjectMapper())); + RedisCacheConfiguration defaultCacheConfig = + RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(valuePair); + //设置默认过期时间是1天 + defaultCacheConfig.entryTtl(Duration.ofDays(Constants.REDIS_TIMEOUT)); + + //初始化RedisCacheManager + RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, defaultCacheConfig, + redisCacheConfigurationMap); + cacheManager.afterPropertiesSet(); + logger.info("RedisCacheManager config success"); + + return cacheManager; + } + + @Bean(name = "redisTemplate") + @Primary + public RedisTemplate redisTemplate() { + return getTemplate(redisConnectionFactory()); + } + + @Bean(name = "stringRedisTemplate") + public StringRedisTemplate stringRedisTemplate() { + return new StringRedisTemplate(redisConnectionFactory()); + } + + private RedisConnectionFactory redisConnectionFactory() { + return connectionFactory(maxActive, maxIdle, minIdle, maxWait, host, password, timeout, port, database); + } + + private RedisConnectionFactory connectionFactory(Integer maxActive, + Integer maxIdle, + Integer minIdle, + Integer maxWait, + String host, + String password, + Integer timeout, + Integer port, + Integer database) { + RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(); + redisStandaloneConfiguration.setHostName(host); + redisStandaloneConfiguration.setPort(port); + redisStandaloneConfiguration.setDatabase(database); + redisStandaloneConfiguration.setPassword(RedisPassword.of(password)); + + GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); + poolConfig.setMaxTotal(maxActive); + poolConfig.setMaxIdle(maxIdle); + poolConfig.setMinIdle(minIdle); + poolConfig.setMaxWaitMillis(maxWait); + LettuceClientConfiguration lettucePoolingConfig = LettucePoolingClientConfiguration.builder() + //.commandTimeout(Duration.ofSeconds(15)) + .poolConfig(poolConfig).shutdownTimeout(Duration.ofMillis(timeout)).build(); + LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(redisStandaloneConfiguration, + lettucePoolingConfig); + connectionFactory.afterPropertiesSet(); + + return connectionFactory; + } + + private RedisTemplate getTemplate(RedisConnectionFactory factory) { + RedisTemplate template = new RedisTemplate(); + template.setConnectionFactory(factory); + template.setKeySerializer(STRING_SERIALIZER); + template.setValueSerializer(jackson2JsonRedisSerializer()); + //不能用stringSerializer,有地方使用ShiroUser做key,不能转换 + //template.setHashKeySerializer(STRING_SERIALIZER); + //template.setHashKeySerializer(jackson2JsonRedisSerializer()); + + template.afterPropertiesSet(); + + return template; + } + + private RedisSerializer jackson2JsonRedisSerializer() { + Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = + new Jackson2JsonRedisSerializer<>(Object.class); + ObjectMapper objectMapper = new ObjectMapper(); + jackson2JsonRedisSerializer.setObjectMapper(objectMapper); + objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); + jackson2JsonRedisSerializer.setObjectMapper(objectMapper); + return jackson2JsonRedisSerializer; + } + +} \ No newline at end of file diff --git a/springboot-shiro2/src/main/java/cn/abel/rest/constants/Constants.java b/springboot-shiro2/src/main/java/cn/abel/rest/constants/Constants.java new file mode 100644 index 0000000..07702de --- /dev/null +++ b/springboot-shiro2/src/main/java/cn/abel/rest/constants/Constants.java @@ -0,0 +1,19 @@ +package cn.abel.rest.constants; + + +public interface Constants { + + /** + * 第三方登录的token。 + */ + String THIRD_PARTY_ACCESS_TOKEN_NAME = "accessToken"; + + /** 用户注册和绑定时的ip */ + String CLIENT_IP = "127.0.0.1"; + /** redis缓存 key前缀 */ + String REDIS_KEY_PRE = "abel-user"; + /** 超时时间(天) */ + long REDIS_TIMEOUT = 10L; + + +} diff --git a/springboot-shiro2/src/main/java/cn/abel/rest/controller/LoginController.java b/springboot-shiro2/src/main/java/cn/abel/rest/controller/LoginController.java new file mode 100644 index 0000000..33d6b5f --- /dev/null +++ b/springboot-shiro2/src/main/java/cn/abel/rest/controller/LoginController.java @@ -0,0 +1,82 @@ +package cn.abel.rest.controller; +import cn.abel.response.ResponseEntity; +import cn.abel.rest.shiro.ShiroUser; +import cn.abel.user.models.User; +import cn.abel.user.service.PermissionService; +import cn.abel.user.service.RoleService; +import cn.abel.user.service.UserService; +import com.alibaba.dubbo.config.annotation.Reference; +import com.google.common.collect.Sets; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.subject.Subject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * @author yyb + * @time 2020/3/6 + */ +@RestController +@CrossOrigin +@RequestMapping("/") +public class LoginController { + + private static final Logger logger = LoggerFactory.getLogger(LoginController.class); + + @Reference + private UserService userService; + + + /** + * @return + */ + @RequestMapping(value = "/login", method = RequestMethod.POST) + public ResponseEntity login() { + //TODO:登录日志。 + ShiroUser user = (ShiroUser) SecurityUtils.getSubject().getPrincipal(); + user.setToken(SecurityUtils.getSubject().getSession().getId().toString()); + return ResponseEntity.ok(user); + } + + /** + * 获取当前登录人的登录信息,包括拥有的角色、权限等。 + * + * @return + */ + @GetMapping("/logininfo") + public ResponseEntity loginInfo() { + ShiroUser shiroUser = (ShiroUser) SecurityUtils.getSubject().getPrincipal(); + + Map map = new HashMap<>(); + Set permissions = Sets.newHashSet(); + //TODO 获取当前用户所有的权限和角色 + User user = userService.getById(shiroUser.getUserId().intValue()); + map.put("roleList", user.getRoles()); + map.put("permissionList", permissions); + map.put("userId", shiroUser.getUserId()); + map.put("username", shiroUser.getLoginName()); + + return ResponseEntity.ok(map); + } + + + /** + * 注销 + * @return + */ + @PostMapping("/logout") + public ResponseEntity logout() { + Subject subject = SecurityUtils.getSubject(); + subject.logout(); + return ResponseEntity.ok(); + } + + +} + + diff --git a/springboot-shiro2/src/main/java/cn/abel/rest/exception/DefaultErrorController.java b/springboot-shiro2/src/main/java/cn/abel/rest/exception/DefaultErrorController.java new file mode 100644 index 0000000..c4a5f15 --- /dev/null +++ b/springboot-shiro2/src/main/java/cn/abel/rest/exception/DefaultErrorController.java @@ -0,0 +1,59 @@ +package cn.abel.rest.exception; + +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController; +import org.springframework.boot.web.servlet.error.DefaultErrorAttributes; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.servlet.ModelAndView; + +/** + * 404 覆盖默认error处理方法(不设置则在根目录查找error页面) + * + */ +@Controller +public class DefaultErrorController extends BasicErrorController { + + public DefaultErrorController(ServerProperties serverProperties) { + super(new DefaultErrorAttributes(), serverProperties.getError()); + } + + /** + * 覆盖默认的Json响应 + */ + @Override + public ResponseEntity> error(HttpServletRequest request) { + Map body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL)); + HttpStatus status = getStatus(request); + + //输出自定义的Json格式 + Map map = new HashMap<>(2); + map.put("status", false); + map.put("msg", body.get("message")); + + return new ResponseEntity<>(map, status); + } + + /** + * 覆盖默认的HTML响应 + */ + @Override + public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { + //请求的状态 + HttpStatus status = getStatus(request); + response.setStatus(getStatus(request).value()); + + Map model = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML)); + ModelAndView modelAndView = resolveErrorView(request, response, status, model); + //指定自定义的视图 + return (modelAndView == null ? new ModelAndView("error/error", model) : modelAndView); + } +} \ No newline at end of file diff --git a/springboot-shiro2/src/main/java/cn/abel/rest/exception/DefaultExceptionHandler.java b/springboot-shiro2/src/main/java/cn/abel/rest/exception/DefaultExceptionHandler.java new file mode 100644 index 0000000..c9f645f --- /dev/null +++ b/springboot-shiro2/src/main/java/cn/abel/rest/exception/DefaultExceptionHandler.java @@ -0,0 +1,142 @@ +package cn.abel.rest.exception; + +import cn.abel.exception.ServiceException; +import cn.abel.response.ResponseEntity; +import org.apache.shiro.authz.UnauthorizedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.ui.Model; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.context.request.NativeWebRequest; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.validation.ConstraintViolationException; + +/** + * 统一异常处理 + * + */ +@ControllerAdvice +public class DefaultExceptionHandler { + + /** + * logger + */ + private static final Logger logger = LoggerFactory.getLogger(DefaultExceptionHandler.class); + + private static final String ERROR_500 = "error/500"; + private static final String ERROR_403 = "error/403"; + private static final int ARG_ERROR_CODE = 400; + + + /** + * 参数验证异常。 + * + * @param ex + * @return + */ + @ResponseBody + @ResponseStatus(HttpStatus.OK) + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity handleMethodArgumentNotValidException( + HttpServletRequest request, + HttpServletResponse response, + MethodArgumentNotValidException ex) { + String errMsg = null; + if (ex.getBindingResult() != null && ex.getBindingResult().hasErrors()) { + errMsg = ex.getBindingResult().getFieldErrors().get(0).getDefaultMessage(); + } + return ResponseEntity.error(ARG_ERROR_CODE, errMsg); + } + + + /** + * 参数验证异常。 + * + * @param ex + * @return + */ + @ResponseBody + @ResponseStatus(HttpStatus.OK) + @ExceptionHandler(ConstraintViolationException.class) + public ResponseEntity handleConstraintViolationException( + HttpServletRequest request, + HttpServletResponse response, + ConstraintViolationException ex) { + String errMsg = ((ConstraintViolationException) ex).getConstraintViolations() + .iterator().next().getMessage(); + return ResponseEntity.error(ARG_ERROR_CODE, errMsg); + } + + + /** + * ServiceException异常 + * + * @param exception + * @return + */ + @ExceptionHandler({ServiceException.class}) + @ResponseStatus(HttpStatus.OK) + public String processServiceException(Exception exception, Model model) { + model.addAttribute("exception", exception.getMessage()); + logger.error("rpc接口调用异常。{}", exception.getMessage()); + return ERROR_500; + } + + /** + * 没有权限 异常 + *

+ * 后续根据不同的需求定制即可 + * 应用到所有@RequestMapping注解的方法,在其抛出UnauthorizedException异常时执行 + * + * @param request + * @param e + * @return + */ + @ExceptionHandler({UnauthorizedException.class}) + @ResponseStatus(HttpStatus.UNAUTHORIZED) + public String processUnauthorizedException(NativeWebRequest request, Model model, UnauthorizedException e) { + model.addAttribute("exception", e.getMessage()); + logger.error("权限异常。{}", e.getMessage()); + return ERROR_403; + } + + /** + * 运行时异常 + * + * @param exception + * @return + */ + @ExceptionHandler({RuntimeException.class}) + @ResponseStatus(HttpStatus.OK) + public String processException(RuntimeException exception, Model model) { + model.addAttribute("exception", exception.getMessage()); + logger.error("程序异常", exception); + return ERROR_500; + } + + /** + * Exception异常 + * + * @param exception + * @return + */ + @ExceptionHandler({Exception.class}) + @ResponseStatus(HttpStatus.OK) + public String processException(Exception exception, Model model) { + model.addAttribute("exception", exception.getMessage()); + logger.error("程序异常", exception); + return ERROR_500; + //logger.info("自定义异常处理-Exception"); + //ModelAndView m = new ModelAndView(); + //m.addObject("exception", exception.getMessage()); + //m.setViewName("error/500"); + //return m; + } +} diff --git a/springboot-shiro2/src/main/java/cn/abel/rest/freemarker/CustomFreeMarkerView.java b/springboot-shiro2/src/main/java/cn/abel/rest/freemarker/CustomFreeMarkerView.java new file mode 100644 index 0000000..b0d9929 --- /dev/null +++ b/springboot-shiro2/src/main/java/cn/abel/rest/freemarker/CustomFreeMarkerView.java @@ -0,0 +1,29 @@ +package cn.abel.rest.freemarker; + +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.web.servlet.view.freemarker.FreeMarkerView; + +/** + * freemarker全局变量 + * + * @date 2018/08/12 17:06 + */ +public class CustomFreeMarkerView extends FreeMarkerView { + + private static final String CONTEXT_PATH = "base"; + + @Override + protected void exposeHelpers(Map model, HttpServletRequest request) throws Exception { + String scheme = request.getScheme(); + String serverName = request.getServerName(); + int port = request.getServerPort(); + String path = request.getContextPath(); + String basePath = scheme + "://" + serverName + ":" + port + path; + model.put(CONTEXT_PATH, basePath); + + super.exposeHelpers(model, request); + } +} \ No newline at end of file diff --git a/springboot-shiro2/src/main/java/cn/abel/rest/freemarker/FreeMarkerConfig.java b/springboot-shiro2/src/main/java/cn/abel/rest/freemarker/FreeMarkerConfig.java new file mode 100644 index 0000000..c290e63 --- /dev/null +++ b/springboot-shiro2/src/main/java/cn/abel/rest/freemarker/FreeMarkerConfig.java @@ -0,0 +1,38 @@ +package cn.abel.rest.freemarker; + +import javax.annotation.PostConstruct; + +import com.jagregory.shiro.freemarker.ShiroTags; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.view.InternalResourceViewResolver; +import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver; + +/** + * freemarker配置 + * + * @date 2018/08/13 11:43 + */ +@Configuration +public class FreeMarkerConfig { + + @Autowired + freemarker.template.Configuration config; + @Autowired + FreeMarkerViewResolver resolver; + @Autowired + InternalResourceViewResolver springResolver; + + @PostConstruct + public void setSharedVariable() { + config.setNumberFormat("0.##"); + config.setDateFormat("yyyy/MM/dd"); + config.setDateTimeFormat("yyyy-MM-dd HH:mm:ss"); + + //自定义视图变量和方法 + resolver.setViewClass(CustomFreeMarkerView.class); + + //shiro标签 + config.setSharedVariable("shiro", new ShiroTags()); + } +} diff --git a/springboot-shiro2/src/main/java/cn/abel/rest/shiro/HttpHeaderSessionManager.java b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/HttpHeaderSessionManager.java new file mode 100644 index 0000000..0e680aa --- /dev/null +++ b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/HttpHeaderSessionManager.java @@ -0,0 +1,27 @@ +package cn.abel.rest.shiro; + +import org.apache.shiro.web.servlet.ShiroHttpServletRequest; +import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; +import org.apache.shiro.web.util.WebUtils; +import org.springframework.util.StringUtils; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import java.io.Serializable; + +public class HttpHeaderSessionManager extends DefaultWebSessionManager { + public static final String LOGIN_TOKEN_KEY = "sid"; + private static final String REFERENCED_SESSION_ID_SOURCE = "header"; + + @Override + protected Serializable getSessionId(ServletRequest request, ServletResponse response) { + String id = WebUtils.toHttp(request).getHeader(LOGIN_TOKEN_KEY); + if (!StringUtils.isEmpty(id)) { + request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE); + request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id); + request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE); + return id; + } + return null; + } +} diff --git a/springboot-shiro2/src/main/java/cn/abel/rest/shiro/RedisSessionDao.java b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/RedisSessionDao.java new file mode 100644 index 0000000..742e36f --- /dev/null +++ b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/RedisSessionDao.java @@ -0,0 +1,99 @@ +package cn.abel.rest.shiro; + +import java.io.Serializable; + +import javax.servlet.http.HttpServletRequest; + +import cn.abel.rest.utils.ServletKit; +import org.apache.shiro.cache.Cache; +import org.apache.shiro.session.Session; +import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 重写SessionDAO,实现session的CRUD功能 + * + * @date 2019/02/22 14:06 + */ +public class RedisSessionDao extends EnterpriseCacheSessionDAO { + + /** logger */ + private static final Logger logger = LoggerFactory.getLogger(RedisSessionDao.class); + + private Cache cache() { + Cache cache = getCacheManager().getCache(this.getClass().getName()); + return cache; + } + + /** + * 创建session,保存到数据库 + * + * @param session + * @return + */ + @Override + protected Serializable doCreate(Session session) { + Serializable sessionId = super.doCreate(session); + cache().put(sessionId.toString(), session); + return sessionId; + } + + /** + * 获取session + * + * @param sessionId + * @return + */ + @Override + protected Session doReadSession(Serializable sessionId) { + Session session = null; + HttpServletRequest request = ServletKit.getRequest(); + if (request != null) { + String uri = request.getServletPath(); + if (ServletKit.isStaticFile(uri)) { + return null; + } + session = (Session) request.getAttribute("session_" + sessionId); + } + if (session == null) { + session = super.doReadSession(sessionId); + } + if (session == null) { + session = (Session) cache().get(sessionId.toString()); + } + return session; + } + + /** + * 更新session的最后一次访问时间 + * + * @param session + */ + @Override + protected void doUpdate(Session session) { + HttpServletRequest request = ServletKit.getRequest(); + if (request != null) { + String uri = request.getServletPath(); + if (ServletKit.isStaticFile(uri)) { + return; + } + } + + super.doUpdate(session); + cache().put(session.getId().toString(), session); + + logger.debug("{}", session.getAttribute("shiroUserId")); + } + + /** + * 删除session + * + * @param session + */ + @Override + protected void doDelete(Session session) { + super.doDelete(session); + cache().remove(session.getId().toString()); + } +} \ No newline at end of file diff --git a/springboot-shiro2/src/main/java/cn/abel/rest/shiro/ShiroConfig.java b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/ShiroConfig.java new file mode 100644 index 0000000..690d502 --- /dev/null +++ b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/ShiroConfig.java @@ -0,0 +1,299 @@ +package cn.abel.rest.shiro; + +import java.util.LinkedHashMap; +import java.util.Map; + +import javax.servlet.Filter; + +import cn.abel.rest.shiro.ext.QuartzSessionValidationScheduler; +import cn.abel.rest.shiro.credentials.RetryLimitHashedCredentialsMatcher; +import cn.abel.rest.shiro.filter.ShiroFormAuthenticationFilter; +import cn.abel.rest.shiro.filter.ShiroLogoutFilter; +import org.apache.shiro.authc.credential.CredentialsMatcher; +import org.apache.shiro.cache.CacheManager; +import org.apache.shiro.codec.Base64; +import org.apache.shiro.mgt.RememberMeManager; +import org.apache.shiro.mgt.SecurityManager; +import org.apache.shiro.realm.Realm; +import org.apache.shiro.session.mgt.SessionValidationScheduler; +import org.apache.shiro.session.mgt.eis.SessionDAO; +import org.apache.shiro.spring.LifecycleBeanPostProcessor; +import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; +import org.apache.shiro.spring.web.ShiroFilterFactoryBean; +import org.apache.shiro.web.mgt.CookieRememberMeManager; +import org.apache.shiro.web.mgt.DefaultWebSecurityManager; +import org.apache.shiro.web.servlet.Cookie; +import org.apache.shiro.web.servlet.SimpleCookie; +import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; +import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; +import org.springframework.beans.factory.config.MethodInvokingFactoryBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.DependsOn; + + +/** + * @date 2018/08/07 23:43 + */ +@Configuration +public class ShiroConfig { + public final static String SHIRO_REALM_NAME = "shiroRealm"; + + /** + * shiro的Web过滤器 + * + * @param securityManager + * @return + */ + @Bean(name = "shiroFilter") + public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { + ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); + bean.setSecurityManager(securityManager); + + //配置鉴权与登出的拦截器。 + Map filterMap = new LinkedHashMap<>(); + filterMap.put("authc", formAuthenticationFilter()); + filterMap.put("logout", new ShiroLogoutFilter()); + bean.setFilters(filterMap); + + //配置各路径需要使用的拦截器。 + //anon表示anonymous。 + Map filterChainDefinitionMap = new LinkedHashMap<>(); + filterChainDefinitionMap.put("/logout", "logout"); + filterChainDefinitionMap.put("/css/**", "anon"); + filterChainDefinitionMap.put("/js/**", "anon"); + filterChainDefinitionMap.put("/img/**", "anon"); + filterChainDefinitionMap.put("/font/**", "anon"); + filterChainDefinitionMap.put("/fonts/**", "anon"); + filterChainDefinitionMap.put("/images/**", "anon"); + + filterChainDefinitionMap.put("/**", "authc"); + bean.setFilterChainDefinitionMap(filterChainDefinitionMap); + return bean; + } + + /** + * 缓存管理器 使用Ehcache实现 + * + * @return + */ + @Bean(name = "shiroRedisCacheManager") + //@ConditionalOnClass(name = {"org.apache.shiro.cache.ehcache.EhCacheManager"}) + @ConditionalOnMissingBean(name = "shiroRedisCacheManager") + public CacheManager shiroRedisCacheManager() { + //EhCacheManager cacheManager = new EhCacheManager(); + //cacheManager.setCacheManagerConfigFile("classpath:ehcache-shiro.xml"); + //return cacheManager; + ShiroRedisCacheManager cacheManager = new ShiroRedisCacheManager(); + return cacheManager; + } + + /** + * realm + * + * @return + */ + @Bean + public Realm realm() { + ShiroRealm shiroRealm = new ShiroRealm(); + shiroRealm.setName(SHIRO_REALM_NAME); + shiroRealm.setCredentialsMatcher(credentialsMatcher()); + shiroRealm.setCachingEnabled(true); + + //不设置账号缓存,每次登录都实时调用用户服务的登录接口。 + shiroRealm.setAuthenticationCachingEnabled(false); + + //不设置角色权限缓存,每次登录都从权限服务实时拉取。 + shiroRealm.setAuthorizationCachingEnabled(false); + + return shiroRealm; + } + + /** + * 会话验证调度器 + * + * @return + */ + @Bean(name = "sessionValidationScheduler") + @ConditionalOnClass(name = {"org.quartz.Scheduler"}) + @ConditionalOnMissingBean(SessionValidationScheduler.class) + public SessionValidationScheduler sessionValidationScheduler(DefaultWebSessionManager sessionManager) { + QuartzSessionValidationScheduler scheduler = new QuartzSessionValidationScheduler(sessionManager); + //scheduler.setSessionValidationInterval(1800000); + //scheduler.setSessionManager(sessionManager()); + sessionManager.setSessionValidationSchedulerEnabled(true); + sessionManager.setSessionValidationInterval(1800000); + sessionManager.setSessionValidationScheduler(scheduler); + return scheduler; + } + + /** + * 会话DAO + * + * @return + */ + @Bean + public SessionDAO sessionDAO() { + RedisSessionDao redisSessionDao = new RedisSessionDao(); + redisSessionDao.setActiveSessionsCacheName("shiro-activeSessionCache"); + redisSessionDao.setSessionIdGenerator(new UuidSessionIdGenerator()); + return redisSessionDao; + } + + /** + * 会话Cookie模板 + * + * @return + */ + @Bean + public Cookie sessionIdCookie() { + SimpleCookie cookie = new SimpleCookie("sid"); + cookie.setHttpOnly(true); + cookie.setMaxAge(-1); + return cookie; + } + + /** + * rememberMe Cookie + * + * @return + */ + @Bean + public Cookie rememberMeCookie() { + SimpleCookie cookie = new SimpleCookie("rememberMe"); + cookie.setHttpOnly(true); + //30天 + cookie.setMaxAge(2592000); + return cookie; + } + + /** + * 会话管理器 + * + * @return + */ + @Bean + public DefaultWebSessionManager sessionManager() { + //SessionManager sessionManager = new SessionManager(); + HttpHeaderSessionManager sessionManager = new HttpHeaderSessionManager(); + sessionManager.setGlobalSessionTimeout(43200000); + sessionManager.setDeleteInvalidSessions(true); + //sessionManager.setSessionValidationSchedulerEnabled(true); + //sessionManager.setSessionValidationScheduler(sessionValidationScheduler(sessionManager)); + //sessionManager.setSessionValidationScheduler(sessionValidationScheduler()); + sessionManager.setSessionDAO(sessionDAO()); + sessionManager.setSessionIdCookieEnabled(false); +// sessionManager.setSessionIdCookie(sessionIdCookie()); + //去掉URL中的JSESSIONID + sessionManager.setSessionIdUrlRewritingEnabled(false); + return sessionManager; + } + + /** + * 安全管理器 + * + * @return + */ + @Bean + @DependsOn("redisTemplate") + public SecurityManager securityManager() { + DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); + securityManager.setRealm(realm()); + securityManager.setCacheManager(shiroRedisCacheManager()); + securityManager.setSessionManager(sessionManager()); + securityManager.setRememberMeManager(rememberMeManager()); + return securityManager; + } + + /** + * rememberMe管理器 + * + * @return + */ + @Bean + public RememberMeManager rememberMeManager() { + CookieRememberMeManager manager = new CookieRememberMeManager(); + //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位) + manager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag==")); + manager.setCookie(rememberMeCookie()); + return manager; + } + + /** + * 凭证匹配器 + * + * @return + */ + @Bean(name = "credentialsMatcher") + public CredentialsMatcher credentialsMatcher() { + //明文密码 + //CustomCredentialsMatcher matcher = new CustomCredentialsMatcher(); + //HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(); + RetryLimitHashedCredentialsMatcher matcher = new RetryLimitHashedCredentialsMatcher(shiroRedisCacheManager()); + //matcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法; + //matcher.setHashIterations(2);//散列的次数,比如散列两次,相当于 md5(md5("")); + matcher.setStoredCredentialsHexEncoded(true); + return matcher; + } + + @Bean + public MethodInvokingFactoryBean methodInvokingFactoryBean() { + MethodInvokingFactoryBean factoryBean = new MethodInvokingFactoryBean(); + factoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager"); + factoryBean.setArguments(securityManager()); + return factoryBean; + } + + /** + * Shiro生命周期处理器 + * + * @return + */ + @Bean + public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { + return new LifecycleBeanPostProcessor(); + } + + public ShiroFormAuthenticationFilter formAuthenticationFilter() { + ShiroFormAuthenticationFilter filter = new ShiroFormAuthenticationFilter(); + filter.setUsernameParam("username"); + filter.setPasswordParam("password"); + filter.setRememberMeParam("rememberMe"); + filter.setLoginUrl("/login"); + filter.setSuccessUrl("/index"); + return filter; + } + + /** + * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类, + * 并在必要时进行安全逻辑验证 * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor) + * 即可实现此功能 + * + * @return + */ + @Bean + @DependsOn({"lifecycleBeanPostProcessor"}) + public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() { + DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); + advisorAutoProxyCreator.setProxyTargetClass(true); + return advisorAutoProxyCreator; + } + + /** + * 开启shiro aop注解支持. + * 使用代理方式;所以需要开启代码支持; + * 需要aspectj支持 + * + * @param securityManager + * @return + */ + @Bean + public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { + AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = + new AuthorizationAttributeSourceAdvisor(); + authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); + return authorizationAttributeSourceAdvisor; + } +} \ No newline at end of file diff --git a/springboot-shiro2/src/main/java/cn/abel/rest/shiro/ShiroProperty.java b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/ShiroProperty.java new file mode 100644 index 0000000..fac4c38 --- /dev/null +++ b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/ShiroProperty.java @@ -0,0 +1,55 @@ +package cn.abel.rest.shiro; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +/** + * @date 2019/02/26 14:39 + */ +@Configuration +public class ShiroProperty { + + @Value("${shiro.retryExpireTimeRedis}") + private int shiroRetryExpireTimeRedis; + + @Value("${shiro.authorizationExpireTimeRedis}") + private int shiroAuthorizationExpireTimeRedis; + + @Value("${shiro.retryMax}") + private int shiroRetryMax; + + @Value("${shiro.sessionExpireTimeRedis}") + private Integer shiroSessionExpireTimeRedis; + + public int getShiroRetryExpireTimeRedis() { + return shiroRetryExpireTimeRedis; + } + + public void setShiroRetryExpireTimeRedis(int shiroRetryExpireTimeRedis) { + this.shiroRetryExpireTimeRedis = shiroRetryExpireTimeRedis; + } + + public int getShiroAuthorizationExpireTimeRedis() { + return shiroAuthorizationExpireTimeRedis; + } + + public void setShiroAuthorizationExpireTimeRedis(int shiroAuthorizationExpireTimeRedis) { + this.shiroAuthorizationExpireTimeRedis = shiroAuthorizationExpireTimeRedis; + } + + public int getShiroRetryMax() { + return shiroRetryMax; + } + + public void setShiroRetryMax(int shiroRetryMax) { + this.shiroRetryMax = shiroRetryMax; + } + + public Integer getShiroSessionExpireTimeRedis() { + return shiroSessionExpireTimeRedis; + } + + public void setShiroSessionExpireTimeRedis(Integer shiroSessionExpireTimeRedis) { + this.shiroSessionExpireTimeRedis = shiroSessionExpireTimeRedis; + } +} diff --git a/springboot-shiro2/src/main/java/cn/abel/rest/shiro/ShiroRealm.java b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/ShiroRealm.java new file mode 100644 index 0000000..8744ee6 --- /dev/null +++ b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/ShiroRealm.java @@ -0,0 +1,145 @@ +package cn.abel.rest.shiro; + +import java.util.List; +import java.util.Random; +import java.util.Set; + +import cn.abel.exception.ServiceException; +import cn.abel.user.models.Permission; +import cn.abel.user.models.Role; +import cn.abel.user.models.User; +import cn.abel.user.service.PermissionService; +import cn.abel.user.service.RoleService; +import cn.abel.user.service.UserService; +import com.alibaba.dubbo.config.annotation.Reference; +import com.google.common.collect.Sets; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.SimpleAccount; +import org.apache.shiro.authc.UsernamePasswordToken; +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.authz.SimpleAuthorizationInfo; +import org.apache.shiro.realm.AuthorizingRealm; +import org.apache.shiro.subject.PrincipalCollection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * @date 2018/08/07 23:48 + */ +@Component +public class ShiroRealm extends AuthorizingRealm { + + /** + * logger + */ + private static final Logger logger = LoggerFactory.getLogger(ShiroRealm.class); + + @Reference(check = false) + private UserService userService; + + + /** + * 登录 认证回调函数,登录时调用 + * + * @param authenticationToken + * @return + * @throws AuthenticationException + */ + @Override + protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) + throws AuthenticationException { + UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; + String username = token.getUsername(); + + Random random = new Random(); + Long userId = random.nextLong(); + + //交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配 + return new SimpleAccount(new ShiroUser(userId, username, username), "", getName()); + } + + /** + * 授权 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用 + * + * @param principals + * @return + */ + @Override + protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { + ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal(); + + SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); + + Set roles = Sets.newHashSet(); + Set permissions = Sets.newHashSet(); + +// try { + //TODO 此处需要获取当前用户的所有角色和权限,放入验证器 + //登录名为超级管理员账号 +// +// //获取用户所有角色 +// User user = userService.getById( shiroUser.getUserId()); +// if (CollectionUtils.isEmpty(user.getRoles())) { +// return authorizationInfo; +// } +// +// for (Role role : user.getRoles()) { +// //停用的角色不查询权限 +// roles.add(role.getName()); +// List permissionList = permissionService.getByMap(role.getId()); +// permissionList.forEach(p -> { +// permissions.add(p.getCode()); +// }); +// } +// } catch (ServiceException e) { +// logger.error(e.getMessage()); +// return authorizationInfo; +// } + + authorizationInfo.setRoles(roles); + authorizationInfo.setStringPermissions(permissions); + + return authorizationInfo; + } + + @Override + public void clearCachedAuthorizationInfo(PrincipalCollection principals) { + super.clearCachedAuthorizationInfo(principals); + clearAllCache(); + } + + @Override + public void clearCachedAuthenticationInfo(PrincipalCollection principals) { + super.clearCachedAuthenticationInfo(principals); + clearAllCache(); + } + + @Override + public void clearCache(PrincipalCollection principals) { + super.clearCache(principals); + clearAllCache(); + } + + public void clearAllCachedAuthorizationInfo() { + if (getAuthorizationCache() != null) { + getAuthorizationCache().clear(); + } + } + + public void clearAllCachedAuthenticationInfo() { + if (getAuthenticationCache() != null) { + getAuthenticationCache().clear(); + } + + } + + public void clearAllCache() { + clearAllCachedAuthenticationInfo(); + clearAllCachedAuthorizationInfo(); + } +} diff --git a/springboot-shiro2/src/main/java/cn/abel/rest/shiro/ShiroRedisCacheManager.java b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/ShiroRedisCacheManager.java new file mode 100644 index 0000000..03b19b7 --- /dev/null +++ b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/ShiroRedisCacheManager.java @@ -0,0 +1,126 @@ +package cn.abel.rest.shiro; + +import java.util.Collection; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import cn.abel.rest.constants.Constants; +import org.apache.shiro.cache.Cache; +import org.apache.shiro.cache.CacheException; +import org.apache.shiro.cache.CacheManager; +import org.apache.shiro.subject.PrincipalCollection; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.BoundHashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +/** + * 实现Shiro的缓存管理器CacheManger接口,将Spring应用缓存管理器注入shiro缓存管理器,这样shiro的缓存都由Spring处理 + * + * @date 2019/02/21 18:12 + */ +@Component +public class ShiroRedisCacheManager implements CacheManager { + + private final static String CACHE_KEY_PREFIX = Constants.REDIS_KEY_PRE + ":shiro:"; + + @Autowired + private ShiroProperty shiroProperty; + + @Autowired + private RedisTemplate redisTemplate; + + /** 超时时间 */ + private long expireTime; + + @Override + public Cache getCache(String name) throws CacheException { + if ("passwordRetryCache".equals(name)) { + expireTime = shiroProperty.getShiroRetryExpireTimeRedis(); + } else { + expireTime = shiroProperty.getShiroAuthorizationExpireTimeRedis(); + } + return new ShiroRedisCache(CACHE_KEY_PREFIX + name, expireTime); + } + + /** + * 为shiro量身定做的一个redis cache,为Authorization cache做了特别优化 + */ + public class ShiroRedisCache implements Cache { + + private String cacheKey; + + // 缓存的超时时间,单位为s + private long expireTime = 3600; + + public ShiroRedisCache(String cacheKey, long expireTime) { + this.cacheKey = cacheKey; + this.expireTime = expireTime; + } + + @Override + public V get(K key) throws CacheException { + BoundHashOperations hash = redisTemplate.boundHashOps(cacheKey); + Object k = hashKey(key); + return hash.get(k); + } + + @Override + public V put(K key, V value) throws CacheException { + BoundHashOperations hash = redisTemplate.boundHashOps(cacheKey); + //超时时间 + Object k = hashKey(key); + hash.put((K) k, value); + hash.expire(expireTime, TimeUnit.SECONDS); + return value; + } + + @Override + public V remove(K key) throws CacheException { + BoundHashOperations hash = redisTemplate.boundHashOps(cacheKey); + + V value = null; + try { + Object k = hashKey(key); + value = hash.get(k); + hash.delete(k); + } catch (Exception e) { + e.printStackTrace(); + } + return value; + } + + @Override + public void clear() throws CacheException { + redisTemplate.delete(cacheKey); + } + + @Override + public int size() { + BoundHashOperations hash = redisTemplate.boundHashOps(cacheKey); + return hash.size().intValue(); + } + + @Override + public Set keys() { + BoundHashOperations hash = redisTemplate.boundHashOps(cacheKey); + return hash.keys(); + } + + @Override + public Collection values() { + BoundHashOperations hash = redisTemplate.boundHashOps(cacheKey); + return hash.values(); + } + + protected Object hashKey(K key) { + //此处很重要,如果key是登录凭证,那么这是访问用户的授权缓存;将登录凭证转为user对象,返回user的id属性做为hash key,否则会以user对象做为hash key,这样就不好清除指定用户的缓存了 + if (key instanceof PrincipalCollection) { + PrincipalCollection pc = (PrincipalCollection) key; + ShiroUser user = (ShiroUser) pc.getPrimaryPrincipal(); + return user.getUserId(); + } + return key; + } + } +} \ No newline at end of file diff --git a/springboot-shiro2/src/main/java/cn/abel/rest/shiro/ShiroUser.java b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/ShiroUser.java new file mode 100644 index 0000000..ea91eab --- /dev/null +++ b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/ShiroUser.java @@ -0,0 +1,102 @@ +package cn.abel.rest.shiro; + +import java.io.Serializable; + +import com.google.common.base.Objects; + +/** + * 自定义Authentication对象,使得Subject除了携带用户的登录名外还可以携带更多信息. + * + */ +public class ShiroUser implements Serializable { + + private static final long serialVersionUID = -1373760761780840081L; + + private Long userId; + + private String loginName; + + private String nickName; + + private String token; + + public Long getUserId() { + return userId; + } + + public void setUserId(Long userId) { + this.userId = userId; + } + + public String getLoginName() { + return loginName; + } + + public void setLoginName(String loginName) { + this.loginName = loginName; + } + + public void setNickName(String nickName) { + this.nickName = nickName; + } + + public ShiroUser(Long userId, String loginName, String nickName) { + this.userId = userId; + this.loginName = loginName; + this.nickName = nickName; + } + + public String getNickName() { + return nickName; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + /** + * 本函数输出将作为默认的输出. + */ + @Override + public String toString() { + return loginName; + } + + /** + * 重载hashCode,只计算loginName; + */ + @Override + public int hashCode() { + return Objects.hashCode(loginName); + } + + /** + * 重载equals,只计算loginName; + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + ShiroUser other = (ShiroUser) obj; + if (loginName == null) { + if (other.loginName != null) { + return false; + } + } else if (!loginName.equals(other.loginName)) { + return false; + } + return true; + } + +} \ No newline at end of file diff --git a/springboot-shiro2/src/main/java/cn/abel/rest/shiro/UuidSessionIdGenerator.java b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/UuidSessionIdGenerator.java new file mode 100644 index 0000000..53d4c59 --- /dev/null +++ b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/UuidSessionIdGenerator.java @@ -0,0 +1,20 @@ +package cn.abel.rest.shiro; + +import java.io.Serializable; +import java.util.UUID; + +import org.apache.shiro.session.Session; +import org.apache.shiro.session.mgt.eis.SessionIdGenerator; + +/** + * 会话ID生成器 + * + * @date 2018/08/08 11:04 + */ +public class UuidSessionIdGenerator implements SessionIdGenerator { + + @Override + public Serializable generateId(Session session) { + return UUID.randomUUID().toString().replace("-", "").toUpperCase(); + } +} diff --git a/springboot-shiro2/src/main/java/cn/abel/rest/shiro/credentials/PasswordHelper.java b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/credentials/PasswordHelper.java new file mode 100644 index 0000000..663c312 --- /dev/null +++ b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/credentials/PasswordHelper.java @@ -0,0 +1,27 @@ +package cn.abel.rest.shiro.credentials; + +import org.apache.shiro.crypto.hash.SimpleHash; +import org.apache.shiro.util.ByteSource; + +/** + * 密码 + * + */ +public class PasswordHelper { + + private static String algorithmName = "md5"; + private static int hashIterations = 2; + + public void setAlgorithmName(String algorithmName) { + PasswordHelper.algorithmName = algorithmName; + } + + public void setHashIterations(int hashIterations) { + PasswordHelper.hashIterations = hashIterations; + } + + public static String encryptPassword(String userName, String password) { + return new SimpleHash(algorithmName, password, ByteSource.Util.bytes(userName), hashIterations).toHex(); + } + +} diff --git a/springboot-shiro2/src/main/java/cn/abel/rest/shiro/credentials/RetryLimitHashedCredentialsMatcher.java b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/credentials/RetryLimitHashedCredentialsMatcher.java new file mode 100644 index 0000000..959e5e9 --- /dev/null +++ b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/credentials/RetryLimitHashedCredentialsMatcher.java @@ -0,0 +1,118 @@ +package cn.abel.rest.shiro.credentials; + +import java.util.concurrent.atomic.AtomicInteger; +import cn.abel.rest.shiro.ShiroUser; +import cn.abel.rest.shiro.ShiroProperty; + +import cn.abel.user.service.UserService; +import com.alibaba.dubbo.config.annotation.Reference; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.authc.*; +import org.apache.shiro.authc.credential.HashedCredentialsMatcher; +import org.apache.shiro.cache.Cache; +import org.apache.shiro.cache.CacheManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import javax.websocket.Session; + +/** + * 登录验证 + */ +public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher { + private static final Logger logger = LoggerFactory.getLogger(RetryLimitHashedCredentialsMatcher.class); + + @Autowired + private ShiroProperty shiroProperty; + @Reference + private UserService userService; + + private Cache passwordRetryCache; + + public RetryLimitHashedCredentialsMatcher(CacheManager cacheManager) { + passwordRetryCache = cacheManager.getCache("passwordRetryCache"); + } + + @Override + public boolean doCredentialsMatch(AuthenticationToken originToken, AuthenticationInfo info) { + ThirdPartySupportedToken token = (ThirdPartySupportedToken) originToken; + ShiroUser shiroUser = (ShiroUser) info.getPrincipals().getPrimaryPrincipal(); + + //优先使用第三方token进行登录验证。 + if (StringUtils.isNotBlank(token.getAccessToken())) { + Session session = null; +// try { + //TODO 从用户服务中通过 token 获取 session +// session = userService.getSessionByToken(token.getAccessToken()); +// } catch (ServiceException e) { +// throw new ExpiredCredentialsException(); +// } + if (session == null) { + throw new ExpiredCredentialsException(); + } + //TODO 从session 中获取用户相关信息,放入shiro 验证对象 shiroUser +// shiroUser.setUserId(session.getUser().getProfileId()); +// shiroUser.setLoginName(session.getCurrentLoginName()); +// shiroUser.setNickName(session.getUser().getNickName()); + return true; + } + + String username = token.getUsername(); + String password = String.valueOf(token.getPassword()); + + //判断该账号登录重试次数是否超过上限。 + AtomicInteger retryCount = passwordRetryCache.get(username); + if (retryCount != null && retryCount.get() >= shiroProperty.getShiroRetryMax()) { + throw new ExcessiveAttemptsException(); + } + + //TODO 没有token,则调用用户服务的登录接口重新登录 获取 session +// Login login = new Login(); +// login.setIp(Constants.CLIENT_IP); +// login.setLoginMode(2); +// login.setLoginName(username); +// login.setLoginType(3); +// login.setPassword(password); +// try { +// Session session = userService.login(login); +// if (session == null) { +// logger.error("登录失败。username={}", username); +// throw new IncorrectCredentialsException(); +// } +// if (session.getUser().getDeleteFlag() != 1) { +// logger.error("登录失败。用户已被锁定。username={}", username); +// throw new LockedAccountException(); +// } + + //登录成功,清空登录次数缓存。 + if (retryCount != null) { + passwordRetryCache.remove(username); + } + //TODO 将 session 中的用户id 信息放入 shiro 对shiroUser象中 +// shiroUser.setUserId(session.getUser().getProfileId()); +// return true; +// } catch (ServiceException e) { + //TODO 根据登录接口的异常信息判读 失败情况 +// if (e.getErrorCode() == InfoCode.USER_LOGIN_NOT_EXIST.getStatus() +// || e.getErrorCode() == InfoCode.REQUEST_PARAM_ERROR.getStatus()) { +// logger.error("登录失败,用户不存在。username={}", username); +// throw new UnknownAccountException(); +// } else if (e.getErrorCode() == InfoCode.PASSWORD_ERROR.getStatus()) { +// //密码错误缓存登录重试次数缓存。 +// if (retryCount == null) { +// retryCount = new AtomicInteger(0); +// } +// retryCount.incrementAndGet(); +// passwordRetryCache.put(username, retryCount); +// +// logger.error("登录失败,用户名或密码错误。username={}", username); +// throw new IncorrectCredentialsException(); +// } else { +// logger.error("登录失败,msg={}", e.getMessage()); +// } +// } + + return false; + } +} diff --git a/springboot-shiro2/src/main/java/cn/abel/rest/shiro/credentials/ThirdPartySupportedToken.java b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/credentials/ThirdPartySupportedToken.java new file mode 100644 index 0000000..79b9b48 --- /dev/null +++ b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/credentials/ThirdPartySupportedToken.java @@ -0,0 +1,92 @@ +package cn.abel.rest.shiro.credentials; + +import org.apache.shiro.authc.UsernamePasswordToken; +import org.springframework.stereotype.Component; + +/** + * 支持第三方登录的shiro token。 + */ +public class ThirdPartySupportedToken extends UsernamePasswordToken { + private String username; + private char[] password; + private boolean rememberMe = false; + private String host; + + /** + * 第三方token。 + */ + private String accessToken; + + public ThirdPartySupportedToken( + String username, + String password, + String accessToken) { + this.username = username; + this.password = password != null ? password.toCharArray() : null; + this.accessToken = accessToken; + } + + @Override + public String getUsername() { + return this.username; + } + + @Override + public void setUsername(String username) { + this.username = username; + } + + @Override + public char[] getPassword() { + return this.password; + } + + @Override + public void setPassword(char[] password) { + this.password = password; + } + + @Override + public Object getPrincipal() { + return this.getUsername(); + } + + @Override + public Object getCredentials() { + return this.getPassword(); + } + + @Override + public String getHost() { + return this.host; + } + + @Override + public void setHost(String host) { + this.host = host; + } + + @Override + public boolean isRememberMe() { + return this.rememberMe; + } + + @Override + public void setRememberMe(boolean rememberMe) { + this.rememberMe = rememberMe; + } + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + @Override + public void clear() { + super.clear(); + this.accessToken = null; + } +} diff --git a/springboot-shiro2/src/main/java/cn/abel/rest/shiro/ext/QuartzSessionValidationJob.java b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/ext/QuartzSessionValidationJob.java new file mode 100644 index 0000000..56ec875 --- /dev/null +++ b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/ext/QuartzSessionValidationJob.java @@ -0,0 +1,63 @@ +package cn.abel.rest.shiro.ext; + +import org.apache.shiro.session.mgt.ValidatingSessionManager; +import org.quartz.Job; +import org.quartz.JobDataMap; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 基于Quartz 2.* 版本的实现 + * + * @author web + */ +public class QuartzSessionValidationJob implements Job { + + /** + * Key used to store the session manager in the job data map for this job. + */ + public static final String SESSION_MANAGER_KEY = "sessionManager"; + + /**-------------------------------------------- + | I N S T A N C E V A R I A B L E S | + ============================================*/ + private static final Logger log = LoggerFactory.getLogger(QuartzSessionValidationJob.class); + + /*-------------------------------------------- + | C O N S T R U C T O R S | + ============================================*/ + + /*-------------------------------------------- + | A C C E S S O R S / M O D I F I E R S | + ============================================*/ + + /*-------------------------------------------- + | M E T H O D S | + ============================================*/ + + /** + * Called when the job is executed by quartz. This method delegates to the validateSessions() method on the + * associated session manager. + * + * @param context the Quartz job execution context for this execution. + */ + @Override + public void execute(JobExecutionContext context) throws JobExecutionException { + + JobDataMap jobDataMap = context.getMergedJobDataMap(); + ValidatingSessionManager sessionManager = (ValidatingSessionManager) jobDataMap.get(SESSION_MANAGER_KEY); + + if (log.isDebugEnabled()) { + log.debug("Executing session validation Quartz job..."); + } + + sessionManager.validateSessions(); + + if (log.isDebugEnabled()) { + log.debug("Session validation Quartz job complete."); + } + } + +} diff --git a/springboot-shiro2/src/main/java/cn/abel/rest/shiro/ext/QuartzSessionValidationScheduler.java b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/ext/QuartzSessionValidationScheduler.java new file mode 100644 index 0000000..323511b --- /dev/null +++ b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/ext/QuartzSessionValidationScheduler.java @@ -0,0 +1,156 @@ +package cn.abel.rest.shiro.ext; + +import org.apache.shiro.session.mgt.DefaultSessionManager; +import org.apache.shiro.session.mgt.SessionValidationScheduler; +import org.apache.shiro.session.mgt.ValidatingSessionManager; +import org.quartz.JobBuilder; +import org.quartz.JobDetail; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.SimpleScheduleBuilder; +import org.quartz.SimpleTrigger; +import org.quartz.TriggerBuilder; +import org.quartz.TriggerKey; +import org.quartz.impl.StdSchedulerFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 基于Quartz 2.* 版本的实现 + * + * @author web + */ +public class QuartzSessionValidationScheduler implements SessionValidationScheduler { + + public static final long DEFAULT_SESSION_VALIDATION_INTERVAL = + DefaultSessionManager.DEFAULT_SESSION_VALIDATION_INTERVAL; + private static final String JOB_NAME = "SessionValidationJob"; + private static final Logger log = LoggerFactory.getLogger(QuartzSessionValidationScheduler.class); + private static final String SESSION_MANAGER_KEY = QuartzSessionValidationJob.SESSION_MANAGER_KEY; + private Scheduler scheduler; + private boolean schedulerImplicitlyCreated = false; + + private boolean enabled = false; + private ValidatingSessionManager sessionManager; + private long sessionValidationInterval = DEFAULT_SESSION_VALIDATION_INTERVAL; + + public QuartzSessionValidationScheduler() { + } + + public QuartzSessionValidationScheduler(ValidatingSessionManager sessionManager) { + this.sessionManager = sessionManager; + } + + protected Scheduler getScheduler() throws SchedulerException { + if (this.scheduler == null) { + this.scheduler = StdSchedulerFactory.getDefaultScheduler(); + this.schedulerImplicitlyCreated = true; + } + return this.scheduler; + } + + public void setScheduler(Scheduler scheduler) { + this.scheduler = scheduler; + } + + public void setSessionManager(ValidatingSessionManager sessionManager) { + this.sessionManager = sessionManager; + } + + @Override + public boolean isEnabled() { + return this.enabled; + } + + public void setSessionValidationInterval(long sessionValidationInterval) { + this.sessionValidationInterval = sessionValidationInterval; + } + + @Override + public void enableSessionValidation() { + if (log.isDebugEnabled()) { + log.debug("Scheduling session validation job using Quartz with session validation interval of [" + this.sessionValidationInterval + "]ms..."); + } + + try { + //Quartz 2中的实现 + SimpleTrigger trigger = TriggerBuilder.newTrigger().startNow().withIdentity(JOB_NAME, + Scheduler.DEFAULT_GROUP).withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInMilliseconds(sessionValidationInterval)).build(); + + JobDetail detail = JobBuilder.newJob(QuartzSessionValidationJob.class).withIdentity(JOB_NAME, + Scheduler.DEFAULT_GROUP).build(); + detail.getJobDataMap().put(SESSION_MANAGER_KEY, this.sessionManager); + Scheduler scheduler = getScheduler(); + + scheduler.scheduleJob(detail, trigger); + if (this.schedulerImplicitlyCreated) { + scheduler.start(); + if (log.isDebugEnabled()) { + log.debug("Successfully started implicitly created Quartz Scheduler instance."); + } + } + this.enabled = true; + + if (log.isDebugEnabled()) { + log.debug("Session validation job successfully scheduled with Quartz."); + } + } catch (SchedulerException e) { + if (log.isErrorEnabled()) { + log.error("Error starting the Quartz session validation job. Session validation may not occur.", e); + } + } + } + + @Override + public void disableSessionValidation() { + if (log.isDebugEnabled()) { + log.debug("Stopping Quartz session validation job..."); + } + Scheduler scheduler; + try { + scheduler = getScheduler(); + if (scheduler == null) { + if (log.isWarnEnabled()) { + log.warn("getScheduler() method returned a null Quartz scheduler, which is unexpected. Please " + + "check your configuration and/or implementation. Returning quietly since there is no " + + "validation job to remove (scheduler does not exist)."); + } + + return; + } + } catch (SchedulerException e) { + if (log.isWarnEnabled()) { + log.warn("Unable to acquire Quartz Scheduler. Ignoring and returning (already stopped?)", e); + } + return; + } + try { + scheduler.unscheduleJob(new TriggerKey("SessionValidationJob", "DEFAULT")); + if (log.isDebugEnabled()) { + log.debug("Quartz session validation job stopped successfully."); + } + } catch (SchedulerException e) { + if (log.isDebugEnabled()) { + log.debug("Could not cleanly remove SessionValidationJob from Quartz scheduler. Ignoring and " + + "stopping.", e); + } + + } + + this.enabled = false; + + if (this.schedulerImplicitlyCreated) { + try { + scheduler.shutdown(); + } catch (SchedulerException e) { + if (log.isWarnEnabled()) { + log.warn("Unable to cleanly shutdown implicitly created Quartz Scheduler instance.", e); + } + } finally { + setScheduler(null); + this.schedulerImplicitlyCreated = false; + } + } + } + +} diff --git a/springboot-shiro2/src/main/java/cn/abel/rest/shiro/filter/ShiroFormAuthenticationFilter.java b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/filter/ShiroFormAuthenticationFilter.java new file mode 100644 index 0000000..2804e71 --- /dev/null +++ b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/filter/ShiroFormAuthenticationFilter.java @@ -0,0 +1,160 @@ +package cn.abel.rest.shiro.filter; + +import java.io.IOException; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletResponse; + +import cn.abel.code.InfoCode; +import cn.abel.response.ResponseEntity; +import cn.abel.rest.constants.Constants; +import cn.abel.rest.shiro.ShiroUser; +import cn.abel.rest.shiro.credentials.ThirdPartySupportedToken; +import com.alibaba.fastjson.JSON; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.apache.shiro.authc.*; +import org.apache.shiro.subject.Subject; +import org.apache.shiro.web.filter.authc.FormAuthenticationFilter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.MediaType; + +/** + * 登录filter + * + */ +public class ShiroFormAuthenticationFilter extends FormAuthenticationFilter { + private static final Logger logger = LoggerFactory.getLogger(ShiroFormAuthenticationFilter.class); + + @Override + public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { + //登录请求用户名和accessToken不能同时为空。 + if (isLoginRequest(request, response)) { + if (StringUtils.isBlank(getUsername(request)) + && StringUtils.isBlank(request.getParameter(Constants.THIRD_PARTY_ACCESS_TOKEN_NAME))) { + return responseDirectly((HttpServletResponse) response, + ResponseEntity.error(InfoCode.REQUEST_PARAM_ERROR)); + } + } + return super.onPreHandle(request, response, mappedValue); + } + + /** + * 当鉴权失败的时候执行的方法。 + * + * @param request + * @param response + * @return + * @throws Exception + */ + @Override + protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { + if (isLoginRequest(request, response)) { + if (isLoginSubmission(request, response)) { + return executeLogin(request, response); + } + //登录仅限HttpPost方式,其他任何方式是不合法的。 + return responseDirectly((HttpServletResponse) response, ResponseEntity.error(InfoCode.LOGIN_TYPE_ERROR)); + } + //调用非登录方法时认证失败的情况。 + return responseDirectly((HttpServletResponse) response, ResponseEntity.error(InfoCode.INVALID_TOKEN)); + } + + /** + * 登录验证失败后执行的方法。 + * + * @param token + * @param e + * @param request + * @param response + * @return + */ + @Override + protected boolean onLoginFailure( + AuthenticationToken token, + AuthenticationException e, + ServletRequest request, + ServletResponse response) { + HttpServletResponse resp = (HttpServletResponse) response; + if (e instanceof ExcessiveAttemptsException) { + return responseDirectly(resp, ResponseEntity.error(InfoCode.PASSWORD_ERROR_MORE_THAN)); + } else if (e instanceof UnknownAccountException) { + return responseDirectly(resp, ResponseEntity.error(InfoCode.PASSWORD_ERROR)); + } else if (e instanceof IncorrectCredentialsException) { + return responseDirectly(resp, ResponseEntity.error(InfoCode.PASSWORD_ERROR)); + } else if (e instanceof LockedAccountException) { + return responseDirectly(resp, ResponseEntity.error(InfoCode.USER_PROFILE_LOCK)); + } else if (e instanceof ExpiredCredentialsException) { + return responseDirectly(resp, ResponseEntity.error(InfoCode.INVALID_LOGIN)); + } else { + return responseDirectly(resp, ResponseEntity.error(InfoCode.SERVICE_UNAVAILABLE)); + } + } + + /** + * 登录成功后执行的方法。 + * + * @param token + * @param subject + * @param request + * @param response + * @return + * @throws Exception + */ + @Override + protected boolean onLoginSuccess( + AuthenticationToken token, + Subject subject, + ServletRequest request, + ServletResponse response) throws Exception { + ShiroUser user = (ShiroUser) subject.getPrincipal(); + user.setToken(subject.getSession().getId().toString()); + return true; + } + + /** + * 创建包含第三方accessToken的shiro token。 + * + * @param request + * @param response + * @return + */ + @Override + protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) { + String username = getUsername(request); + String password = getPassword(request); + String accessToken = request.getParameter(Constants.THIRD_PARTY_ACCESS_TOKEN_NAME); + //shiro后续流程可能会用到username,所以如果用accessToken登录时赋值username为它的值。 + if (StringUtils.isBlank(username)) { + username = accessToken; + } + return new ThirdPartySupportedToken(username, password, accessToken); + } + + /** + * 直接构造HttpResponse,不再执行后续的所有方法。 + * + * @param response + * @param entity + * @return + */ + private boolean responseDirectly(HttpServletResponse response, ResponseEntity entity) { + response.reset(); + response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); + + //设置跨域信息。 + response.setHeader("Access-Control-Allow-Origin", "*"); + response.setHeader("Access-Control-Allow-Credentials", "true"); + response.setHeader("Access-Control-Allow-Methods", "POST, GET, DELETE, PUT, OPTIONS, HEAD"); + response.setHeader("Access-Control-Max-Age", "3600"); + response.setHeader("Access-Control-Allow-Headers", "*"); + try { + response.getWriter().write(JSON.toJSONString(entity)); + } catch (IOException e) { + logger.error("ResponseError ex:{}", ExceptionUtils.getStackTrace(e)); + } + return false; + } +} diff --git a/springboot-shiro2/src/main/java/cn/abel/rest/shiro/filter/ShiroLogoutFilter.java b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/filter/ShiroLogoutFilter.java new file mode 100644 index 0000000..80b0d10 --- /dev/null +++ b/springboot-shiro2/src/main/java/cn/abel/rest/shiro/filter/ShiroLogoutFilter.java @@ -0,0 +1,26 @@ +package cn.abel.rest.shiro.filter; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +import org.apache.shiro.subject.Subject; +import org.apache.shiro.web.filter.authc.LogoutFilter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 注销filter + * + * @date 2019/02/26 13:32 + */ +public class ShiroLogoutFilter extends LogoutFilter { + + private static final Logger logger = LoggerFactory.getLogger(ShiroLogoutFilter.class); + + @Override + protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { + Subject subject = getSubject(request, response); + subject.logout(); + return true; + } +} \ No newline at end of file diff --git a/springboot-shiro2/src/main/java/cn/abel/rest/utils/ServletKit.java b/springboot-shiro2/src/main/java/cn/abel/rest/utils/ServletKit.java new file mode 100644 index 0000000..090d00c --- /dev/null +++ b/springboot-shiro2/src/main/java/cn/abel/rest/utils/ServletKit.java @@ -0,0 +1,36 @@ +package cn.abel.rest.utils; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.servlet.resource.ResourceUrlProvider; + +/** + * Servlet工具类 + * + * @date 2019/02/22 14:07 + */ +public class ServletKit { + + /** + * 在任意位置获取Request + * + * @return + */ + public static HttpServletRequest getRequest() { + return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + } + + /** + * 判断请求uri是否是静态资源方法 + * + * @param uri + * @return + */ + public static boolean isStaticFile(String uri) { + ResourceUrlProvider resourceUrlProvider = SpringContextKit.getBean(ResourceUrlProvider.class); + String staticUri = resourceUrlProvider.getForLookupPath(uri); + return staticUri != null; + } +} \ No newline at end of file diff --git a/springboot-shiro2/src/main/java/cn/abel/rest/utils/SpringContextKit.java b/springboot-shiro2/src/main/java/cn/abel/rest/utils/SpringContextKit.java new file mode 100644 index 0000000..7633886 --- /dev/null +++ b/springboot-shiro2/src/main/java/cn/abel/rest/utils/SpringContextKit.java @@ -0,0 +1,60 @@ +package cn.abel.rest.utils; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +/** + * 这个类是为了解决在普通类调用service的问题 + * + * @date 2018/10/24 15:16 + * @content OfflineMessageService offlineMessageService = (OfflineMessageService) SpringContextUtil + * .getBean("offlineMessageService"); + */ +@Component +public class SpringContextKit implements ApplicationContextAware { + + /** Spring应用上下文 */ + private static ApplicationContext applicationContext; + + /** 下面的这个方法上加了@Override注解,原因是继承ApplicationContextAware接口是必须实现的方法 */ + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + SpringContextKit.applicationContext = applicationContext; + } + + public static ApplicationContext getApplicationContext() { + return applicationContext; + } + + public static Object getBean(String name) throws BeansException { + return applicationContext.getBean(name); + } + + public static Object getBean(String name, Class requiredType) throws BeansException { + + return applicationContext.getBean(name, requiredType); + } + + public static T getBean(Class clazz) throws BeansException { + return applicationContext.getBean(clazz); + } + + public static boolean containsBean(String name) { + return applicationContext.containsBean(name); + } + + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { + return applicationContext.isSingleton(name); + } + + public static Class getType(String name) throws NoSuchBeanDefinitionException { + return applicationContext.getType(name); + } + + public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { + return applicationContext.getAliases(name); + } +} \ No newline at end of file diff --git a/springboot-shiro2/src/main/resources/META-INF/spring/consumer.xml b/springboot-shiro2/src/main/resources/META-INF/spring/consumer.xml new file mode 100644 index 0000000..a3cf9cf --- /dev/null +++ b/springboot-shiro2/src/main/resources/META-INF/spring/consumer.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/springboot-shiro2/src/main/resources/dev/application.properties b/springboot-shiro2/src/main/resources/dev/application.properties new file mode 100644 index 0000000..b2be9c2 --- /dev/null +++ b/springboot-shiro2/src/main/resources/dev/application.properties @@ -0,0 +1,94 @@ +## tomcat\u914D\u7F6E +server.servlet.context-path=/shiro +server.port=9738 +#server.tomcat.maxHttpHeaderSize=8192 +server.tomcat.uri-encoding=UTF-8 +spring.http.encoding.charset=UTF-8 +spring.http.encoding.enabled=true +spring.http.encoding.force=true +spring.messages.encoding=UTF-8 +# tomcat\u6700\u5927\u7EBF\u7A0B\u6570\uFF0C\u9ED8\u8BA4\u4E3A200 +server.tomcat.max-threads=800 +# session\u6700\u5927\u8D85\u65F6\u65F6\u95F4(\u5206\u949F)\uFF0C\u9ED8\u8BA4\u4E3A30 +server.session-timeout=60 + +## spring \u914D\u7F6E +spring.application.name=springboot-shiro +application.main=cn.abel.rest.ShiroRestApplication + +## LOG +logging.file=./logs/springboot-shiro.log + +## dubbo \u914D\u7F6E +dubbo.application.name=springboot-shiro +dubbo.registry.group=abel +#dubbo.registry.address=127.0.0.1:2181 +dubbo.registry.address=127.0.0.1:2181 +dubbo.registry.version=1.0.0 +dubbo.protocol.port=13182 +dubbo.annotation.package=cn.abel.rest +dubbo.log.file=./logs/springboot-shiro-dubbo.log + +## spring cache +#\u7F13\u5B58\u7684\u540D\u79F0\u96C6\u5408\uFF0C\u591A\u4E2A\u91C7\u7528\u9017\u53F7\u5206\u5272 +#spring.cache.cache-names=admin,role +#\u7F13\u5B58\u7684\u7C7B\u578B\uFF0C\u5B98\u65B9\u63D0\u4F9B\u4E86\u5F88\u591A\uFF0C\u8FD9\u91CC\u6211\u4EEC\u586B\u5199redis +spring.cache.type=redis +#\u662F\u5426\u7F13\u5B58null\u6570\u636E\uFF0C\u9ED8\u8BA4\u662Ffalse +#spring.cache.redis.cache-null-values=false +#redis\u4E2D\u7F13\u5B58\u8D85\u65F6\u7684\u65F6\u95F4\uFF0C\u9ED8\u8BA460000ms +#spring.cache.redis.time-to-live=60000 +#\u7F13\u5B58\u6570\u636Ekey\u662F\u5426\u4F7F\u7528\u524D\u7F00\uFF0C\u9ED8\u8BA4\u662Ftrue +#spring.cache.redis.use-key-prefix=true + +## redis \u914D\u7F6E +# Redis\u6570\u636E\u5E93\u7D22\u5F15 +spring.redis.database=3 +# Redis\u670D\u52A1\u5668\u5730\u5740 +spring.redis.host=127.0.0.1 +# Redis\u670D\u52A1\u5668\u8FDE\u63A5\u7AEF\u53E3 +spring.redis.port=6379 +# Redis\u670D\u52A1\u5668\u8FDE\u63A5\u5BC6\u7801\uFF08\u9ED8\u8BA4\u4E3A\u7A7A\uFF09 +spring.redis.password= +# \u8FDE\u63A5\u6C60\u6700\u5927\u8FDE\u63A5\u6570\uFF08\u4F7F\u7528\u8D1F\u503C\u8868\u793A\u6CA1\u6709\u9650\u5236\uFF09 +spring.redis.lettuce.pool.max-active=100 +# \u8FDE\u63A5\u6C60\u6700\u5927\u963B\u585E\u7B49\u5F85\u65F6\u95F4\uFF08\u4F7F\u7528\u8D1F\u503C\u8868\u793A\u6CA1\u6709\u9650\u5236\uFF09 +spring.redis.lettuce.pool.max-wait=1000 +# \u8FDE\u63A5\u6C60\u4E2D\u7684\u6700\u5927\u7A7A\u95F2\u8FDE\u63A5 +spring.redis.lettuce.pool.max-idle=50 +# \u8FDE\u63A5\u6C60\u4E2D\u7684\u6700\u5C0F\u7A7A\u95F2\u8FDE\u63A5 +spring.redis.lettuce.pool.min-idle=0 +# \u8FDE\u63A5\u8D85\u65F6\u65F6\u95F4\uFF08\u6BEB\u79D2\uFF09 +spring.redis.lettuce.shutdown-timeout=0 + +## view\u914D\u7F6E +# FreeMarker\u914D\u7F6E +# \u662F\u5426\u5F00\u542F\u6A21\u677F\u7F13\u5B58 +spring.freemarker.cache=false +# \u7F16\u7801\u683C\u5F0F +spring.freemarker.charset=UTF-8 +# \u6A21\u677F\u7684\u5A92\u4F53\u7C7B\u578B\u8BBE\u7F6E +spring.freemarker.content-type=text/html +# \u524D\u7F00\u8BBE\u7F6E \u9ED8\u8BA4\u4E3A "" +spring.freemarker.prefix= +# \u540E\u7F00\u8BBE\u7F6E \u9ED8\u8BA4\u4E3A .ftl +spring.freemarker.suffix=.ftl +# +#spring.freemarker.viewClass=com.yekai.demo.admin.freemarker.CustomFreeMarkerView +# +spring.freemarker.expose-request-attributes=true +spring.freemarker.expose-session-attributes=true +# \u5F00\u542F\u5B8F\u652F\u6301 +spring.freemarker.expose-spring-macro-helpers=true +# ftl\u4E2D\u83B7\u53D6request +spring.freemarker.request-context-attribute=request + +#\u767B\u5F55\u91CD\u8BD5\u6B21\u6570 +shiro.retryMax=5 +#\u767B\u5F55\u5931\u8D25\u9501\u5B9A\u65F6\u95F4\uFF08\u79D2\uFF09 +shiro.retryExpireTimeRedis=900 +#\u6388\u6743\u8D85\u65F6\u65F6\u95F4\uFF08\u79D2\uFF09 +shiro.authorizationExpireTimeRedis=3600 +#session\u8D85\u65F6\u65F6\u95F4\uFF08\u79D2\uFF09 +shiro.sessionExpireTimeRedis=3600 + diff --git a/springboot-shiro2/src/main/resources/dev/banner.txt b/springboot-shiro2/src/main/resources/dev/banner.txt new file mode 100644 index 0000000..07d3606 --- /dev/null +++ b/springboot-shiro2/src/main/resources/dev/banner.txt @@ -0,0 +1,5 @@ +######################################################## +# # +# dev # +# # +######################################################## \ No newline at end of file diff --git a/springboot-shiro2/src/main/resources/dev/logback-spring.xml b/springboot-shiro2/src/main/resources/dev/logback-spring.xml new file mode 100644 index 0000000..92810cd --- /dev/null +++ b/springboot-shiro2/src/main/resources/dev/logback-spring.xml @@ -0,0 +1,41 @@ + + + springboot-shiro + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + ${LOG_FILE} + + + ${LOG_FILE}.%d{yyyy-MM-dd} + + 60 + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%method - %msg%n + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/springboot-shiro2/src/main/resources/local/application.properties b/springboot-shiro2/src/main/resources/local/application.properties new file mode 100644 index 0000000..879dfe0 --- /dev/null +++ b/springboot-shiro2/src/main/resources/local/application.properties @@ -0,0 +1,94 @@ +## tomcat\u914D\u7F6E +server.servlet.context-path=/shiro +server.port=9738 +#server.tomcat.maxHttpHeaderSize=8192 +server.tomcat.uri-encoding=UTF-8 +spring.http.encoding.charset=UTF-8 +spring.http.encoding.enabled=true +spring.http.encoding.force=true +spring.messages.encoding=UTF-8 +# tomcat\u6700\u5927\u7EBF\u7A0B\u6570\uFF0C\u9ED8\u8BA4\u4E3A200 +server.tomcat.max-threads=800 +# session\u6700\u5927\u8D85\u65F6\u65F6\u95F4(\u5206\u949F)\uFF0C\u9ED8\u8BA4\u4E3A30 +server.session-timeout=60 + +## spring \u914D\u7F6E +spring.application.name=springboot-shiro +application.main=cn.abel.rest.ShiroRestApplication + +## LOG +logging.file=./logs/springboot-shiro.log + +## dubbo \u914D\u7F6E +dubbo.application.name=springboot-shiro +dubbo.registry.group=abel +#dubbo.registry.address=127.0.0.1:2181 +dubbo.registry.address=127.0.0.1:2181 +dubbo.registry.version=1.0.0 +dubbo.protocol.port=13182 +dubbo.annotation.package=cn.abel.rest +dubbo.log.file=./logs/springboot-shiro-dubbo.log + +## spring cache +#\u7F13\u5B58\u7684\u540D\u79F0\u96C6\u5408\uFF0C\u591A\u4E2A\u91C7\u7528\u9017\u53F7\u5206\u5272 +#spring.cache.cache-names=admin,role +#\u7F13\u5B58\u7684\u7C7B\u578B\uFF0C\u5B98\u65B9\u63D0\u4F9B\u4E86\u5F88\u591A\uFF0C\u8FD9\u91CC\u6211\u4EEC\u586B\u5199redis +spring.cache.type=redis +#\u662F\u5426\u7F13\u5B58null\u6570\u636E\uFF0C\u9ED8\u8BA4\u662Ffalse +#spring.cache.redis.cache-null-values=false +#redis\u4E2D\u7F13\u5B58\u8D85\u65F6\u7684\u65F6\u95F4\uFF0C\u9ED8\u8BA460000ms +#spring.cache.redis.time-to-live=60000 +#\u7F13\u5B58\u6570\u636Ekey\u662F\u5426\u4F7F\u7528\u524D\u7F00\uFF0C\u9ED8\u8BA4\u662Ftrue +#spring.cache.redis.use-key-prefix=true + +## redis \u914D\u7F6E +# Redis\u6570\u636E\u5E93\u7D22\u5F15 +spring.redis.database=3 +# Redis\u670D\u52A1\u5668\u5730\u5740 +spring.redis.host=127.0.0.1 +# Redis\u670D\u52A1\u5668\u8FDE\u63A5\u7AEF\u53E3 +spring.redis.port=6379 +# Redis\u670D\u52A1\u5668\u8FDE\u63A5\u5BC6\u7801\uFF08\u9ED8\u8BA4\u4E3A\u7A7A\uFF09 +spring.redis.password= +# \u8FDE\u63A5\u6C60\u6700\u5927\u8FDE\u63A5\u6570\uFF08\u4F7F\u7528\u8D1F\u503C\u8868\u793A\u6CA1\u6709\u9650\u5236\uFF09 +spring.redis.lettuce.pool.max-active=100 +# \u8FDE\u63A5\u6C60\u6700\u5927\u963B\u585E\u7B49\u5F85\u65F6\u95F4\uFF08\u4F7F\u7528\u8D1F\u503C\u8868\u793A\u6CA1\u6709\u9650\u5236\uFF09 +spring.redis.lettuce.pool.max-wait=1000 +# \u8FDE\u63A5\u6C60\u4E2D\u7684\u6700\u5927\u7A7A\u95F2\u8FDE\u63A5 +spring.redis.lettuce.pool.max-idle=50 +# \u8FDE\u63A5\u6C60\u4E2D\u7684\u6700\u5C0F\u7A7A\u95F2\u8FDE\u63A5 +spring.redis.lettuce.pool.min-idle=0 +# \u8FDE\u63A5\u8D85\u65F6\u65F6\u95F4\uFF08\u6BEB\u79D2\uFF09 +spring.redis.lettuce.shutdown-timeout=0 + +## view\u914D\u7F6E +# FreeMarker\u914D\u7F6E +# \u662F\u5426\u5F00\u542F\u6A21\u677F\u7F13\u5B58 +spring.freemarker.cache=false +# \u7F16\u7801\u683C\u5F0F +spring.freemarker.charset=UTF-8 +# \u6A21\u677F\u7684\u5A92\u4F53\u7C7B\u578B\u8BBE\u7F6E +spring.freemarker.content-type=text/html +# \u524D\u7F00\u8BBE\u7F6E \u9ED8\u8BA4\u4E3A "" +spring.freemarker.prefix= +# \u540E\u7F00\u8BBE\u7F6E \u9ED8\u8BA4\u4E3A .ftl +spring.freemarker.suffix=.ftl +# +#spring.freemarker.viewClass=com.yekai.demo.admin.freemarker.CustomFreeMarkerView +# +spring.freemarker.expose-request-attributes=true +spring.freemarker.expose-session-attributes=true +# \u5F00\u542F\u5B8F\u652F\u6301 +spring.freemarker.expose-spring-macro-helpers=true +# ftl\u4E2D\u83B7\u53D6request +spring.freemarker.request-context-attribute=request + +#\u767B\u5F55\u91CD\u8BD5\u6B21\u6570 +shiro.retryMax=5 +#\u767B\u5F55\u5931\u8D25\u9501\u5B9A\u65F6\u95F4\uFF08\u79D2\uFF09 +shiro.retryExpireTimeRedis=900 +#\u6388\u6743\u8D85\u65F6\u65F6\u95F4\uFF08\u79D2\uFF09 +shiro.authorizationExpireTimeRedis=3600 +#session\u8D85\u65F6\u65F6\u95F4\uFF08\u79D2\uFF09 +shiro.sessionExpireTimeRedis=3600 + diff --git a/springboot-shiro2/src/main/resources/local/banner.txt b/springboot-shiro2/src/main/resources/local/banner.txt new file mode 100644 index 0000000..42f7c59 --- /dev/null +++ b/springboot-shiro2/src/main/resources/local/banner.txt @@ -0,0 +1,5 @@ +######################################################## +# # +# local # +# # +######################################################## \ No newline at end of file diff --git a/springboot-shiro2/src/main/resources/local/logback-spring.xml b/springboot-shiro2/src/main/resources/local/logback-spring.xml new file mode 100644 index 0000000..1787857 --- /dev/null +++ b/springboot-shiro2/src/main/resources/local/logback-spring.xml @@ -0,0 +1,41 @@ + + + springboot-shiro + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + ${LOG_FILE} + + + ${LOG_FILE}.%d{yyyy-MM-dd} + + 60 + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%method - %msg%n + + + + + + + + + + + + + + + + + + \ No newline at end of file From 934dfa30fb2275a217b2b6332c997f133a6fdf87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Tue, 24 Mar 2020 15:40:06 +0800 Subject: [PATCH 37/44] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=9B=BE=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/cn/abel/code/InfoCode.java | 2 + .../\346\225\210\346\236\234\345\233\276.png" | Bin 0 -> 370571 bytes springboot-neo4j/pom.xml | 83 +++++++++++ .../main/java/cn/abel/neo4j/Application.java | 17 +++ .../main/java/cn/abel/neo4j/bean/King.java | 141 ++++++++++++++++++ .../main/java/cn/abel/neo4j/bean/Person.java | 25 ++++ .../main/java/cn/abel/neo4j/bean/Queen.java | 111 ++++++++++++++ .../bean/relation/FatherAndSonRelation.java | 49 ++++++ .../abel/neo4j/controller/KingController.java | 45 ++++++ .../main/java/cn/abel/neo4j/dao/KingDao.java | 36 +++++ .../main/java/cn/abel/neo4j/dao/Neo4jDao.java | 55 +++++++ .../java/cn/abel/neo4j/dao/Neo4jSession.java | 59 ++++++++ .../cn/abel/neo4j/service/KingService.java | 79 ++++++++++ .../src/main/resources/application.properties | 24 +++ .../src/main/resources/banner.txt | 5 + .../src/main/resources/logback-spring.xml | 41 +++++ 16 files changed, 772 insertions(+) create mode 100644 "springboot-neo4j/doc/\346\225\210\346\236\234\345\233\276.png" create mode 100644 springboot-neo4j/pom.xml create mode 100644 springboot-neo4j/src/main/java/cn/abel/neo4j/Application.java create mode 100644 springboot-neo4j/src/main/java/cn/abel/neo4j/bean/King.java create mode 100644 springboot-neo4j/src/main/java/cn/abel/neo4j/bean/Person.java create mode 100644 springboot-neo4j/src/main/java/cn/abel/neo4j/bean/Queen.java create mode 100644 springboot-neo4j/src/main/java/cn/abel/neo4j/bean/relation/FatherAndSonRelation.java create mode 100644 springboot-neo4j/src/main/java/cn/abel/neo4j/controller/KingController.java create mode 100644 springboot-neo4j/src/main/java/cn/abel/neo4j/dao/KingDao.java create mode 100644 springboot-neo4j/src/main/java/cn/abel/neo4j/dao/Neo4jDao.java create mode 100644 springboot-neo4j/src/main/java/cn/abel/neo4j/dao/Neo4jSession.java create mode 100644 springboot-neo4j/src/main/java/cn/abel/neo4j/service/KingService.java create mode 100644 springboot-neo4j/src/main/resources/application.properties create mode 100644 springboot-neo4j/src/main/resources/banner.txt create mode 100644 springboot-neo4j/src/main/resources/logback-spring.xml diff --git a/abel-util/src/main/java/cn/abel/code/InfoCode.java b/abel-util/src/main/java/cn/abel/code/InfoCode.java index a2a6c39..be6d98e 100644 --- a/abel-util/src/main/java/cn/abel/code/InfoCode.java +++ b/abel-util/src/main/java/cn/abel/code/InfoCode.java @@ -36,6 +36,8 @@ public enum InfoCode { ACCOUNT_SEARCH_FAIL(20037, "账号查询失败"), LOGIN_NAME_TYPE_ERROR(20038, "登录名不为空时,登录方式不能为空"), PASSWORD_SIMPLE_ERROR(20042, "密码错误,长度应为6-18,包含字母、数字和特殊字符"), + PASSWORD_ERROR_MORE_THAN(20043,"用户名密码连续错误已达5次,请15分钟后再试"), + LOGIN_TYPE_ERROR(20044, "不支持的登录类型"), SERVICE_UNAVAILABLE(50000, "操作失败,请重试"); private int status; diff --git "a/springboot-neo4j/doc/\346\225\210\346\236\234\345\233\276.png" "b/springboot-neo4j/doc/\346\225\210\346\236\234\345\233\276.png" new file mode 100644 index 0000000000000000000000000000000000000000..dd49d1c8ba8bcc2f9043f1e8c31667ed2fcf65eb GIT binary patch literal 370571 zcmZ_0by!v1);SPw`{tRZlt@VySux)rKP*;+n)0t!Qb~h zf55e`wP3C>$GAt#dyKXHZ?U*f%lfPg@j5Pz=#0Rg`X0RdwS4-38`0WabX0f8iL zBqAa&AtFL7Z*2iIGS!EG5ciK#f%~l3i=C{lAYteMFOJaYPbwaR@h*^<_)`gbgwQ*G z5)!|eaD6O|dO!EN^n6mr12qU$x{EUSi-(fb^nyT`H?$TCLMqLdOP52JgN<%OiKi(M zwUsfz89k~Z?15Y`meWi>}SQyt%xu(wHs+&NPr>{?cjHxxjormx&t?H_X*YF`$n zX;^=>b`gBk7L_CZehMk1OCpQYb6tye2m`^iBBg%~f!3I|alxE30tcaHoAmX~#dRZYy%zYe{Aq6o{47Gh{K9QnHL5z@y;J4r$L!*fa zy(Q(ctHTiC`x*9tCATTaJ?_ix&@ez!P$WvRj#jD>-=1*oR<-6PWgeo->_dRh9!^gx ztID=Ff4;XJE-CF*BNjnO@S;?pLG|(tJj?GY{Ma5;F-1>*yUR3FkL% z=?F}MtkE0;zxEomb}UcqfYVw8yk*gM4u%ar^s;(P{ZXh>feaY_wWD|%QB0D4BS(Adr%Kmvgcs<)mtev2z&$>~03yHrd`0 z+d`v&m#6{lJtPV|Bz>wo8G$sMs?fsJ_{0mhT!S_U)E*&9G$@>4;0T#22bssTy*%`T zr#Sq&h-+(}Dhajwjhu+u`6LYB2M|$-aP}v9M=deNddu?5s2BjX8iqYQ$LeAF>6aoN zOX{tr>C!Yx$&8OkCuLt@8T`{a&~%|$f`0iW{DiC_B7`XxwyarMsu|X)(%wS|g(vX7 z^``TdZ3{M9)t%)!FKMwKK_K%sGM0U;11_MHIsR;|5nT6O;Kt;FWW0L)+CLbsZH7?3 ze-uzu@SuL+HgWW6ngjGo<)tL<2ZVHP=iS`>%zc)Q^u6tA0)?%3feDqNk%8)C9&KAS zh{Nz@HQm4x{u!D*Y7i`+I|n2-=(S1LkRDu=y&)FAQek?lp;ex98A<3>z5QDmX??1V zqVuA~BBDu~>$Z0rx9FDHQU-k;ap}(3Zg)k4+xI`)Oh zPl#Qs7s)NC&>r&4C>`$5VNls^jDbHLqdjnSI6p&33iEtMnR2HsK{th+Z!Om0GK7L_ z`Dlre_+tO(%NiIJIUIXf$JRF^p~UbAX=F$puSCjHbynTc{uFdK?5Iz&v+gS<{S7ToY&AoaDR2(moe1PZ}7#|%?;3>l%E z)Hp*zHSg$DsZq%a>_vahbg@yJUsx5GEU~i#c+#XL^VeqKd^x zL}-F${6b0+$?PRB`GeS$Vvj7*T}}~a1vy+CfU)ndbz~$>0z@}Jk3pNpv_lU?Mq3R5?&vaIxF(NRc+GgG+%?&&YDG;q9t`BeT$ps+P#%c&J2@AY$5_gq8 z$oZvUO4IcHb$~l-WLZ$zx3PD%Lfh$QiswHrl-!J=I0Eb3)g;VPlT(+no5se*%EnGc z<5MZ7;)_4!o+nHh6*{RM8r`r{;rVjLWv4P^nPm{9Ev7c6k8gv{h#hJ9Q<}bOi3xsa zmU7D(93jjdoWwR99xs|QpJ@7Vui=}nm)|}Wm#3Jim{u;f~GH>IW zGcQjZ9;(5iN;Hb!Shqes8&mx(ruzE{3b_E+vyt}i(FtXSAs$X1}D zUafXH_jx9$I?hbd!e%^Lch`)rOw+`v@1iiNQL}6@6eI(Z+Y8>Sy%4=Pzo;hoh2nwY zfKrJPL*O5>6fzsKOE8}#PlZYqu2?w^V=ys0uDaK-RI3O5#d{dO*KZDeTEmdhGzKch zF~*53jx6H?cBm=dwe$DE9}^$xJ|y9t&rY^;blV2*7LMLzbO$pMF0$TaB}BeiE}Yvh z<6Gn(a7lJ5zYSy=((~5i45~e7J$ik3oFSIqO(n-97iZvQP#qJj60Z_G&0+E4@be+X zQjQC}K&!y1Gs;!N<@wp*<>LNrbxNRi)iO#mmEL~$%WiYr+m_|Nl&$98W;S=MFzh26 zA%j$XVx|(#BiAt7Fk(ZK5xpuhzQD%F`?oV3pRpU5t{A#w*nDlsp%D>KX%LMNCsAdP z3STz90b)htDick;IY1YnyAy{dA!XGS?cgZFjTtUny9hbO<1$z{7FPo}13s}8Duk@nSq0~*9Sk^B|o`2=z!mpZHtDIHb#ZUUyhx{`rhvMuRJIO^8#}XwaWh58l=eTIT zj>Su8M)Fg}$TsnIx=OBmXH%5P-jwF&mNCGym`i4Vr)E7DQW4tlT8p8WDJelKa;bmo zX!;}t!4oc^>q8&IA7+_n1Yc(vc*z-gO+NhX+D0qTsYU(n-=6-hi_i1 zaGi~w?w&iIc5D&AJ(>x+@2`?mw%Kit2f9VSh&B(j85|o_4pk*K>RdGP?C~UQlG==K zRSlIVtFRWExL)1uBmO`v!?{!4Q)?)@I&WM5#y|n2IF%y2LwgW$$pz&WoXv&03Yu$p zGhr|ptM4`0FRl*jdzO`N6m4K_5XBtDB;FgH$wdhw(+_^0s~WJG_1Wmyh}ck3fi9Y9 zQn1h;?SsqjDmK;_RMV`wIgd9o8Bz+$ovDegQ!cG4XU!{fNE)o$*cDLr&nr@bQ#s6I zDOA-s1vTp2cOnbF!Q+fKqhG#SF*qKc=K5w~Tu_-`I3vBFQF1*Roym~ndi;=Op5Hrl zQkYUDQDti$I`7&rlwV}-yB(r`ZrGUKkF%QR3{(Jh}0XsjSh#jG(!jNuYLrli-Jk(tY<0j~y=` zxEO+TLRubn%bH)7%jnzT$r*8CL>00ojkcz%rTyJh_!fNV4y$LqC6V2-rE&xU`))Ta zxXWy}!;UFtyju=M#|_=rE^dx@wZ((wg~y)L-!?%h7ttZ51ip;0T5la`*2jAL7y7q~ z-fM5y#M__WRQ68u79$o3)`r`xbq;za+*fZdQ!X&pXE^1o7Hu!ix$!)hsN@)IwK|bp z4lRc^yX`_gwwC*z_!8Ai5{?pmdRt|$aoMro>v2$WaFG+44C0Rz*gd4VmpUwt-$EI_ zd>}Zxyk7Ee)MNrM!D=x&=-(O7@3IuD-|F5TH1)cSKUVD}9oO$4HZG^!gx#%JJqCql zhDN2}3syRZ+&McD+vy1PMII}uLn5KDlf7_{nTJTtX=xPUgP4%mu}PLhJD*kh*5cN- zQ=TgM7IFp~T4>c7BK;?!{tNsnwDF&swV&kRp?=igj*R%dZC{ib6!oGaY^(S1Pys@O zsX<(kzj^&jTafw42vPb17DDV~VoNFuZDsbS0To3Z^OU{7)rSY+GuPUmNI*ly+P_>KPAs9?!`c(O zFeIeXr|1;Y#YNTU$o9h<>E<5~-15tvrs|_Z8G6eYR7nZmf74UXB$r$dYbW6mvF1+1 z*B&aiG55}!TP}1J`jj?WJh~{vg?F{Y!|}sD{{ZfnBg6q}v&sFy@S_jYD`+$!2*^Lb zJovFnx-})?c2G1cUEu$f*(8bAg^TUJ)yFC1ViC z5w!Vq?f0ia`k!xq2y^q&f6Dzz$+uxR{Mo3qkg(jppvvX#6mZLO%f8 z(lbzjuNfLcLRrP%Y_F_7hg<3fbOb5o8`+B3l^+VSv~_t5TfTfg0OE0N^ZLoPz}_Gc z;lG#p1BZ)1utsk;;#PT|LnM^~8sl;|OiHzRAv!$d%eKtr_n3K?P0P!)&+-wy_<&Gv z5&Oz|)P1pkT-9R}n;0#pHif7N<@qku#9Qz#Is|Z0B%)*h>pIhhh?SOGGNlaaC@M%F zHx_W>_wSGa30C%Rt8}ki_<< zmj?C@C%@s`znr5IrhsH{X$%)=u`P931@d|`!He1OT?WusIq%#4Gy6pHUtj{^K?x3w zIS9$njDNYenqrH{Jps+sQmLbZD9&3fM(l>rIX-O6^P`(hvkV44=FIOpXfy@_7F6~5;5uJ?_ni%Ob>`G(wv;tA~ zjXpQ|(|+YGz{~6~NV@!W+x_Kx->g1zR$X=HySZ(|3=!X)0POr2p~N3>r+@w7pM92G zLOEkLTn#6Ycc?k!BJirkU5r_lnD4f1{ifMFgh%)jK?5ppPh%32i0zx)u*5En9^46Nzk z0N7CWyl^c>D#Wd!!hC_*NeTk7knht>$%0HVk0(;r)5VW!4vRpxrd`P|u39JM<-CIu zt_M>Yj5s(r8|zyFbiK*RgL%3A7nw;B4eFos^V@0}sZufiZC`Lu(5rh8p(4+{1;Qf_ zD$br*b_}JwMrpAbg)Xqa-+KSB2``^Ukc(=OO0Mr%&MmJ&06uT@XWV?$Y>ZY!t%{Z= zV#6r!OGHG{2ekzH`RKD2cff}anY+Jg06tG z#>*wL-Xe1eke$Rkw%viZmx%>SiD8P(SBM-FW3ieT(U{5_EBNL*ey z#R#{m@h}#{5hzoQ@3@ztzli8|VZeIYhPa{66%&c`Sp2ANt*E$Rn=r^7R}s5*QTAE) zTh11ynPDsbUq1N90ocDmXsdH}j<<+DMF3)VAt{9Sx}wtcwAgQ!lrS~Mou)N)XR8HH zuAAr^96n$qC2iR7ZC3zL@x#C-r!Ag0?UFU<0E`;Nm(7>Rj9W57|FccwKe?yn#-GQn z)IHO*kj~@F4$M`*;>Uf_;{efK8EL;Laq*A7`8(XspQD+4MWk#~c(XjXnfg608!Z+$ z2Ai+58?NEpUaMPa*U7fGy`6b4shj1MhcekL>9%j`MVq(Figf;qrCX|g z2NLWza3p_>@muO@bn1@k+T;&G+{d=(T6H&3%~8FFE^-adTHS4BnYZf_xR3rs)6yJz z)+@u08ob@z(@AA(J?K`fxT@Sv%CO5fQ!^I>J$^wATl}TAz%M&+!vQ=+j}N!gX7j;V zMMNK#ufEy0jGHH>*!-K0sxeC}s>99xAF4ugmpNYfN$j@dNLQ4t*xy2en5H{0+7LNdW=nL?wSu74BrmpZK@V4G|#S zhOqN37z_bfpIMYYd8z+$(%M6!^peSqCCQ?vL&A7Dr9$s?{Vt5c;9?_2|9FoFj6zQM zHeAPb0_$}>qH&=_yWSvR2hSsw_IJIM8=5$mZGDzFTMPn%ta0{rt1L9Tvn|@W(Xes_ zntu*Z#PapvdZbdllo->JY$AUbOxn_`K54}B)4W_ks8+s!1%2JXDLrj z8D)G-cbkh)K_;e0!N+$Clb?8w<5K*U$HT5SR;>%PETYMaSI{R=60@f{ z?ugH^?V5IgS$DeKyd%u2U!ohLP$&9^tVSPjNP&UwTN8`x-ra!U-<2;LdKBz--N^eFutb_xohD1z%`&^^#2kbyoi~utW;Tsi5}9;LFf3n zr?_)Dyvvsx*Zw&D$QMK03U`pr3^ZPQI$b-*wy!yo9N(7Kos9-DM~`-Ychw9l_%Y+` zl?o~EI)vdSS}wq_yXGl-%4>v8tsO)2gtBL!D#{_2)v8X*<>sH#+?jO z(w>o-s{)Zs72>#EQNBy$etIgRLy~smWUlNKy6CLdvH6@Xk%kmWS(x^M76m>Q{J<8D zE=E^$89yYr_P%w8T@13Ysq&^UN|{c;D9^Vr)*?71rR?&CovyJT5)?sk*dTWOZ_dpr0Z+$7!6o13H_ z++Cjn)Oj1s?5aYIj|Qz8sgR~Yg5rEe5}Jus^$KSJx}N>k z=2|Zgx{S&PTzXi>y)dBH?6>d_a4Dq!@uaDy0G2q~SW>(?SBE2xNp~z*;b7Vox6y>M2R4BlVB+Ez6f^A)ybyj(-BdQj$d>>`R{WgSwro%83>6;ILD85i~~lr(pJ| zLT46{<)Nz7yPdf~Ih6F4CM`_bn;X^Y1Feu#e4*$lB^U&Ksau-nU7jw}H0%mgpJhCW z-`Aa#?);=WDhtwn%%@jY_Od>(^y&q49}kVS5^|g{8h;2^_uv*qmW@?S3V)Dx`--#4 zh0YAzNN2J;OjpzbP%`SIj5K%Fb3lms`sFF}o(YU3Y)2(4*`e7hnxzyL*|6({;93t# zw^yTRO?;Udu+*iD46hA+AJ`iaIYI;9mbUfNo5Zfs@kvDxAUDhoGS-*io5nc>s!bH& zNbQ13Q4Mv%WFLYk4ov)YsN-$5IaPT>b%oLH9S&rtJ0Px9x{IwY1A$4TZhu;>?#aY2C|b) zXzWc03k^9n{1jMe3PW=<+MTu6#rkIoKnU~q=Ah-EVxI4q0Sf4cyG4lHGE4kIR?W-c zhSg;(=AckLJFst;R=<&ZgohMnFrF=kV7ENqh8JJvEp_+KCB^ZO=M|MLe0&TCnBwOg zvQM)@F9IU4s(MMYh+M|DKu3mqm({kc_bHiDU22#K>z8_|Ik%1qF|ob+r2m^LJa`?E zwwDTQnyYUXK2_ry{{ifZ(}?@tyK!ZMhooxD&2(_;uhWy)2P*lyGW|b1wz&STTLt;m zHRztaY5rv_68F%3{HH9*HoxSr)kr|tOsd85d;y95buNNCpc+k)U+=)w{1n^AEwohF^2;~)8}mV7X1NXNg9e^Ktf)Ah zJHe%g-+HFk*Z;X8Van^pvN;y`a(Xy&nq|yZx#J2b(bC-eFhd0r#RDhjW_rT zoY*zB?CA5q8ihF%G5mVn^$w7uI+27q=4^!`KxNj7&YDQXqUH+#WhQ=mjMwkLjNf$+^V$HKDOFD5Zzv$)LQ=u43N-jD#Y+w4syZ+VM?Q8 zQG4G@lgWgu0yD=olJ-M3#H&$+^>}Cam&c`f@;9Wa&lCpH*ds8G=S#d6wM#m9uuzC9 zb~Zgt-{}(rb)imcVQPzn@v?NwR%(h*)G^jp-e!t?+-9~N)%8@Dyo5Qmt04-gbpxlh z+9eodS-Pm7T2@MjSTIs|MxIuZjBo#`y4^zsm$nz(ryZZ3?pDMJz4J}5ItaF_n2%=g ztw-JY`dK>1i4CV4#6B+-`qg-3#F&Ljlxbk$3+jz-@YU+_1PW;1&>|sXx+-3W+^m_V zt7Gi3P9e!mq$J8mE4KNy)8ebMMs(@Au^+7s~~IYAv6_mt>k$)^Yqpre$x4EqA%BJ1e>XzEF zekq7v64G5UQLB=HmDXE>sx}DaG_@AIfDfa@MIE>2OQ|AUqWx(_UaU)Ol$7!of;gD( zzvew=F0H}p4q6^qcS2Jt{4=_lpl)vy>h9AAY@d`&Ebrw$xgm9O?|@Jy^!`F{hJ&`- zG1M44aTCvn*4jT|D0(ieq&@9hL4ncXO^c@t-TW}0VHxV>x2qCi73`w@E$qqrIUHNR z^fyvm{pUCN(KSpy#R(@qp!m^ED%)6I%ob%@%A>9Y93MU($$YidV?W>>>t&!Ji-*5| zHAWC7#}~qXsBuyqepB8{TRwD8l(!)nWPUd)>&j;rlXmR5Y`$o+Zl}<~e#&xfRcYmv z@%ZC5B$ZE$)jo4zdKtp&qzXIfc!+0J6G1S50V{faXZ0N0Y=0_#cYaQvUng z1}t6&XVp*7+XC2_^>&gx%4jNpqir^fdR--@W_&X}50|z8HN0Bg%o340=HXa#aQKl$ zJD`g6CFZU_l*18yAI7Z?p0A$QeM37Be|!u@mYZwTda@$jJ>AsONsKb$%U zGX`$P{Q%bcL9fK3TwsDbe33mB^h2XdR=5Kw9dkI)`1tTIP;}p@o3?6~W?o4<*HB=3 zm&Ixumqt%5CFTcC0~ox%(en8%sn@1?vreE zZk>5+IjNKQLyYc?eT+x-$tMK&lIdO07NNkRYB%Sh6F^cbuv zK2`&w={+VpZ%C@>&v@Q!jI-D%BZ2JAZ|76CBgqzh`}%>D$Dl@w3*XDZnGgT0z!|;Y zG3pB8=h;)_-KPt)&j*710PZZRj3JkVSluCT1{Gyt2wf}$`y1({IR198-p_frxUi6D z0e)qrt9-Yy!TD;Rtr3?wX+Wuxy*{^k{+TUsStdY`0A&$teg+m%IR)4^b}Ld5bQvOH zP{vl5XSNAf?q!{7LvC8!#ExMxc?N|%?950%oYBR+F_QYlI zujvxb7sjR;)G01*0O5q;Edt?4zH?Rmls?|hwC8FM1v+psZ2^a?MS=K{Fk5|XlQZQz z0ywC$0cNI=-#SEYZbCaqfMAs6Qqd{Ul>u49dYO-ez-L0VxG)Wg(l@|s(tVM>TcW5$%v4XCMJ#WT`4Zz zeQWJe;>4IaCNia0tqn^_PVL0}Y|@J>2O^`IbB+Q>zXF`NEf5;#@O-aLju;J4{~Df+J9Y&K4yki#+X+>rY_l|!)HG^yxsi**stQ*?T2ASx(i(0z zi6REA(6YVx>%;^^&%kWuY&|neZE3%_xkm2GPA|!UWsx!2Dn6-#dvJpow+|(HT9qHK zAyd`+pAQ`Dzni&(C(@P#PvS5zp?!TVR;Ee52R+M3n{12ueIi5T_#z;faJ-jqCN+Bq=%m$Yk*PduKMF~~zxPdK zL1s&PL_-w7#wbYv1_UDH!`Wn~Z%~ z*Zr&4UsC;>jU|o~@V!&*tyQ~QbftvYti_xnp9Pna9 zG5D@#n`XWH6g zEO@h%3#f_-Rislq>6n8Q9dS;^mLzZ}SyahiB@hFUK?21XRY zhoDg@+TmL|aG|wZnPS_w8;bywsCWD$To~LQoM6;xh(?T=Mj5%HxQ*>MDFg}pV;kGh z9qh`;LRpf3a0v-538fs$bdgq)(Wvc3&gyU3lQri0RvCwRR+y1VLv=oOOcsPJbUajH zA>`YfH*1wF;g==MFq9-(+TAuCE!2eJcHb*E3U z?)+@f@x48O0GkOiGy@awz2JW!2;>cZcV3YsZJ$CC{Ck+aj4xF6sun(|>Aw|H#I)#V zS5|FBEGsdTGU}d~6QSF<8QA*@Kor_MG~h}Z85U>GCbuqIFD`_w@s{ z5fyl->T_5~F^%urWknm6ms-Lv@$B`WoTWAUJFzsi1oxD->jpA#CHn!*EV#~qdwo$m zvs;H-KPb`F4Cz!t{avW8LBKF;C*D|EQ{DNeh88qK^_X?|XFC615c6;ZGXaXIqC3xk zrSk&P>8&)(Soj`1YouujT|O5YGcJ>4EBRC^sO-{+>Xtrf&xa1a=eh~l;~?y1^wp0n zQnT|)iH;8My30EJ05n9tzrL`vAuoFMs8^qy3zO7Y_x0zD#>LsUxQJ|vnCvvuxP zbTF&28NvCt#<#$sxjJI8CxYqe(}W`UTGa;~18-ZZk1Ovi8E@tA z1()bp6k!XcFXibI1iSg%!}tf~xpKRz?>AF=pwe$MYE zy-|4Hy#EA4kL=?Yy@WK&m`+$Zl(6!BphPmul##Nh!=-d8W8OkAlA$MQGA&JO>St^P z>;}@cF0!G#Y_q_{ukXlS?N#Y(!|KA6q;4NxSY&^74H)23Kh9s67D&DHh5uq#S8t6I zT}?ca;fmRB;=w&tGDms-b3Qb^>f78b)}NZ|DIh?2%z=+Fxr+h(&jQ>`gb@3@s7jt< zMAbHKwy9VP0Fee}^>ZVh^jOxlIyP>yv7*%A_chIrIlnAF-mq5iTEMIuriSQiMQ=u? z9b7S;`=OeEYx_Qq*AXik+rC~gYza? z(#xtLKU_SgF7cb>|KRqQtTP&5;|>PTJZtiMFd)VXq381jY5bB6-`DN;3x5%`e4h1Y zgCiQ{c7w^d3#V@ihF!|9H(oE>uy*=lgDR4eyCXt(G@{uGLD%nQ!f6RWOB)peP{t3O zw&~U53)So$n)%XNVhX+5A^rIo$p0v7h5)ijb&tZQs;-K z-0|$8I!w@4=C(`|JWm}Pp?<^}uP;<383Y}aVcunI9z%fF1>j0+M%5Od0G&H))-ptG z#bnC+?Y%uYGgTQ~xh-0W;{ z%r&|9;<3ggpwcpnxJgJFoX!`Al8JbP@_gY;9GeT}PIm6J1^Crqlmx}jW%X?{_1Nbu-FTrLrPlj@Dse&26+0BZO8R^w3o9We~#I>t% ze0xkKaZ-z^XtP%XY^zmdTUDYHOoQarrv9K%tu*g%tBws1)k`Xtg-MwgRHo?hl@zH1 zVTTpc2|VQSJ!?Ouiq!;M{gDpNaA;&XPh!H5&&5jr{hi?p3v$&8k5cF!hIZWxOmT+pWT-k-amO z1q(GKAX0SGKr=u3bpv2WFR-(>!%Oc@_ai+&r`m~)O~lTAaM9e9b5_PROsQaPVIDU% z>Cq*|my7J49i5Ahg8`>pMGTP-Ls}M5=T-OCoO|90*nUSKI^y zV@_L12?qwCqZ0=fx2@}0)uhc#=IRT>=)wEnGcomRPRa~85oK$TGSMOMuO+7yUfljS z;>i<1s{4K~>$c@hSA|=9d^@=XJ*swQ7LPk5`PKSh+U*v|aLfoMJSI{T^_SX3b7P;4 zPwG@duP%ojPO0*sJ`3Rt^k~ql@8@CykYX@Y8w+Qzr?q3G9`~F38z0ZO^tzX?Hn}>X zTa}`V^tv;5ua5gBSFv7NZ-N_-WT67IWQVR54U2;t>jOj&Vz~DszUFWIY%UNVC1431 zsX==k<8u8AQcV51+IQcqT>`g;`{M*}^lGXs!V9(haHX-x=!?NkDJ;`+Yr04AWK-A30Zi#p1i0M(nW&|+e3A-}kdDzy>62j};(j)Lh{?nh z6q~3W-O6_TFF1nSUB!I!&DRVa8c!Ve$v!>Tv|@-Pn_STgB9jkq8N;@0e4T#BSft;wmi^p^mP%|2&UkLq(Pl!K{e@{s(^^mX=ayhqp0gf4z{&Pu*A!~Q27~q5 z?s8b%mUh@f^{-*8%Gg~z`y-DTTbMUh*Mj2`;dZC3qm=V7`UX&;MqLSm0SFVg<0xrT zfX>;Vyo>#C?Jg=1%NyBkD8BBl^^RfJ?24KLtJT~bz-H~%bRkn=?86Ew#pE;RNWyQI7(I&WT2irFBmnN|kLon~7!c1)PFb=3R<;Ka8{D!<9p__* z$XT@!I*YGYNCFel5kr8SujJ*rQ05?o6TI)`8XD1AcM`I51Lvs)x z?K||P-{QsA@3||XdW4zy`>tB`7nZzvj|tr=)=L*b@qUnsCoFxy+O#7TVWWx%}Hx`w{0!VV!gp5 z!qqgX(OJ3&==GY)McOgHXUX0qb8_)iu2jvNS+Z{beE3@Lg4f9Kx-X+cibyY`Hkar( z!K4mp(i7QnkLR8d8r2CMf(nEKj|>-ku!ghSNAXQL@Fev3#eLvIg~qkcR>{^^eqjUd z-TZ*$;jOjcUTsR%d{M2dl5xxY*IXYT7;FYxKF=q}g*1pGe(L53b7$+0mzwgJM5$e< z4O=^0PdSz^N1(9iLupH9)l?|OGYDd{hZ$_2WSv+WbzQz?Njjt)m#H9UojqYz1iBn* z+C_0C4Bh<>VZJ$z)v@5oqZ4t5`3*x3OhV6INM6CY4rb7nQsO*KbiV$u-wrRPrN z!O&c+f$0~!p<&hkRDx>SEw#n30j;^Fxw!9a$@$1d2t=Y1KrXd>lN3N+`GMZ`zD#K& zdD#l?99!x_5xlU|c5izXe~sX|=eiN%hrYtDBn+7Fcsid6DG`rdzX2Hdna_7M^olF? zNwPiO?3P)d=iy&pbx%#QXQ&!(YP%FR2yhWRwz=3^D-4XVc%c>!OS0XrN{!jx)u6E1 zRyaEw0k>C0xADhG3fYON)rt&D29N5ODEvc0bOOw6*iB^fs#Sd3@Is50j-3bCTzUA4 z>XT^g2M}IV8Jk_kqUfKy@AujeCUI28o7HHqcb_&b2v7std8>|WDLy;@QQe$; z%g#B)v89zJHvZ|u1RxD}`?Wc5lr$-WSRHD=JV@T0elbYDrDH6N;?o18l9<;Mv+l=?}hnYEn6F;E~(-?-v|=(yo_IzpEQ6*9*4R z2Q2|2z&Zl;sl+^7sC7bj<&`X$e$|v52ObWe^|ls$l$aaj z2O59yE#RHwW4s4*2vuJ-l{pk;bGhw)p{ctx)htW-HA`nY^?nokqc=!s5Gj~elRe|c zemcl_7(<1-+74SJw}cZ14i!rC8Z;lJkn?j>`?{?k!Z6scJl-+j-j+5KA@S>%sWn_! zxQZRzO=^c{0xNUr*_axrW18(1@lIT5bhd{e{9s6;u#-I(z8SdjSWd?U_D zix;Zo@}iwDRWrxsSo4*e{#|6s_k*==-FZoopPkA}YQK0~)PARgs@}UnIl;@sGr^;l zDxa!=PgTJ%nBJK6Tbm9^=3e0<|G^zCbfS}n)j{Fwrb*eJl_IaP9Ps-!xo4S99nvZ| z#l0OTfa|e+bh!dltjwzl*fr?_x0KII;eWjq@k2uAzB-SCpiA}%b@0R`X+k7>cftAw zGe_=&TpCy7f>9fEDqaH|lsy|!ZrAgz!f=8oU$*NnwT}6j;C*^!WTNQURL#>jy10_z zL%4SL-oMLsJM#KDO7FxA7(7EwiQMQ{0UWn|BUT?;zihJLgvokrg(GKUe+8JRy~1%L znGFvI-%3YOxBWfJ*6p^Lv$3>%w=7i;Co=w(fR8eR=uXH%lOMPseu38fns++m0F=Ad zFKVEvC}(IX1O6%r8$3(QLy(#sHC+{17@~H)sKuqXjB>sqkj`fN+c%hFO_egZ59Fhh zl}S_ILnTm=pdq;tEgEQEpcWHB6*uU40cjcTkGda_dC4aK(+;hub3c{;n5cN1I-C5} zj(I_`+@Q5~(Z)vH%a1pbz}jdfpr+c>v#@}v7uAq?$Sz_P+_BgKxtcz8D1~&`z-$yJ zrY_&JB3u2{5!o!`Kw7p9cUp7&qD1fT;BJW`RVG)&U>g$4TG3{QrYbrc`$X2`ag46& z7r5q}q+QMKww2U?>I=5%6+roz-Li|Wr=v9P2oiR7?8d;6C393&+hH zshsn>mw||7UsuxcZ_jc7t9){Wy9-?1?CCY5so!31eMl_U+@UuOtLbepwqugc)T}r^ zG@`iUW3Y!I$n2X=%WJa@4r9Iy+$MUk)SS$sS1f*9YQ}2xD3~`_Ubq~7R2_6%bGclm zVq7Tf6&>n4JQRx?O~>)Z2noQ(*d2u` zZGj6VGft9y4D6aS7s=AM^VWHpWokyWXTD2`KBHqWWj#b9~|?FSiqC;edon)85L4Tu_;MWou^xRyGdUqeU{IQW?a=Z zsoz6c-M!o@oa%f?Yx&U1Dd9p>Y>t+RNjSKm40D%tg%0i#7*P>ut8Qh5+X>QAmVc_a zibvSJOL6`9qfv-2AE;$0CdILi9HaVrb!EEX@5od+2~tvvk1Zuk3lscSO7(wt8N5bM;jMQJNhm zp+nMEZv9u!iToK)q5b7-vor<6-~>{~Z~G_Xh!gvCc~4b}Gy@AcaUEX<29};vA`Z(2 z;BL}!o(-G;8D@HC=XPfT-jZ%~QE?~+W=qg8u_+Qkx{(Mhv>UTb9|d?h z)$D=Bi}-=JkDd#-2;>c#n)8cQJ4HUe#!xSytlpt){wD+)#MMv%$P19-2KfYwxAc%cV|9Bw(9(@h-L4FqYOyyd%gWwp~7ZxsP z^NxR9vl)2Ob8s9G?tw1B@2(UmHqDhX5V&bsXnM=2dS{!*pl6Z40#CKklZ; zz%`LZ3jz0sg+X?AeS#6EkLPlpI*ku-?0iEas1g?~9`-OzOv$jz8(Uj|oj3!)@d9{c z*-k3}&)#lNQINvra)C@t;MmIHT>CMB*k;P(vPorA%T*MXeO@-BUy=o>BbVL$?7xMu zP%6jod{HXb<(t8~g6 zqHqwGB0djg7$aw-O+b@UUTIErf;p-&o`x((T2RyPR{%`3@6{X%RYsV)D%qUDN5xJ+ z$q)MMa_0V zaKA`w*$sWcUPq4;-Jf%0u+2=b_g`L2KGNM~0&8pO>UK*n9wCRo(^66hJbPp$@Lq5r zDC03Pet`R1@lJi4^Hp+Mh2UR9Sn|JTV0Rb`o;jk97?d#8ZJ`i8ctV&^|0*d|+)Y&3 zKVkVMI@7i$Zp@MG{lo;DAex;mMUN88QOk=@a`!jQ_xE|bbSj_}0nJ+D;b@ig)|ZrF zUy_I19rfdnH`lM@nCE^?}W{FF(zY665hgd{pZ9{@#L_S7MsR&#wSotno=QoqGF*TAe zc85r$mzP_|)G;OysUug~dO^_b?PrU27GLd3F%h-}tVn07RJ&_> z?ZbBt0|~U%#4j!+v#opL$3D0WAfXz8f)0A(lZLy>G{Xqpwo=3Kq^lzl5Ufad2lg(s zOR=y@Yh+szDmny8tLC7?=r?0B=_YG*uuknPvJ1bTjfCOf{1Vgi<(@47A8GAO7e63w zUX5NVtZR+^@4En+-7*GkHJd-953I`-En@CEV$lAl4lq>p=xC20W5sB9NVRznemRw4 zerXVo6FjM9F_aiJhWc_(B$?Z;ezi&A+T5xJgFhK( zKl@p0%{A9t^9ZxNiaymLYs)V|$fwYB-@b7&W^rQbU>4eq;o7r#5A5uua4I7H^>asL&s(xL$CW}G=lbjlqoF0 z%&6f1GwdjVHws2Q7P)cYOe)ddkG1Y}14$qNpA`^ibG7Z7(;Mz88o6)sQAPo?351(k1#YN=K zSCx<)F=jF)0d<4!0gWQeY!rCy6Aj8g>o}blmsse)aQ?SOP)_4@Q|CTk=t_;xC8KQTfNZlYA+6Y2I^`P!GaXaqOlB_M}&8 zWizQ=ln|ut);aZZOKY0nmL2^ROl zyfs6M8JhK8?Eh)^P{?6V5EaF||9g6S)GK91WcQaRG>-awVyQCIm6O6%nt|@y$C_{+ zk@J*ocw#O`}PxO8~8HBLBUTdDPvmT$C)3VY}j7S z0E%p%;Tev}em(5WKmXNA9XuLwJM3eY#GF{aRnLeyvUb+5z*khlA=q>O zS1dpId&+q9L+w9DwnwDpFGineEv|2qD18+rOcO4T$q#Em?DC1Gr%L7!LXmMzMKROX zq%4Zt6yszG0rKk1;tOd!nJHIgi(|4%S|E5mYR}B87a?3A3>z70zdletnGDPw-#tfCB9AlX-Vg|L43yQBH>ZQxDi#MU_%;B5iRZQ1BhP zn=<&NTtL24r7o__ae#xz_=( zN65G#75{|vcVaDnMatNi=qPY}hakD+c3GA*c5)L{b;N`J>VRBePG}TexQ46I{>`dd z$k5)@h^yC+a*18q$aEzIGMc*>gE(UoZ(3#OO3AeeJ{7=1t;G=IJmk}}9+_≤K9f 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

45? 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

y+i9_|R-Q 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 + + 4.0.0 + + cn.abel + springboot-neo4j + 1.0-SNAPSHOT + + org.springframework.boot + spring-boot-starter-parent + 2.0.4.RELEASE + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-starter-freemarker + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 1.3.2 + + + com.github.pagehelper + pagehelper-spring-boot-starter + 1.2.5 + + + mysql + mysql-connector-java + 5.1.40 + + + + + org.apache.commons + commons-lang3 + 3.4 + + + com.alibaba + fastjson + 1.2.40 + + + + + org.springframework.boot + spring-boot-starter-data-neo4j + + + org.neo4j + neo4j-ogm-http-driver + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + + \ No newline at end of file diff --git a/springboot-neo4j/src/main/java/cn/abel/neo4j/Application.java b/springboot-neo4j/src/main/java/cn/abel/neo4j/Application.java new file mode 100644 index 0000000..ee58401 --- /dev/null +++ b/springboot-neo4j/src/main/java/cn/abel/neo4j/Application.java @@ -0,0 +1,17 @@ +package cn.abel.neo4j; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; + +/** + * @author yyb + * @time 2019/3/26 + */ +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) +public class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/springboot-neo4j/src/main/java/cn/abel/neo4j/bean/King.java b/springboot-neo4j/src/main/java/cn/abel/neo4j/bean/King.java new file mode 100644 index 0000000..86a0f56 --- /dev/null +++ b/springboot-neo4j/src/main/java/cn/abel/neo4j/bean/King.java @@ -0,0 +1,141 @@ +package cn.abel.neo4j.bean; + + +import cn.abel.neo4j.bean.relation.FatherAndSonRelation; +import org.neo4j.ogm.annotation.NodeEntity; +import org.neo4j.ogm.annotation.Relationship; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * 皇帝 + * + * @author yyb + * @time 2020/3/23 + */ +@NodeEntity(label = "King") +public class King extends Person implements Serializable { + + /** + * 名字 + */ + private String name; + /** + * 家系:家族关系 + */ + private String clan; + /** + * 年号:生活的时代 + */ + private String times; + /** + * 谥号:人死之后,后人给予评价 + */ + private String posthumousTitle; + /** + * 庙号:君主于庙中被供奉时所称呼的名号 + */ + private String TempleNumber; + /** + * 陵墓 + */ + private String tomb; + /** + * 大事件 + */ + private String remark; + + @Relationship(type = "传位") + King successor; + + /** + * 第二种保存关系的方式 + */ + @Relationship + List sons; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getClan() { + return clan; + } + + public void setClan(String clan) { + this.clan = clan; + } + + public String getTimes() { + return times; + } + + public void setTimes(String times) { + this.times = times; + } + + public String getPosthumousTitle() { + return posthumousTitle; + } + + public void setPosthumousTitle(String posthumousTitle) { + this.posthumousTitle = posthumousTitle; + } + + public String getTempleNumber() { + return TempleNumber; + } + + public void setTempleNumber(String templeNumber) { + TempleNumber = templeNumber; + } + + public String getTomb() { + return tomb; + } + + public void setTomb(String tomb) { + this.tomb = tomb; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + public King getSuccessor() { + return successor; + } + + public void setSuccessor(King successor) { + this.successor = successor; + } + + public List getSons() { + return sons; + } + + public void setSons(List sons) { + this.sons = sons; + } + + /** + * 添加友谊的关系 + * @param + */ + public void addRelation(FatherAndSonRelation fatherAndSonRelation){ + if(this.sons == null){ + this.sons = new ArrayList<>(); + } + this.sons.add(fatherAndSonRelation); + } +} diff --git a/springboot-neo4j/src/main/java/cn/abel/neo4j/bean/Person.java b/springboot-neo4j/src/main/java/cn/abel/neo4j/bean/Person.java new file mode 100644 index 0000000..684069c --- /dev/null +++ b/springboot-neo4j/src/main/java/cn/abel/neo4j/bean/Person.java @@ -0,0 +1,25 @@ +package cn.abel.neo4j.bean; + +import org.neo4j.ogm.annotation.GeneratedValue; +import org.neo4j.ogm.annotation.Id; +import org.neo4j.ogm.annotation.Index; + +/** + * @author yyb + * @time 2020/3/23 + */ +public class Person { + + @Id +// @GeneratedValue 该注解应去除否则插入不成功 + @Index + private long id; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } +} diff --git a/springboot-neo4j/src/main/java/cn/abel/neo4j/bean/Queen.java b/springboot-neo4j/src/main/java/cn/abel/neo4j/bean/Queen.java new file mode 100644 index 0000000..db5dec5 --- /dev/null +++ b/springboot-neo4j/src/main/java/cn/abel/neo4j/bean/Queen.java @@ -0,0 +1,111 @@ +package cn.abel.neo4j.bean; + +import org.neo4j.ogm.annotation.Relationship; + +/** + * 皇后 + * + * @author yyb + * @time 2020/3/23 + */ +public class Queen extends Person { + /** + * 名字 + */ + private String name; + /** + * 家系:家族关系 + */ + private String clan; + /** + * 年号:生活的时代 + */ + private String times; + /** + * 庙号:君主于庙中被供奉时所称呼的名号 + */ + private String TempleNumber; + /** + * 谥号:人死之后,后人给予评价 + */ + private String posthumousTitle; + /** + * 陵墓 + */ + private String son; + /** + * 备注 + */ + private String remark; + + /** + * 关系指向自己 + */ + @Relationship(type = "皇后", direction = Relationship.INCOMING) + private King king; + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getClan() { + return clan; + } + + public void setClan(String clan) { + this.clan = clan; + } + + public String getTimes() { + return times; + } + + public void setTimes(String times) { + this.times = times; + } + + public String getTempleNumber() { + return TempleNumber; + } + + public void setTempleNumber(String templeNumber) { + TempleNumber = templeNumber; + } + + public String getPosthumousTitle() { + return posthumousTitle; + } + + public void setPosthumousTitle(String posthumousTitle) { + this.posthumousTitle = posthumousTitle; + } + + public String getSon() { + return son; + } + + public void setSon(String son) { + this.son = son; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + public King getKing() { + return king; + } + + public void setKing(King king) { + this.king = king; + } +} diff --git a/springboot-neo4j/src/main/java/cn/abel/neo4j/bean/relation/FatherAndSonRelation.java b/springboot-neo4j/src/main/java/cn/abel/neo4j/bean/relation/FatherAndSonRelation.java new file mode 100644 index 0000000..1b71a26 --- /dev/null +++ b/springboot-neo4j/src/main/java/cn/abel/neo4j/bean/relation/FatherAndSonRelation.java @@ -0,0 +1,49 @@ +package cn.abel.neo4j.bean.relation; + +import cn.abel.neo4j.bean.King; +import org.neo4j.ogm.annotation.EndNode; +import org.neo4j.ogm.annotation.Id; +import org.neo4j.ogm.annotation.RelationshipEntity; +import org.neo4j.ogm.annotation.StartNode; + +/** + * 父子关系实体 + * @author yyb + * @time 2020/3/24 + */ +@RelationshipEntity(type = "father_son") +public class FatherAndSonRelation { + + @Id + private long id; + + @StartNode + private King from; + + @EndNode + private King to; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public King getFrom() { + return from; + } + + public void setFrom(King from) { + this.from = from; + } + + public King getTo() { + return to; + } + + public void setTo(King to) { + this.to = to; + } +} diff --git a/springboot-neo4j/src/main/java/cn/abel/neo4j/controller/KingController.java b/springboot-neo4j/src/main/java/cn/abel/neo4j/controller/KingController.java new file mode 100644 index 0000000..0a34f80 --- /dev/null +++ b/springboot-neo4j/src/main/java/cn/abel/neo4j/controller/KingController.java @@ -0,0 +1,45 @@ +package cn.abel.neo4j.controller; + + +import cn.abel.neo4j.bean.King; +import cn.abel.neo4j.service.KingService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author yyb + * @time 2020/3/23 + */ +@Controller +@RequestMapping("/king") +public class KingController { + + @Autowired + private KingService kingService; + @RequestMapping(value = "/save/{id}", method = RequestMethod.GET) + @ResponseBody + public String save(@PathVariable long id){ + List kingList = new ArrayList<>(); + King king = new King(); + king.setId(id); + king.setName("朱元璋"); + king.setClan("创业者"); + king.setPosthumousTitle("高皇帝"); + king.setTimes("洪武(1368年——1398年)"); + king.setTempleNumber("太祖"); + king.setTomb("孝陵"); + king.setRemark("空印案,胡惟庸案"); + kingList.add(king); + kingService.saveKing(kingList); + return "ok"; + } + + +} diff --git a/springboot-neo4j/src/main/java/cn/abel/neo4j/dao/KingDao.java b/springboot-neo4j/src/main/java/cn/abel/neo4j/dao/KingDao.java new file mode 100644 index 0000000..b1247f1 --- /dev/null +++ b/springboot-neo4j/src/main/java/cn/abel/neo4j/dao/KingDao.java @@ -0,0 +1,36 @@ +package cn.abel.neo4j.dao; + +import cn.abel.neo4j.bean.King; +import org.springframework.data.neo4j.annotation.Query; +import org.springframework.data.neo4j.repository.Neo4jRepository; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 使用Springboot - data - neo4j 生成 + * @author yyb + * @time 2020/3/23 + */ +@Repository +@Transactional(rollbackFor = Exception.class) +public interface KingDao extends Neo4jRepository { + + /** + * 通过名字查找King + * + * @param name + * @return + */ + King findByName(String name); + + + @Query("match " + + "p=(a:King) - [r:传位*0..] -> " + + "(b:King) " + + "where b.name = {0} " + + "return p") + List inFriendship(String name); + +} diff --git a/springboot-neo4j/src/main/java/cn/abel/neo4j/dao/Neo4jDao.java b/springboot-neo4j/src/main/java/cn/abel/neo4j/dao/Neo4jDao.java new file mode 100644 index 0000000..6469a75 --- /dev/null +++ b/springboot-neo4j/src/main/java/cn/abel/neo4j/dao/Neo4jDao.java @@ -0,0 +1,55 @@ +package cn.abel.neo4j.dao; + +import org.neo4j.ogm.config.Configuration; +import org.neo4j.ogm.config.ConfigurationSource; +import org.neo4j.ogm.session.SessionFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Repository; + +import java.util.Properties; + +/** + * @author yyb + * @time 2020/3/23 + */ +@Repository +public class Neo4jDao { + private static final Logger logger = LoggerFactory.getLogger(Neo4jDao.class); + private static final String packages = "cn.abel.neo4j.bean"; + + private SessionFactory sessionFactory = null; + + @Value("${neo4j.url}") + private String boltUri; + @Value("${neo4j.username}") + private String username; + @Value("${neo4j.password}") + private String password; + + /** + * 打开一个到neo4j的连接会话。 + */ + public Neo4jSession open() { + SessionFactory factory = getSessionFactory(); + Neo4jSession session = new Neo4jSession(factory); + return session; + } + + private synchronized SessionFactory getSessionFactory() { + if (sessionFactory == null) { + ConfigurationSource props = () -> { + Properties properties = new Properties(); + properties.setProperty("URI", boltUri); + properties.setProperty("username", username); + properties.setProperty("password", password); + return properties; + }; + Configuration configuration = new Configuration.Builder(props).build(); + sessionFactory = new SessionFactory(configuration, packages); + } + return sessionFactory; + } +} + diff --git a/springboot-neo4j/src/main/java/cn/abel/neo4j/dao/Neo4jSession.java b/springboot-neo4j/src/main/java/cn/abel/neo4j/dao/Neo4jSession.java new file mode 100644 index 0000000..9e91857 --- /dev/null +++ b/springboot-neo4j/src/main/java/cn/abel/neo4j/dao/Neo4jSession.java @@ -0,0 +1,59 @@ +package cn.abel.neo4j.dao; + +import org.neo4j.ogm.session.Session; +import org.neo4j.ogm.session.SessionFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; + +/** + * @author yyb + * @time 2020/3/23 + */ +public class Neo4jSession { + private static final Logger logger = LoggerFactory.getLogger(Neo4jSession.class); + private Session session; + private static final int DEPTH_ENTITY = 1; + + public Neo4jSession(SessionFactory factory) { + session = factory.openSession(); + } + + /** + * 执行cypher命令。 + * 增、删、改、查的 cypher 命令都可以用此方法操作 + * + * @param cypher + * @return + */ + public Iterator> exec(String cypher) { + long time = System.currentTimeMillis(); + Iterator> it = session.query(cypher, Collections.EMPTY_MAP).iterator(); + logger.info("ExecCypher {} cost:{}ms", cypher, System.currentTimeMillis() - time); + return it; + } + + + /** + * 存储对象 + * + * @param o + * @return + */ + public void execSave(Object o) { + long time = System.currentTimeMillis(); + session.clear(); + session.save(o); + logger.info("ExecCypher {} cost:{}ms", o.toString(), System.currentTimeMillis() - time); + } + + /** + * 清空 session 缓存。 + */ + public void clear() { + session.clear(); + } +} diff --git a/springboot-neo4j/src/main/java/cn/abel/neo4j/service/KingService.java b/springboot-neo4j/src/main/java/cn/abel/neo4j/service/KingService.java new file mode 100644 index 0000000..2a682e9 --- /dev/null +++ b/springboot-neo4j/src/main/java/cn/abel/neo4j/service/KingService.java @@ -0,0 +1,79 @@ +package cn.abel.neo4j.service; + +import cn.abel.neo4j.bean.King; +import cn.abel.neo4j.bean.Queen; +import cn.abel.neo4j.bean.relation.FatherAndSonRelation; +import cn.abel.neo4j.dao.KingDao; +import cn.abel.neo4j.dao.Neo4jDao; +import cn.abel.neo4j.dao.Neo4jSession; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; + +/** + * @author yyb + * @time 2020/3/23 + */ +@Service +public class KingService { + @Autowired + private Neo4jDao neo4jDao; + + @Autowired + private KingDao kingDao; + + /** + * 保存皇帝信息 + * + * @param list + */ + public void saveKing(List list) { + Neo4jSession session = neo4jDao.open(); + list.forEach(x -> { + session.execSave(x); + }); + } + + /** + * 保存皇后信息 + * + * @param list + */ + public void saveQueen(List list) { + Neo4jSession session = neo4jDao.open(); + list.forEach(x -> { + session.execSave(x); + }); + } + + + /** + * 查询一个皇帝信息,此处使用spring-data-neo4j 接口 + * + * @param name + */ + public King findByName(String name) { + King t = kingDao.findByName(name); + return t; + } + + + /** + * 保存父子关系 + * + * @param fatherName + * @param sonName + * @return + */ + public void saveRelation(String fatherName, String sonName) { + King from = kingDao.findByName(fatherName); + King to = kingDao.findByName(sonName); + FatherAndSonRelation fatherAndSonRelation = new FatherAndSonRelation(); + fatherAndSonRelation.setFrom(from); + fatherAndSonRelation.setTo(to); + from.addRelation(fatherAndSonRelation); + kingDao.save(from); + } +} diff --git a/springboot-neo4j/src/main/resources/application.properties b/springboot-neo4j/src/main/resources/application.properties new file mode 100644 index 0000000..92cf343 --- /dev/null +++ b/springboot-neo4j/src/main/resources/application.properties @@ -0,0 +1,24 @@ +#application +spring.application.name=springboot-neo4j +application.main=cn.abel.Application +server.port=9657 +server.tomcat.uri-encoding=UTF-8 +spring.http.encoding.charset=UTF-8 +spring.http.encoding.enabled=true +spring.http.encoding.force=true +spring.messages.encoding=UTF-8 +server.tomcat.max-threads=200 +server.session-timeout=60 +log4j.logger.java.sql.Connection=DEBUG + + +logging.file=./logs/springboot-neo4j/springboot-neo4j.log + +spring.data.neo4j.url=bolt://localhost:7687 +spring.data.neo4j.username=neo4j +spring.data.neo4j.password=admin + + +neo4j.url=bolt://localhost:7687 +neo4j.username=neo4j +neo4j.password=admin \ No newline at end of file diff --git a/springboot-neo4j/src/main/resources/banner.txt b/springboot-neo4j/src/main/resources/banner.txt new file mode 100644 index 0000000..42f7c59 --- /dev/null +++ b/springboot-neo4j/src/main/resources/banner.txt @@ -0,0 +1,5 @@ +######################################################## +# # +# local # +# # +######################################################## \ No newline at end of file diff --git a/springboot-neo4j/src/main/resources/logback-spring.xml b/springboot-neo4j/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..6b5d7c9 --- /dev/null +++ b/springboot-neo4j/src/main/resources/logback-spring.xml @@ -0,0 +1,41 @@ + + + springboot-neo4j + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%method - %msg%n + + + + + + ${LOG_FILE} + + + ${LOG_FILE}.%d{yyyy-MM-dd} + + 60 + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36}.%method - %msg%n + + + + + + + + + + + + + + + + + + \ No newline at end of file From f759e1cc5d92659adeda5e62816e107c5ef4d391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Tue, 24 Mar 2020 15:41:06 +0800 Subject: [PATCH 38/44] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/test/java/cn/abel/neo4j/BaseTest.java | 32 + .../java/cn/abel/neo4j/service/InitData.java | 642 ++++++++++++++++++ .../abel/neo4j/service/KingServiceTest.java | 47 ++ 3 files changed, 721 insertions(+) create mode 100644 springboot-neo4j/src/test/java/cn/abel/neo4j/BaseTest.java create mode 100644 springboot-neo4j/src/test/java/cn/abel/neo4j/service/InitData.java create mode 100644 springboot-neo4j/src/test/java/cn/abel/neo4j/service/KingServiceTest.java diff --git a/springboot-neo4j/src/test/java/cn/abel/neo4j/BaseTest.java b/springboot-neo4j/src/test/java/cn/abel/neo4j/BaseTest.java new file mode 100644 index 0000000..c432af8 --- /dev/null +++ b/springboot-neo4j/src/test/java/cn/abel/neo4j/BaseTest.java @@ -0,0 +1,32 @@ +package cn.abel.neo4j; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.FilterType; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * @author yyb + * @time 2019/3/27 + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = Application.class) +@TestPropertySource("/application.properties") +public class BaseTest { + + @Configuration + @ComponentScan(basePackages = {"cn.abel"}, excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, value = Configuration.class)) + static class ComponentScanConfig { + + } + + + @Test + public void contextLoads() { + } + +} \ No newline at end of file diff --git a/springboot-neo4j/src/test/java/cn/abel/neo4j/service/InitData.java b/springboot-neo4j/src/test/java/cn/abel/neo4j/service/InitData.java new file mode 100644 index 0000000..4789cfa --- /dev/null +++ b/springboot-neo4j/src/test/java/cn/abel/neo4j/service/InitData.java @@ -0,0 +1,642 @@ +package cn.abel.neo4j.service; + +import cn.abel.neo4j.bean.King; +import cn.abel.neo4j.bean.Queen; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author yyb + * @time 2020/3/24 + */ +public class InitData { + + public static List initKing() { + List kingList = new ArrayList<>(); + + King king1 = new King(); + king1.setId(1L); + king1.setName("朱元璋"); + king1.setClan("朱五四之子朱棣之父"); + king1.setPosthumousTitle("高皇帝"); + king1.setTimes("洪武(1368年——1398年)"); + king1.setTempleNumber("太祖"); + king1.setTomb("孝陵"); + king1.setRemark("空印案,胡惟庸案"); + + King king2 = new King(); + king2.setId(2L); + king2.setName("朱允炆"); + king2.setClan("朱元璋之孙朱标次子"); + king2.setPosthumousTitle("成皇帝"); + king2.setTimes("建文(1399年——1402年)"); + king2.setTempleNumber("惠宗"); + king2.setTomb("上金贝古墓"); + king2.setRemark(""); + + + King king3 = new King(); + king3.setId(3L); + king3.setName("朱棣"); + king3.setClan("朱元璋四子"); + king3.setPosthumousTitle("文皇帝"); + king3.setTimes("永乐(1403年——1424年)"); + king3.setTempleNumber("成祖(原为太宗)"); + king3.setTomb("长陵"); + king3.setRemark(""); + + King king4 = new King(); + king4.setId(4L); + king4.setName("朱高炽"); + king4.setClan("朱棣长子"); + king4.setPosthumousTitle("昭皇帝"); + king4.setTimes("洪熙(1424年——1425年)"); + king4.setTempleNumber("仁宗"); + king4.setTomb("献陵"); + king4.setRemark(""); + + King king5 = new King(); + king5.setId(5L); + king5.setName("朱瞻基"); + king5.setClan("朱高炽长子"); + king5.setPosthumousTitle("章皇帝"); + king5.setTimes("宣德(1426年——1435年)"); + king5.setTempleNumber("宣宗"); + king5.setTomb("景陵"); + king5.setRemark(""); + + King king6 = new King(); + king6.setId(6L); + king6.setName("朱祁镇"); + king6.setClan("朱瞻基长子"); + king6.setPosthumousTitle("睿皇帝"); + king6.setTimes("正统(1436年——1449年) 天顺(1457年——1464年)"); + king6.setTempleNumber("英宗"); + king6.setTomb("裕陵"); + king6.setRemark(""); + + King king7 = new King(); + king7.setId(7L); + king7.setName("朱祁钰"); + king7.setClan("朱瞻基次子(庶出),朱祁镇之弟(后英宗复位,代宗被废)"); + king7.setPosthumousTitle("景皇帝"); + king7.setTimes("景泰(1450年——1457年) "); + king7.setTempleNumber("代宗"); + king7.setTomb("景泰陵"); + king7.setRemark(""); + + King king8 = new King(); + king8.setId(8L); + king8.setName("朱见深"); + king8.setClan("乃朱祁镇长子(庶出),朱祁钰从子"); + king8.setPosthumousTitle("纯皇帝"); + king8.setTimes("成化(1465年——1487年)"); + king8.setTempleNumber("宪宗"); + king8.setTomb("茂陵"); + king8.setRemark(""); + + King king9 = new King(); + king9.setId(9L); + king9.setName("朱祐樘"); + king9.setClan("乃朱见深三子(庶出)"); + king9.setPosthumousTitle("敬皇帝"); + king9.setTimes("弘治(1488年——1505年)"); + king9.setTempleNumber("孝宗"); + king9.setTomb("泰陵"); + king9.setRemark(""); + + King king10 = new King(); + king10.setId(10L); + king10.setName("朱厚照"); + king10.setClan("乃朱祐樘长子(武宗无嗣,乃由旁支皇室选择朱厚熜入继大统)"); + king10.setPosthumousTitle("高皇帝"); + king10.setTimes("洪武(1368年——1398年)"); + king10.setTempleNumber("武宗"); + king10.setTomb("孝陵"); + king10.setRemark(""); + + King king11 = new King(); + king11.setId(11L); + king11.setName("朱厚熜"); + king11.setClan("乃朱见深四子兴献王朱祐杬(庶出)之嫡长子,朱祐樘从子,朱厚照从弟"); + king11.setPosthumousTitle("肃皇帝"); + king11.setTimes("嘉靖(1522年——1566年)"); + king11.setTempleNumber("世宗"); + king11.setTomb("永陵"); + king11.setRemark("大仪礼"); + + King king12 = new King(); + king12.setId(12L); + king12.setName("朱载垕"); + king12.setClan("乃朱厚熜三子(庶出)"); + king12.setPosthumousTitle("庄皇帝"); + king12.setTimes("隆庆(1567年——1572年)"); + king12.setTempleNumber("穆宗"); + king12.setTomb("昭陵"); + king12.setRemark(""); + + + King king13 = new King(); + king13.setId(13L); + king13.setName("朱翊钧"); + king13.setClan("乃朱载垕三子(庶出)"); + king13.setPosthumousTitle("显皇帝"); + king13.setTimes("万历(1573年——1620年)"); + king13.setTempleNumber("神宗"); + king13.setTomb("定陵"); + king13.setRemark(""); + + King king14 = new King(); + king14.setId(14L); + king14.setName("朱常洛"); + king14.setClan("乃朱翊钧长子"); + king14.setPosthumousTitle("贞皇帝"); + king14.setTimes("泰昌(1620年)实际在位一个月"); + king14.setTempleNumber("光宗"); + king14.setTomb("庆陵"); + king14.setRemark(""); + + + King king15 = new King(); + king15.setId(15L); + king15.setName("朱由校"); + king15.setClan("乃朱常洛长子(庶出)(熹宗死后无嗣,乃诏令五弟信王朱由检入继大统)"); + king15.setPosthumousTitle("悊皇帝"); + king15.setTimes("天启(1621年——1627年)"); + king15.setTempleNumber("熹宗"); + king15.setTomb("德陵"); + king15.setRemark(""); + + + King king16 = new King(); + king16.setId(16L); + king16.setName("朱由检"); + king16.setClan("乃朱常洛五子(庶出)(毅宗死后,明朝覆灭,宗室乃于南方割据)"); + king16.setPosthumousTitle("烈皇帝"); + king16.setTimes("崇祯(1628年——1644年)"); + king16.setTempleNumber("毅宗(原为思宗)"); + king16.setTomb("思陵"); + king16.setRemark(""); + + + king1.setSuccessor(king2); + king2.setSuccessor(king3); + king3.setSuccessor(king4); + king4.setSuccessor(king5); + king5.setSuccessor(king6); + king6.setSuccessor(king7); + king7.setSuccessor(king8); + king8.setSuccessor(king9); + king9.setSuccessor(king10); + king10.setSuccessor(king11); + king11.setSuccessor(king12); + king12.setSuccessor(king13); + king13.setSuccessor(king14); + king14.setSuccessor(king15); + king15.setSuccessor(king16); + + kingList.add(king1); + kingList.add(king2); + kingList.add(king3); + kingList.add(king4); + kingList.add(king5); + kingList.add(king6); + kingList.add(king7); + kingList.add(king8); + kingList.add(king9); + kingList.add(king10); + kingList.add(king11); + kingList.add(king12); + kingList.add(king13); + kingList.add(king14); + kingList.add(king15); + kingList.add(king16); + + + return kingList; + } + + public static List initQueen() { + + King king1 = new King(); + king1.setId(1L); + king1.setName("朱元璋"); + king1.setClan("朱五四之子朱棣之父"); + king1.setPosthumousTitle("高皇帝"); + king1.setTimes("洪武(1368年——1398年)"); + king1.setTempleNumber("太祖"); + king1.setTomb("孝陵"); + king1.setRemark("空印案,胡惟庸案"); + + King king2 = new King(); + king2.setId(2L); + king2.setName("朱允炆"); + king2.setClan("朱元璋之孙朱标次子"); + king2.setPosthumousTitle("成皇帝"); + king2.setTimes("建文(1399年——1402年)"); + king2.setTempleNumber("惠宗"); + king2.setTomb("上金贝古墓"); + king2.setRemark(""); + + + King king3 = new King(); + king3.setId(3L); + king3.setName("朱棣"); + king3.setClan("朱元璋四子"); + king3.setPosthumousTitle("文皇帝"); + king3.setTimes("永乐(1403年——1424年)"); + king3.setTempleNumber("成祖(原为太宗)"); + king3.setTomb("长陵"); + king3.setRemark(""); + + King king4 = new King(); + king4.setId(4L); + king4.setName("朱高炽"); + king4.setClan("朱棣长子"); + king4.setPosthumousTitle("昭皇帝"); + king4.setTimes("洪熙(1424年——1425年)"); + king4.setTempleNumber("仁宗"); + king4.setTomb("献陵"); + king4.setRemark(""); + + King king5 = new King(); + king5.setId(5L); + king5.setName("朱瞻基"); + king5.setClan("朱高炽长子"); + king5.setPosthumousTitle("章皇帝"); + king5.setTimes("宣德(1426年——1435年)"); + king5.setTempleNumber("宣宗"); + king5.setTomb("景陵"); + king5.setRemark(""); + + King king6 = new King(); + king6.setId(6L); + king6.setName("朱祁镇"); + king6.setClan("朱瞻基长子"); + king6.setPosthumousTitle("睿皇帝"); + king6.setTimes("正统(1436年——1449年) 天顺(1457年——1464年)"); + king6.setTempleNumber("英宗"); + king6.setTomb("裕陵"); + king6.setRemark(""); + + King king7 = new King(); + king7.setId(7L); + king7.setName("朱祁钰"); + king7.setClan("朱瞻基次子(庶出),朱祁镇之弟(后英宗复位,代宗被废)"); + king7.setPosthumousTitle("景皇帝"); + king7.setTimes("景泰(1450年——1457年) "); + king7.setTempleNumber("代宗"); + king7.setTomb("景泰陵"); + king7.setRemark(""); + + King king8 = new King(); + king8.setId(8L); + king8.setName("朱见深"); + king8.setClan("乃朱祁镇长子(庶出),朱祁钰从子"); + king8.setPosthumousTitle("纯皇帝"); + king8.setTimes("成化(1465年——1487年)"); + king8.setTempleNumber("宪宗"); + king8.setTomb("茂陵"); + king8.setRemark(""); + + King king9 = new King(); + king9.setId(9L); + king9.setName("朱祐樘"); + king9.setClan("乃朱见深三子(庶出)"); + king9.setPosthumousTitle("敬皇帝"); + king9.setTimes("弘治(1488年——1505年)"); + king9.setTempleNumber("孝宗"); + king9.setTomb("泰陵"); + king9.setRemark(""); + + King king10 = new King(); + king10.setId(10L); + king10.setName("朱厚照"); + king10.setClan("乃朱祐樘长子(武宗无嗣,乃由旁支皇室选择朱厚熜入继大统)"); + king10.setPosthumousTitle("高皇帝"); + king10.setTimes("洪武(1368年——1398年)"); + king10.setTempleNumber("武宗"); + king10.setTomb("孝陵"); + king10.setRemark(""); + + King king11 = new King(); + king11.setId(11L); + king11.setName("朱厚熜"); + king11.setClan("乃朱见深四子兴献王朱祐杬(庶出)之嫡长子,朱祐樘从子,朱厚照从弟"); + king11.setPosthumousTitle("肃皇帝"); + king11.setTimes("嘉靖(1522年——1566年)"); + king11.setTempleNumber("世宗"); + king11.setTomb("永陵"); + king11.setRemark("大仪礼"); + + King king12 = new King(); + king12.setId(12L); + king12.setName("朱载垕"); + king12.setClan("乃朱厚熜三子(庶出)"); + king12.setPosthumousTitle("庄皇帝"); + king12.setTimes("隆庆(1567年——1572年)"); + king12.setTempleNumber("穆宗"); + king12.setTomb("昭陵"); + king12.setRemark(""); + + + King king13 = new King(); + king13.setId(13L); + king13.setName("朱翊钧"); + king13.setClan("乃朱载垕三子(庶出)"); + king13.setPosthumousTitle("显皇帝"); + king13.setTimes("万历(1573年——1620年)"); + king13.setTempleNumber("神宗"); + king13.setTomb("定陵"); + king13.setRemark(""); + + King king14 = new King(); + king14.setId(14L); + king14.setName("朱常洛"); + king14.setClan("乃朱翊钧长子"); + king14.setPosthumousTitle("贞皇帝"); + king14.setTimes("泰昌(1620年)实际在位一个月"); + king14.setTempleNumber("光宗"); + king14.setTomb("庆陵"); + king14.setRemark(""); + + + King king15 = new King(); + king15.setId(15L); + king15.setName("朱由校"); + king15.setClan("乃朱常洛长子(庶出)(熹宗死后无嗣,乃诏令五弟信王朱由检入继大统)"); + king15.setPosthumousTitle("悊皇帝"); + king15.setTimes("天启(1621年——1627年)"); + king15.setTempleNumber("熹宗"); + king15.setTomb("德陵"); + king15.setRemark(""); + + + King king16 = new King(); + king16.setId(16L); + king16.setName("朱由检"); + king16.setClan("乃朱常洛五子(庶出)(毅宗死后,明朝覆灭,宗室乃于南方割据)"); + king16.setPosthumousTitle("烈皇帝"); + king16.setTimes("崇祯(1628年——1644年)"); + king16.setTempleNumber("毅宗(原为思宗)"); + king16.setTomb("思陵"); + king16.setRemark(""); + + /** + * 初始化皇后信息 + */ + List queenList = new ArrayList<>(); + + Queen queen1 = new Queen(); + queen1.setId(101L); + queen1.setPosthumousTitle("孝慈高皇后"); + queen1.setName("马秀英"); + queen1.setRemark("安徽宿州人,早年丧母,被郭子兴夫妇收养为义女。1382年(洪武十五年)五十一岁的马皇后病逝。"); + queen1.setTimes("1368年1月-1382年"); + queen1.setSon("明兴宗朱标、秦愍王朱樉、晋恭王朱㭎、明成祖朱棣、周定王朱橚(五子皆存疑),宁国公主,安庆公主"); + queen1.setKing(king1); + + + Queen queen2 = new Queen(); + queen2.setId(102L); + queen2.setPosthumousTitle("孝愍让皇后"); + queen2.setName("马全女"); + queen2.setRemark("洪武二十八年十月,册后为皇太孙妃。建文改元,立为后。生二子,文煃、文圭。"); + queen2.setTimes("1399年-1402年"); + queen2.setSon("和简太子朱文奎,润怀王朱文圭"); + queen2.setKing(king2); + + Queen queen3 = new Queen(); + queen3.setId(103L); + queen3.setPosthumousTitle("孝文皇后"); + queen3.setName("徐氏 "); + queen3.setTimes("1402年11月-1407年"); + queen3.setSon("明仁宗朱高炽、汉王朱高煦、赵王朱高燧"); + queen3.setRemark("洪武九年(1376年)册封为燕王妃,朱棣称帝册封为皇后。永乐五年(1407年)七月四日去世,享年46岁。"); + queen3.setKing(king3); + + Queen queen4 = new Queen(); + queen4.setId(104L); + queen4.setPosthumousTitle("诚孝昭皇后"); + queen4.setName("张氏"); + queen4.setRemark("仁宗元配,永城人,指挥使赠彭城侯张麒诚孝昭皇后画像之女。洪武二十八年(1395年)封燕王世子妃,永乐二年(1404年)封皇太子妃。仁宗即位,册立为皇后。宣宗即位,尊为皇太后。英宗即位,尊为太皇太后。正统七年(1442年)十月十八日去世."); + queen4.setTimes("1424年7月-1442年"); + queen4.setSon("明宣宗朱瞻基、越靖王朱瞻墉、襄宪王朱瞻墡、嘉兴公主"); + queen4.setKing(king4); + + Queen queen5 = new Queen(); + queen5.setId(105L); + queen5.setPosthumousTitle("恭让章皇后"); + queen5.setName("胡善祥"); + queen5.setTimes("1426年-1428年"); + queen5.setSon("顺德公主"); + queen5.setRemark("皇后,后被废黜,后被追谥为恭让章皇后"); + queen5.setKing(king5); + + Queen queen51 = new Queen(); + queen51.setId(106L); + queen51.setPosthumousTitle("孝翼皇后"); + queen51.setName("吴氏"); + queen51.setSon("明代宗朱祁钰"); + queen51.setRemark("太子妾,贤妃,后成皇太后,后被废,南明追谥孝翼温惠淑慎慈仁匡天锡圣太后"); + queen51.setKing(king5); + + Queen queen52 = new Queen(); + queen52.setId(107L); + queen52.setPosthumousTitle("孝恭章皇后"); + queen52.setName("孙氏"); + queen52.setTimes("1426年-1462年"); + queen52.setSon("常德公主、明英宗(存疑)"); + queen52.setRemark(""); + queen52.setKing(king5); + + Queen queen6 = new Queen(); + queen6.setId(108L); + queen6.setPosthumousTitle("孝庄睿皇后"); + queen6.setName("钱氏"); + queen6.setTimes("1442年-1468年6月"); + queen6.setRemark("英宗元配,海州人,都指挥佥事(后封安昌伯)钱贵女。正统七年(1442年)立为皇后。宪宗即位,尊为皇太后,加“慈懿”徽号。成化四年(1468年)六月二十六日,钱氏病故。 "); + queen6.setKing(king6); + + Queen queen61 = new Queen(); + queen61.setId(109L); + queen61.setPosthumousTitle("孝肃后"); + queen61.setName("周氏"); + queen61.setTimes("1464年-1504"); + queen61.setSon("重庆公主、明宪宗朱见深、崇简王朱见泽"); + queen61.setRemark("宪宗生母,昌平州文宁里柳林村(今属北京市海淀区)人,锦衣卫千户追封庆云侯赠宁国公周能的女儿。正统十二年(1447年)生宪宗皇帝,天顺元年(1457年)册封为贵妃。宪宗即位,尊为皇太后。孝宗即位后,尊为太皇太后。弘治十七年(1504年)三月一日去世."); + queen61.setKing(king6); + + Queen queen7 = new Queen(); + queen7.setId(110L); + queen7.setPosthumousTitle("孝渊皇后"); + queen7.setName("汪氏"); + queen7.setTimes("1449年-1452年"); + queen7.setSon("固安公主、二公主(出家)"); + queen7.setRemark("北京人,祖父汪泉世为金吾左卫指挥使。正统十年(1445),郕王朱祁钰18岁,册封汪氏为郕王妃。正统十四年(1449) 册封王妃汪氏为皇后。进汪皇后祖父汪泉为都指挥同知;,汪氏去世与景帝合葬金山。"); + queen7.setKing(king7); + + Queen queen71 = new Queen(); + queen71.setId(111L); + queen71.setPosthumousTitle("肃孝皇后"); + queen71.setName("杭氏"); + queen71.setSon("怀献太子朱见济"); + queen71.setTimes("1452年-1453年"); + queen71.setRemark("妾,后为贵妃,后成皇后,后被明英宗追废"); + queen71.setKing(king7); + + Queen queen8 = new Queen(); + queen8.setId(112L); + queen8.setPosthumousTitle("皇后"); + queen8.setName("吴氏"); + queen8.setTimes("1464年7月27日-1464年8月28日"); + queen8.setSon("曾抚养明孝宗朱祐樘"); + queen8.setRemark("皇后,后被废黜,在位仅31日。为明朝在位时期最短的皇后。"); + queen8.setKing(king8); + + Queen queen81 = new Queen(); + queen81.setId(113L); + queen81.setPosthumousTitle("孝贞纯皇后"); + queen81.setName("王氏"); + queen81.setTimes("1464年10月12日-1518年2月"); + queen81.setRemark("皇后,后成皇太后"); + queen81.setKing(king8); + + + Queen queen82 = new Queen(); + queen82.setId(114L); + queen82.setPosthumousTitle("孝穆皇后"); + queen82.setName("纪氏"); + queen82.setSon("明孝宗朱祐樘"); + queen82.setRemark("宫人,后成淑妃,后被追谥孝穆皇后, 瑶族人。暴死"); + queen82.setKing(king8); + + Queen queen83 = new Queen(); + queen83.setId(115L); + queen83.setPosthumousTitle("孝惠皇后"); + queen83.setName("邵氏"); + queen83.setSon("宸妃,后成贵妃,嘉靖帝尊为寿安皇太后,后被追谥孝惠皇后"); + queen83.setRemark("宫人,后成淑妃,后被追谥孝穆皇后, 瑶族人。暴死"); + queen83.setKing(king8); + + Queen queen9 = new Queen(); + queen9.setId(116L); + queen9.setPosthumousTitle("孝成敬皇后"); + queen9.setName("张氏"); + queen9.setTimes("1486年-1541年"); + queen9.setSon("明武宗朱厚照、朱厚炜、太康公主朱秀荣"); + queen9.setRemark("皇后,后成皇伯母皇太后,追谥孝康敬皇后,后改谥孝成敬皇后,丈夫孝宗在世期间未曾册封嫔妃,是中国历史上唯一终生一夫一妻的皇帝"); + queen9.setKing(king9); + + + + Queen queen10 = new Queen(); + queen10.setId(117L); + queen10.setPosthumousTitle("孝静皇后"); + queen10.setName("夏氏"); + queen10.setTimes("1506年-1535年1月"); + queen10.setRemark("皇后,嘉靖帝尊封为皇嫂庄肃皇后"); + queen10.setKing(king10); + + + + Queen queen11 = new Queen(); + queen11.setId(118L); + queen11.setPosthumousTitle("孝洁肃皇后"); + queen11.setName("陈氏"); + queen11.setTimes("1522年-1528年10月"); + queen11.setRemark("皇后,谥悼灵皇后,后改谥孝洁恭懿慈睿安庄相天翊圣肃皇后, 受惊流产而亡"); + queen11.setKing(king11); + + Queen queen12 = new Queen(); + queen12.setId(119L); + queen12.setPosthumousTitle("孝懿庄皇后"); + queen12.setName("李氏"); + queen12.setTimes("?-1558年4月"); + queen12.setSon("宪怀太子朱翊、蓬莱公主"); + queen12.setRemark("结髪之妻,正室,后被追谥为孝懿贞惠顺哲恭仁俪天襄圣庄皇后"); + queen12.setKing(king12); + + + Queen queen121 = new Queen(); + queen121.setId(120L); + queen121.setPosthumousTitle("孝安皇后"); + queen121.setName("陈氏"); + queen121.setTimes("1567年3月-1596年7月"); + queen121.setSon("太和公主"); + queen121.setRemark("继室,后为皇后,后成皇太后,后被追谥为孝安贞懿恭纯温惠佐天弘圣皇后"); + queen121.setKing(king12); + + Queen queen13 = new Queen(); + queen13.setId(121L); + queen13.setPosthumousTitle("孝端皇后"); + queen13.setName("王喜姐"); + queen13.setTimes("1578年-1620年"); + queen13.setRemark("荣昌公主朱轩媖"); + queen13.setKing(king13); + + + Queen queen14 = new Queen(); + queen14.setId(122L); + queen14.setPosthumousTitle("孝元贞皇后"); + queen14.setName("郭氏"); + queen14.setSon("怀淑公主朱徽娟"); + queen14.setRemark("结髪之妻,正室太子妃,后被追谥为孝元昭懿哲惠庄仁合天弼圣贞皇后"); + queen14.setKing(king14); + + + Queen queen15 = new Queen(); + queen15.setId(123L); + queen15.setPosthumousTitle("孝哀悊皇后"); + queen15.setName("张氏"); + queen15.setTimes("1621年4月-1644年2月28日"); + queen15.setSon("太子朱慈燃"); + queen15.setRemark("皇后,崇祯帝尊封为皇嫂懿安皇后,南明追谥孝哀慈靖恭惠温贞偕天协圣悊皇后,明亡时自缢身亡。"); + queen15.setKing(king15); + + + Queen queen16 = new Queen(); + queen16.setId(124L); + queen16.setPosthumousTitle("孝节烈皇后"); + queen16.setName("周氏"); + queen16.setTimes("1628年-1644年2月28日"); + queen16.setSon("坤仪公主、明悼帝朱慈烺、怀隐王朱慈烜、定哀王朱慈炯"); + queen16.setRemark("结髪之妻,正室,后成皇后,清追谥为庄烈愍皇后,南明追谥孝节贞肃渊恭庄毅奉天靖圣烈皇后,自杀"); + queen16.setKing(king16); + + + queenList.add(queen1); + queenList.add(queen2); + queenList.add(queen3); + queenList.add(queen4); + queenList.add(queen5); + queenList.add(queen5); + queenList.add(queen51); + queenList.add(queen52); + + queenList.add(queen6); + queenList.add(queen61); + + queenList.add(queen7); + queenList.add(queen71); + + queenList.add(queen8); + queenList.add(queen81); + queenList.add(queen82); + queenList.add(queen83); + + queenList.add(queen9); + queenList.add(queen10); + queenList.add(queen11); + queenList.add(queen12); + queenList.add(queen121); + + queenList.add(queen13); + queenList.add(queen14); + queenList.add(queen15); + queenList.add(queen16); + + return queenList; + } +} diff --git a/springboot-neo4j/src/test/java/cn/abel/neo4j/service/KingServiceTest.java b/springboot-neo4j/src/test/java/cn/abel/neo4j/service/KingServiceTest.java new file mode 100644 index 0000000..9ff7709 --- /dev/null +++ b/springboot-neo4j/src/test/java/cn/abel/neo4j/service/KingServiceTest.java @@ -0,0 +1,47 @@ +package cn.abel.neo4j.service; + +import cn.abel.neo4j.BaseTest; +import cn.abel.neo4j.bean.King; +import cn.abel.neo4j.bean.Queen; +import cn.abel.neo4j.dao.KingDao; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; + + +/** + * @author yyb + * @time 2020/3/23 + */ +public class KingServiceTest extends BaseTest { + @Autowired + private KingService kingService; + @Autowired + private KingDao kingDao; + + + @Test + public void saveKing() throws Exception { + List kingList = InitData.initKing(); + kingService.saveKing(kingList); + } + + @Test + public void saveQueen() throws Exception { + List queenList = InitData.initQueen(); + kingService.saveQueen(queenList); + } + + @Test + public void saveRelation() throws Exception { + kingService.saveRelation("朱元璋", "朱棣"); + } + + @Test + public void getOneKing() throws Exception { + King king = kingService.findByName("朱棣"); + System.out.println(); + + } +} \ No newline at end of file From 092e20ba41252c9493011a91777136d0176d791d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Tue, 24 Mar 2020 16:37:43 +0800 Subject: [PATCH 39/44] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=A4=9A=E7=A7=8D?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- springboot-neo4j/pom.xml | 2 +- .../main/java/cn/abel/neo4j/dao/KingDao.java | 17 +++-- .../main/java/cn/abel/neo4j/dto/GraphDTO.java | 29 ++++++++ .../cn/abel/neo4j/service/KingService.java | 73 ++++++++++++++++++- .../abel/neo4j/service/KingServiceTest.java | 9 +++ 5 files changed, 120 insertions(+), 10 deletions(-) create mode 100644 springboot-neo4j/src/main/java/cn/abel/neo4j/dto/GraphDTO.java diff --git a/springboot-neo4j/pom.xml b/springboot-neo4j/pom.xml index 69edbe4..0276891 100644 --- a/springboot-neo4j/pom.xml +++ b/springboot-neo4j/pom.xml @@ -58,7 +58,7 @@ fastjson 1.2.40 - + org.springframework.boot diff --git a/springboot-neo4j/src/main/java/cn/abel/neo4j/dao/KingDao.java b/springboot-neo4j/src/main/java/cn/abel/neo4j/dao/KingDao.java index b1247f1..3bb635e 100644 --- a/springboot-neo4j/src/main/java/cn/abel/neo4j/dao/KingDao.java +++ b/springboot-neo4j/src/main/java/cn/abel/neo4j/dao/KingDao.java @@ -10,12 +10,13 @@ /** * 使用Springboot - data - neo4j 生成 + * * @author yyb * @time 2020/3/23 */ @Repository @Transactional(rollbackFor = Exception.class) -public interface KingDao extends Neo4jRepository { +public interface KingDao extends Neo4jRepository { /** * 通过名字查找King @@ -26,11 +27,13 @@ public interface KingDao extends Neo4jRepository { King findByName(String name); - @Query("match " + - "p=(a:King) - [r:传位*0..] -> " + - "(b:King) " + - "where b.name = {0} " + - "return p") - List inFriendship(String name); + /** + * 获取当前节点下的所有king + * + * @param name + * @return + */ + @Query("match p=(a:King)-[r:`传位`*0..]->(b:King) where a.name={0} return p") + List getKings(String name); } diff --git a/springboot-neo4j/src/main/java/cn/abel/neo4j/dto/GraphDTO.java b/springboot-neo4j/src/main/java/cn/abel/neo4j/dto/GraphDTO.java new file mode 100644 index 0000000..a7f1a7c --- /dev/null +++ b/springboot-neo4j/src/main/java/cn/abel/neo4j/dto/GraphDTO.java @@ -0,0 +1,29 @@ +package cn.abel.neo4j.dto; + +import java.util.List; + +/** + * 用于输出图 + * @author yyb + * @time 2020/3/24 + */ +public class GraphDTO { + private List nodes; + private List links; + + public List getNodes() { + return nodes; + } + + public void setNodes(List nodes) { + this.nodes = nodes; + } + + public List getLinks() { + return links; + } + + public void setLinks(List links) { + this.links = links; + } +} diff --git a/springboot-neo4j/src/main/java/cn/abel/neo4j/service/KingService.java b/springboot-neo4j/src/main/java/cn/abel/neo4j/service/KingService.java index 2a682e9..a2116fa 100644 --- a/springboot-neo4j/src/main/java/cn/abel/neo4j/service/KingService.java +++ b/springboot-neo4j/src/main/java/cn/abel/neo4j/service/KingService.java @@ -6,11 +6,18 @@ import cn.abel.neo4j.dao.KingDao; import cn.abel.neo4j.dao.Neo4jDao; import cn.abel.neo4j.dao.Neo4jSession; +import cn.abel.neo4j.dto.GraphDTO; +import org.neo4j.driver.internal.InternalNode; +import org.neo4j.driver.internal.InternalPath; +import org.neo4j.driver.internal.InternalRelationship; +import org.neo4j.driver.v1.types.Node; +import org.neo4j.driver.v1.types.Relationship; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.util.List; -import java.util.Optional; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; /** * @author yyb @@ -49,6 +56,57 @@ public void saveQueen(List list) { } + /** + * 通过皇帝名字,查到当前的皇后和上一任皇帝 + * + * @param + */ + public GraphDTO getKingAndQueen(String kingName) { + Neo4jSession session = neo4jDao.open(); + String cypher = "match p=(a:King)-[r:`传位`]->(b:King)-[r2:`皇后`*1..]->(c:Queen) where a.name ='" + kingName + "' return p"; + Iterator> iterator = session.exec(cypher); + GraphDTO gto = mapToGraph(iterator); + return gto; + } + + + private GraphDTO mapToGraph( + Iterator> neo4jDataIterator) { + Map nodeMap = new HashMap<>(); + Map linkMap = new HashMap<>(); + + while (neo4jDataIterator.hasNext()) { + Map each = neo4jDataIterator.next(); + if (!each.containsKey("p")) { + continue; + } + + InternalPath internalPath = (InternalPath) each.get("p"); + //归纳出所有节点。 + for (Node node : internalPath.nodes()) { + InternalNode internalNode = (InternalNode) node; + if (nodeMap.containsKey(internalNode.id())) { + continue; + } + nodeMap.put(internalNode.id(), internalNode); + } + //归纳出所有关系。 + for (Relationship relation : internalPath.relationships()) { + InternalRelationship internalRelation = (InternalRelationship) relation; + if (linkMap.containsKey(internalRelation.id())) { + continue; + } + linkMap.put(internalRelation.id(), internalRelation); + } + } + + GraphDTO dto = new GraphDTO(); + dto.setNodes(nodeMap.values().stream().collect(Collectors.toList())); + dto.setLinks(linkMap.values().stream().collect(Collectors.toList())); + return dto; + } + + /** * 查询一个皇帝信息,此处使用spring-data-neo4j 接口 * @@ -60,6 +118,17 @@ public King findByName(String name) { } + /** + * 获取当前节点下的所有king + * + * @param name + * @return + */ + public List getKings(String name) { + return kingDao.getKings(name); + } + + /** * 保存父子关系 * diff --git a/springboot-neo4j/src/test/java/cn/abel/neo4j/service/KingServiceTest.java b/springboot-neo4j/src/test/java/cn/abel/neo4j/service/KingServiceTest.java index 9ff7709..d3066a7 100644 --- a/springboot-neo4j/src/test/java/cn/abel/neo4j/service/KingServiceTest.java +++ b/springboot-neo4j/src/test/java/cn/abel/neo4j/service/KingServiceTest.java @@ -4,6 +4,7 @@ import cn.abel.neo4j.bean.King; import cn.abel.neo4j.bean.Queen; import cn.abel.neo4j.dao.KingDao; +import cn.abel.neo4j.dto.GraphDTO; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -44,4 +45,12 @@ public void getOneKing() throws Exception { System.out.println(); } + + @Test + public void getKingAndQueen() throws Exception { + GraphDTO graphDTO = kingService.getKingAndQueen("朱棣"); + System.out.println(); + + } + } \ No newline at end of file From b0977caea890b3b5d89f030769789dc3a7d4bab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Tue, 24 Mar 2020 16:46:28 +0800 Subject: [PATCH 40/44] add note --- .../src/main/java/cn/abel/neo4j/service/KingService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/springboot-neo4j/src/main/java/cn/abel/neo4j/service/KingService.java b/springboot-neo4j/src/main/java/cn/abel/neo4j/service/KingService.java index a2116fa..a68487f 100644 --- a/springboot-neo4j/src/main/java/cn/abel/neo4j/service/KingService.java +++ b/springboot-neo4j/src/main/java/cn/abel/neo4j/service/KingService.java @@ -64,6 +64,8 @@ public void saveQueen(List list) { public GraphDTO getKingAndQueen(String kingName) { Neo4jSession session = neo4jDao.open(); String cypher = "match p=(a:King)-[r:`传位`]->(b:King)-[r2:`皇后`*1..]->(c:Queen) where a.name ='" + kingName + "' return p"; +// 通过皇帝名字查询到当前皇帝皇后以及上一任皇帝皇后 +// match p=(a:King)-[r:`传位`]->(b:King)-[r2:`皇后`*1..]->(c:Queen), (a:King)-[r3:`皇后`*1..]->(d:Queen) where b.name ='朱棣' return a,b,c,d Iterator> iterator = session.exec(cypher); GraphDTO gto = mapToGraph(iterator); return gto; From 5ad703f21aef4f3f8755934c9ecd3fb2525a443c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Wed, 25 Mar 2020 15:09:11 +0800 Subject: [PATCH 41/44] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=E9=98=BF=E9=87=8C=20recketmq=20=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- springboot-rocketmq-ali/pom.xml | 71 +++++++++++++++++ .../main/java/cn/abel/queue/Application.java | 16 ++++ .../abel/queue/config/ALiConsumerClient.java | 79 +++++++++++++++++++ .../cn/abel/queue/config/ALiMqConfig.java | 79 +++++++++++++++++++ .../abel/queue/config/ALiProducerClient.java | 28 +++++++ .../cn/abel/queue/service/MessageHandler.java | 30 +++++++ .../abel/queue/service/ProducerService.java | 67 ++++++++++++++++ .../resources/local/application.properties | 28 +++++++ .../src/main/resources/local/banner.txt | 5 ++ .../main/resources/local/logback-spring.xml | 33 ++++++++ 10 files changed, 436 insertions(+) create mode 100644 springboot-rocketmq-ali/pom.xml create mode 100644 springboot-rocketmq-ali/src/main/java/cn/abel/queue/Application.java create mode 100644 springboot-rocketmq-ali/src/main/java/cn/abel/queue/config/ALiConsumerClient.java create mode 100644 springboot-rocketmq-ali/src/main/java/cn/abel/queue/config/ALiMqConfig.java create mode 100644 springboot-rocketmq-ali/src/main/java/cn/abel/queue/config/ALiProducerClient.java create mode 100644 springboot-rocketmq-ali/src/main/java/cn/abel/queue/service/MessageHandler.java create mode 100644 springboot-rocketmq-ali/src/main/java/cn/abel/queue/service/ProducerService.java create mode 100644 springboot-rocketmq-ali/src/main/resources/local/application.properties create mode 100644 springboot-rocketmq-ali/src/main/resources/local/banner.txt create mode 100644 springboot-rocketmq-ali/src/main/resources/local/logback-spring.xml diff --git a/springboot-rocketmq-ali/pom.xml b/springboot-rocketmq-ali/pom.xml new file mode 100644 index 0000000..87feb02 --- /dev/null +++ b/springboot-rocketmq-ali/pom.xml @@ -0,0 +1,71 @@ + + + 4.0.0 + + cn.abel + springboot-rocketmq-ali + 1.0-SNAPSHOT + + org.springframework.boot + spring-boot-starter-parent + 2.0.4.RELEASE + + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + + + org.apache.commons + commons-pool2 + + + org.springframework.boot + spring-boot-autoconfigure + + + + com.aliyun.openservices + ons-client + 1.8.3.Final + + + com.alibaba + fastjson + 1.2.58 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + 2.18.1 + + true + + + + + \ No newline at end of file diff --git a/springboot-rocketmq-ali/src/main/java/cn/abel/queue/Application.java b/springboot-rocketmq-ali/src/main/java/cn/abel/queue/Application.java new file mode 100644 index 0000000..287bdd7 --- /dev/null +++ b/springboot-rocketmq-ali/src/main/java/cn/abel/queue/Application.java @@ -0,0 +1,16 @@ +package cn.abel.queue; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author yyb + * @time 2019/8/13 + */ +@SpringBootApplication +public class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} diff --git a/springboot-rocketmq-ali/src/main/java/cn/abel/queue/config/ALiConsumerClient.java b/springboot-rocketmq-ali/src/main/java/cn/abel/queue/config/ALiConsumerClient.java new file mode 100644 index 0000000..ebb5b66 --- /dev/null +++ b/springboot-rocketmq-ali/src/main/java/cn/abel/queue/config/ALiConsumerClient.java @@ -0,0 +1,79 @@ +package cn.abel.queue.config; + +import cn.abel.queue.service.MessageHandler; +import com.aliyun.openservices.ons.api.MessageListener; +import com.aliyun.openservices.ons.api.PropertyKeyConst; +import com.aliyun.openservices.ons.api.bean.ConsumerBean; +import com.aliyun.openservices.ons.api.bean.Subscription; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +/** + * @author yyb + * @time 2020/3/25 + */ + +@Configuration +public class ALiConsumerClient { + private static final Logger logger = LoggerFactory.getLogger(ALiConsumerClient.class); + /** + * topic tag。 + */ + private static final String TOPIC_TAG = "*"; + /** + * 消费者线程数。 + */ + private static final int CONSUMER_THREAD_COUNT = 10; + /** + * 重新消费次数。 + */ + private static final int RECONSUME_TIMES = 2; + + private MessageHandler handler; + + @Value("${ali.rocket.mq.access-key}") + private String accessKey; + @Value("${ali.rocket.mq.secret-key}") + private String secretKey; + @Value("${ali.rocket.mq.name-srv-addr}") + private String nameSrvAddr; + @Value("${ali.rocket.mq.test-topic}") + private String topic; + @Value("${ali.rocket.mq.test-group-id}") + private String group; + + @Bean(initMethod = "start", destroyMethod = "shutdown") + public ConsumerBean buildConsumer() { + ConsumerBean consumerBean = new ConsumerBean(); + Properties properties = new Properties(); + properties.setProperty(PropertyKeyConst.AccessKey, accessKey); + properties.setProperty(PropertyKeyConst.SecretKey, secretKey); + properties.setProperty(PropertyKeyConst.NAMESRV_ADDR, nameSrvAddr); + properties.setProperty(PropertyKeyConst.GROUP_ID, group); + properties.setProperty(PropertyKeyConst.ConsumeThreadNums, String.valueOf(CONSUMER_THREAD_COUNT)); + properties.setProperty(PropertyKeyConst.MaxReconsumeTimes, String.valueOf(RECONSUME_TIMES)); + consumerBean.setProperties(properties); + + Map subscriptionTable = new HashMap<>(); + Subscription subscription = new Subscription(); + subscription.setTopic(topic); + subscription.setExpression(TOPIC_TAG); + subscriptionTable.put(subscription, handler); + consumerBean.setSubscriptionTable(subscriptionTable); + logger.info("初始化rocketMq完成 config:{}"); + return consumerBean; + } + + @Autowired + public ALiConsumerClient(MessageHandler handler) { + this.handler = handler; + } +} diff --git a/springboot-rocketmq-ali/src/main/java/cn/abel/queue/config/ALiMqConfig.java b/springboot-rocketmq-ali/src/main/java/cn/abel/queue/config/ALiMqConfig.java new file mode 100644 index 0000000..3fb00d8 --- /dev/null +++ b/springboot-rocketmq-ali/src/main/java/cn/abel/queue/config/ALiMqConfig.java @@ -0,0 +1,79 @@ +package cn.abel.queue.config; + +import com.aliyun.openservices.ons.api.PropertyKeyConst; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.Properties; + +/** + * @author yyb + * @time 2020/3/25 + */ +@Component +@ConfigurationProperties(prefix = "ali.rocket.mq") +public class ALiMqConfig { + private String accessKey; + private String secretKey; + private String nameSrvAddr; + private String testTopic; + private String testGroupId; + private String testTag; + + public Properties getMqProperties() { + Properties properties = new Properties(); + properties.setProperty(PropertyKeyConst.AccessKey, this.accessKey); + properties.setProperty(PropertyKeyConst.SecretKey, this.secretKey); + properties.setProperty(PropertyKeyConst.GROUP_ID, testGroupId); + properties.setProperty(PropertyKeyConst.NAMESRV_ADDR, this.nameSrvAddr); + return properties; + } + + public String getAccessKey() { + return accessKey; + } + + public void setAccessKey(String accessKey) { + this.accessKey = accessKey; + } + + public String getSecretKey() { + return secretKey; + } + + public void setSecretKey(String secretKey) { + this.secretKey = secretKey; + } + + public String getNameSrvAddr() { + return nameSrvAddr; + } + + public void setNameSrvAddr(String nameSrvAddr) { + this.nameSrvAddr = nameSrvAddr; + } + + public String getTestTopic() { + return testTopic; + } + + public void setTestTopic(String testTopic) { + this.testTopic = testTopic; + } + + public String getTestGroupId() { + return testGroupId; + } + + public void setTestGroupId(String testGroupId) { + this.testGroupId = testGroupId; + } + + public String getTestTag() { + return testTag; + } + + public void setTestTag(String testTag) { + this.testTag = testTag; + } +} diff --git a/springboot-rocketmq-ali/src/main/java/cn/abel/queue/config/ALiProducerClient.java b/springboot-rocketmq-ali/src/main/java/cn/abel/queue/config/ALiProducerClient.java new file mode 100644 index 0000000..c81c137 --- /dev/null +++ b/springboot-rocketmq-ali/src/main/java/cn/abel/queue/config/ALiProducerClient.java @@ -0,0 +1,28 @@ +package cn.abel.queue.config; + +import com.aliyun.openservices.ons.api.bean.ProducerBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; + +/** + * @author yyb + * @time 2020/3/25 + */ +@Component +public class ALiProducerClient { + @Autowired + private ALiMqConfig aLiMqConfig; + + @Bean(name = "producer", initMethod = "start", destroyMethod = "shutdown") + public ProducerBean build() { + ProducerBean bean = new ProducerBean(); + bean.setProperties(aLiMqConfig.getMqProperties()); + return bean; + } + + @Autowired + public ALiProducerClient(ALiMqConfig aLiMqConfig) { + this.aLiMqConfig = aLiMqConfig; + } +} \ No newline at end of file diff --git a/springboot-rocketmq-ali/src/main/java/cn/abel/queue/service/MessageHandler.java b/springboot-rocketmq-ali/src/main/java/cn/abel/queue/service/MessageHandler.java new file mode 100644 index 0000000..7dcca53 --- /dev/null +++ b/springboot-rocketmq-ali/src/main/java/cn/abel/queue/service/MessageHandler.java @@ -0,0 +1,30 @@ +package cn.abel.queue.service; + +import com.aliyun.openservices.ons.api.Action; +import com.aliyun.openservices.ons.api.ConsumeContext; +import com.aliyun.openservices.ons.api.Message; +import com.aliyun.openservices.ons.api.MessageListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.nio.charset.StandardCharsets; + +/** + * @author yyb + * @time 2020/3/25 + */ +@Component +public class MessageHandler implements MessageListener { + private static final Logger logger = LoggerFactory.getLogger(MessageHandler.class); + + @Override + public Action consume(Message message, ConsumeContext consumeContext) { + String msgStr = new String(message.getBody(), StandardCharsets.UTF_8); + //接收到消息 + logger.info("Receive message msgId:{} retryTimes:{} body:{}", message.getMsgID(), + message.getReconsumeTimes(), msgStr); + //提交 + return Action.CommitMessage; + } +} diff --git a/springboot-rocketmq-ali/src/main/java/cn/abel/queue/service/ProducerService.java b/springboot-rocketmq-ali/src/main/java/cn/abel/queue/service/ProducerService.java new file mode 100644 index 0000000..17e70c1 --- /dev/null +++ b/springboot-rocketmq-ali/src/main/java/cn/abel/queue/service/ProducerService.java @@ -0,0 +1,67 @@ +package cn.abel.queue.service; + +import cn.abel.queue.config.ALiMqConfig; +import com.alibaba.fastjson.JSON; +import com.aliyun.openservices.ons.api.Message; +import com.aliyun.openservices.ons.api.OnExceptionContext; +import com.aliyun.openservices.ons.api.SendCallback; +import com.aliyun.openservices.ons.api.SendResult; +import com.aliyun.openservices.ons.api.bean.ProducerBean; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.nio.charset.StandardCharsets; +import java.util.function.Predicate; + + +/** + * @author yyb + * @time 2019/8/13 + */ + +@Service +public class ProducerService { + private static final Logger logger = LoggerFactory.getLogger(ProducerService.class); + + @Autowired + private ALiMqConfig aLiMqConfig; + @Autowired + private ProducerBean producer; + + + /** + * 调用此方法 发送消息,o 为自定义的消息体 + */ + public void sendMessage(Object o) { + Message message = new Message(aLiMqConfig.getStockTopic(), "test", JSON.toJSON(o).toString().getBytes(StandardCharsets.UTF_8)); + //向mq发送消息 + sendAsync(ProducerBean::isStarted, producer, message); + } + + /** + * 向阿里云rocket mq发送消息。 + * + * @param predicate 断言 + * @param bean 发送消息bean + * @param message 需要发送的消息 + */ + private static void sendAsync(Predicate predicate, ProducerBean bean, Message message) { + if (predicate.test(bean)) { + bean.sendAsync(message, new SendCallback() { + @Override + public void onSuccess(SendResult sendResult) { + logger.info("向mq推送库存消息成功,消息是:{}", sendResult.toString()); + } + @Override + public void onException(OnExceptionContext e) { + logger.error("向mq推送库存消息失败,消息id 为 {} 错误是:{}", e.getMessageId(), e.getException().getMessage()); + } + }); + } else { + logger.error("mq库存生产端启动失败!!!消息是:{}", message.toString()); + } + } + +} \ No newline at end of file diff --git a/springboot-rocketmq-ali/src/main/resources/local/application.properties b/springboot-rocketmq-ali/src/main/resources/local/application.properties new file mode 100644 index 0000000..c815b81 --- /dev/null +++ b/springboot-rocketmq-ali/src/main/resources/local/application.properties @@ -0,0 +1,28 @@ +## tomcat\u914D\u7F6E +server.port=9666 +#server.tomcat.maxHttpHeaderSize=8192 +server.tomcat.uri-encoding=UTF-8 +spring.http.encoding.charset=UTF-8 +spring.http.encoding.enabled=true +spring.http.encoding.force=true +spring.messages.encoding=UTF-8 +# tomcat\u6700\u5927\u7EBF\u7A0B\u6570\uFF0C\u9ED8\u8BA4\u4E3A200 +server.tomcat.max-threads=800 +# session\u6700\u5927\u8D85\u65F6\u65F6\u95F4(\u5206\u949F)\uFF0C\u9ED8\u8BA4\u4E3A30 +server.session-timeout=60 + +## spring \u914D\u7F6E +spring.application.name=rocketmq-ali-queue +application.main=cn.abel.queue.Application + +## LOG +logging.file=./logs/rocketmq-queue-ali.log + + +# rocket mq +ali.rocket.mq.access-key=xxxxx +ali.rocket.mq.secret-key=xxxxxx +ali.rocket.mq.name-srv-addr=127.0.0.1:9876 +ali.rocket.mq.test-topic=mq_test +ali.rocket.mq.test-group-id=test +ali.rocket.mq.test-tag= \ No newline at end of file diff --git a/springboot-rocketmq-ali/src/main/resources/local/banner.txt b/springboot-rocketmq-ali/src/main/resources/local/banner.txt new file mode 100644 index 0000000..42f7c59 --- /dev/null +++ b/springboot-rocketmq-ali/src/main/resources/local/banner.txt @@ -0,0 +1,5 @@ +######################################################## +# # +# local # +# # +######################################################## \ No newline at end of file diff --git a/springboot-rocketmq-ali/src/main/resources/local/logback-spring.xml b/springboot-rocketmq-ali/src/main/resources/local/logback-spring.xml new file mode 100644 index 0000000..1f021e4 --- /dev/null +++ b/springboot-rocketmq-ali/src/main/resources/local/logback-spring.xml @@ -0,0 +1,33 @@ + + + springboot-rocketmq-ali + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + ${LOG_FILE} + + + ${LOG_FILE}.%d{yyyy-MM-dd} + + 60 + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + \ No newline at end of file From 98088e541ce4a4a5a9b3c5e2bc603a6e84b5d157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Thu, 2 Apr 2020 18:20:44 +0800 Subject: [PATCH 42/44] =?UTF-8?q?=E4=BF=AE=E6=94=B9springboot-es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- springboot-elasticsearch/README.md | 9 + springboot-elasticsearch/pom.xml | 9 +- .../src/main/java/cn/abel/bean/User.java | 19 ++ .../cn/abel/config/ESRestClient2Config.java | 48 +++++ .../cn/abel/config/ESRestClientConfig.java | 5 +- .../java/cn/abel/service/UserService.java | 191 +++++++++++++++++- .../resources/local/application.properties | 9 +- 7 files changed, 279 insertions(+), 11 deletions(-) create mode 100644 springboot-elasticsearch/README.md create mode 100644 springboot-elasticsearch/src/main/java/cn/abel/config/ESRestClient2Config.java diff --git a/springboot-elasticsearch/README.md b/springboot-elasticsearch/README.md new file mode 100644 index 0000000..90a60de --- /dev/null +++ b/springboot-elasticsearch/README.md @@ -0,0 +1,9 @@ + +## springboot-elasticsearch + +* 本项目是speingboot结合es 的小李子。 +* 演示了同一个项目配置多个es 数据源 +* es操作是用es的rest api 进行操作,操作语句是 通过 JSONObject 对象进行拼接,可以执行增、删、改、查 行为。 +具体的操作命令可以查看 Es RESTful API 官方文档 + +https://www.elastic.co/guide/cn/elasticsearch/guide/current/index.html \ No newline at end of file diff --git a/springboot-elasticsearch/pom.xml b/springboot-elasticsearch/pom.xml index 9983a63..bcf0f44 100644 --- a/springboot-elasticsearch/pom.xml +++ b/springboot-elasticsearch/pom.xml @@ -52,16 +52,11 @@ 3.4 - - com.sun.jna - jna - 3.0.9 - org.elasticsearch.client - rest - 5.5.3 + elasticsearch-rest-client + 6.8.0 diff --git a/springboot-elasticsearch/src/main/java/cn/abel/bean/User.java b/springboot-elasticsearch/src/main/java/cn/abel/bean/User.java index 58acdf2..e1ddbf5 100644 --- a/springboot-elasticsearch/src/main/java/cn/abel/bean/User.java +++ b/springboot-elasticsearch/src/main/java/cn/abel/bean/User.java @@ -11,6 +11,9 @@ public class User { private String email; private Date createTime; private Integer role; + private String idCard; + private int type; + public Integer getId() { return id; @@ -67,4 +70,20 @@ public Integer getRole() { public void setRole(Integer role) { this.role = role; } + + public String getIdCard() { + return idCard; + } + + public void setIdCard(String idCard) { + this.idCard = idCard; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } } \ No newline at end of file diff --git a/springboot-elasticsearch/src/main/java/cn/abel/config/ESRestClient2Config.java b/springboot-elasticsearch/src/main/java/cn/abel/config/ESRestClient2Config.java new file mode 100644 index 0000000..694d8d6 --- /dev/null +++ b/springboot-elasticsearch/src/main/java/cn/abel/config/ESRestClient2Config.java @@ -0,0 +1,48 @@ +package cn.abel.config; + +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; +import org.elasticsearch.client.RestClient; +import org.elasticsearch.client.RestClientBuilder; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 第二数据源 + * @author yyb + * @time 2020/4/2 + */ +@Configuration +public class ESRestClient2Config { + public static final String NAME = "ESRestClient2Config"; + + @Value("${elasticsearch2.userName}") + private String userName; + @Value("${elasticsearch2.password}") + private String password; + @Value("${elasticsearch2.rest.hostNames}") + private String hostName; + @Value("${elasticsearch2.rest.port}") + private Integer port; + + @Bean(name = NAME, destroyMethod = "close") + public RestClient getRestClient() { + final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(AuthScope.ANY, + new UsernamePasswordCredentials(userName, password)); + RestClientBuilder restClientBuilder = RestClient.builder(new HttpHost(hostName, port)); + //配置身份验证 + restClientBuilder.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() { + @Override + public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) { + return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); + } + }); + return restClientBuilder.build(); + } +} diff --git a/springboot-elasticsearch/src/main/java/cn/abel/config/ESRestClientConfig.java b/springboot-elasticsearch/src/main/java/cn/abel/config/ESRestClientConfig.java index af260fe..8f122ab 100644 --- a/springboot-elasticsearch/src/main/java/cn/abel/config/ESRestClientConfig.java +++ b/springboot-elasticsearch/src/main/java/cn/abel/config/ESRestClientConfig.java @@ -13,11 +13,14 @@ import org.springframework.context.annotation.Configuration; /** + * 第一数据源 * @author yangyibo * @time 2019/4/2 */ @Configuration public class ESRestClientConfig { + public static final String NAME = "ESRestClientConfig"; + @Value("${elasticsearch.userName}") private String userName; @Value("${elasticsearch.password}") @@ -27,7 +30,7 @@ public class ESRestClientConfig { @Value("${elasticsearch.rest.port}") private Integer port; - @Bean(destroyMethod = "close") + @Bean(name = NAME, destroyMethod = "close") public RestClient getRestClient() { final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials(AuthScope.ANY, diff --git a/springboot-elasticsearch/src/main/java/cn/abel/service/UserService.java b/springboot-elasticsearch/src/main/java/cn/abel/service/UserService.java index 99f3c4b..10f08f2 100644 --- a/springboot-elasticsearch/src/main/java/cn/abel/service/UserService.java +++ b/springboot-elasticsearch/src/main/java/cn/abel/service/UserService.java @@ -1,31 +1,62 @@ package cn.abel.service; +import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Map; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.elasticsearch.client.Request; +import org.elasticsearch.client.Response; +import org.elasticsearch.client.RestClient; +import cn.abel.config.ESRestClient2Config; +import cn.abel.config.ESRestClientConfig; import cn.abel.constants.Constants; +import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.entity.ContentType; import org.apache.http.nio.entity.NStringEntity; import org.apache.http.util.EntityUtils; -import org.elasticsearch.client.Response; -import org.elasticsearch.client.RestClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; import cn.abel.dao.UserDao; import cn.abel.bean.User; +/** + * @author yyb + */ @Service public class UserService { + private static final Logger logger = LoggerFactory.getLogger(UserService.class); + protected static final ObjectMapper mapper = new ObjectMapper(); + @Autowired private UserDao userDao; + + /** + * 数据源1 + */ @Autowired + @Qualifier(ESRestClientConfig.NAME) private RestClient restClient; + /** + * 数据源2 + */ + @Autowired + @Qualifier(ESRestClient2Config.NAME) + private RestClient restClient2; + + public List getByMap(Map map) { return userDao.getByMap(map); } @@ -41,6 +72,43 @@ public User getById(Integer id) throws Exception { return userDao.getById(id); } + + /** + * 查询用户列表 + * + * @param keyword 关键字用户名字/用户手机号/用户身份证号 + * @param type 用户类型/1:注册用户/2:转化用户 + * @param startTime 时间范围 起始时间 + * @param endTime 时间范围 结束时间 + * @param pageSize + * @param pageIndex + * @return + * @throws Exception + */ + public JSONObject getList(String keyword, Integer type, String startTime, String endTime, int pageSize, int pageIndex) throws Exception { + JsonNode searchResponse; + JSONObject response = new JSONObject(); + JSONObject search = searchListQuery(keyword, type, startTime, endTime, pageSize, pageIndex); + //根据档案英文名切换不同的索引 + String endpoint = "/user_Info_index/user/_search"; + + searchResponse = esSearch(search, endpoint, restClient); + if (null == searchResponse) { + return response; + } + JsonNode hitsNode = searchResponse.get("hits"); + ArrayNode hitsArray = (ArrayNode) hitsNode.get("hits"); + response.put("total", hitsNode.get("total").intValue()); + JSONArray list = new JSONArray(); + for (JsonNode hit : hitsArray) { + JSONObject result = (JSONObject) JSONObject.parse(hit.get("_source").toString()); + list.add(result); + } + response.put("list", list); + return response; + } + + public User create(User user) { userDao.create(user); return user; @@ -55,4 +123,123 @@ public int delete(Integer id) { return userDao.delete(id); } + + /** + * 拼装列表查询query + * + * @param keyword + * @return + */ + private JSONObject searchListQuery(String keyword, Integer type, String startTime, String endTime, int pageSize, int pageIndex) { + //只返回列表所需数据 + String[] source = new String[]{"id", "user_name", "id_card", "user_name", "mobile", "type", "create_time"}; + JSONObject search = new JSONObject(); + search.put("_source", source); + if (pageIndex <= 0) { + pageIndex = 1; + } + JSONObject bool = new JSONObject(); + JSONArray sort = new JSONArray(); + + //查询类型 + JSONArray mustArray = new JSONArray(); + if (null != type) { + mustArray.add(getJSONObject("term", getJSONObject("type", type))); + } + bool.put("must", mustArray); + + //关键字查询 + if (StringUtils.isNotEmpty(keyword)) { + JSONObject multiMatch = new JSONObject(); + multiMatch.put("query", keyword); + multiMatch.put("fields", new String[]{"user_name.keyword", "user_name"}); + //查询条件 + JSONArray shouldArray = new JSONArray(); + //虚化查询 + shouldArray.add(getJSONObject("term", getJSONObject("id_card", keyword))); + shouldArray.add(getJSONObject("term", getJSONObject("mobile", keyword))); + shouldArray.add(getJSONObject("multi_match", multiMatch)); + //关键字评分排序 + sort.add(getJSONObject("_score", getJSONObject("order", "desc"))); + //should 必须命中一个 + bool.put("minimum_should_match", 1); + bool.put("should", shouldArray); + } + + //排序和时间过滤 + JSONObject rangeField = new JSONObject(); + //筛选创建时间大于 startTime 小于 endTime 的数据 + rangeField.put("gte", startTime); + rangeField.put("lte", endTime); + JSONObject range = new JSONObject(); + range.put("range", getJSONObject("create_time", rangeField)); + //按照创建时间规则倒叙排序 + sort.add(getJSONObject("create_time", getJSONObject("order", "desc"))); + bool.put("filter", range); + + search.put("sort", sort); + search.put("query", getJSONObject("bool", bool)); + return setPage(pageIndex, pageSize, search); + } + + + /** + * ES查询 + * + * @param search + * @return + */ + public JsonNode esSearch(JSONObject search, String endpoint, RestClient restClient) { + JsonNode responseNode = null; + try { + logger.info("查询es语句为 {}, endpoint 为:{}", search, endpoint.toString()); + + Request request = new Request(HttpGet.METHOD_NAME, endpoint); + if (search != null) { + String data = search.toString(); + request.setJsonEntity(data); + } + try { + restClient.performRequest(request); + } catch (IOException e) { + logger.error("查询es语句报错为 {}", e.getMessage()); + } + Response response = restClient.performRequest(request); + String responseStr = EntityUtils.toString(response.getEntity()); + logger.info("查询结果为", responseStr); + responseNode = mapper.readTree(responseStr); + } catch (IOException e) { + logger.error("查询失败", e); + } + return responseNode; + } + + + /** + * 设置页面 + * + * @param pageIndex + * @param pageSize + * @param search + * @return + */ + public static JSONObject setPage(Integer pageIndex, Integer pageSize, JSONObject search) { + search.put("from", (pageIndex - 1) * pageSize); + search.put("size", pageSize); + return search; + } + + + /** + * 获取json 对象 + * + * @param key + * @param value + * @return + */ + public static JSONObject getJSONObject(String key, Object value) { + JSONObject term = new JSONObject(); + term.put(key, value); + return term; + } } \ No newline at end of file diff --git a/springboot-elasticsearch/src/main/resources/local/application.properties b/springboot-elasticsearch/src/main/resources/local/application.properties index 5808183..0186699 100644 --- a/springboot-elasticsearch/src/main/resources/local/application.properties +++ b/springboot-elasticsearch/src/main/resources/local/application.properties @@ -60,4 +60,11 @@ pagehelper.params=count=countSql elasticsearch.userName= elasticsearch.password= elasticsearch.rest.hostNames=127.0.0.1 -elasticsearch.rest.port=9200 \ No newline at end of file +elasticsearch.rest.port=9200 + + +#es2\u670D\u52A1\u5668\u5730\u5740\u914D\u7F6E +elasticsearch2.userName= +elasticsearch2.password= +elasticsearch2.rest.hostNames=127.0.0.1 +elasticsearch2.rest.port=9200 \ No newline at end of file From 909d2b2683f103cb9a4366a52b5258ca52c891dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Thu, 2 Apr 2020 18:34:56 +0800 Subject: [PATCH 43/44] =?UTF-8?q?=E4=BF=AE=E6=94=B9c3p0=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- springboot-Cache/pom.xml | 2 +- springboot-Cache2/pom.xml | 2 +- springboot-SpringSecurity0/pom.xml | 2 +- springboot-SpringSecurity1/pom.xml | 2 +- springboot-shiro/pom.xml | 2 +- springboot-springSecurity2/pom.xml | 2 +- springboot-springSecurity3/pom.xml | 2 +- springboot-springSecurity4/pom.xml | 2 +- springboot-swagger-ui/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/springboot-Cache/pom.xml b/springboot-Cache/pom.xml index fb8b681..eda2a37 100644 --- a/springboot-Cache/pom.xml +++ b/springboot-Cache/pom.xml @@ -63,7 +63,7 @@ com.mchange c3p0 - 0.9.5.2 + 0.9.5.4 commons-logging diff --git a/springboot-Cache2/pom.xml b/springboot-Cache2/pom.xml index 67364da..f255f15 100644 --- a/springboot-Cache2/pom.xml +++ b/springboot-Cache2/pom.xml @@ -52,7 +52,7 @@ com.mchange c3p0 - 0.9.5.2 + 0.9.5.4 commons-logging diff --git a/springboot-SpringSecurity0/pom.xml b/springboot-SpringSecurity0/pom.xml index ff6894e..22e6fd8 100644 --- a/springboot-SpringSecurity0/pom.xml +++ b/springboot-SpringSecurity0/pom.xml @@ -49,7 +49,7 @@ com.mchange c3p0 - 0.9.5.2 + 0.9.5.4 commons-logging diff --git a/springboot-SpringSecurity1/pom.xml b/springboot-SpringSecurity1/pom.xml index 88ccf01..79b8c52 100644 --- a/springboot-SpringSecurity1/pom.xml +++ b/springboot-SpringSecurity1/pom.xml @@ -49,7 +49,7 @@ com.mchange c3p0 - 0.9.5.2 + 0.9.5.4 commons-logging diff --git a/springboot-shiro/pom.xml b/springboot-shiro/pom.xml index 0f01d61..e953479 100644 --- a/springboot-shiro/pom.xml +++ b/springboot-shiro/pom.xml @@ -87,7 +87,7 @@ com.mchange c3p0 - 0.9.5.2 + 0.9.5.4 commons-logging diff --git a/springboot-springSecurity2/pom.xml b/springboot-springSecurity2/pom.xml index 041860b..2d8a95a 100644 --- a/springboot-springSecurity2/pom.xml +++ b/springboot-springSecurity2/pom.xml @@ -44,7 +44,7 @@ com.mchange c3p0 - 0.9.5.2 + 0.9.5.4 commons-logging diff --git a/springboot-springSecurity3/pom.xml b/springboot-springSecurity3/pom.xml index 29dbd84..8ef9b02 100644 --- a/springboot-springSecurity3/pom.xml +++ b/springboot-springSecurity3/pom.xml @@ -49,7 +49,7 @@ com.mchange c3p0 - 0.9.5.2 + 0.9.5.4 commons-logging diff --git a/springboot-springSecurity4/pom.xml b/springboot-springSecurity4/pom.xml index 45a72ea..ef987f6 100644 --- a/springboot-springSecurity4/pom.xml +++ b/springboot-springSecurity4/pom.xml @@ -48,7 +48,7 @@ com.mchange c3p0 - 0.9.5.2 + 0.9.5.4 diff --git a/springboot-swagger-ui/pom.xml b/springboot-swagger-ui/pom.xml index dff48e7..e0fc743 100644 --- a/springboot-swagger-ui/pom.xml +++ b/springboot-swagger-ui/pom.xml @@ -42,7 +42,7 @@ com.mchange c3p0 - 0.9.5.2 + 0.9.5.4 commons-logging From 8fadfc31240917e2e9347ec8c394c774082e8d8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E4=BC=8A=E5=8D=9A?= <527515025@qq.com> Date: Thu, 2 Apr 2020 18:38:04 +0800 Subject: [PATCH 44/44] modify c3p0 version --- springboot-mybatis/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/springboot-mybatis/pom.xml b/springboot-mybatis/pom.xml index 6ee2fd2..6227a2a 100644 --- a/springboot-mybatis/pom.xml +++ b/springboot-mybatis/pom.xml @@ -44,7 +44,7 @@ com.mchange c3p0 - 0.9.5.2 + 0.9.5.4 commons-logging