转自:http://www.codeceo.com/article/apache-commons-tools.html Apache Commons包含了很多开源的工具,用于解决平时编程经常会遇到的问题,减少重复劳动。我选了一些比较常用的项目做简单介绍。文中用了很多网上现成的东西,我只是做了一个汇总整理。 一、Commons BeanUtils http://jakarta.apache.org/commons/beanutils/index.html 说明:针对Bean的一个工具集。由于Bean往往是有一堆get和set组成,所以BeanUtils也是在此基础上进行一些包装。 使用示例:功能有很多,网站上有详细介绍。一个比较常用的功能是Bean Copy,也就是copy bean的属性。如果做分层架构开发的话就会用到,比如从PO(Persistent Object)拷贝数据到VO(Value Object)。 传统方法如下: //得到TeacherForm TeacherForm teacherForm=(TeacherForm)form; //构造Teacher对象 Teacher teacher=new Teacher(); //赋值 teacher.setName(teacherForm.getName()); teacher.setAge(teacherForm.getAge()); teacher.setGender(teacherForm.getGender()); teacher.setMajor(teacherForm.getMajor()); teacher.setDepartment(teacherForm.getDepartment()); //持久化Teacher对象到数据库 HibernateDAO= ; HibernateDAO.save(teacher); 使用BeanUtils后,代码就大大改观了,如下所示: //得到TeacherForm TeacherForm teacherForm=(TeacherForm)form; //构造Teacher对象 Teacher teacher=new Teacher(); //赋值 BeanUtils.copyProperties(teacher,teacherForm); //持久化Teacher对象到数据库 HibernateDAO= ; HibernateDAO.save(teacher); 二、Commons CLI http://jakarta.apache.org/commons/cli/index.html 说明:这是一个处理命令的工具。比如main方法输入的string[]需要解析。你可以预先定义好参数的规则,然后就可以调用CLI来解析。 使用示例: // create Options object Options options = new Options(); // add t option, option is the command parameter, false indicates that // this parameter is not required. options.addOption(“t”, false, “display current time”); options.addOption("c", true, "country code"); CommandLineParser parser = new PosixParser(); CommandLine cmd […]
View Details为什么使用雪花ID 在以前的项目中,最常见的两种主键类型是自增Id和UUID,在比较这两种ID之前首先要搞明白一个问题,就是为什么主键有序比无序查询效率要快,因为自增Id和UUID之间最大的不同点就在于有序性。 我们都知道,当我们定义了主键时,数据库会选择表的主键作为聚集索引(B+Tree),mysql 在底层是以数据页为单位来存储数据的。 也就是说如果主键为自增 id 的话,mysql 在写满一个数据页的时候,直接申请另一个新数据页接着写就可以了。如果一个数据页存满了,mysql 就会去申请一个新的数据页来存储数据。如果主键是UUID,为了确保索引有序,mysql 就需要将每次插入的数据都放到合适的位置上。这就造成了页分裂,这个大量移动数据的过程是会严重影响插入效率的。 一句话总结就是,InnoDB表的数据写入顺序能和B+树索引的叶子节点顺序一致的话,这时候存取效率是最高的。 但是为什么很多情况又不用自增id作为主键呢? 容易导致主键重复。比如导入旧数据时,线上又有新的数据新增,这时就有可能在导入时发生主键重复的异常。为了避免导入数据时出现主键重复的情况,要选择在应用停业后导入旧数据,导入完成后再启动应用。显然这样会造成不必要的麻烦。而UUID作为主键就不用担心这种情况。 不利于数据库的扩展。当采用自增id时,分库分表也会有主键重复的问题。UUID则不用担心这种问题。 那么问题就来了,自增id会担心主键重复,UUID不能保证有序性,有没有一种ID既是有序的,又是唯一的呢? 当然有,就是雪花ID。 什么是雪花ID snowflake是Twitter开源的分布式ID生成算法,结果是64bit的Long类型的ID,有着全局唯一和有序递增的特点。 最高位是符号位,因为生成的 ID 总是正数,始终为0,不可用。 41位的时间序列,精确到毫秒级,41位的长度可以使用69年。时间位还有一个很重要的作用是可以根据时间进行排序。 10位的机器标识,10位的长度最多支持部署1024个节点。 12位的计数序列号,序列号即一系列的自增ID,可以支持同一节点同一毫秒生成多个ID序号,12位的计数序列号支持每个节点每毫秒产生4096个ID序号。 缺点也是有的,就是强依赖机器时钟,如果机器上时钟回拨,有可能会导致主键重复的问题。 Java实现雪花ID 下面是用Java实现雪花ID的代码,供大家参考一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
public class SnowflakeIdWorker { /** * 开始时间:2020-01-01 00:00:00 */ private final long beginTs = 1577808000000L; private final long workerIdBits = 10; /** * 2^10 - 1 = 1023 */ private final long maxWorkerId = -1L ^ (-1L << workerIdBits); private final long sequenceBits = 12; /** * 2^12 - 1 = 4095 */ private final long maxSequence = -1L ^ (-1L << sequenceBits); /** * 时间戳左移22位 */ private final long timestampLeftOffset = workerIdBits + sequenceBits; /** * 业务ID左移12位 */ private final long workerIdLeftOffset = sequenceBits; /** * 合并了机器ID和数据标示ID,统称业务ID,10位 */ private long workerId; /** * 毫秒内序列,12位,2^12 = 4096个数字 */ private long sequence = 0L; /** * 上一次生成的ID的时间戳,同一个worker中 */ private long lastTimestamp = -1L; public SnowflakeIdWorker(long workerId) { if (workerId > maxWorkerId || workerId < 0) { throw new IllegalArgumentException(String.format("WorkerId必须大于或等于0且小于或等于%d", maxWorkerId)); } this.workerId = workerId; } public synchronized long nextId() { long ts = System.currentTimeMillis(); if (ts < lastTimestamp) { throw new RuntimeException(String.format("系统时钟回退了%d毫秒", (lastTimestamp - ts))); } // 同一时间内,则计算序列号 if (ts == lastTimestamp) { // 序列号溢出 if (++sequence > maxSequence) { ts = tilNextMillis(lastTimestamp); sequence = 0L; } } else { // 时间戳改变,重置序列号 sequence = 0L; } lastTimestamp = ts; // 0 - 00000000 00000000 00000000 00000000 00000000 0 - 00000000 00 - 00000000 0000 // 左移后,低位补0,进行按位或运算相当于二进制拼接 // 本来高位还有个0<<63,0与任何数字按位或都是本身,所以写不写效果一样 return (ts - beginTs) << timestampLeftOffset | workerId << workerIdLeftOffset | sequence; } /** * 阻塞到下一个毫秒 * * @param lastTimestamp * @return */ private long tilNextMillis(long lastTimestamp) { long ts = System.currentTimeMillis(); while (ts <= lastTimestamp) { ts = System.currentTimeMillis(); } return ts; } public static void main(String[] args) { SnowflakeIdWorker snowflakeIdWorker = new SnowflakeIdWorker(7); for (int i = 0; i < 10; i++) { long id = snowflakeIdWorker.nextId(); System.out.println(id); } } } |
main方法,测试结果如下:
1 2 3 4 5 6 7 8 9 10 |
184309536616640512 184309536616640513 184309536616640514 184309536616640515 184309536616640516 184309536616640517 184309536616640518 184309536616640519 184309536616640520 184309536616640521 |
总结 在大部分公司的开发项目中里,雪花ID是主流的ID生成策略,除了自己实现之外,目前市场上也有很多开源的实现,比如: 美团开源的Leaf 百度开源的UidGenerator 有兴趣的可以自行观摩一下,那么这篇文章就写到这里了,感谢大家的阅读。 作者:java技术爱好者 链接:https://juejin.cn/post/6965510420387856398 来源:掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
View DetailsStringBuilder转化为String
1 2 |
String str = "abcdefghijklmnopqrs"; StringBuilder stb = new StringBuilder(str); |
整型数组转化为字符串
1 2 3 4 5 |
StringBuilder s = new StringBuilder(); for(i=1;i<=n;i++) { s.append(String.valueOf(a[i])); } String str = ""+s; |
字符串转化为整形数组
1 2 3 4 5 |
String str="123456"; int[] a = new int[str.length()]; for(int i=0;i<str.length();i++) { a[i] = str.charAt(i)-'0'; } |
字符串转化为字符数组
1 2 3 |
String str="123456"; char[] c = str.toCharArray() ; System.out.println(c); |
字符数组转化为字符串
1 2 3 |
char[] c = {'a','s','d','4','5',}; String str = new String(c); System.out.println(str); |
字符数组转化为整型数组
1 2 3 4 5 6 |
char[] c = { '1', '2', '3', '4', '5', }; int[] a = new int[c.length]; for (int i = 0; i < 5; i++) { a[i] = c[i] - '0'; System.out.println(a[i]); } |
整型数组转化为字符数组
1 2 3 4 5 6 |
int[] a = {1,2,3,4,5}; char[] c = new char[a.length]; for (int i = 0; i < 5; i++) { c[i] = (char) (a[i]+'0'); System.out.println(c[i]); } |
整型数转化为字符串
1 2 3 |
1.String str = Integer.toString(i); 2.String s = String.valueOf(i); 3.String s = "" + i; |
字符串转化为整型数
1 |
int i = Integer.valueOf(str).intValue(); |
java类型转换 Integer String Long Float Double Date 1如何将字串 String 转换成整数 int? A. 有两个方法:
1 2 3 4 5 6 7 |
1). int i = Integer.parseInt([String]); 或 i = Integer.parseInt([String],[int radix]); 2). int i = Integer.valueOf(my_str).intValue(); 注: 字串转成 Double, Float, Long 的方法大同小异. |
2 如何将整数 int 转换成字串 String ? A. 有叁种方法:
1 2 3 4 5 6 7 |
1.) String s = String.valueOf(i); 2.) String s = Integer.toString(i); 3.) String s = "" + i; 注: Double, Float, Long 转成字串的方法大同小异. |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
package cn.com.lwkj.erts.register; import java.sql.Date; public class TypeChange { public TypeChange() { } //change the string type to the int type public static int stringToInt(String intstr) { Integer integer; integer = Integer.valueOf(intstr); return integer.intValue(); } //change int type to the string type public static String intToString(int value) { Integer integer = new Integer(value); return integer.toString(); } //change the string type to the float type public static float stringToFloat(String floatstr) { Float floatee; floatee = Float.valueOf(floatstr); return floatee.floatValue(); } //change the float type to the string type public static String floatToString(float value) { Float floatee = new Float(value); return floatee.toString(); } //change the string type to the sqlDate type public static java.sql.Date stringToDate(String dateStr) { return java.sql.Date.valueOf(dateStr); } //change the sqlDate type to the string type public static String dateToString(java.sql.Date datee) { return datee.toString(); } public static void main(String[] args) { java.sql.Date day ; day = TypeChange.stringToDate("2003-11-3"); String strday = TypeChange.dateToString(day); System.out.println(strday); } } /* 我们 www.jb51.net */ |
JAVA中常用数据类型转换函数 虽然都能在JAVA API中找到,整理一下做个备份。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
string->byte Byte static byte parseByte(String s) byte->string Byte static String toString(byte b) char->string Character static String to String (char c) string->Short Short static Short parseShort(String s) Short->String Short static String toString(Short s) String->Integer Integer static int parseInt(String s) Integer->String Integer static String tostring(int i) String->Long Long static long parseLong(String s) Long->String Long static String toString(Long i) String->Float Float static float parseFloat(String s) Float->String Float static String toString(float f) String->Double Double static double parseDouble(String s) Double->String Double static String toString(Double) ++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
数据类型 基本类型有以下四种: int长度数据类型有:byte(8bits)、short(16bits)、int(32bits)、long(64bits)、 float长度数据类型有:单精度(32bits float)、双精度(64bits double) boolean类型变量的取值有:ture、false char数据类型有:unicode字符,16位 对应的类类型:Integer、Float、Boolean、Character、Double、Short、Byte、Long 转换原则 从低精度向高精度转换 byte 、short、int、long、float、double、char 注:两个char型运算时,自动转换为int型;当char与别的类型运算时,也会先自动转换为int型的,再做其它类型的自动转换 基本类型向类类型转换 正向转换:通过类包装器来new出一个新的类类型的变量 Integer a= new Integer(2); 反向转换:通过类包装器来转换 int b=a.intValue(); 类类型向字符串转换 正向转换:因为每个类都是object类的子类,而所有的object类都有一个toString()函数,所以通过toString()函数来转换即可 反向转换:通过类包装器new出一个新的类类型的变量 eg1: int i=Integer.valueOf(“123”).intValue() 说明:上例是将一个字符串转化成一个Integer对象,然后再调用这个对象的intValue()方法返回其对应的int数值。 eg2: float f=Float.valueOf(“123”).floatValue() 说明:上例是将一个字符串转化成一个Float对象,然后再调用这个对象的floatValue()方法返回其对应的float数值。 eg3: boolean b=Boolean.valueOf(“123”).booleanValue() 说明:上例是将一个字符串转化成一个Boolean对象,然后再调用这个对象的booleanValue()方法返回其对应的boolean数值。 eg4:double d=Double.valueOf(“123”).doublue() […]
View Details类型转换虽然很简单,但是还是有些小细节要多注意。 String转化为int:
1 2 |
String test="123"; int number=Integer.parseInt(test); |
String转化为Integer,可以如下所示。 也适用于int转化为Integer:
1 2 3 |
String test="123"; // String test="abc"; //会报错:NumberFormatException: For input string Integer number=Integer.valueOf(test); |
注意:不管是使用Integer.parseInt(),还是使用Integer.valueOf()将字符串转换成数字, 如果是非数字的字符串,会报错:NumberFormatException: For input string: "" 另外,Integer类取值和 int 类型取值一致,取值范围是从-2147483648 至 2147483647(-231至 231-1) ,包括-2147483648 和 2147483647。 如果超过了这个范围,也会报错。比如Integer.valueOf("2147483648"),超过了Integer范围。因此会报错: For input string: "2147483648" 更安全的做法是,使用apache包的NumberUtils,如下: 注意:NumberUtils只处理整数,不能用来处理小数。
1 2 3 4 5 |
String str="abc"; //str不为数字时,设置默认值为 0 int num = NumberUtils.toInt(str); //str不为数字时,设置默认值为其他值,比如1 int defaultNum=NumberUtils.toInt(str,1); |
String转BigDecimal:
1 2 |
String str1="2.30"; BigDecimal bd=new BigDecimal(str1); |
String转double :
1 |
double value = NumberUtils.toDouble("4.23"); |
Double转化为int:
1 2 |
Double test=new Double("1.23"); //Double初始化,最好用String保证精度 int result=test.intValue(); |
其他类型转String:
1 2 |
// Object obj="123"; String test=String.valueOf(obj); |
注意:当String.valueOf()的参数obj为null时,返回值是字符串"null"!!而不是null。 如果希望obj为null时,返回"",可以使用apache-commons-lang的包,如下所示:
1 2 |
Object object=null; String str = ObjectUtils.toString(object); //object为null时,结果为"" |
如果希望obj为null时,返回null,如下: ObjectUtils.toString(object,nullStr),第二参数nullStr表示,当object为null时,方法返回的值。
1 2 3 4 |
// Object obj=null; Object object="123"; String str = ObjectUtils.toString(object,null); //相当于 String str= (object == null) ? null : object.toString(); |
Integer转double: 使用doubleValue()方法,或者 (double)强制转换。
1 2 3 |
Integer a= new Integer(5); int intvalue=a.intValue(); double doublevalue=a.doubleValue(); |
其他类型转Double:
1 |
Double rate= Double.valueOf(obj); |
比较小数是否相等。 比较Double是否相等。比较BigDecimal是否相等。 如下所示:
1 2 3 4 |
double value=1.23; if (BigDecimal.ZERO.compareTo(BigDecimal.valueOf(value)) == 0) { // } |
比较Double类型的大小:
1 2 3 |
if (Double.valueOf(d1).compareTo(Double.valueOf(d2))<0) { //... } |
比较double类型的大小: 除了用BigDemical的compare()方法,可以直接用Double.doubleToLongBits()的结果值用==,>,<进行比较
1 2 3 |
if(Double.doubleToLongBits(d1) == Double.doubleToLongBits(d2))){ // } |
from:https://www.cnblogs.com/expiator/p/12602446.html
View Details
1 2 |
Severity Code Description Project File Line Suppression State Error CS7038 Failed to emit module 'JTHY.Web': Changing the version of an assembly reference is not allowed during debugging: 'RUC.Base, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' changed version to '0.0.0.0'. JTHY.Web 1 Active |
如题,公司的一个项目。 原因:由于未知的原因,被引用项目的GUID和引用的GUID不一致引起的这个问题。 解决:重新引用也无效,那只有手动编辑项目文件了~
View Detailsvolatile这个关键字可能很多朋友都听说过,或许也都用过。在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果。在Java 5之后,volatile关键字才得以重获生机。 volatile关键字虽然从字面上理解起来比较简单,但是要用好不是一件容易的事情。由于volatile关键字是与Java的内存模型有关的,因此在讲述volatile关键之前,我们先来了解一下与内存模型相关的概念和知识,然后分析了volatile关键字的实现原理,最后给出了几个使用volatile关键字的场景。 以下是本文的目录大纲: 一.内存模型的相关概念 二.并发编程中的三个概念 三.Java内存模型 四..深入剖析volatile关键字 五.使用volatile关键字的场景 若有不正之处请多多谅解,并欢迎批评指正。 请尊重作者劳动成果,转载请标明原文链接: http://www.cnblogs.com/dolphin0520/p/3920373.html 一.内存模型的相关概念 大家都知道,计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入。由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这时就存在一个问题,由于CPU执行速度很快,而从内存读取数据和向内存写入数据的过程跟CPU执行指令的速度比起来要慢的多,因此如果任何时候对数据的操作都要通过和内存的交互来进行,会大大降低指令执行的速度。因此在CPU里面就有了高速缓存。 也就是,当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高速缓存中的数据刷新到主存当中。举个简单的例子,比如下面的这段代码: 1 i = i + 1; 当线程执行这个语句时,会先从主存当中读取i的值,然后复制一份到高速缓存当中,然后CPU执行指令对i进行加1操作,然后将数据写入高速缓存,最后将高速缓存中i最新的值刷新到主存当中。 这个代码在单线程中运行是没有任何问题的,但是在多线程中运行就会有问题了。在多核CPU中,每条线程可能运行于不同的CPU中,因此每个线程运行时有自己的高速缓存(对单核CPU来说,其实也会出现这种问题,只不过是以线程调度的形式来分别执行的)。本文我们以多核CPU为例。 比如同时有2个线程执行这段代码,假如初始时i的值为0,那么我们希望两个线程执行完之后i的值变为2。但是事实会是这样吗? 可能存在下面一种情况:初始时,两个线程分别读取i的值存入各自所在的CPU的高速缓存当中,然后线程1进行加1操作,然后把i的最新值1写入到内存。此时线程2的高速缓存当中i的值还是0,进行加1操作之后,i的值为1,然后线程2把i的值写入内存。 最终结果i的值是1,而不是2。这就是著名的缓存一致性问题。通常称这种被多个线程访问的变量为共享变量。 也就是说,如果一个变量在多个CPU中都存在缓存(一般在多线程编程时才会出现),那么就可能存在缓存不一致的问题。 为了解决缓存不一致性问题,通常来说有以下2种解决方法: 1)通过在总线加LOCK#锁的方式 2)通过缓存一致性协议 这2种方式都是硬件层面上提供的方式。 在早期的CPU当中,是通过在总线上加LOCK#锁的形式来解决缓存不一致的问题。因为CPU和其他部件进行通信都是通过总线来进行的,如果对总线加LOCK#锁的话,也就是说阻塞了其他CPU对其他部件访问(如内存),从而使得只能有一个CPU能使用这个变量的内存。比如上面例子中 如果一个线程在执行 i = i +1,如果在执行这段代码的过程中,在总线上发出了LCOK#锁的信号,那么只有等待这段代码完全执行完毕之后,其他CPU才能从变量i所在的内存读取变量,然后进行相应的操作。这样就解决了缓存不一致的问题。 但是上面的方式会有一个问题,由于在锁住总线期间,其他CPU无法访问内存,导致效率低下。 所以就出现了缓存一致性协议。最出名的就是Intel 的MESI协议,MESI协议保证了每个缓存中使用的共享变量的副本是一致的。它核心的思想是:当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,发现自己缓存中缓存该变量的缓存行是无效的,那么它就会从内存重新读取。 二.并发编程中的三个概念 在并发编程中,我们通常会遇到以下三个问题:原子性问题,可见性问题,有序性问题。我们先看具体看一下这三个概念: 1.原子性 原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。 一个很经典的例子就是银行账户转账问题: 比如从账户A向账户B转1000元,那么必然包括2个操作:从账户A减去1000元,往账户B加上1000元。 试想一下,如果这2个操作不具备原子性,会造成什么样的后果。假如从账户A减去1000元之后,操作突然中止。然后又从B取出了500元,取出500元之后,再执行 往账户B加上1000元 的操作。这样就会导致账户A虽然减去了1000元,但是账户B没有收到这个转过来的1000元。 所以这2个操作必须要具备原子性才能保证不出现一些意外的问题。 同样地反映到并发编程中会出现什么结果呢? 举个最简单的例子,大家想一下假如为一个32位的变量赋值过程不具备原子性的话,会发生什么后果? 1 i = 9; 假若一个线程执行到这个语句时,我暂且假设为一个32位的变量赋值包括两个过程:为低16位赋值,为高16位赋值。 那么就可能发生一种情况:当将低16位数值写入之后,突然被中断,而此时又有一个线程去读取i的值,那么读取到的就是错误的数据。 2.可见性 可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。 举个简单的例子,看下面这段代码: 1 2 3 4 5 6 //线程1执行的代码 int i = 0; i = 10; //线程2执行的代码 j = i; 假若执行线程1的是CPU1,执行线程2的是CPU2。由上面的分析可知,当线程1执行 i =10这句时,会先把i的初始值加载到CPU1的高速缓存中,然后赋值为10,那么在CPU1的高速缓存当中i的值变为10了,却没有立即写入到主存当中。 此时线程2执行 j = i,它会先去主存读取i的值并加载到CPU2的缓存当中,注意此时内存当中i的值还是0,那么就会使得j的值为0,而不是10. 这就是可见性问题,线程1对变量i修改了之后,线程2没有立即看到线程1修改的值。 3.有序性 有序性:即程序执行的顺序按照代码的先后顺序执行。举个简单的例子,看下面这段代码: 1 2 […]
View Details推荐资料 IDEA 《IntelliJ-IDEA-Tutorial》:https://github.com/judasn/IntelliJ-IDEA-Tutorial 特别全的IDEA使用教程,可以学到很多实用的技巧。 Spring 《Spring实战(第4版)》:https://book.douban.com/subject/26767354/ 经典的、畅销的Spring学习和实践指南,从此书可以学习到Spring的实用用法,对Spring有个整体的了解,推荐整本阅读。 SpringBoot 《Spring Boot实战》:https://book.douban.com/subject/26857423/ SpringBoot的入门书,一共也就200多页,反正我是看完了,其中关于Groovy和Grails部分大可不看。 MyBatis 《MyBatis从入门到精通》:https://book.douban.com/subject/27074809/ 很好的一本MyBatis入门书,作者是开源插件PageHelper的项目主,平时忘了MyBatis的一些用法的时候可以当工具书使用,推荐整本阅读 MySql 《深入浅出MySQL》:https://book.douban.com/subject/25817684/ 网易DBA写的一本MySql书籍,作为一个开发者,我们只要看第一部分基础篇、第二部分开发篇、第三部分优化篇即可。 Linux 《循序渐进Linux(第2版)》:https://book.douban.com/subject/26758194/ 南非蚂蚁写的一本Linux书籍,作为一个开发者,我们只要看第一篇基础知识篇、第二篇服务器搭建篇即可,后面讲到生产环境部署项目会用到。 Elasticsearch 《Elasticsearch 权威指南》:https://www.elastic.co/guide/cn/elasticsearch/guide/current/index.html Elasticsearch官方推荐的中文学习资料,基于Elasticsearch2.4.x版本,比较老,但是可堪一用。大多数成熟的框架,版本迭代用法相差不会很大。 《Elasticsearch 技术解析与实战》:https://book.douban.com/subject/26967826/ 如果你觉得上面那本ElasticSearch版本太老的话可以看这本。 Mongodb 《MongoDB实战(第二版)》:https://book.douban.com/subject/27061123/ 很好的一本MongoDB实战书,作者参与过MongoDB的驱动开发,感兴趣的可以都看下。 Docker 《Spring Cloud与Docker微服务架构实战》:https://book.douban.com/subject/27028228/ 我们只需要看下这本书的Docker部分即可,后面讲到生产环境部署项目会用到。 结语 如果你按照我的推荐看了以上部分的资料,或者你已经有了以上部分的基础,那么你学习mall的时候会非常顺利。 推荐阅读 mall架构及功能概览 from:https://mp.weixin.qq.com/s?__biz=MzU1Nzg4NjgyMw==&mid=2247483680&idx=1&sn=4e5e2294a275a7709f9a239b34d1fa60&scene=21#wechat_redirect
View Details商品管理 数据库表结构 功能结构 订单管理 数据库表结构 功能结构 营销管理 数据库表结构 功能结构 内容管理 数据库表结构 功能结构 用户管理 数据库表结构 功能结构 注意:部分功能暂未实现,只是对表结构进行了设计,商品管理、订单管理、营销管理大部分功能均已实现。 相关资料 PowerDesigner数据库设计文件 商品管理:https://github.com/macrozheng/mall-learning/blob/master/document/pdm/mall_pms.pdm 订单管理:https://github.com/macrozheng/mall-learning/blob/master/document/pdm/mall_oms.pdm 营销管理:https://github.com/macrozheng/mall-learning/blob/master/document/pdm/mall_sms.pdm 内容管理:https://github.com/macrozheng/mall-learning/blob/master/document/pdm/mall_cms.pdm 用户管理:https://github.com/macrozheng/mall-learning/blob/master/document/pdm/mall_ums.pdm MindMaster功能思维导图 商品管理:https://github.com/macrozheng/mall-learning/blob/master/document/mind/pms.emmx 订单管理:https://github.com/macrozheng/mall-learning/blob/master/document/mind/oms.emmx 营销管理:https://github.com/macrozheng/mall-learning/blob/master/document/mind/sms.emmx 内容管理:https://github.com/macrozheng/mall-learning/blob/master/document/mind/cms.emmx 用户管理:https://github.com/macrozheng/mall-learning/blob/master/document/mind/ums.emmx 使用到的工具 PowerDesigner:http://powerdesigner.de/ MindMaster:http://www.edrawsoft.cn/mindmaster 推荐阅读 mall在Linux环境下的部署(基于Docker Compose) mall在Linux环境下的部署(基于Docker容器) mall在Windows环境下的部署 mall整合SpringBoot+MyBatis搭建基本骨架 mall整合Swagger-UI实现在线API文档 mall整合SpringSecurity和JWT实现认证和授权(一) mall整合Elasticsearch实现商品搜索 mall整合Mongodb实现文档操作 mall整合RabbitMQ实现延迟消息 mall整合OSS实现文件上传 from:https://mp.weixin.qq.com/s?__biz=MzU1Nzg4NjgyMw==&mid=2247483835&idx=1&sn=895ae94d5a0bc5fbe5bed8aa715d92af&scene=21#wechat_redirect
View Detailsmall-swarm简介 mall-swarm是一套微服务商城系统,采用了 Spring Cloud Greenwich、Spring Boot 2、MyBatis、Docker、Elasticsearch等核心技术,同时提供了基于Vue的管理后台方便快速搭建系统。mall-swarm在电商业务的基础集成了注册中心、配置中心、监控中心、网关等系统功能。 系统架构图 系统架构图 后端技术栈 技术 说明 Spring Cloud 微服务框架 Spring Boot 容器+MVC框架 Spring Security 认证和授权框架 MyBatis ORM框架 MyBatisGenerator 数据层代码生成 PageHelper MyBatis物理分页插件 Swagger-UI 文档生产工具 Elasticsearch 搜索引擎 RabbitMq 消息队列 Redis 分布式缓存 MongoDb NoSql数据库 Docker 应用容器引擎 Druid 数据库连接池 OSS 对象存储 MinIO 对象存储 JWT JWT登录支持 LogStash 日志收集 Lombok 简化对象封装工具 Seata 全局事务管理框架 Portainer 可视化Docker容器管理 Jenkins 自动化部署工具 项目结构
1 2 3 4 5 6 7 8 9 10 11 12 13 |
mall ├── mall-common -- 工具类及通用代码模块 ├── mall-mbg -- MyBatisGenerator生成的数据库操作代码模块 ├── mall-security -- 封装SpringSecurity+JWT的安全认证的模块 ├── mall-registry -- 基于Eureka的微服务注册中心 ├── mall-config-- 基于Spring Cloud Config的微服务配置中心 ├── mall-gateway -- 基于Spring Cloud Gateway的微服务API网关服务 ├── mall-monitor -- 基于Spring Boot Admin的微服务监控中心 ├── mall-admin -- 后台管理系统服务 ├── mall-search -- 基于Elasticsearch的商品搜索系统服务 ├── mall-portal -- 移动端商城系统服务 └── mall-demo -- 微服务远程调用测试服务 |
学习路线 之前有朋友问我,mall-swarm这个项目有没有学习教程?其实这个项目的功能与mall项目基本一致,只是在此基础上改成了微服务版本,只要看我写的《mall学习教程》和《Spring Cloud学习教程》即可,下面聊聊我所推荐的学习路线。 学习mall 《mall学习教程》主要分为如下几个部分,推荐学习顺序是除参考篇以外可以按下面的顺序学习,对于参考篇,可以在用到里面相关技术时再学习。 序章:mall项目的整体架构及功能介绍,同时对于新手推荐了一些相关书籍资料; 架构篇:mall项目的架构搭建教程,手把手教你搭建一个mall项目在使用的基本项目骨架; 业务篇:mall项目电商业务相关教程,对于了解项目业务有很大帮助; 技术要点篇:mall项目中的一些技术要点解析,主要介绍一些技术在项目中的运用; 部署篇:mall项目的部署教程,包括Windows、Linux和自动化部署方案; 参考篇:mall项目中所用技术和工具的入门教程,每一篇都可以单独学习,对于mall项目,这些教程的深入程度都刚刚好。 项目地址:https://github.com/macrozheng/mall-learning 学习SpringCloud 《Spring Cloud学习教程》是一套涵盖大部分核心组件使用的教程,包括Spring Cloud Alibaba及分布式事务Seata,基于Spring Cloud Greenwich及SpringBoot 2.1.7。20篇文章,篇篇精华,30个Demo,涵盖大部分应用场景。mall-swarm项目中所用到的Spring Cloud技术该教程基本都涵盖了,学习该教程可以为学习mall-swarm项目打下良好的Spring Cloud基础。 项目地址:https://github.com/macrozheng/springcloud-learning 学习mall-swarm 当我们学习了《mall学习教程》和《Spring Cloud学习教程》之后就可以着手学习mall-swarm这个项目了。首先需要的就是按之前的教程把项目跑起来,然后进行源码的学习,相信有了学习上面两套教程的基础,搞懂源码并不是什么难事。下面提供下mall-swarm的部署教程: mall-swarm在Windows环境下的部署 mall-swarm在Linux环境下的部署(基于Docker容器) 微服务架构下的自动化部署,使用Jenkins来实现! […]
View Details