├── .gitattributes ├── .gitignore ├── README-other.md ├── README.md ├── db ├── cassandra-demo │ ├── .gitignore │ ├── pom.xml │ └── src │ │ └── main │ │ ├── java │ │ └── org │ │ │ └── flylib │ │ │ └── cassandrademo │ │ │ ├── app │ │ │ └── CassandraDemoApp.java │ │ │ ├── pojo │ │ │ ├── Person.java │ │ │ ├── Person2.java │ │ │ └── Person2Key.java │ │ │ ├── repository │ │ │ └── PersonRepository.java │ │ │ └── service │ │ │ ├── PersonService.java │ │ │ └── impl │ │ │ └── PersonServiceImpl.java │ │ └── resources │ │ ├── application.xml │ │ └── cassandra.properties ├── consistent-hash │ ├── .gitignore │ ├── pom.xml │ └── src │ │ ├── main │ │ └── java │ │ │ └── io │ │ │ └── github │ │ │ └── flylib │ │ │ └── consistenthash │ │ │ ├── ByteUtils.java │ │ │ ├── ConsistentHash.java │ │ │ ├── HashAlgorithm.java │ │ │ └── Node.java │ │ └── test │ │ └── java │ │ └── TestConsistentHash.java └── distarch-jdbc │ ├── pom.xml │ └── src │ └── main │ ├── java │ └── com │ │ └── appjishu │ │ └── jdbc │ │ ├── config │ │ ├── .gitignore │ │ └── DataSourceConfigConst.java │ │ ├── demo │ │ ├── JdbcApp.java │ │ ├── controller │ │ │ └── HomeController.java │ │ ├── dao │ │ │ ├── ProductDAO.java │ │ │ └── UserDAO.java │ │ └── service │ │ │ └── DemoService.java │ │ └── util │ │ ├── ConnectionManager.java │ │ └── DBUtil.java │ └── resources │ └── application.properties ├── doc └── image │ ├── group-qrcode.png │ └── public-account.jpg ├── encrypt └── distarch-encrypt │ ├── pom.xml │ └── src │ └── main │ └── java │ └── com │ └── liushaoming │ └── encrypt │ ├── EncryptTest.java │ ├── EncryptUtil.java │ └── UUIDUtil.java ├── lock ├── distarch-lock-curator │ ├── .gitignore │ ├── README.md │ ├── pom.xml │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── liushaoming │ │ └── lock │ │ └── curator │ │ ├── CuratorHolder.java │ │ ├── LockCuratorTest.java │ │ └── ZKConfigBean.java └── distarch-lock-zookeeper │ ├── .gitignore │ ├── README.md │ ├── pom.xml │ └── src │ └── main │ └── java │ └── com │ └── liushaoming │ └── lock │ └── zk │ ├── DistributedLock.java │ ├── ZkTest.java │ └── ZookeeperClient.java ├── pom.xml ├── spring-boot-starter ├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── jframe-app │ ├── .gitignore │ ├── pom.xml │ └── src │ │ └── main │ │ ├── java │ │ └── com │ │ │ └── lsm │ │ │ └── jframe │ │ │ ├── JframeApp.java │ │ │ └── app │ │ │ └── controller │ │ │ └── HomeController.java │ │ └── resources │ │ └── application.yml ├── jframe-spring-boot-starter │ ├── .gitignore │ ├── pom.xml │ └── src │ │ └── main │ │ ├── java │ │ └── com │ │ │ └── lsm │ │ │ └── jframe │ │ │ └── core │ │ │ ├── api │ │ │ └── JframeService.java │ │ │ └── config │ │ │ ├── JframeAutoConfigure.java │ │ │ └── JframeServiceProperties.java │ │ └── resources │ │ └── META-INF │ │ └── spring.factories └── pom.xml └── spring └── distarch-springboot ├── .gitignore ├── README.md ├── pom.xml └── src └── main ├── java └── com │ └── appjishu │ ├── SwordBootApp.java │ └── swordboot │ ├── boot │ ├── AppContextHolder.java │ └── SysConfig.java │ ├── controller │ └── HomeController.java │ ├── model │ └── Student.java │ └── util │ └── TestUtil.java └── resources └── application.properties /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-language=java 2 | *.css linguist-language=java 3 | *.html linguist-language=java 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # maven ignore 2 | target/ 3 | *.jar 4 | *.war 5 | *.zip 6 | *.tar 7 | 8 | # eclipse ignore 9 | .settings/ 10 | .project 11 | .classpath 12 | 13 | # idea ignore 14 | .idea/ 15 | *.ipr 16 | *.iml 17 | *.iws 18 | 19 | # temp ignore 20 | logs/ 21 | *.doc 22 | *.log 23 | *.cache 24 | *.diff 25 | *.patch 26 | *.tmp 27 | 28 | # system ignore 29 | .DS_Store 30 | Thumbs.db 31 | -------------------------------------------------------------------------------- /README-other.md: -------------------------------------------------------------------------------- 1 | #sword 2 | 3 | Java高级编程中的利器(英文Sword,指的是" 宝剑", 像宝剑一样的利器)
4 | 里面包含了一致性hash,cassandra数据库的使用,ApplicationContextAware等 5 | 6 | ## 子项目列表 7 | 8 | | 序号 | 项目名称 | 简介 | 9 | | ------------- |:-------------:|:-------------:| 10 | | 1 | [consistent-hash](https://github.com/liushaoming/sword/tree/master/consistent-hash) | 一致性哈希的入门示例, 可以实现分布式存储 | 11 | | 2 | [cassandra-demo](https://github.com/liushaoming/sword/tree/master/cassandra-demo) | cassandra列存储数据库的入门 | 12 | | 3 | [sword-springboot](https://github.com/liushaoming/sword/tree/master/sword-springboot) | AppContextHolder(可获取到ApplicationContext) | 13 | 14 |
15 | 详细介绍 16 | 17 | ### 1. consistent-hash
18 | Java implementation of consistent-hashing 19 |
20 | 21 | ### 2. cassandra-demo
22 | cassandra数据库的入门编程 23 | 24 | 基于java的一致性hash的实现 25 | 26 | 一致性hash(consistent-hashing), 27 | 致性哈希算法在1997年由麻省理工学院提出(参见扩展阅读[1]),设计目标是为了解决因特网中的热点(Hot spot)问题,初衷和CARP十分类似。一致性哈希修正了CARP使用的简单哈希算法带来的问题,使得DHT可以在P2P环境中真正得到应用。 28 | 29 | 主要用于分布式缓存的实现 30 | 31 | 比如弹性伸缩(动态扩容) 32 | redis存储节点的增加和减少的时候,可以把需要迁移和hash()值的变化量做到最小。 33 | 34 | ### 3. sword-springboot 35 | 36 | com.appjishu.swordboot.boot.AppContextHolder 37 |
38 | 39 | 应用场景:
40 | 一般情况下,使用SpringMVC/SpringBoot的时候,各种bean注册到Spring容器里了,然后在需要这个bean的地方,
41 | 使用@Autowired或者@Resource标注的bean都可以被自动注入。 但是在某些场景下,
42 | 需要手动注入。比如在一个Util里面,这个Util里面的方法都是static的,这个时候,如果需要获取Spring容器中的某个
43 | bean,或者获取到ApplicationContext, 这个时候,就需要一个ApplicationContext Holder的东西,这里命名为AppContextHolder 44 | 45 | 其实Spring里有一个接口就是为了这个应用场景而生的ApplicationContextAware
46 | 本人就开发了一个com.appjishu.swordboot.boot.AppContextHolder实现了这个接口, 源码如下:
47 | ```java 48 | package com.appjishu.swordboot.boot; 49 | 50 | import org.springframework.beans.BeansException; 51 | import org.springframework.context.ApplicationContext; 52 | import org.springframework.context.ApplicationContextAware; 53 | import org.springframework.stereotype.Component; 54 | 55 | /** 56 | * 57 | * @author liushaoming 58 | * 59 | */ 60 | @Component 61 | public class AppContextHolder implements ApplicationContextAware { 62 | private static ApplicationContext applicationContext = null; 63 | 64 | @Override 65 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 66 | if (AppContextHolder.applicationContext == null) { 67 | AppContextHolder.applicationContext = applicationContext; 68 | } 69 | } 70 | 71 | // 获取applicationContext 72 | public static ApplicationContext getApplicationContext() { 73 | return applicationContext; 74 | } 75 | 76 | // 通过name获取 Bean. 77 | public static Object getBean(String name) { 78 | return getApplicationContext().getBean(name); 79 | } 80 | 81 | // 通过class获取Bean. 82 | public static T getBean(Class clazz) { 83 | return getApplicationContext().getBean(clazz); 84 | } 85 | 86 | // 通过name,以及Clazz返回指定的Bean 87 | public static T getBean(String name, Class clazz) { 88 | return getApplicationContext().getBean(name, clazz); 89 | } 90 | } 91 | ``` 92 | 使用的时候,很简单, 就像使用Spring原生的ApplicationContext一样, 现在要获取一个已经注册到容器里的一个bean Student
93 | ```java 94 | @Configuration 95 | public class SysConfig { 96 | @Bean(name = "myStudent") 97 | public Student student() { 98 | Student student=new Student(); 99 | student.setName("Frank Liu"); 100 | student.setAddress("Shanghai"); 101 | return student; 102 | } 103 | } 104 | ``` 105 | 使用AppContextHolder获取bean的操作 106 | ```java 107 | Student student0 = (Student) AppContextHolder.getBean("myStudent"); 108 | // 或者 109 | Student student1 = (Student) AppContextHolder.getBean(Student.class); 110 | log.info("student0.name={}", student0.getName()); 111 | ``` 112 | 测试方法: 113 | 在idea/eclipse里,导入maven项目sword-springboot, 右键运行SwordBootApp -> run as -> Java Application
114 | 这是一个springboot项目,打开浏览器访问地址[http://localhost:15000/test](http://localhost:15000/test)
115 | 可以运行测试AppContextHolder的测试代码, 贴出TestUtil里的方法 116 | ```java 117 | public class TestUtil { 118 | private static final Logger log = LoggerFactory.getLogger(TestUtil.class); 119 | 120 | public static void testAppContextHolder() { 121 | log.info("--testAppContextHolder start---"); 122 | Student student0 = (Student) AppContextHolder.getBean("myStudent"); 123 | Student student1 = (Student) AppContextHolder.getBean(Student.class); 124 | 125 | // 如果"student0 == student1"成立,则验证了AppContextHolder的确能获取到Spring容器ApplicationContext 126 | // 而且Spring容器默认注册bean使用的是单例模式 127 | log.info("student0==student1? {}", student0 == student1); 128 | log.info("-------------------"); 129 | log.info("student0.name={}", student0.getName()); 130 | log.info("student0.address={}", student0.getAddress()); 131 | log.info("-------------------"); 132 | log.info("student1.name={}", student1.getName()); 133 | log.info("student1.address={}", student1.getAddress()); 134 | log.info("--testAppContextHolder done---"); 135 | } 136 | } 137 | ``` 138 | 打印结果: 说明测试成功了 139 | ``` 140 | 2019-01-06 15:43:47.563 INFO 21612 --- [io-15000-exec-1] com.appjishu.swordboot.util.TestUtil : --testAppContextHolder start--- 141 | 2019-01-06 15:43:47.564 INFO 21612 --- [io-15000-exec-1] com.appjishu.swordboot.util.TestUtil : student0==student1? true 142 | 2019-01-06 15:43:47.564 INFO 21612 --- [io-15000-exec-1] com.appjishu.swordboot.util.TestUtil : ------------------- 143 | 2019-01-06 15:43:47.565 INFO 21612 --- [io-15000-exec-1] com.appjishu.swordboot.util.TestUtil : student0.name=Frank Liu 144 | 2019-01-06 15:43:47.565 INFO 21612 --- [io-15000-exec-1] com.appjishu.swordboot.util.TestUtil : student0.address=Shanghai 145 | 2019-01-06 15:43:47.566 INFO 21612 --- [io-15000-exec-1] com.appjishu.swordboot.util.TestUtil : ------------------- 146 | 2019-01-06 15:43:47.566 INFO 21612 --- [io-15000-exec-1] com.appjishu.swordboot.util.TestUtil : student1.name=Frank Liu 147 | 2019-01-06 15:43:47.566 INFO 21612 --- [io-15000-exec-1] com.appjishu.swordboot.util.TestUtil : student1.address=Shanghai 148 | 2019-01-06 15:43:47.566 INFO 21612 --- [io-15000-exec-1] com.appjishu.swordboot.util.TestUtil : --testAppContextHolder done--- 149 | ``` 150 | 151 | 注意事项:
152 | AppContextHolder在SpringBoot里使用,只需要在类名AppContextHolder前标注@Component
153 | 如果在SpringMVC里使用的,需要用@Component标注类AppContextHolder, 并且在applicationContext.xml里面设置
154 | 包含AppContextHolder所在的package, 或者直接用bean标签来注册它。 155 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # distarch 2 | 3 | Java分布式系统架构. 分布式架构, Distributed Architecture, 这里简称distarch. 4 | 5 | ## 子项目列表 6 | 7 | | 序号 | 项目名称 | 简介 | 8 | | ------------- |:-------------:|:-------------:| 9 | | 1 | [db/consistent-hash](https://github.com/liushaoming/distarch/tree/master/db/consistent-hash) | 一致性哈希的入门示例, 可以实现分布式存储 | 10 | | 2 | [db/cassandra-demo](https://github.com/liushaoming/distarch/tree/master/db/cassandra-demo) | cassandra列存储数据库的入门 | 11 | | 3 | [db/distarch-jdbc](https://github.com/liushaoming/distarch/tree/master/db/distarch-jdbc) | jdbc的ThreadLocal使用,原生事务操作 | 12 | | 4 | [spring/distarch-springboot](https://github.com/liushaoming/distarch/tree/master/spring/distarch-springboot) | Spring的一些利器的使用,如ApplicationContextAware | 13 | | 5 | [lock/distarch-lock-zookeeper](https://github.com/liushaoming/distarch/tree/master/lock/distarch-lock-zookeeper) | 分布式锁的实现--基于Zookeeper | 14 | | 6 | [lock/distarch-lock-curator](https://github.com/liushaoming/distarch/tree/master/lock/distarch-lock-curator) | 分布式锁的实现--基于Curator(内部集成了自动恢复状态的ZK客户端,更易用) | 15 | 16 | 17 | 有代码改进优化的建议的统一在Issues里面提 18 |
19 | 加QQ群讨论 20 |
21 | ![](doc/image/group-qrcode.png) 22 | 23 | 微信公众号 24 | 25 | ![](doc/image/public-account.jpg) 26 | -------------------------------------------------------------------------------- /db/cassandra-demo/.gitignore: -------------------------------------------------------------------------------- 1 | # maven ignore 2 | target/ 3 | *.jar 4 | *.war 5 | *.zip 6 | *.tar 7 | 8 | # eclipse ignore 9 | .settings/ 10 | .project 11 | .classpath 12 | 13 | # idea ignore 14 | .idea/ 15 | *.ipr 16 | *.iml 17 | *.iws 18 | 19 | # temp ignore 20 | logs/ 21 | *.doc 22 | *.log 23 | *.cache 24 | *.diff 25 | *.patch 26 | *.tmp 27 | 28 | # system ignore 29 | .DS_Store 30 | Thumbs.db 31 | -------------------------------------------------------------------------------- /db/cassandra-demo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | org.flylib 15 | cassandra-demo 16 | 1.0-SNAPSHOT 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | org.springframework.data 30 | spring-data-cassandra 31 | 2.0.2.RELEASE 32 | 33 | 34 | -------------------------------------------------------------------------------- /db/cassandra-demo/src/main/java/org/flylib/cassandrademo/app/CassandraDemoApp.java: -------------------------------------------------------------------------------- 1 | package org.flylib.cassandrademo.app; 2 | 3 | import org.flylib.cassandrademo.service.PersonService; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.context.support.ClassPathXmlApplicationContext; 6 | 7 | /** 8 | * @author liushaoming 9 | * @create 2017-11-29 14:38 10 | **/ 11 | public class CassandraDemoApp { 12 | 13 | public static void main(String[] args) { 14 | ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext("application.xml"); 15 | appContext.start(); 16 | PersonService service = appContext.getBean(PersonService.class); 17 | service.test(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /db/cassandra-demo/src/main/java/org/flylib/cassandrademo/pojo/Person.java: -------------------------------------------------------------------------------- 1 | package org.flylib.cassandrademo.pojo; 2 | 3 | import org.springframework.data.cassandra.core.mapping.Column; 4 | import org.springframework.data.cassandra.core.mapping.PrimaryKey; 5 | import org.springframework.data.cassandra.core.mapping.Table; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Entity 11 | * 12 | * @author liushaoming 13 | * @create 2017-11-29 13:22 14 | **/ 15 | @Table(value="person") 16 | public class Person { 17 | // 主键 18 | @PrimaryKey 19 | @Column(value = "id") 20 | private String id; 21 | 22 | // 列名 与数据库列名一致时可不加 23 | @Column(value = "name") 24 | private String name; 25 | 26 | @Column(value = "age") 27 | private int age; 28 | 29 | @Column(value = "record") 30 | private List record; 31 | 32 | // 支持构造函数 33 | public Person(String id, String name, int age) { 34 | this.id = id; 35 | this.name = name; 36 | this.age = age; 37 | } 38 | 39 | public String getId() { 40 | return id; 41 | } 42 | 43 | public void setId(String id) { 44 | this.id = id; 45 | } 46 | 47 | public String getName() { 48 | return name; 49 | } 50 | 51 | public void setName(String name) { 52 | this.name = name; 53 | } 54 | 55 | public int getAge() { 56 | return age; 57 | } 58 | 59 | public void setAge(int age) { 60 | this.age = age; 61 | } 62 | 63 | public List getRecord() { 64 | return record; 65 | } 66 | 67 | public void setRecord(List record) { 68 | this.record = record; 69 | } 70 | 71 | @Override 72 | public String toString() { 73 | return "Person [id=" + id + ", name=" + name + ", age=" + age + ", records=" + record.toString() 74 | +"]"; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /db/cassandra-demo/src/main/java/org/flylib/cassandrademo/pojo/Person2.java: -------------------------------------------------------------------------------- 1 | package org.flylib.cassandrademo.pojo; 2 | 3 | import org.springframework.data.cassandra.core.mapping.PrimaryKey; 4 | import org.springframework.data.cassandra.core.mapping.Table; 5 | 6 | /** 7 | * @author liushaoming 8 | * @create 2017-11-29 13:47 9 | **/ 10 | @Table(value = "person2") 11 | public class Person2 { 12 | @PrimaryKey 13 | private Person2Key pKey; 14 | 15 | private int age; 16 | 17 | public Person2Key getpKey() { 18 | return pKey; 19 | } 20 | 21 | public void setpKey(Person2Key pKey) { 22 | this.pKey = pKey; 23 | } 24 | 25 | public int getAge() { 26 | return age; 27 | } 28 | 29 | public void setAge(int age) { 30 | this.age = age; 31 | } 32 | 33 | @Override 34 | public String toString() { 35 | return "Person2 [pKey=" + pKey + ", age=" + age + "]"; 36 | } 37 | } -------------------------------------------------------------------------------- /db/cassandra-demo/src/main/java/org/flylib/cassandrademo/pojo/Person2Key.java: -------------------------------------------------------------------------------- 1 | package org.flylib.cassandrademo.pojo; 2 | 3 | import org.springframework.data.cassandra.core.cql.Ordering; 4 | import org.springframework.data.cassandra.core.cql.PrimaryKeyType; 5 | import org.springframework.data.cassandra.core.mapping.PrimaryKeyClass; 6 | import org.springframework.data.cassandra.core.mapping.PrimaryKeyColumn; 7 | 8 | /** 9 | * Person2Key mapping 10 | * 11 | * @author liushaoming 12 | * @create 2017-11-29 13:41 13 | **/ 14 | 15 | @PrimaryKeyClass 16 | public class Person2Key { 17 | 18 | // 分区键 19 | @PrimaryKeyColumn(name = "id", ordinal = 0, type = PrimaryKeyType.PARTITIONED) 20 | private String id; 21 | 22 | // 集群键 23 | @PrimaryKeyColumn(name = "name", ordinal = 1, type = PrimaryKeyType.CLUSTERED, ordering = Ordering.DESCENDING) 24 | private String name; 25 | 26 | public String getId() { 27 | return id; 28 | } 29 | 30 | public void setId(String id) { 31 | this.id = id; 32 | } 33 | 34 | public String getName() { 35 | return name; 36 | } 37 | 38 | public void setName(String name) { 39 | this.name = name; 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | return "Person2Key [id=" + id + ", name=" + name + "]"; 45 | } 46 | } -------------------------------------------------------------------------------- /db/cassandra-demo/src/main/java/org/flylib/cassandrademo/repository/PersonRepository.java: -------------------------------------------------------------------------------- 1 | package org.flylib.cassandrademo.repository; 2 | 3 | import org.flylib.cassandrademo.pojo.Person; 4 | import org.springframework.data.cassandra.repository.Query; 5 | import org.springframework.data.repository.CrudRepository; 6 | import org.springframework.stereotype.Repository; 7 | import org.springframework.data.repository.query.Param; 8 | 9 | import java.util.List; 10 | 11 | @Repository 12 | public interface PersonRepository extends CrudRepository { 13 | @Query("select * from Person where id=?1 and name=?2") 14 | List findByIdAndName(String id, String name); 15 | 16 | /** 17 | * 会报错的, 不能这样用 18 | * @param name 19 | * @return 20 | */ 21 | // @Query("select * from Person where name=?1 ALLOW FILTERING") 22 | // List findByName1(String name); 23 | 24 | @Query("select * from Person where name=:name ALLOW FILTERING") 25 | List findByName(@Param("name") String name); 26 | 27 | @Query("update person set record = record + [ :record ] where id=:id ") 28 | int addRecord(@Param("id") String id, @Param("record") String record); 29 | } 30 | -------------------------------------------------------------------------------- /db/cassandra-demo/src/main/java/org/flylib/cassandrademo/service/PersonService.java: -------------------------------------------------------------------------------- 1 | package org.flylib.cassandrademo.service; 2 | 3 | /** 4 | * @author liushaoming 5 | * @create 2017-11-29 14:16 6 | **/ 7 | public interface PersonService { 8 | void test(); 9 | } 10 | -------------------------------------------------------------------------------- /db/cassandra-demo/src/main/java/org/flylib/cassandrademo/service/impl/PersonServiceImpl.java: -------------------------------------------------------------------------------- 1 | package org.flylib.cassandrademo.service.impl; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Iterator; 5 | import java.util.List; 6 | 7 | import com.datastax.driver.core.BoundStatement; 8 | import com.datastax.driver.core.ConsistencyLevel; 9 | import com.datastax.driver.core.PreparedStatement; 10 | import com.datastax.driver.core.Session; 11 | import com.datastax.driver.core.querybuilder.Assignment; 12 | import com.datastax.driver.core.querybuilder.Update; 13 | import org.flylib.cassandrademo.pojo.Person; 14 | import org.flylib.cassandrademo.repository.PersonRepository; 15 | import org.flylib.cassandrademo.service.PersonService; 16 | import org.springframework.beans.factory.annotation.Autowired; 17 | import org.springframework.data.cassandra.config.CassandraCqlSessionFactoryBean; 18 | import org.springframework.data.cassandra.core.CassandraOperations; 19 | import org.springframework.data.cassandra.core.CassandraTemplate; 20 | import org.springframework.data.cassandra.core.cql.CqlOperations; 21 | import org.springframework.data.cassandra.core.cql.PreparedStatementBinder; 22 | import org.springframework.stereotype.Service; 23 | 24 | import com.datastax.driver.core.querybuilder.QueryBuilder; 25 | import com.datastax.driver.core.querybuilder.Select; 26 | import com.datastax.driver.core.querybuilder.Update; 27 | 28 | import javax.management.Query; 29 | 30 | 31 | /** 32 | * @author liushaoming 33 | * @create 2017-11-29 14:03 34 | **/ 35 | @Service 36 | public class PersonServiceImpl implements PersonService { 37 | @Autowired 38 | private PersonRepository personRepository; 39 | 40 | @Autowired 41 | private CassandraOperations cassandraOperations; 42 | 43 | @Autowired 44 | private CassandraTemplate cassandraTemplate; 45 | 46 | @Autowired 47 | private CassandraCqlSessionFactoryBean sessionFactoryBean; 48 | 49 | @Override 50 | public void test() { 51 | //通过Repository查询 52 | Iterable iterable = personRepository.findAll(); 53 | Iterator it = iterable.iterator(); 54 | System.out.println("==>findAll:"); 55 | while (it.hasNext()) { 56 | Person p = it.next(); 57 | System.out.println(p.toString()); 58 | } 59 | 60 | //通过Repository 自定义查询查询 61 | // List list = personRepository.findByName("one", "11111"); 62 | List list = personRepository.findByName("one"); 63 | 64 | System.out.println("==>findByName:"); 65 | for (Person person : list) {System.out.println(person.toString()); 66 | System.out.println("=====> " + person.toString()); 67 | } 68 | 69 | 70 | 71 | System.out.println("Try to add value to one 'list column'---------------------"); 72 | 73 | // for (int i = 0; i < 3; i++) { 74 | // personRepository.addRecord("t" +, "my record" + i); 75 | // } 76 | // List list2 = personRepository.findByName("one"); 77 | // System.out.println("==>findByName again:"); 78 | // for (Person p : list2) { 79 | // System.out.println("=====> " + p.toString()); 80 | // } 81 | 82 | // Update update = QueryBuilder.update("person"); 83 | // update.setConsistencyLevel(ConsistencyLevel.ONE); 84 | //// update.with(QueryBuilder.set("age", 35)); 85 | // Assignment assignment = QueryBuilder.add("record", "xxx"); 86 | // update.with(assignment); 87 | // update.where(QueryBuilder.eq("id", "1")); 88 | //// cassandraOperations.execute(update); 89 | // cassandraOperations.update(update, Person.class); 90 | 91 | /** 92 | * 如何在list类型的列里增加一个数据 93 | */ 94 | // method 1 95 | // String updateColumnCql = "update person set record = record + ['yyy'] where id='1';"; 96 | // boolean cqlSuccess = cassandraTemplate.getCqlOperations().execute(updateColumnCql); 97 | 98 | // method 2 PreparedStatement 99 | String updateColumnCql = "update person set record = record + ? where id= ? ;"; 100 | Session session = sessionFactoryBean.getObject(); 101 | PreparedStatement preparedStatement = session.prepare(updateColumnCql); 102 | List addedColumn = new ArrayList(); 103 | addedColumn.add("Added"); 104 | BoundStatement statement = preparedStatement.bind(addedColumn, "1"); 105 | session.execute(statement); 106 | 107 | 108 | //通过cassandraOperations查询 109 | Select select = QueryBuilder.select().from("person"); 110 | select.where(QueryBuilder.eq("id", "1")); 111 | Person person = cassandraOperations.selectOne(select, Person.class); 112 | System.out.println("==> Finally, cassandraOperations:"); 113 | System.out.println(person.toString()); 114 | } 115 | 116 | } -------------------------------------------------------------------------------- /db/cassandra-demo/src/main/resources/application.xml: -------------------------------------------------------------------------------- 1 | 2 | 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 | -------------------------------------------------------------------------------- /db/cassandra-demo/src/main/resources/cassandra.properties: -------------------------------------------------------------------------------- 1 | #cassandra数据库连接 2 | #节点ip 3 | cassandra_contactpoints=127.0.0.1 4 | #端口 5 | cassandra_port=9042 6 | #当前操作键空间 7 | cassandra_keyspace=mstest 8 | #登录用户名 9 | cassandra_username=cassandra 10 | #登录密码 11 | cassandra_password=cassandra 12 | -------------------------------------------------------------------------------- /db/consistent-hash/.gitignore: -------------------------------------------------------------------------------- 1 | # maven ignore 2 | target/ 3 | *.jar 4 | *.war 5 | *.zip 6 | *.tar 7 | 8 | # eclipse ignore 9 | .settings/ 10 | .project 11 | .classpath 12 | 13 | # idea ignore 14 | .idea/ 15 | *.ipr 16 | *.iml 17 | *.iws 18 | 19 | # temp ignore 20 | logs/ 21 | *.doc 22 | *.log 23 | *.cache 24 | *.diff 25 | *.patch 26 | *.tmp 27 | 28 | # system ignore 29 | .DS_Store 30 | Thumbs.db 31 | -------------------------------------------------------------------------------- /db/consistent-hash/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.github.flylib 8 | consistent-hash 9 | jar 10 | 1.0.0 11 | https://github.com/flylib 12 | 13 | 14 | 15 | org.apache.maven.plugins 16 | maven-compiler-plugin 17 | 3.7.0 18 | 19 | 1.8 20 | 1.8 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /db/consistent-hash/src/main/java/io/github/flylib/consistenthash/ByteUtils.java: -------------------------------------------------------------------------------- 1 | package io.github.flylib.consistenthash; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.nio.ByteBuffer; 5 | import java.nio.charset.Charset; 6 | 7 | 8 | /** 9 | * @author liushaoming 10 | *

11 | * Utilities for byte process 12 | */ 13 | public final class ByteUtils { 14 | public static final String DEFAULT_CHARSET_NAME = "utf-8"; 15 | public static final Charset DEFAULT_CHARSET = Charset 16 | .forName(DEFAULT_CHARSET_NAME); 17 | /** 18 | * if it is testing,check key argument even if use binary protocol. The user 19 | * must never change this value at all. 20 | */ 21 | public static boolean testing; 22 | 23 | private ByteUtils() { 24 | } 25 | 26 | public static boolean isNumber(String string) { 27 | if (string == null || string.isEmpty()) { 28 | return false; 29 | } 30 | int i = 0; 31 | if (string.charAt(0) == '-') { 32 | if (string.length() > 1) { 33 | i++; 34 | } else { 35 | return false; 36 | } 37 | } 38 | for (; i < string.length(); i++) { 39 | if (!Character.isDigit(string.charAt(i))) { 40 | return false; 41 | } 42 | } 43 | return true; 44 | } 45 | 46 | public static final byte[] getBytes(String k) { 47 | if (k == null || k.length() == 0) { 48 | throw new IllegalArgumentException("Key must not be blank"); 49 | } 50 | try { 51 | return k.getBytes(DEFAULT_CHARSET_NAME); 52 | } catch (UnsupportedEncodingException e) { 53 | throw new RuntimeException(e); 54 | } 55 | } 56 | 57 | 58 | private static int maxKeyLength = 250; 59 | 60 | 61 | public static final int normalizeCapacity(int requestedCapacity) { 62 | switch (requestedCapacity) { 63 | case 0: 64 | case 1 << 0: 65 | case 1 << 1: 66 | case 1 << 2: 67 | case 1 << 3: 68 | case 1 << 4: 69 | case 1 << 5: 70 | case 1 << 6: 71 | case 1 << 7: 72 | case 1 << 8: 73 | case 1 << 9: 74 | case 1 << 10: 75 | case 1 << 11: 76 | case 1 << 12: 77 | case 1 << 13: 78 | case 1 << 14: 79 | case 1 << 15: 80 | case 1 << 16: 81 | case 1 << 17: 82 | case 1 << 18: 83 | case 1 << 19: 84 | case 1 << 21: 85 | case 1 << 22: 86 | case 1 << 23: 87 | case 1 << 24: 88 | case 1 << 25: 89 | case 1 << 26: 90 | case 1 << 27: 91 | case 1 << 28: 92 | case 1 << 29: 93 | case 1 << 30: 94 | case Integer.MAX_VALUE: 95 | return requestedCapacity; 96 | } 97 | 98 | int newCapacity = 1; 99 | while (newCapacity < requestedCapacity) { 100 | newCapacity <<= 1; 101 | if (newCapacity < 0) { 102 | return Integer.MAX_VALUE; 103 | } 104 | } 105 | return newCapacity; 106 | } 107 | 108 | public static final boolean stepBuffer(ByteBuffer buffer, int remaining) { 109 | if (buffer.remaining() >= remaining) { 110 | buffer.position(buffer.position() + remaining); 111 | return true; 112 | } else { 113 | return false; 114 | } 115 | } 116 | 117 | public static String getString(byte[] bytes) { 118 | try { 119 | return new String(bytes, DEFAULT_CHARSET_NAME); 120 | } catch (UnsupportedEncodingException e) { 121 | throw new RuntimeException(e); 122 | } 123 | } 124 | 125 | public static void byte2hex(byte b, StringBuffer buf) { 126 | char[] hexChars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 127 | 'A', 'B', 'C', 'D', 'E', 'F'}; 128 | int high = ((b & 0xf0) >> 4); 129 | int low = (b & 0x0f); 130 | buf.append(hexChars[high]); 131 | buf.append(hexChars[low]); 132 | } 133 | 134 | public static void int2hex(int a, StringBuffer str) { 135 | str.append(Integer.toHexString(a)); 136 | } 137 | 138 | public static void short2hex(int a, StringBuffer str) { 139 | str.append(Integer.toHexString(a)); 140 | } 141 | 142 | public static void getBytes(long i, int index, byte[] buf) { 143 | long q; 144 | int r; 145 | int pos = index; 146 | byte sign = 0; 147 | 148 | if (i < 0) { 149 | sign = '-'; 150 | i = -i; 151 | } 152 | 153 | // Get 2 digits/iteration using longs until quotient fits into an int 154 | while (i > Integer.MAX_VALUE) { 155 | q = i / 100; 156 | // really: r = i - (q * 100); 157 | r = (int) (i - ((q << 6) + (q << 5) + (q << 2))); 158 | i = q; 159 | buf[--pos] = DigitOnes[r]; 160 | buf[--pos] = DigitTens[r]; 161 | } 162 | 163 | // Get 2 digits/iteration using ints 164 | int q2; 165 | int i2 = (int) i; 166 | while (i2 >= 65536) { 167 | q2 = i2 / 100; 168 | // really: r = i2 - (q * 100); 169 | r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2)); 170 | i2 = q2; 171 | buf[--pos] = DigitOnes[r]; 172 | buf[--pos] = DigitTens[r]; 173 | } 174 | 175 | // Fall thru to fast mode for smaller numbers 176 | // assert(i2 <= 65536, i2); 177 | for (; ; ) { 178 | q2 = (i2 * 52429) >>> (16 + 3); 179 | r = i2 - ((q2 << 3) + (q2 << 1)); // r = i2-(q2*10) ... 180 | buf[--pos] = digits[r]; 181 | i2 = q2; 182 | if (i2 == 0) 183 | break; 184 | } 185 | if (sign != 0) { 186 | buf[--pos] = sign; 187 | } 188 | } 189 | 190 | /** 191 | * Places characters representing the integer i into the character array 192 | * buf. The characters are placed into the buffer backwards starting with 193 | * the least significant digit at the specified index (exclusive), and 194 | * working backwards from there. 195 | *

196 | * Will fail if i == Integer.MIN_VALUE 197 | */ 198 | static void getBytes(int i, int index, byte[] buf) { 199 | int q, r; 200 | int pos = index; 201 | byte sign = 0; 202 | 203 | if (i < 0) { 204 | sign = '-'; 205 | i = -i; 206 | } 207 | 208 | // Generate two digits per iteration 209 | while (i >= 65536) { 210 | q = i / 100; 211 | // really: r = i - (q * 100); 212 | r = i - ((q << 6) + (q << 5) + (q << 2)); 213 | i = q; 214 | buf[--pos] = DigitOnes[r]; 215 | buf[--pos] = DigitTens[r]; 216 | } 217 | 218 | // Fall thru to fast mode for smaller numbers 219 | // assert(i <= 65536, i); 220 | for (; ; ) { 221 | q = (i * 52429) >>> (16 + 3); 222 | r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ... 223 | buf[--pos] = digits[r]; 224 | i = q; 225 | if (i == 0) 226 | break; 227 | } 228 | if (sign != 0) { 229 | buf[--pos] = sign; 230 | } 231 | } 232 | 233 | /** 234 | * All possible chars for representing a number as a String 235 | */ 236 | final static byte[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', 237 | '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 238 | 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 239 | 'z'}; 240 | 241 | final static byte[] DigitTens = {'0', '0', '0', '0', '0', '0', '0', '0', 242 | '0', '0', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '2', 243 | '2', '2', '2', '2', '2', '2', '2', '2', '2', '3', '3', '3', '3', 244 | '3', '3', '3', '3', '3', '3', '4', '4', '4', '4', '4', '4', '4', 245 | '4', '4', '4', '5', '5', '5', '5', '5', '5', '5', '5', '5', '5', 246 | '6', '6', '6', '6', '6', '6', '6', '6', '6', '6', '7', '7', '7', 247 | '7', '7', '7', '7', '7', '7', '7', '8', '8', '8', '8', '8', '8', 248 | '8', '8', '8', '8', '9', '9', '9', '9', '9', '9', '9', '9', '9', 249 | '9',}; 250 | 251 | final static byte[] DigitOnes = {'0', '1', '2', '3', '4', '5', '6', '7', 252 | '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 253 | '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', 254 | '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', 255 | '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 256 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', 257 | '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', 258 | '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', 259 | '9',}; 260 | 261 | final static int[] sizeTable = {9, 99, 999, 9999, 99999, 999999, 9999999, 262 | 99999999, 999999999, Integer.MAX_VALUE}; 263 | 264 | // Requires positive x 265 | public static final int stringSize(int x) { 266 | for (int i = 0; ; i++) 267 | if (x <= sizeTable[i]) 268 | return i + 1; 269 | } 270 | 271 | // Requires positive x 272 | public static final int stringSize(long x) { 273 | long p = 10; 274 | for (int i = 1; i < 19; i++) { 275 | if (x < p) 276 | return i; 277 | p = 10 * p; 278 | } 279 | return 19; 280 | } 281 | 282 | final static int[] byte_len_array = new int[256]; 283 | 284 | static { 285 | for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; ++i) { 286 | int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i); 287 | byte_len_array[i & 0xFF] = size; 288 | } 289 | } 290 | } -------------------------------------------------------------------------------- /db/consistent-hash/src/main/java/io/github/flylib/consistenthash/ConsistentHash.java: -------------------------------------------------------------------------------- 1 | package io.github.flylib.consistenthash; 2 | 3 | import java.util.Set; 4 | import java.util.TreeMap; 5 | 6 | /** 7 | * @author liushaoming 8 | * 一致性hash的实现 9 | * Java implementation of consistent-hashing 10 | */ 11 | public class ConsistentHash { 12 | 13 | private int virtualNum = 5; //平均虚拟节点数 14 | 15 | private HashAlgorithm alg = HashAlgorithm.KETAMA_HASH;//采用的HASH算法 16 | 17 | private Set nodeSet; //节点列表 18 | 19 | private final TreeMap nodeMap = new TreeMap(); 20 | 21 | private static class SingletonHolder { 22 | private static ConsistentHash instance = new ConsistentHash(); 23 | } 24 | 25 | private ConsistentHash() { 26 | } 27 | 28 | public static ConsistentHash getInstance() { 29 | return SingletonHolder.instance; 30 | } 31 | 32 | /** 33 | * 构建一致性HASH环 34 | */ 35 | public void buildHashCircle() { 36 | if (nodeSet == null) return; 37 | for (Node node : nodeSet) { 38 | for (int i = 0; i < virtualNum; i++) { 39 | long nodeKey = this.alg.hash(node.toString() + "-" + i); 40 | nodeMap.put(nodeKey, node); 41 | } 42 | } 43 | } 44 | 45 | /** 46 | * 沿环的顺时针找到虚拟节点 47 | * 48 | * @param key 49 | * @return 50 | */ 51 | public Node findNodeByKey(String key) { 52 | final Long hash = this.alg.hash(key); 53 | Long target = hash; 54 | if (!nodeMap.containsKey(hash)) { 55 | target = nodeMap.ceilingKey(hash); 56 | if (target == null && !nodeMap.isEmpty()) { 57 | target = nodeMap.firstKey(); 58 | } 59 | } 60 | return nodeMap.get(target); 61 | } 62 | 63 | /** 64 | * 设置每个节点的虚拟节点个数,该参数默认是100 65 | * 66 | * @param virtualNum 虚拟节点数 67 | */ 68 | public void setVirtualNum(int virtualNum) { 69 | this.virtualNum = virtualNum; 70 | } 71 | 72 | /** 73 | * 设置一致性HASH的算法,默认采用 KETAMA_HASH 74 | * 对于一致性HASH而言选择的HASH算法首先要考虑发散度其次再考虑性能 75 | * 76 | * @param alg 具体支持的算法 77 | * @see HashAlgorithm 78 | */ 79 | public void setAlg(HashAlgorithm alg) { 80 | this.alg = alg; 81 | } 82 | 83 | /** 84 | * 配置实际的节点,允许同一个IP上多个节点,但是应该用name区分开 85 | * 86 | * @param nodeList 节点列表 87 | */ 88 | public void setNodeList(Set nodeList) { 89 | this.nodeSet = nodeList; 90 | } 91 | 92 | /** 93 | * 获取环形HASH 94 | * 95 | * @return 96 | */ 97 | public TreeMap getNodeMap() { 98 | return nodeMap; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /db/consistent-hash/src/main/java/io/github/flylib/consistenthash/HashAlgorithm.java: -------------------------------------------------------------------------------- 1 | package io.github.flylib.consistenthash; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.nio.ByteBuffer; 5 | import java.nio.ByteOrder; 6 | import java.security.MessageDigest; 7 | import java.security.NoSuchAlgorithmException; 8 | import java.util.zip.CRC32; 9 | 10 | public enum HashAlgorithm { 11 | 12 | /** 13 | * Native hash (String.hashCode()). 14 | */ 15 | NATIVE_HASH, 16 | /** 17 | * CRC32_HASH as used by the perl API. This will be more consistent both 18 | * across multiple API users as well as java versions, but is mostly likely 19 | * significantly slower. 20 | */ 21 | CRC32_HASH, 22 | /** 23 | * FNV hashes are designed to be fast while maintaining a low collision 24 | * rate. The FNV speed allows one to quickly hash lots of data while 25 | * maintaining a reasonable collision rate. 26 | *

27 | * // * @see http://www.isthe.com/chongo/tech/comp/fnv/ 28 | * // * @see http://en.wikipedia.org/wiki/Fowler_Noll_Vo_hash 29 | */ 30 | FNV1_64_HASH, 31 | /** 32 | * Variation of FNV. 33 | */ 34 | FNV1A_64_HASH, 35 | /** 36 | * 32-bit FNV1. 37 | */ 38 | FNV1_32_HASH, 39 | /** 40 | * 32-bit FNV1a. 41 | */ 42 | FNV1A_32_HASH, 43 | /** 44 | * MD5-based hash algorithm used by ketama. 45 | */ 46 | KETAMA_HASH, 47 | 48 | /** 49 | * From mysql source 50 | */ 51 | MYSQL_HASH, 52 | 53 | ELF_HASH, 54 | 55 | RS_HASH, 56 | 57 | /** 58 | * From lua source,it is used for long key 59 | */ 60 | LUA_HASH, 61 | /** 62 | * MurMurHash算法,是非加密HASH算法,性能很高, 63 | * 比传统的CRC32,MD5,SHA-1(这两个算法都是加密HASH算法,复杂度本身就很高,带来的性能上的损害也不可避免) 64 | * 等HASH算法要快很多,这个算法的碰撞率很低. 65 | * http://murmurhash.googlepages.com/ 66 | */ 67 | MurMurHash, 68 | /** 69 | * The Jenkins One-at-a-time hash ,please see 70 | * http://www.burtleburtle.net/bob/hash/doobs.html 71 | */ 72 | ONE_AT_A_TIME; 73 | 74 | private static final long FNV_64_INIT = 0xcbf29ce484222325L; 75 | private static final long FNV_64_PRIME = 0x100000001b3L; 76 | 77 | private static final long FNV_32_INIT = 2166136261L; 78 | private static final long FNV_32_PRIME = 16777619; 79 | 80 | /** 81 | * Compute the hash for the given key. 82 | * 83 | * @return a positive integer hash 84 | */ 85 | public long hash(final String k) { 86 | long rv = 0; 87 | switch (this) { 88 | case NATIVE_HASH: 89 | rv = k.hashCode(); 90 | break; 91 | case CRC32_HASH: 92 | // return (crc32(shift) >> 16) & 0x7fff; 93 | CRC32 crc32 = new CRC32(); 94 | crc32.update(ByteUtils.getBytes(k)); 95 | rv = crc32.getValue() >> 16 & 0x7fff; 96 | break; 97 | case FNV1_64_HASH: { 98 | // Thanks to pierre@demartines.com for the pointer 99 | rv = FNV_64_INIT; 100 | int len = k.length(); 101 | for (int i = 0; i < len; i++) { 102 | rv *= FNV_64_PRIME; 103 | rv ^= k.charAt(i); 104 | } 105 | } 106 | break; 107 | case MurMurHash: 108 | ByteBuffer buf = ByteBuffer.wrap(k.getBytes()); 109 | int seed = 0x1234ABCD; 110 | 111 | ByteOrder byteOrder = buf.order(); 112 | buf.order(ByteOrder.LITTLE_ENDIAN); 113 | 114 | long m = 0xc6a4a7935bd1e995L; 115 | int r = 47; 116 | 117 | rv = seed ^ (buf.remaining() * m); 118 | 119 | long ky; 120 | while (buf.remaining() >= 8) { 121 | ky = buf.getLong(); 122 | 123 | ky *= m; 124 | ky ^= ky >>> r; 125 | ky *= m; 126 | 127 | rv ^= ky; 128 | rv *= m; 129 | } 130 | 131 | if (buf.remaining() > 0) { 132 | ByteBuffer finish = ByteBuffer.allocate(8).order( 133 | ByteOrder.LITTLE_ENDIAN); 134 | // for big-endian version, do this first: 135 | // finish.position(8-buf.remaining()); 136 | finish.put(buf).rewind(); 137 | rv ^= finish.getLong(); 138 | rv *= m; 139 | } 140 | 141 | rv ^= rv >>> r; 142 | rv *= m; 143 | rv ^= rv >>> r; 144 | buf.order(byteOrder); 145 | break; 146 | case FNV1A_64_HASH: { 147 | rv = FNV_64_INIT; 148 | int len = k.length(); 149 | for (int i = 0; i < len; i++) { 150 | rv ^= k.charAt(i); 151 | rv *= FNV_64_PRIME; 152 | } 153 | } 154 | break; 155 | case FNV1_32_HASH: { 156 | rv = FNV_32_INIT; 157 | int len = k.length(); 158 | for (int i = 0; i < len; i++) { 159 | rv *= FNV_32_PRIME; 160 | rv ^= k.charAt(i); 161 | } 162 | } 163 | break; 164 | case FNV1A_32_HASH: { 165 | rv = FNV_32_INIT; 166 | int len = k.length(); 167 | for (int i = 0; i < len; i++) { 168 | rv ^= k.charAt(i); 169 | rv *= FNV_32_PRIME; 170 | } 171 | } 172 | break; 173 | case KETAMA_HASH: 174 | byte[] bKey = computeMd5(k); 175 | rv = (long) (bKey[3] & 0xFF) << 24 | (long) (bKey[2] & 0xFF) << 16 176 | | (long) (bKey[1] & 0xFF) << 8 | bKey[0] & 0xFF; 177 | break; 178 | 179 | case MYSQL_HASH: 180 | int nr2 = 4; 181 | for (int i = 0; i < k.length(); i++) { 182 | rv ^= ((rv & 63) + nr2) * k.charAt(i) + (rv << 8); 183 | nr2 += 3; 184 | } 185 | break; 186 | case ELF_HASH: 187 | long x = 0; 188 | for (int i = 0; i < k.length(); i++) { 189 | rv = (rv << 4) + k.charAt(i); 190 | if ((x = rv & 0xF0000000L) != 0) { 191 | rv ^= x >> 24; 192 | rv &= ~x; 193 | } 194 | } 195 | rv = rv & 0x7FFFFFFF; 196 | break; 197 | case RS_HASH: 198 | long b = 378551; 199 | long a = 63689; 200 | for (int i = 0; i < k.length(); i++) { 201 | rv = rv * a + k.charAt(i); 202 | a *= b; 203 | } 204 | rv = rv & 0x7FFFFFFF; 205 | break; 206 | case LUA_HASH: 207 | int step = (k.length() >> 5) + 1; 208 | rv = k.length(); 209 | for (int len = k.length(); len >= step; len -= step) { 210 | rv = rv ^ (rv << 5) + (rv >> 2) + k.charAt(len - 1); 211 | } 212 | case ONE_AT_A_TIME: 213 | try { 214 | int hash = 0; 215 | for (byte bt : k.getBytes("utf-8")) { 216 | hash += (bt & 0xFF); 217 | hash += (hash << 10); 218 | hash ^= (hash >>> 6); 219 | } 220 | hash += (hash << 3); 221 | hash ^= (hash >>> 11); 222 | hash += (hash << 15); 223 | return hash; 224 | } catch (UnsupportedEncodingException e) { 225 | throw new IllegalStateException("Hash function error", e); 226 | } 227 | default: 228 | assert false; 229 | } 230 | 231 | return rv & 0xffffffffL; /* Truncate to 32-bits */ 232 | } 233 | 234 | /** 235 | * Get the md5 of the given key. 236 | */ 237 | public static byte[] computeMd5(String k) { 238 | MessageDigest md5; 239 | try { 240 | md5 = MessageDigest.getInstance("MD5"); 241 | } catch (NoSuchAlgorithmException e) { 242 | throw new RuntimeException("MD5 not supported", e); 243 | } 244 | md5.reset(); 245 | md5.update(ByteUtils.getBytes(k)); 246 | return md5.digest(); 247 | } 248 | 249 | // public static void main(String[] args) { 250 | // HashAlgorithm alg=HashAlgorithm.LUA_HASH; 251 | // long h=0; 252 | // long start=System.currentTimeMillis(); 253 | // for(int i=0;i<100000;i++) { 254 | // h=alg.hash("dddddd"); 255 | //// System.out.println(h); 256 | // } 257 | // System.out.println(System.currentTimeMillis()-start); 258 | // } 259 | } -------------------------------------------------------------------------------- /db/consistent-hash/src/main/java/io/github/flylib/consistenthash/Node.java: -------------------------------------------------------------------------------- 1 | package io.github.flylib.consistenthash; 2 | 3 | /** 4 | * 节点的IP实现 5 | */ 6 | public class Node { 7 | 8 | private String name; 9 | 10 | private String ip; 11 | 12 | public Node(String name, String ip) { 13 | this.name = name; 14 | this.ip = ip; 15 | } 16 | 17 | public Node(String ip) { 18 | this.ip = ip; 19 | } 20 | 21 | public String getName() { 22 | return name; 23 | } 24 | 25 | public void setName(String name) { 26 | this.name = name; 27 | } 28 | 29 | public String getIp() { 30 | return ip; 31 | } 32 | 33 | public void setIp(String ip) { 34 | this.ip = ip; 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | 40 | if (name != null && !"".equals(name)) { 41 | return ip + "-" + name; 42 | } 43 | return ip; 44 | } 45 | 46 | @Override 47 | public boolean equals(Object o) { 48 | if (o == null) return false; 49 | Node node = (Node) o; 50 | if (node.getIp() == null && ip == null && node.getName() == null && name == null) return true; 51 | if (name == null && node.getName() != null) return false; 52 | if (ip == null && node.getIp() != null) return false; 53 | assert ip != null; 54 | assert name != null; 55 | return name.equals(node.getName()) && ip.equals(node.getIp()); 56 | } 57 | 58 | @Override 59 | public int hashCode() { 60 | int result = name != null ? name.hashCode() : 0; 61 | result = 31 * result + (ip != null ? ip.hashCode() : 0); 62 | return result; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /db/consistent-hash/src/test/java/TestConsistentHash.java: -------------------------------------------------------------------------------- 1 | 2 | import io.github.flylib.consistenthash.ConsistentHash; 3 | import io.github.flylib.consistenthash.Node; 4 | 5 | import java.util.*; 6 | import java.util.concurrent.ConcurrentHashMap; 7 | 8 | /** 9 | * @author liushaoming 10 | * 一致性HASH测试--高并发测试 11 | */ 12 | public class TestConsistentHash { 13 | final ConcurrentHashMap stat = new ConcurrentHashMap(); 14 | 15 | public static void main(String[] args) throws InterruptedException { 16 | final TestConsistentHash testConsistHashWithComp = new TestConsistentHash(); 17 | Set ips = new HashSet(); 18 | ips.add(new Node("192.168.10.1")); 19 | ips.add(new Node("192.168.10.2")); 20 | ips.add(new Node("192.168.10.3")); 21 | ips.add(new Node("192.168.10.4")); 22 | ips.add(new Node("192.168.10.5")); 23 | ips.add(new Node("192.168.10.6")); 24 | ips.add(new Node("192.168.10.7")); 25 | ips.add(new Node("192.168.10.8")); 26 | ips.add(new Node("192.168.10.9")); 27 | ips.add(new Node("192.168.1.10")); 28 | 29 | final ConsistentHash hash = ConsistentHash.getInstance(); 30 | hash.setNodeList(ips); 31 | // hash.setAlg(HashAlgorithm.LUA_HASH); 32 | // hash.set(1024); 33 | // hash.setAlg(HashAlgorithm.CRC32_HASH); 34 | hash.buildHashCircle(); 35 | 36 | long start = System.currentTimeMillis(); 37 | 38 | // for (int i = 0; i < 20000; i++) { 39 | for (int i = 0; i < 500; i++) { 40 | final String name = "thread" + i; 41 | Thread t = new Thread(new Runnable() { 42 | @Override 43 | public void run() { 44 | // for (int h = 0; h < 1000; h++) { 45 | for (int h = 0; h < 2; h++) { 46 | Node node = hash.findNodeByKey(name + h); 47 | testConsistHashWithComp.send(node); 48 | } 49 | testConsistHashWithComp.print(); 50 | } 51 | }, name); 52 | t.start(); 53 | } 54 | System.out.println(System.currentTimeMillis() - start); 55 | Thread.sleep(1000 * 20); 56 | testConsistHashWithComp.print(); 57 | } 58 | 59 | public synchronized void send(Node node) { 60 | Long count = stat.get(node.getIp()); 61 | if (count == null) { 62 | stat.put(node.getIp(), 1L); 63 | } else { 64 | stat.put(node.getIp(), count + 1); 65 | } 66 | } 67 | 68 | public ConcurrentHashMap getStat() { 69 | return stat; 70 | } 71 | 72 | public void print() { 73 | long all = 0; 74 | for (Map.Entry entry : stat.entrySet()) { 75 | long num = entry.getValue(); 76 | all += num; 77 | System.out.println("mac:" + entry.getKey() + " hits:" + num); 78 | } 79 | System.out.println("all:" + all); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /db/distarch-jdbc/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.appjishu 8 | distarch-jdbc 9 | 1.0-SNAPSHOT 10 | 11 | 12 | org.springframework.boot 13 | spring-boot-starter-parent 14 | 2.1.0.RELEASE 15 | 16 | 17 | 18 | 19 | UTF-8 20 | UTF-8 21 | 1.8 22 | 23 | 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-web 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-test 32 | test 33 | 34 | 35 | mysql 36 | mysql-connector-java 37 | 8.0.11 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /db/distarch-jdbc/src/main/java/com/appjishu/jdbc/config/.gitignore: -------------------------------------------------------------------------------- 1 | DataSourceConfigConst 2 | -------------------------------------------------------------------------------- /db/distarch-jdbc/src/main/java/com/appjishu/jdbc/config/DataSourceConfigConst.java: -------------------------------------------------------------------------------- 1 | package com.appjishu.jdbc.config; 2 | 3 | public class DataSourceConfigConst { 4 | public static final String DRIVER_CLASS_NAME = "com.mysql.cj.jdbc.Driver"; 5 | public static final String URL = "jdbc:mysql://localhost:3306/mall?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false"; 6 | public static final String USERNAME = "root"; 7 | public static final String PASSWORD = "yourPassword"; 8 | 9 | 10 | 11 | } 12 | -------------------------------------------------------------------------------- /db/distarch-jdbc/src/main/java/com/appjishu/jdbc/demo/JdbcApp.java: -------------------------------------------------------------------------------- 1 | package com.appjishu.jdbc.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class JdbcApp { 8 | public static void main(String[] args){ 9 | SpringApplication.run(JdbcApp.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /db/distarch-jdbc/src/main/java/com/appjishu/jdbc/demo/controller/HomeController.java: -------------------------------------------------------------------------------- 1 | package com.appjishu.jdbc.demo.controller; 2 | 3 | import com.appjishu.jdbc.demo.service.DemoService; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.RestController; 7 | 8 | @RequestMapping("") 9 | @RestController 10 | public class HomeController { 11 | @Autowired 12 | private DemoService demoService; 13 | 14 | @RequestMapping("") 15 | public String index() { 16 | return "Access sword-springboot OK."; 17 | } 18 | 19 | @RequestMapping("/test") 20 | public String test() { 21 | demoService.handle(); 22 | return "Test done."; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /db/distarch-jdbc/src/main/java/com/appjishu/jdbc/demo/dao/ProductDAO.java: -------------------------------------------------------------------------------- 1 | package com.appjishu.jdbc.demo.dao; 2 | 3 | import com.appjishu.jdbc.util.ConnectionManager; 4 | import org.springframework.stereotype.Repository; 5 | 6 | import java.sql.Connection; 7 | import java.sql.ResultSet; 8 | import java.sql.SQLException; 9 | import java.sql.Statement; 10 | import java.util.Random; 11 | 12 | @Repository 13 | public class ProductDAO { 14 | public int add() throws SQLException { 15 | Random random = new Random(); 16 | String sql = "insert into product (name, price) values ('iphone xr', " + random.nextInt(100) + " );"; 17 | Connection conn = ConnectionManager.getConnection(); 18 | Statement statement = conn.createStatement(); 19 | int updatedCount = statement.executeUpdate(sql); 20 | return updatedCount; 21 | } 22 | 23 | public int count() throws SQLException { 24 | String sql = "SELECT COUNT(1) FROM product "; 25 | Connection connection = ConnectionManager.getConnection(); 26 | Statement statement = connection.createStatement(); 27 | ResultSet resultSet = statement.executeQuery(sql); 28 | return resultSet.next() ? resultSet.getInt(1) : -1; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /db/distarch-jdbc/src/main/java/com/appjishu/jdbc/demo/dao/UserDAO.java: -------------------------------------------------------------------------------- 1 | package com.appjishu.jdbc.demo.dao; 2 | 3 | import com.appjishu.jdbc.util.ConnectionManager; 4 | import org.springframework.stereotype.Repository; 5 | 6 | import java.sql.Connection; 7 | import java.sql.SQLException; 8 | import java.sql.Statement; 9 | import java.util.Random; 10 | 11 | @Repository 12 | public class UserDAO { 13 | 14 | public int add() throws SQLException { 15 | /** 16 | * user_table表不存在,会抛异常 17 | */ 18 | Random random = new Random(); 19 | String sql ="insert into user_table (name, price) values ('iphone xr', " + random.nextInt(100) + " );"; 20 | Connection conn = ConnectionManager.getConnection(); 21 | Statement statement = conn.createStatement(); 22 | int updatedCount =statement.executeUpdate(sql); 23 | return updatedCount; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /db/distarch-jdbc/src/main/java/com/appjishu/jdbc/demo/service/DemoService.java: -------------------------------------------------------------------------------- 1 | package com.appjishu.jdbc.demo.service; 2 | 3 | import com.appjishu.jdbc.demo.dao.ProductDAO; 4 | import com.appjishu.jdbc.demo.dao.UserDAO; 5 | import com.appjishu.jdbc.util.ConnectionManager; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Service; 10 | 11 | import java.sql.Connection; 12 | import java.sql.SQLException; 13 | 14 | @Service 15 | public class DemoService { 16 | private static final Logger log = LoggerFactory.getLogger(DemoService.class); 17 | 18 | @Autowired 19 | private ProductDAO productDAO; 20 | @Autowired 21 | private UserDAO userDAO; 22 | 23 | 24 | public void handle() { 25 | Connection connection = ConnectionManager.getConnection(); 26 | 27 | try { 28 | int count0 = productDAO.count(); 29 | log.info("count0={}", count0); 30 | ConnectionManager.beginTransaction(connection); 31 | productDAO.add(); 32 | int count1 = productDAO.count(); 33 | log.info("count1={}", count1); 34 | boolean test = true; 35 | if (test) { 36 | userDAO.add(); 37 | } 38 | ConnectionManager.commitTransaction(connection); 39 | } catch (Exception e) { 40 | e.printStackTrace(); 41 | ConnectionManager.rollbackTransaction(connection); 42 | } 43 | int count2 = 0; 44 | boolean nowAutoCommit = false; 45 | try { 46 | count2 = productDAO.count(); 47 | nowAutoCommit = connection.getAutoCommit(); 48 | } catch (SQLException e) { 49 | e.printStackTrace(); 50 | } 51 | log.info("getAutoCommit()={}", nowAutoCommit); 52 | log.info("count2={}", count2); 53 | 54 | log.info("---Done---"); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /db/distarch-jdbc/src/main/java/com/appjishu/jdbc/util/ConnectionManager.java: -------------------------------------------------------------------------------- 1 | package com.appjishu.jdbc.util; 2 | 3 | import java.sql.Connection; 4 | import java.sql.SQLException; 5 | 6 | public class ConnectionManager { 7 | private static ThreadLocal connectionHolder = new ThreadLocal(); 8 | 9 | /** 10 | * 采用ThreadLocal封装Connection 11 | * 12 | * @return 13 | */ 14 | public static Connection getConnection() { 15 | //获得线程变量connectionHolder的值conn 16 | Connection conn = connectionHolder.get(); 17 | if (conn == null) { 18 | //如果连接为空,则创建连接,另一个工具类,创建连接 19 | conn = DBUtil.getConnection(); 20 | //将局部变量connectionHolder的值设置为conn 21 | connectionHolder.set(conn); 22 | } 23 | return conn; 24 | } 25 | 26 | 27 | /** 28 | * 关闭连接和从线程变量中删除conn 29 | */ 30 | public static void closeConnection() { 31 | //获得线程变量connectionHolder的值conn 32 | Connection conn = connectionHolder.get(); 33 | if (conn != null) { 34 | try { 35 | //关闭连接 36 | conn.close(); 37 | //从线程局部变量中移除conn,如果没有移除掉,下次还会用这个已经关闭的连接,就会出错 38 | connectionHolder.remove(); 39 | } catch (SQLException e) { 40 | e.printStackTrace(); 41 | } 42 | } 43 | } 44 | 45 | /** 46 | *开启事务,手动开启 47 | */ 48 | public static void beginTransaction(Connection conn){ 49 | try { 50 | //如果连接存在,再设置连接,否则会出错 51 | if (conn != null){ 52 | //默认conn是自动提交, 53 | if (conn.getAutoCommit()){ 54 | //关闭自动提交,即是手动开启事务 55 | conn.setAutoCommit(false); 56 | } 57 | } 58 | }catch(SQLException e){ 59 | e.printStackTrace(); 60 | } 61 | } 62 | 63 | /** 64 | * 提交事务 65 | */ 66 | public static void commitTransaction(Connection conn){ 67 | try{ 68 | if (conn != null){ 69 | if (!conn.getAutoCommit()){ 70 | conn.commit(); 71 | } 72 | } 73 | }catch(SQLException e){ 74 | e.printStackTrace(); 75 | } 76 | } 77 | 78 | /** 79 | * 回滚事务 80 | */ 81 | public static void rollbackTransaction(Connection conn){ 82 | try { 83 | if (conn != null){ 84 | if(!conn.getAutoCommit()){ 85 | conn.rollback(); 86 | } 87 | } 88 | }catch(SQLException e){ 89 | e.printStackTrace(); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /db/distarch-jdbc/src/main/java/com/appjishu/jdbc/util/DBUtil.java: -------------------------------------------------------------------------------- 1 | package com.appjishu.jdbc.util; 2 | 3 | import com.appjishu.jdbc.config.DataSourceConfigConst; 4 | import com.mysql.cj.jdbc.MysqlConnectionPoolDataSource; 5 | import com.mysql.cj.jdbc.MysqlDataSource; 6 | 7 | import javax.sql.DataSource; 8 | import java.sql.Connection; 9 | import java.sql.DriverManager; 10 | import java.sql.SQLException; 11 | 12 | public class DBUtil { 13 | public static Connection getConnection() { 14 | Connection connection = null; 15 | try { 16 | Class.forName(DataSourceConfigConst.DRIVER_CLASS_NAME); 17 | 18 | MysqlDataSource mysqlDataSource = new MysqlConnectionPoolDataSource(); 19 | mysqlDataSource.setUrl(DataSourceConfigConst.URL); 20 | mysqlDataSource.setUser(DataSourceConfigConst.USERNAME); 21 | mysqlDataSource.setPassword(DataSourceConfigConst.PASSWORD); 22 | connection = mysqlDataSource.getConnection(); 23 | } catch (SQLException e) { 24 | e.printStackTrace(); 25 | connection = null; 26 | } catch (ClassNotFoundException e1) { 27 | e1.printStackTrace(); 28 | connection = null; 29 | } 30 | return connection; 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /db/distarch-jdbc/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=15001 2 | -------------------------------------------------------------------------------- /doc/image/group-qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bootsrc/distarch/224a27cc1cf7df6ac80807c97c95a2ff8d06a61b/doc/image/group-qrcode.png -------------------------------------------------------------------------------- /doc/image/public-account.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bootsrc/distarch/224a27cc1cf7df6ac80807c97c95a2ff8d06a61b/doc/image/public-account.jpg -------------------------------------------------------------------------------- /encrypt/distarch-encrypt/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.liushaoming.distarch 8 | distarch-encrypt 9 | 1.0-SNAPSHOT 10 | 11 | 12 | org.springframework.boot 13 | spring-boot-starter-parent 14 | 2.1.0.RELEASE 15 | 16 | 17 | 18 | 19 | UTF-8 20 | UTF-8 21 | 1.8 22 | 23 | 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-web 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-test 32 | test 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /encrypt/distarch-encrypt/src/main/java/com/liushaoming/encrypt/EncryptTest.java: -------------------------------------------------------------------------------- 1 | package com.liushaoming.encrypt; 2 | 3 | /** 4 | * 右键--run as java application此class即可调试 5 | * 使用md5加盐加密。不可逆加密。 6 | */ 7 | public class EncryptTest { 8 | public static void main(String[] args){ 9 | testEncrypt(); 10 | } 11 | 12 | private static void testEncrypt() { 13 | System.out.println("You should save code in deploy database. And sell license to customers."); 14 | 15 | for (int i=0;i< 5; i++) { 16 | String license = UUIDUtil.newUuid(); 17 | String code = EncryptUtil.encrypt(license); 18 | 19 | System.out.println("license_" + i + "=" + license + ", code=" + code); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /encrypt/distarch-encrypt/src/main/java/com/liushaoming/encrypt/EncryptUtil.java: -------------------------------------------------------------------------------- 1 | package com.liushaoming.encrypt; 2 | 3 | import org.springframework.util.DigestUtils; 4 | import org.springframework.util.StringUtils; 5 | 6 | import java.security.InvalidParameterException; 7 | 8 | public class EncryptUtil { 9 | private static final String SALT = "nnjskslsl@#$*&!ap"; 10 | 11 | /** 12 | * 加密函数: 计算公式, code = encrypt(license) 13 | * @param license 14 | * @return 15 | */ 16 | public static String encrypt(String license) { 17 | if (StringUtils.isEmpty(license)) { 18 | throw new InvalidParameterException("license can not be null or empty"); 19 | } 20 | return DigestUtils.md5DigestAsHex(license.getBytes()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /encrypt/distarch-encrypt/src/main/java/com/liushaoming/encrypt/UUIDUtil.java: -------------------------------------------------------------------------------- 1 | package com.liushaoming.encrypt; 2 | 3 | import java.util.UUID; 4 | 5 | public class UUIDUtil { 6 | 7 | 8 | // public static void main(String[] args) { 9 | // String uuid = newUuid(); 10 | // System.out.println("uuid=" + uuid); 11 | // } 12 | 13 | /** 14 | * UUID是由一个十六位的数字组成,表现出来的形式例如 15 | * 668eee3274eb4873a1b921b7f9bb85d6 16 | * @return 17 | */ 18 | public static String newUuid() { 19 | return UUID.randomUUID().toString().replace("-", ""); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lock/distarch-lock-curator/.gitignore: -------------------------------------------------------------------------------- 1 | # maven ignore 2 | target/ 3 | *.jar 4 | *.war 5 | *.zip 6 | *.tar 7 | 8 | # eclipse ignore 9 | .settings/ 10 | .project 11 | .classpath 12 | 13 | # idea ignore 14 | .idea/ 15 | *.ipr 16 | *.iml 17 | *.iws 18 | 19 | # temp ignore 20 | logs/ 21 | *.doc 22 | *.log 23 | *.cache 24 | *.diff 25 | *.patch 26 | *.tmp 27 | 28 | # system ignore 29 | .DS_Store 30 | Thumbs.db 31 | -------------------------------------------------------------------------------- /lock/distarch-lock-curator/README.md: -------------------------------------------------------------------------------- 1 | # distarch-lock-curator 2 | 3 | 基于apache curator框架来实现分布式锁 4 | 5 | 在 JDK 的 java.util.concurrent.locks 中, 为我们提供了可重入锁, 读写锁, 6 | 7 | 及超时获取锁的方法. 为我们提供了完好的支持, 但是在分布式系统中, 当多个应用需 8 | 9 | 要共同操作某一个资源时. 我么就无法使用 JDK 来实现了, 这时就需要使用一个外部 10 | 11 | 服务来为此进行支持, 现在我们选用 ZooKeeper + Curator 来完成分布式锁 12 | 13 | 14 | ## 演示方法 15 | 16 | 导入项目到eclipse/idea,然后选中LockCuratorTest.java 右键--run as -java application 17 | 18 | 即可运行测试. 19 | 20 | ## 实现原理 21 | 22 | 使用curator里面的可重入分布式锁InterProcessMutex 23 | LockCuratorTest代码如下 24 | 25 | ```java 26 | 27 | CuratorHolder curatorHolder = new CuratorHolder(); 28 | CuratorFramework client = curatorHolder.getClient(); 29 | 30 | if (client.getState() != CuratorFrameworkState.STARTED) { 31 | client.start(); 32 | } 33 | InterProcessLock lock = new InterProcessMutex(client, lockRoot); 34 | 35 | try { 36 | lockSuccess = lock.acquire(1, TimeUnit.SECONDS); 37 | } catch (Exception e) { 38 | e.printStackTrace(); 39 | } 40 | 41 | logger.info(">>>>>threadId={}, lock_success={}, threadIndex={}", 42 | new Object[]{threadId, lockSuccess, index + ""}); 43 | 44 | 45 | if (lockSuccess) { 46 | try { 47 | Thread.sleep(RandomUtils.nextInt(10, 20)); 48 | lock.release(); 49 | logger.info(">>>>>threadId={}, lock_released={}, threadIndex={}", 50 | new Object[]{threadId, lockSuccess, index}); 51 | } catch (Exception e) { 52 | e.printStackTrace(); 53 | 54 | } 55 | } 56 | ``` 57 | 58 | 其中curatorHolder.getClient() 使用了ThreadLocal来实现每个线程中都各自拥有一个独立的CuratorFramework 59 | 60 | 实例。在多线程环境下(比如多线程环境下),有效减小CuratorFramework实例的个数. 61 | -------------------------------------------------------------------------------- /lock/distarch-lock-curator/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.liushaoming 8 | distarch-lock-curator 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | org.springframework.boot 14 | spring-boot-starter-parent 15 | 2.1.2.RELEASE 16 | 17 | 18 | 19 | 20 | 1.8 21 | UTF-8 22 | UTF-8 23 | 24 | 25 | 26 | 27 | org.apache.zookeeper 28 | zookeeper 29 | 3.4.13 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-web 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-starter-test 38 | test 39 | 40 | 41 | 42 | org.apache.curator 43 | curator-recipes 44 | 4.1.0 45 | 46 | 47 | 48 | org.apache.commons 49 | commons-lang3 50 | 3.8.1 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | org.apache.curator 59 | curator-recipes 60 | 61 | 62 | org.apache.zookeeper 63 | zookeeper 64 | 65 | 66 | 67 | 68 | 69 | org.apache.zookeeper 70 | zookeeper 71 | 72 | 73 | org.slf4j 74 | slf4j-log4j12 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | distlockboot 83 | 84 | 85 | org.springframework.boot 86 | spring-boot-maven-plugin 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /lock/distarch-lock-curator/src/main/java/com/liushaoming/lock/curator/CuratorHolder.java: -------------------------------------------------------------------------------- 1 | package com.liushaoming.lock.curator; 2 | 3 | import org.apache.curator.RetryPolicy; 4 | import org.apache.curator.framework.CuratorFramework; 5 | import org.apache.curator.framework.CuratorFrameworkFactory; 6 | import org.apache.curator.retry.ExponentialBackoffRetry; 7 | 8 | public class CuratorHolder { 9 | 10 | public void setZkConfigBean(ZKConfigBean zkConfigBean) { 11 | this.zkConfigBean = zkConfigBean; 12 | } 13 | 14 | private ZKConfigBean zkConfigBean; 15 | 16 | private final ThreadLocal lockHolder = new ThreadLocal() { 17 | @Override 18 | protected CuratorFramework initialValue() { 19 | return newClient(); 20 | } 21 | }; 22 | 23 | private CuratorFramework newClient(){ 24 | RetryPolicy retry = new ExponentialBackoffRetry(1000, 3); 25 | return CuratorFrameworkFactory.builder().connectString(zkConfigBean.getConnectStr()) 26 | .retryPolicy(retry) 27 | .sessionTimeoutMs(zkConfigBean.getSessionTimeout()) 28 | .connectionTimeoutMs(zkConfigBean.getConnectTimeout()) 29 | .build(); 30 | } 31 | 32 | public CuratorFramework getClient() { 33 | CuratorFramework client = lockHolder.get(); 34 | if (client == null) { 35 | client = newClient(); 36 | lockHolder.set(client); 37 | } 38 | return client; 39 | } 40 | 41 | public void setClient(CuratorFramework client) { 42 | lockHolder.set(client); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lock/distarch-lock-curator/src/main/java/com/liushaoming/lock/curator/LockCuratorTest.java: -------------------------------------------------------------------------------- 1 | package com.liushaoming.lock.curator; 2 | 3 | import org.apache.commons.lang3.RandomUtils; 4 | import org.apache.curator.framework.CuratorFramework; 5 | import org.apache.curator.framework.imps.CuratorFrameworkState; 6 | import org.apache.curator.framework.recipes.locks.InterProcessLock; 7 | import org.apache.curator.framework.recipes.locks.InterProcessMutex; 8 | import org.apache.curator.utils.CloseableUtils; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import java.util.concurrent.ExecutorService; 13 | import java.util.concurrent.Executors; 14 | import java.util.concurrent.TimeUnit; 15 | 16 | public class LockCuratorTest { 17 | private static final Logger logger = LoggerFactory.getLogger(LockCuratorTest.class); 18 | static int index = 0; 19 | 20 | public static void main(String[] args) { 21 | 22 | final int threadCount = 20; 23 | CuratorHolder curatorHolder = new CuratorHolder(); 24 | ZKConfigBean zkConfigBean = new ZKConfigBean(); 25 | int sessionTimeout = 10000; 26 | String connectionStr = "localhost:2181"; 27 | String lockRoot = "/lock_curator_test"; 28 | int connectTimeout = 8000; 29 | 30 | zkConfigBean.setConnectStr(connectionStr); 31 | zkConfigBean.setLockRoot(lockRoot); 32 | zkConfigBean.setSessionTimeout(sessionTimeout); 33 | zkConfigBean.setConnectTimeout(connectTimeout); 34 | curatorHolder.setZkConfigBean(zkConfigBean); 35 | 36 | ExecutorService threadPool = Executors.newFixedThreadPool(3); 37 | for (int i = 0; i < threadCount; i++) { 38 | Thread thread = new Thread(new Runnable() { 39 | public void run() { 40 | index++; 41 | CuratorFramework client = curatorHolder.getClient(); 42 | if (client.getState() != CuratorFrameworkState.STARTED) { 43 | client.start(); 44 | } 45 | InterProcessLock lock = new InterProcessMutex(client, lockRoot); 46 | boolean lockSuccess = false; 47 | 48 | long threadId = Thread.currentThread().getId(); 49 | logger.info("---tringToAcquire,thredId={}", threadId); 50 | 51 | try { 52 | lockSuccess = lock.acquire(1, TimeUnit.SECONDS); 53 | } catch (Exception e) { 54 | e.printStackTrace(); 55 | } 56 | 57 | logger.info(">>>>>threadId={}, lock_success={}, threadIndex={}", 58 | new Object[]{threadId, lockSuccess, index + ""}); 59 | 60 | 61 | if (lockSuccess) { 62 | try { 63 | Thread.sleep(RandomUtils.nextInt(10, 20)); 64 | lock.release(); 65 | logger.info(">>>>>threadId={}, lock_released={}, threadIndex={}", 66 | new Object[]{threadId, lockSuccess, index}); 67 | } catch (Exception e) { 68 | e.printStackTrace(); 69 | 70 | } 71 | } 72 | 73 | // if (client.getState() == CuratorFrameworkState.STARTED) { 74 | // logger.info("---Stop CuratorClient---,thredId={}", threadId); 75 | // CloseableUtils.closeQuietly(client); 76 | // } 77 | 78 | 79 | CloseableUtils.closeQuietly(client); 80 | } 81 | }); 82 | 83 | // threadPool.submit(thread); 84 | thread.start(); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /lock/distarch-lock-curator/src/main/java/com/liushaoming/lock/curator/ZKConfigBean.java: -------------------------------------------------------------------------------- 1 | package com.liushaoming.lock.curator; 2 | 3 | public class ZKConfigBean { 4 | private String lockRoot; 5 | private int sessionTimeout; 6 | private String connectStr; 7 | private int connectTimeout; 8 | 9 | public String getLockRoot() { 10 | return lockRoot; 11 | } 12 | 13 | public void setLockRoot(String lockRoot) { 14 | this.lockRoot = lockRoot; 15 | } 16 | 17 | public int getSessionTimeout() { 18 | return sessionTimeout; 19 | } 20 | 21 | public void setSessionTimeout(int sessionTimeout) { 22 | this.sessionTimeout = sessionTimeout; 23 | } 24 | 25 | public String getConnectStr() { 26 | return connectStr; 27 | } 28 | 29 | public void setConnectStr(String connectStr) { 30 | this.connectStr = connectStr; 31 | } 32 | 33 | public int getConnectTimeout() { 34 | return connectTimeout; 35 | } 36 | 37 | public void setConnectTimeout(int connectTimeout) { 38 | this.connectTimeout = connectTimeout; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lock/distarch-lock-zookeeper/.gitignore: -------------------------------------------------------------------------------- 1 | # maven ignore 2 | target/ 3 | *.jar 4 | *.war 5 | *.zip 6 | *.tar 7 | 8 | # eclipse ignore 9 | .settings/ 10 | .project 11 | .classpath 12 | 13 | # idea ignore 14 | .idea/ 15 | *.ipr 16 | *.iml 17 | *.iws 18 | 19 | # temp ignore 20 | logs/ 21 | *.doc 22 | *.log 23 | *.cache 24 | *.diff 25 | *.patch 26 | *.tmp 27 | 28 | # system ignore 29 | .DS_Store 30 | Thumbs.db 31 | -------------------------------------------------------------------------------- /lock/distarch-lock-zookeeper/README.md: -------------------------------------------------------------------------------- 1 | # distarch-lock-zookeeper 2 | 3 | 其实基于ZooKeeper,就是使用它的临时有序节点来实现的分布式锁。 4 | 5 | 原理就是:当某客户端要进行逻辑的加锁时,就在zookeeper上的某个指定节点的目录下,去生成一个唯一的临时有序节点, 6 | 7 | 然后判断自己是否是这些有序节点中序号最小的一个,如果是,则算是获取了锁。如果不是,则说明没有获取到锁,那么就需 8 | 9 | 要在序列中找到比自己小的那个节点,并对其调用exist()方法,对其注册事件监听,当监听到这个节点被删除了,那就再去 10 | 11 | 判断一次自己当初创建的节点是否变成了序列中最小的。如果是,则获取锁,如果不是,则重复上述步骤。 12 | 13 | 当释放锁的时候,只需将这个临时节点删除即可。 14 | -------------------------------------------------------------------------------- /lock/distarch-lock-zookeeper/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.liushaoming.distarch 8 | distarch-lock-zookeeper 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | org.apache.zookeeper 14 | zookeeper 15 | 3.4.13 16 | 17 | 18 | org.apache.commons 19 | commons-lang3 20 | 3.8.1 21 | 22 | 23 | -------------------------------------------------------------------------------- /lock/distarch-lock-zookeeper/src/main/java/com/liushaoming/lock/zk/DistributedLock.java: -------------------------------------------------------------------------------- 1 | package com.liushaoming.lock.zk; 2 | 3 | import org.apache.commons.lang3.RandomUtils; 4 | import org.apache.zookeeper.*; 5 | import org.apache.zookeeper.data.Stat; 6 | 7 | import java.io.IOException; 8 | import java.util.List; 9 | import java.util.SortedSet; 10 | import java.util.TreeSet; 11 | import java.util.concurrent.CountDownLatch; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | /** 15 | * Created by massive on 2016-12-15. 16 | */ 17 | public class DistributedLock { 18 | 19 | private String lockId; 20 | 21 | //-------------------------------------------------------------- 22 | // data为存储的节点数据内容 23 | // 由于锁机制用的是序列功能的特性,data的值不重要,只要利于网络传输即可 24 | //-------------------------------------------------------------- 25 | private final static byte[] data = {0x12, 0x34}; 26 | 27 | private final CountDownLatch latch = new CountDownLatch(1); 28 | 29 | private ZooKeeper zk; 30 | 31 | private String lockRoot = ""; 32 | private int sessionTimeout = 0; 33 | private String connectionString = "0"; 34 | 35 | public DistributedLock(ZooKeeper zk, int sessionTimeout) { 36 | this.zk = zk; 37 | this.sessionTimeout = sessionTimeout; 38 | } 39 | 40 | public DistributedLock(String lockRoot, String connectionString, int sessionTimeout) throws IOException, KeeperException, InterruptedException { 41 | this.lockRoot = lockRoot; 42 | this.connectionString = connectionString; 43 | this.sessionTimeout = sessionTimeout; 44 | this.zk = ZookeeperClient.getInstance(connectionString, sessionTimeout); 45 | } 46 | 47 | class LockWatcher implements Watcher { 48 | @Override 49 | public void process(WatchedEvent event) { 50 | //-------------------------------------------------------------- 51 | // 监控节点变化(本程序为序列的上一节点) 52 | // 若为节点删除,证明序列的上一节点已删除,此时释放阀门让当前的lock获得锁 53 | //-------------------------------------------------------------- 54 | if (event.getType() == Event.EventType.NodeDeleted) 55 | latch.countDown(); 56 | } 57 | } 58 | 59 | /** 60 | * @return 61 | * @throws KeeperException 62 | * @throws InterruptedException 63 | */ 64 | public synchronized boolean lock() { 65 | 66 | //-------------------------------------------------------------- 67 | // 保证锁根节点存在,若不存在则创建它 68 | //-------------------------------------------------------------- 69 | createLockRootIfNotExists(); 70 | 71 | try { 72 | 73 | lockId = zk.create(lockRoot + "/", data, 74 | ZooDefs.Ids.OPEN_ACL_UNSAFE, 75 | CreateMode.EPHEMERAL_SEQUENTIAL); 76 | 77 | System.out.println("thread " + Thread.currentThread().getName() + 78 | " create the lock node: " + lockId + ", trying to get lock now"); 79 | 80 | //-------------------------------------------------------------- 81 | // 获得锁根节点下的各锁子节点,并排序 82 | //-------------------------------------------------------------- 83 | List nodes = zk.getChildren(lockRoot, true); 84 | SortedSet sortedNode = new TreeSet(); 85 | 86 | for (String node : nodes) { 87 | sortedNode.add(lockRoot + "/" + node); 88 | } 89 | 90 | String first = sortedNode.first(); 91 | SortedSet lessThanMe = sortedNode.headSet(lockId); 92 | 93 | long threadId = Thread.currentThread().getId(); 94 | //-------------------------------------------------------------- 95 | // 检查是否有比当前锁节点lockId更小的节点,若有则监控当前节点的前一节点 96 | //-------------------------------------------------------------- 97 | if (lockId.equals(first)) { 98 | System.out.println(">>>lockSuccess,lockId=" + lockId + ", threadId=" + threadId); 99 | return true; 100 | } else if (!lessThanMe.isEmpty()) { 101 | String prevLockId = lessThanMe.last(); 102 | zk.exists(prevLockId, new LockWatcher()); 103 | //-------------------------------------------------------------- 104 | // 阀门等待sessionTimeout的时间 105 | // 当等待sessionTimeout的时间过后,上一个lock的Zookeeper连接会过期,删除所有临时节点,触发监听器 106 | //-------------------------------------------------------------- 107 | latch.await(sessionTimeout, TimeUnit.MILLISECONDS); 108 | System.out.println(">>>lockSuccess,lockId=" + lockId + ", threadId=" + threadId); 109 | } 110 | 111 | 112 | } catch (KeeperException e) { 113 | e.printStackTrace(); 114 | } catch (InterruptedException e) { 115 | e.printStackTrace(); 116 | } 117 | 118 | return true; 119 | } 120 | 121 | 122 | public synchronized boolean unlock() { 123 | long threadId = Thread.currentThread().getId(); 124 | //-------------------------------------------------------------- 125 | // 删除lockId节点以释放锁 126 | //-------------------------------------------------------------- 127 | try { 128 | System.out.println(">>>UnlockSuccess,lockId=" + lockId + ", threadId=" + threadId); 129 | zk.delete(lockId, -1); 130 | return true; 131 | } catch (InterruptedException e) { 132 | e.printStackTrace(); 133 | } catch (KeeperException e) { 134 | e.printStackTrace(); 135 | } finally { 136 | try { 137 | zk.close(); 138 | } catch (InterruptedException e) { 139 | e.printStackTrace(); 140 | } 141 | } 142 | return false; 143 | } 144 | 145 | /** 146 | * 保证锁根节点存在,若不存在则创建它 147 | */ 148 | public void createLockRootIfNotExists() { 149 | try { 150 | Stat stat = zk.exists(lockRoot, false); 151 | if (stat == null) { 152 | zk.create(lockRoot, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); 153 | } 154 | } catch (KeeperException e) { 155 | e.printStackTrace(); 156 | } catch (InterruptedException e) { 157 | e.printStackTrace(); 158 | } 159 | } 160 | 161 | } 162 | 163 | -------------------------------------------------------------------------------- /lock/distarch-lock-zookeeper/src/main/java/com/liushaoming/lock/zk/ZkTest.java: -------------------------------------------------------------------------------- 1 | package com.liushaoming.lock.zk; 2 | 3 | import org.apache.commons.lang3.RandomUtils; 4 | import org.apache.zookeeper.KeeperException; 5 | 6 | import java.io.IOException; 7 | import java.util.concurrent.CountDownLatch; 8 | 9 | public class ZkTest { 10 | private static String lockRootValue = "/lock_zk_test"; 11 | private static int sessionTimeoutValue = 10000; 12 | private static String connectionStringValue = "localhost:2181"; 13 | 14 | public static void main(String[] args) throws IOException, KeeperException, InterruptedException { 15 | 16 | 17 | final CountDownLatch latch = new CountDownLatch(10); 18 | for (int i = 0; i < 10; i++) { 19 | new Thread(new Runnable() { 20 | public void run() { 21 | DistributedLock lock = null; 22 | try { 23 | lock = new DistributedLock(lockRootValue, connectionStringValue, sessionTimeoutValue); 24 | latch.countDown(); 25 | latch.await(); 26 | lock.lock(); 27 | Thread.sleep(RandomUtils.nextInt(200, 500)); 28 | } catch (InterruptedException e) { 29 | e.printStackTrace(); 30 | } catch (KeeperException e) { 31 | e.printStackTrace(); 32 | } catch (IOException e) { 33 | e.printStackTrace(); 34 | } finally { 35 | if (lock != null) { 36 | lock.unlock(); 37 | } 38 | } 39 | } 40 | }).start(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lock/distarch-lock-zookeeper/src/main/java/com/liushaoming/lock/zk/ZookeeperClient.java: -------------------------------------------------------------------------------- 1 | package com.liushaoming.lock.zk; 2 | 3 | import org.apache.zookeeper.WatchedEvent; 4 | import org.apache.zookeeper.Watcher; 5 | import org.apache.zookeeper.ZooKeeper; 6 | 7 | import java.io.IOException; 8 | import java.util.concurrent.CountDownLatch; 9 | import java.util.concurrent.TimeUnit; 10 | 11 | /** 12 | * 13 | */ 14 | public class ZookeeperClient { 15 | 16 | // private static String connectionString = "appjishu.com:2181"; 17 | // private static int sessionTimeout = 10000; 18 | 19 | 20 | // public static ZooKeeper getInstance() throws IOException, InterruptedException { 21 | // //-------------------------------------------------------------- 22 | // // 为避免连接还未完成就执行zookeeper的get/create/exists操作引起的(KeeperErrorCode = ConnectionLoss) 23 | // // 这里等Zookeeper的连接完成才返回实例 24 | // //-------------------------------------------------------------- 25 | // final CountDownLatch connectedSignal = new CountDownLatch(1); 26 | // ZooKeeper zk = new ZooKeeper(connectionString, sessionTimeout, new Watcher() { 27 | // @Override 28 | // public void process(WatchedEvent event) { 29 | // if (event.getState() == Event.KeeperState.SyncConnected) { 30 | // connectedSignal.countDown(); 31 | // } 32 | // } 33 | // }); 34 | // connectedSignal.await(sessionTimeout, TimeUnit.MILLISECONDS); 35 | // return zk; 36 | // } 37 | 38 | public static ZooKeeper getInstance(String connectionString, int sessionTimeout) throws IOException, InterruptedException { 39 | final CountDownLatch connectedSignal = new CountDownLatch(1); 40 | 41 | ZooKeeper zk = new ZooKeeper(connectionString, sessionTimeout, new Watcher() { 42 | @Override 43 | public void process(WatchedEvent event) { 44 | if (event.getState() == Event.KeeperState.SyncConnected) { 45 | connectedSignal.countDown(); 46 | } 47 | } 48 | }); 49 | connectedSignal.await(sessionTimeout, TimeUnit.MILLISECONDS); 50 | return zk; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | com.appjishu 6 | distarch 7 | 1.0-SNAPSHOT 8 | pom 9 | distarch 10 | distarch 11 | -------------------------------------------------------------------------------- /spring-boot-starter/.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-language=java 2 | *.css linguist-language=java 3 | *.html linguist-language=java 4 | -------------------------------------------------------------------------------- /spring-boot-starter/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | /log/ 13 | 14 | ### IntelliJ IDEA ### 15 | .idea 16 | *.iws 17 | *.iml 18 | *.ipr 19 | 20 | ### NetBeans ### 21 | /nbproject/private/ 22 | /build/ 23 | /nbbuild/ 24 | /dist/ 25 | /nbdist/ 26 | /.nb-gradle/ 27 | 28 | /config/ 29 | -------------------------------------------------------------------------------- /spring-boot-starter/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /spring-boot-starter/README.md: -------------------------------------------------------------------------------- 1 | # jframe 2 | 3 | 用Java自己去写框架,需要掌握那些技术? 4 | 这个项目中会展示很多核心技术给你。 5 | 6 | - spring.factories 7 | 8 | **自己写spring-boot-starter** 9 | 10 | 测试 11 | 12 | [http://localhost:29001/starter/x123](http://localhost:29001/starter/x123) 13 | 14 | 返回值: 15 | 16 | ```text 17 | pppx123sss 18 | ``` 19 | 说明starter运行成功 20 | -------------------------------------------------------------------------------- /spring-boot-starter/jframe-app/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | /log/ 13 | 14 | ### IntelliJ IDEA ### 15 | .idea 16 | *.iws 17 | *.iml 18 | *.ipr 19 | 20 | ### NetBeans ### 21 | /nbproject/private/ 22 | /build/ 23 | /nbbuild/ 24 | /dist/ 25 | /nbdist/ 26 | /.nb-gradle/ 27 | 28 | /config/ 29 | -------------------------------------------------------------------------------- /spring-boot-starter/jframe-app/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 4.0.0 7 | jar 8 | 9 | 10 | org.springframework.boot 11 | spring-boot-starter-parent 12 | 2.1.2.RELEASE 13 | 14 | 15 | 16 | com.lsm.jframe 17 | jframe-app 18 | 1.0-SNAPSHOT 19 | 20 | 21 | 1.8 22 | UTF-8 23 | UTF-8 24 | 5.1.4.RELEASE 25 | 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-web 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-starter-test 35 | test 36 | 37 | 38 | com.lsm.jframe 39 | jframe-spring-boot-starter 40 | 1.0-SNAPSHOT 41 | 42 | 43 | 44 | 45 | 46 | jframe-app 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-maven-plugin 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /spring-boot-starter/jframe-app/src/main/java/com/lsm/jframe/JframeApp.java: -------------------------------------------------------------------------------- 1 | package com.lsm.jframe; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class JframeApp { 8 | public static void main(String[] args){ 9 | SpringApplication.run(JframeApp.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /spring-boot-starter/jframe-app/src/main/java/com/lsm/jframe/app/controller/HomeController.java: -------------------------------------------------------------------------------- 1 | package com.lsm.jframe.app.controller; 2 | 3 | import com.lsm.jframe.core.api.JframeService; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.web.bind.annotation.PathVariable; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | @RestController 10 | @RequestMapping("") 11 | public class HomeController { 12 | 13 | @Autowired 14 | private JframeService jframeService; 15 | 16 | @RequestMapping("") 17 | public String index() { 18 | return "jframe-app works."; 19 | } 20 | 21 | @RequestMapping("starter/{word}") 22 | public String starter(@PathVariable String word) { 23 | String result = jframeService.wrap(word); 24 | return result; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /spring-boot-starter/jframe-app/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 29001 3 | 4 | jframe: 5 | service: 6 | enabled: true 7 | prefix: ppp 8 | suffix: sss 9 | -------------------------------------------------------------------------------- /spring-boot-starter/jframe-spring-boot-starter/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | .sts4-cache 12 | /log/ 13 | 14 | ### IntelliJ IDEA ### 15 | .idea 16 | *.iws 17 | *.iml 18 | *.ipr 19 | 20 | ### NetBeans ### 21 | /nbproject/private/ 22 | /build/ 23 | /nbbuild/ 24 | /dist/ 25 | /nbdist/ 26 | /.nb-gradle/ 27 | 28 | /config/ 29 | -------------------------------------------------------------------------------- /spring-boot-starter/jframe-spring-boot-starter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 4.0.0 7 | com.lsm.jframe 8 | jframe-spring-boot-starter 9 | 1.0-SNAPSHOT 10 | 11 | 12 | UTF-8 13 | 1.8 14 | 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-parent 19 | 2.1.2.RELEASE 20 | 21 | 22 | 23 | 25 | 26 | org.springframework.boot 27 | spring-boot-configuration-processor 28 | true 29 | 30 | 31 | org.springframework.boot 32 | spring-boot-autoconfigure 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /spring-boot-starter/jframe-spring-boot-starter/src/main/java/com/lsm/jframe/core/api/JframeService.java: -------------------------------------------------------------------------------- 1 | package com.lsm.jframe.core.api; 2 | 3 | public class JframeService { 4 | private String prefix; 5 | private String suffix; 6 | 7 | public JframeService(String prefix, String suffix) { 8 | this.prefix = prefix; 9 | this.suffix = suffix; 10 | } 11 | public String wrap(String word) { 12 | return prefix + word + suffix; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /spring-boot-starter/jframe-spring-boot-starter/src/main/java/com/lsm/jframe/core/config/JframeAutoConfigure.java: -------------------------------------------------------------------------------- 1 | package com.lsm.jframe.core.config; 2 | 3 | import com.lsm.jframe.core.api.JframeService; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 8 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | 12 | @Configuration 13 | @ConditionalOnClass(JframeService.class) 14 | @EnableConfigurationProperties(JframeServiceProperties.class) 15 | public class JframeAutoConfigure { 16 | 17 | private final JframeServiceProperties properties; 18 | 19 | @Autowired 20 | public JframeAutoConfigure(JframeServiceProperties properties) { 21 | this.properties = properties; 22 | } 23 | 24 | @Bean 25 | @ConditionalOnMissingBean 26 | @ConditionalOnProperty(prefix = "jframe.service", value = "enabled",havingValue = "true") 27 | JframeService jframeService (){ 28 | return new JframeService(properties.getPrefix(), properties.getSuffix()); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /spring-boot-starter/jframe-spring-boot-starter/src/main/java/com/lsm/jframe/core/config/JframeServiceProperties.java: -------------------------------------------------------------------------------- 1 | package com.lsm.jframe.core.config; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | @ConfigurationProperties("jframe.service") 6 | public class JframeServiceProperties { 7 | private String prefix; 8 | private String suffix; 9 | 10 | public String getPrefix() { 11 | return prefix; 12 | } 13 | 14 | public void setPrefix(String prefix) { 15 | this.prefix = prefix; 16 | } 17 | 18 | public String getSuffix() { 19 | return suffix; 20 | } 21 | 22 | public void setSuffix(String suffix) { 23 | this.suffix = suffix; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /spring-boot-starter/jframe-spring-boot-starter/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | com.lsm.jframe.core.config.JframeAutoConfigure -------------------------------------------------------------------------------- /spring-boot-starter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | pom 7 | 8 | com.lsm.jframe 9 | jframe 10 | 1.0-SNAPSHOT 11 | 12 | 13 | jframe-spring-boot-starter 14 | jframe-app 15 | 16 | -------------------------------------------------------------------------------- /spring/distarch-springboot/.gitignore: -------------------------------------------------------------------------------- 1 | # maven ignore 2 | target/ 3 | *.jar 4 | *.war 5 | *.zip 6 | *.tar 7 | 8 | # eclipse ignore 9 | .settings/ 10 | .project 11 | .classpath 12 | 13 | # idea ignore 14 | .idea/ 15 | *.ipr 16 | *.iml 17 | *.iws 18 | 19 | # temp ignore 20 | logs/ 21 | *.doc 22 | *.log 23 | *.cache 24 | *.diff 25 | *.patch 26 | *.tmp 27 | 28 | # system ignore 29 | .DS_Store 30 | Thumbs.db 31 | -------------------------------------------------------------------------------- /spring/distarch-springboot/README.md: -------------------------------------------------------------------------------- 1 | # sword-springboot 2 | 3 | com.appjishu.swordboot.boot.AppContextHolder 4 |
5 | 6 | 应用场景:
7 | 一般情况下,使用SpringMVC/SpringBoot的时候,各种bean注册到Spring容器里了,然后在需要这个bean的地方,
8 | 使用@Autowired或者@Resource标注的bean都可以被自动注入。 但是在某些场景下,
9 | 需要手动注入。比如在一个Util里面,这个Util里面的方法都是static的,这个时候,如果需要获取Spring容器中的某个
10 | bean,或者获取到ApplicationContext, 这个时候,就需要一个ApplicationContext Holder的东西,这里命名为AppContextHolder 11 | 12 | 其实Spring里有一个接口就是为了这个应用场景而生的ApplicationContextAware
13 | 本人就开发了一个com.appjishu.swordboot.boot.AppContextHolder实现了这个接口, 源码如下:
14 | ```java 15 | package com.appjishu.swordboot.boot; 16 | 17 | import org.springframework.beans.BeansException; 18 | import org.springframework.context.ApplicationContext; 19 | import org.springframework.context.ApplicationContextAware; 20 | import org.springframework.stereotype.Component; 21 | 22 | /** 23 | * 24 | * @author liushaoming 25 | * 26 | */ 27 | @Component 28 | public class AppContextHolder implements ApplicationContextAware { 29 | private static ApplicationContext applicationContext = null; 30 | 31 | @Override 32 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 33 | if (AppContextHolder.applicationContext == null) { 34 | AppContextHolder.applicationContext = applicationContext; 35 | } 36 | } 37 | 38 | // 获取applicationContext 39 | public static ApplicationContext getApplicationContext() { 40 | return applicationContext; 41 | } 42 | 43 | // 通过name获取 Bean. 44 | public static Object getBean(String name) { 45 | return getApplicationContext().getBean(name); 46 | } 47 | 48 | // 通过class获取Bean. 49 | public static T getBean(Class clazz) { 50 | return getApplicationContext().getBean(clazz); 51 | } 52 | 53 | // 通过name,以及Clazz返回指定的Bean 54 | public static T getBean(String name, Class clazz) { 55 | return getApplicationContext().getBean(name, clazz); 56 | } 57 | } 58 | ``` 59 | 使用的时候,很简单, 就像使用Spring原生的ApplicationContext一样, 现在要获取一个已经注册到容器里的一个bean Student
60 | ```java 61 | @Configuration 62 | public class SysConfig { 63 | @Bean(name = "myStudent") 64 | public Student student() { 65 | Student student=new Student(); 66 | student.setName("Frank Liu"); 67 | student.setAddress("Shanghai"); 68 | return student; 69 | } 70 | } 71 | ``` 72 | 使用AppContextHolder获取bean的操作 73 | ```java 74 | Student student0 = (Student) AppContextHolder.getBean("myStudent"); 75 | // 或者 76 | Student student1 = (Student) AppContextHolder.getBean(Student.class); 77 | log.info("student0.name={}", student0.getName()); 78 | ``` 79 | 测试方法: 80 | 在idea/eclipse里,导入maven项目sword-springboot, 右键运行SwordBootApp -> run as -> Java Application
81 | 这是一个springboot项目,打开浏览器访问地址[http://localhost:15000/test](http://localhost:15000/test)
82 | 可以运行测试AppContextHolder的测试代码, 贴出TestUtil里的方法 83 | ```java 84 | public class TestUtil { 85 | private static final Logger log = LoggerFactory.getLogger(TestUtil.class); 86 | 87 | public static void testAppContextHolder() { 88 | log.info("--testAppContextHolder start---"); 89 | Student student0 = (Student) AppContextHolder.getBean("myStudent"); 90 | Student student1 = (Student) AppContextHolder.getBean(Student.class); 91 | 92 | // 如果"student0 == student1"成立,则验证了AppContextHolder的确能获取到Spring容器ApplicationContext 93 | // 而且Spring容器默认注册bean使用的是单例模式 94 | log.info("student0==student1? {}", student0 == student1); 95 | log.info("-------------------"); 96 | log.info("student0.name={}", student0.getName()); 97 | log.info("student0.address={}", student0.getAddress()); 98 | log.info("-------------------"); 99 | log.info("student1.name={}", student1.getName()); 100 | log.info("student1.address={}", student1.getAddress()); 101 | log.info("--testAppContextHolder done---"); 102 | } 103 | } 104 | ``` 105 | 打印结果: 说明测试成功了 106 | ``` 107 | 2019-01-06 15:43:47.563 INFO 21612 --- [io-15000-exec-1] com.appjishu.swordboot.util.TestUtil : --testAppContextHolder start--- 108 | 2019-01-06 15:43:47.564 INFO 21612 --- [io-15000-exec-1] com.appjishu.swordboot.util.TestUtil : student0==student1? true 109 | 2019-01-06 15:43:47.564 INFO 21612 --- [io-15000-exec-1] com.appjishu.swordboot.util.TestUtil : ------------------- 110 | 2019-01-06 15:43:47.565 INFO 21612 --- [io-15000-exec-1] com.appjishu.swordboot.util.TestUtil : student0.name=Frank Liu 111 | 2019-01-06 15:43:47.565 INFO 21612 --- [io-15000-exec-1] com.appjishu.swordboot.util.TestUtil : student0.address=Shanghai 112 | 2019-01-06 15:43:47.566 INFO 21612 --- [io-15000-exec-1] com.appjishu.swordboot.util.TestUtil : ------------------- 113 | 2019-01-06 15:43:47.566 INFO 21612 --- [io-15000-exec-1] com.appjishu.swordboot.util.TestUtil : student1.name=Frank Liu 114 | 2019-01-06 15:43:47.566 INFO 21612 --- [io-15000-exec-1] com.appjishu.swordboot.util.TestUtil : student1.address=Shanghai 115 | 2019-01-06 15:43:47.566 INFO 21612 --- [io-15000-exec-1] com.appjishu.swordboot.util.TestUtil : --testAppContextHolder done--- 116 | ``` 117 | 118 | 注意事项:
119 | AppContextHolder在SpringBoot里使用,只需要在类名AppContextHolder前标注@Component
120 | 如果在SpringMVC里使用的,需要用@Component标注类AppContextHolder, 并且在applicationContext.xml里面设置
121 | 包含AppContextHolder所在的package, 或者直接用bean标签来注册它。 122 | -------------------------------------------------------------------------------- /spring/distarch-springboot/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.appjishu 8 | distarch-springboot 9 | 1.0-SNAPSHOT 10 | 11 | 12 | org.springframework.boot 13 | spring-boot-starter-parent 14 | 2.1.0.RELEASE 15 | 16 | 17 | 18 | 19 | UTF-8 20 | UTF-8 21 | 1.8 22 | 23 | 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-web 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-test 32 | test 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /spring/distarch-springboot/src/main/java/com/appjishu/SwordBootApp.java: -------------------------------------------------------------------------------- 1 | package com.appjishu; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SwordBootApp { 8 | public static void main(String[] args){ 9 | SpringApplication.run(SwordBootApp.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /spring/distarch-springboot/src/main/java/com/appjishu/swordboot/boot/AppContextHolder.java: -------------------------------------------------------------------------------- 1 | package com.appjishu.swordboot.boot; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.context.ApplicationContextAware; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * 10 | * @author liushaoming 11 | * 12 | */ 13 | @Component 14 | public class AppContextHolder implements ApplicationContextAware { 15 | private static ApplicationContext applicationContext = null; 16 | 17 | @Override 18 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 19 | if (AppContextHolder.applicationContext == null) { 20 | AppContextHolder.applicationContext = applicationContext; 21 | } 22 | } 23 | 24 | // 获取applicationContext 25 | public static ApplicationContext getApplicationContext() { 26 | return applicationContext; 27 | } 28 | 29 | // 通过name获取 Bean. 30 | public static Object getBean(String name) { 31 | return getApplicationContext().getBean(name); 32 | } 33 | 34 | // 通过class获取Bean. 35 | public static T getBean(Class clazz) { 36 | return getApplicationContext().getBean(clazz); 37 | } 38 | 39 | // 通过name,以及Clazz返回指定的Bean 40 | public static T getBean(String name, Class clazz) { 41 | return getApplicationContext().getBean(name, clazz); 42 | } 43 | } -------------------------------------------------------------------------------- /spring/distarch-springboot/src/main/java/com/appjishu/swordboot/boot/SysConfig.java: -------------------------------------------------------------------------------- 1 | package com.appjishu.swordboot.boot; 2 | 3 | import com.appjishu.swordboot.model.Student; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | @Configuration 8 | public class SysConfig { 9 | @Bean(name = "myStudent") 10 | public Student student() { 11 | Student student=new Student(); 12 | student.setName("Frank Liu"); 13 | student.setAddress("Shanghai"); 14 | return student; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /spring/distarch-springboot/src/main/java/com/appjishu/swordboot/controller/HomeController.java: -------------------------------------------------------------------------------- 1 | package com.appjishu.swordboot.controller; 2 | 3 | import com.appjishu.swordboot.util.TestUtil; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | @RequestMapping("") 8 | @RestController 9 | public class HomeController { 10 | @RequestMapping("") 11 | public String index() { 12 | return "Access sword-springboot OK."; 13 | } 14 | 15 | @RequestMapping("/test") 16 | public String test() { 17 | TestUtil.testAppContextHolder(); 18 | return "Test done."; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /spring/distarch-springboot/src/main/java/com/appjishu/swordboot/model/Student.java: -------------------------------------------------------------------------------- 1 | package com.appjishu.swordboot.model; 2 | 3 | public class Student { 4 | private String name; 5 | private String address; 6 | 7 | public String getName() { 8 | return name; 9 | } 10 | 11 | public void setName(String name) { 12 | this.name = name; 13 | } 14 | 15 | public String getAddress() { 16 | return address; 17 | } 18 | 19 | public void setAddress(String address) { 20 | this.address = address; 21 | } 22 | 23 | @Override 24 | public String toString() { 25 | return "Student{" + 26 | "name='" + name + '\'' + 27 | ", address='" + address + '\'' + 28 | '}'; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /spring/distarch-springboot/src/main/java/com/appjishu/swordboot/util/TestUtil.java: -------------------------------------------------------------------------------- 1 | package com.appjishu.swordboot.util; 2 | 3 | import com.appjishu.swordboot.boot.AppContextHolder; 4 | import com.appjishu.swordboot.model.Student; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | public class TestUtil { 9 | private static final Logger log = LoggerFactory.getLogger(TestUtil.class); 10 | 11 | public static void testAppContextHolder() { 12 | log.info("--testAppContextHolder start---"); 13 | Student student0 = (Student) AppContextHolder.getBean("myStudent"); 14 | Student student1 = (Student) AppContextHolder.getBean(Student.class); 15 | 16 | // 如果"student0 == student1"成立,则验证了AppContextHolder的确能获取到Spring容器ApplicationContext 17 | // 而且Spring容器默认注册bean使用的是单例模式 18 | log.info("student0==student1? {}", student0 == student1); 19 | log.info("-------------------"); 20 | log.info("student0.name={}", student0.getName()); 21 | log.info("student0.address={}", student0.getAddress()); 22 | log.info("-------------------"); 23 | log.info("student1.name={}", student1.getName()); 24 | log.info("student1.address={}", student1.getAddress()); 25 | log.info("--testAppContextHolder done---"); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /spring/distarch-springboot/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=15000 2 | --------------------------------------------------------------------------------