开源的许可证GPL、LGPL、BSD、Apache 2.0的通俗解释

软件开发者要开源软件,不单单是开放源代码就可以了,选择一种许可证很重要,一个许可证之于软件就相当于价值观之于普通人,代表了这个软件的基本品性。一个错误的许可证选择可能会直接导致整个项目的失败。

各种开源的许可证主要的限制还是在redistribution(发布),所以个人/商业公司开发的软件包含了GPL的代码,只要你不发布,是可以任意使用的。

下面是几个开源许可证的区别:

GPL
GPL软件的使用者有权力得到软件的代码,只要使用了GPL,在发布(redistribution)时,整个项目也必须是GPL的,即主程序和静态链接的库(linux的.a和Windows的.lib)必须是GPL的,动态链接库(Linux的.so,Windows的.dll)必须是GPL兼容的。所谓GPL兼容,也就是GPL软件中可以使用的库,这些许可证必须比GPL弱(如LGPL,BSD),而不能是某个商业许可证。正因如此,GPL是带有很强的传染性,只要你的软件使用了GPL的代码,那么就请以GPL开放源代码吧,并且你的项目中也不能有任何和GPL不兼容的库。

LGPL
GPL 带有很强的传染性,那么如果一个库使用GPL发布,那么使用这个库的所有软件也必须使用GPL发布,这对不想开放源代码的商业软件来讲是致命的打击——你可以不使用其他的库,但最基本的libc是无论如何绕不开的,如果libc是以GPL发布,就相当于所有软件必须以GPL发布了。所以,LGPL(Lesser GPL)诞生了。

LGPL定义为,在以LGPL发布的库的基础上开发新的库的时候,新的库必须以LGPL发布,但是如果仅仅是动态链接,那么则不受任何限制。这样商业软件就可以随意的使用LGPL的库了。因此,LGPL也具有传染性,但限制在其基础上开发的库上,而并不限制使用它的程序本身——它的传染性远小于GPL。

BSD、Apache 2.0
相对GPL/LGPL的开放源代码,BSD,Apache 2.0就宽松许多——商业软件可以任意的使用BSD,Apache 2.0发布的软件代码,而不需要开放源代码,只需要提及代码的原出处就可以了。BSD和Apache 2.0提及的方式稍有不同,具体可以参考协议的详细内容。它们是GPL兼容的

 

看看下面选择开源许可证的案例:

andorid 使用宽松的Apache 2.0发布,因为Google作为一个商业公司,并不想失去商业软件的支持,它希望团结一切可以团结的力量加入的Android的开发中来,壮大自己的阵营,使用Apache 2.0就无可厚非了。而Google本身,并没有丧失对Android的控制权,不会担心另外一个公司拿走了Android的代码开发出一个闭源 Android的对手。因为,只要Android不断的出新版,社区不停的跟进,并且不停的修改API,其他基于Android开发的公司不得不把自己的Patch提回到主干上,否则,必然将耗费大量人力物力在维护自己的Patch上(钱这方面你斗得过Google?),得不偿失。而且,闭源之后,与整个社区为敌,作为一个定位软件平台的项目,会流失大量应用软件开发者,以小博大,任何一个商业公司都不会干这种胜算不高的蠢事。
再看以GPL发布的Linux为什么比以BSD发布的FreeBSD成功。其实正是因为GPL的传染性。当一个开发人员在Linux基础上开发一个新功能之后, 不得不以GPL开放源代码,贡献回Linux,这样Linux本身才能越来也越壮大而且留住了相当的开发人员,形成了一个 优秀软件->很多使用者和贡献者->贡献->更优秀的软件->更多的使用者和贡献者… 的良性循环。

 

Java并发编程:volatile关键字解析

volatile这个关键字可能很多朋友都听说过,或许也都用过。在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果。在Java 5之后,volatile关键字才得以重获生机。

volatile关键字虽然从字面上理解起来比较简单,但是要用好不是一件容易的事情。由于volatile关键字是与Java的内存模型有关的,因此在讲述volatile关键之前,我们先来了解一下与内存模型相关的概念和知识,然后分析了volatile关键字的实现原理,最后给出了几个使用volatile关键字的场景。

以下是本文的目录大纲:

内存模型的相关概念
并发编程中的三个概念
Java内存模型
深入剖析volatile关键字
使用volatile关键字的场景
一.内存模型的相关概念

大家都知道,计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入。由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这时就存在一个问题,由于CPU执行速度很快,而从内存读取数据和向内存写入数据的过程跟CPU执行指令的速度比起来要慢的多,因此如果任何时候对数据的操作都要通过和内存的交互来进行,会大大降低指令执行的速度。因此在CPU里面就有了高速缓存。

Spring boot整合Redis做数据缓存

redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set –有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

Redis 是一个高性能的key-value数据库。 redis的出现,很大程度补偿了memcached这类key/value存储的不足,在部 分场合可以对关系数据库起到很好的补充作用。它提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客户端,使用很方便。

Redis支持主从同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。这使得Redis可执行单层树复制。存盘可以有意无意的对数据进行写操作。由于完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取操作的可扩展性和数据冗余很有帮助。

redis的官网地址,非常好记,是redis.io。(特意查了一下,域名后缀io属于国家域名,是british Indian Ocean territory,即英属印度洋领地)目前,Vmware在资助着redis项目的开发和维护。

下面是官方的bench-mark数据:
测试完成了50个并发执行100000个请求。
设置和获取的值是一个256字节字符串。
Linux box是运行Linux 2.6,这是X3320 Xeon 2.5 ghz。
文本执行使用loopback接口(127.0.0.1)。
结果:读的速度是110000次/s,写的速度是81000次/s

redis提供五种数据类型:string,hash,list,set及zset(sorted set)。
string(字符串)
string是最简单的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value,其上支持的操作与Memcached的操作类似。但它的功能更丰富。
redis采用结构sdshdr和sds封装了字符串,字符串相关的操作实现在源文件sds.h/sds.c中。

list(双向链表)
list是一个链表结构,主要功能是push、pop、获取一个范围的所有值等等。操作中key理解为链表的名字。

dict(hash表)
set是集合,和我们数学中的集合概念相似,对集合的操作有添加删除元素,有对多个集合求交并差等操作。操作中key理解为集合的名字。

dict中table为dictEntry指针的数组,数组中每个成员为hash值相同元素的单向链表。set是在dict的基础上实现的,指定了key的比较函数为dictEncObjKeyCompare,若key相等则不再插入。

zset(排序set)
zset是set的一个升级版本,他在set的基础上增加了一个顺序属性,这一属性在添加修改元素的时候可以指定,每次指定后,zset会自动重新按新的值调整顺序。可以理解了有两列的mysql表,一列存value,一列存顺序。操作中key理解为zset的名字。

zset利用dict维护key -> value的映射关系,用zsl(zskiplist)保存value的有序关系。zsl实际是叉数不稳定的多叉树,每条链上的元素从根节点到叶子节点保持升序排序。

Spring对mongoDB分布式数据库整合

MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似的格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。

它的特点是高性能、易部署、易使用,存储数据非常方便。主要功能特性有:

面向集合存储,易存储对象类型的数据。
模式自由。
支持动态查询。
支持完全索引,包含内部对象。
支持查询。
支持复制和故障恢复。
使用高效的二进制数据存储,包括大型对象(如视频等)。
自动处理碎片,以支持云计算层次的扩展性。
支持RUBY,PYTHON,JAVA,C++,PHP,C#等多种语言。
文件存储格式为BSON(一种JSON的扩展)。
可通过网络访问。
所谓“面向集合”(Collection-Oriented),意思是数据被分组存储在数据集中,被称为一个集合(Collection)。每个集合在数据库中都有一个唯一的标识名,并且可以包含无限数目的文档。集合的概念类似关系型数据库(RDBMS)里的表(table),不同的是它不需要定义任何模式(schema)。Nytro MegaRAID技术中的闪存高速缓存算法,能够快速识别数据库内大数据集中的热数据,提供一致的性能改进。

模式自由(schema-free),意味着对于存储在mongodb数据库中的文件,我们不需要知道它的任何结构定义。如果需要的话,你完全可以把不同结构的文件存储在同一个数据库里。

存储在集合中的文档,被存储为键-值对的形式。键用于唯一标识一个文档,为字符串类型,而值则可以是各种复杂的文件类型。我们称这种存储形式为BSON(Binary Serialized Document Format)。

Spring boot JPA实现

每次开发基本上都是使用mybatis,对于hibernate,估计也忘了差不多。今天来重温了一下hibernate的配置实现。

第一步,肯定是配置文件:

/**
 * Created by alan on 2018/6/18.
 */
@Configuration
public class HibernateConfig {

    private String driver = "com.mysql.jdbc.Driver";
    private String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8";
    private String username = "root";
    private String password = "root";

    public HibernateConfig(){
    }

    @Bean
    public BasicDataSource dataSource(){
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setInitialSize(5);
        dataSource.setMaxIdle(100);
        dataSource.setMaxOpenPreparedStatements(100);

        dataSource.setCacheState(true);
        dataSource.setMaxTotal(1000);
        dataSource.setTestWhileIdle(true);
        dataSource.setValidationQuery("SELECT 1");
        dataSource.setTimeBetweenEvictionRunsMillis(3600000L);
        dataSource.setMinEvictableIdleTimeMillis(1800000L);
        dataSource.setTestOnBorrow(true);
        return dataSource;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(){

        HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
        adapter.setDatabasePlatform("org.hibernate.dialect.MySQL5InnoDBDialect");

        Properties properties = new Properties();
        properties.setProperty("hibernate.dialect","org.hibernate.dialect.MySQL5InnoDBDialect");
        properties.setProperty("hibernate.show_sql","true");
        properties.setProperty("hibernate.format_sql","true");
        properties.setProperty("hibernate.auto","update");

        LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
        factoryBean.setPersistenceUnitName("SpringJPA");
        factoryBean.setJpaVendorAdapter(adapter);
        factoryBean.setDataSource(this.dataSource());
        factoryBean.setSharedCacheMode(SharedCacheMode.ENABLE_SELECTIVE);
        factoryBean.setValidationMode(ValidationMode.NONE);

        /**
         * scan @Entity
         */
        factoryBean.setPackagesToScan("com.example.demo.model","com.example.demo.repository");
        //factoryBean.setMappingResources("com/example/demo/repository/resource/User.hbm.xml");
        factoryBean.setJpaProperties(properties);
        return factoryBean;
    }

    @Bean
    public PlatformTransactionManager platformTransactionManager(){
        return new JpaTransactionManager(this.entityManagerFactory().getObject());
    }

    @Bean
    public PersistenceAnnotationBeanPostProcessor persistenceAnnotationBeanPostProcessor(){
        return new PersistenceAnnotationBeanPostProcessor();
    }
    
}

配置文件由dateSource、LocalContainerEntityManagerFactoryBean、PlatformTransactionManager组成,改有的都有了,现在来实现DAO层,emmmmm,本人比较懒,直接在service层里直接写代码了,如下:

public class TestByHibernateServiceImpl {

    private static final Logger logger = LoggerFactory.getLogger(TestByHibernateServiceImpl.class);

    @PersistenceContext
    private EntityManager manager;

    @Resource
    private LxUserMapper userMapper;

    public TestByHibernateServiceImpl() {
    }

    public List<LxUserModel> getAll() {
        Query query = manager.createNativeQuery("SELECT *  FROM lx_user ORDER BY id DESC",LxUserModel.class);
        return query.getResultList();
    }

    public LxUserModel getRow() {
//        return manager.createQuery("SELECT a  FROM LxUserModel a ORDER BY id DESC",LxUserModel.class).setMaxResults(1).getSingleResult();
        return userMapper.getRow();
    }

    public int add(LxUserModel userModel) {
        manager.persist(userModel);
        return userModel.getId();
    }

哇塞,跟昨天写的那篇文章比,正篇肯真简短,没错大部分代码都是EntityManager去实现了,很多人包括我都因为懒,不去写自己的DAO实现,而去用开源的,如果有时间还是建议自己实现或看一下EntityManager的原理。

/**
 * Created by alan on 2018/6/18.
 */

@Repository
public interface LxUserMapper extends JpaRepository<LxUserModel,Integer> {

    @Query(value = "SELECT * FROM lx_user ORDER BY id DESC limit 1",nativeQuery = true)
    LxUserModel getRow();
}

 

下面提供一个例子,在真实场景中,查询数据可是千奇百怪,所以按照自己的思维封装了几个通用的方法,当然啦,肯定是使用了EntityManager:

/**
 * Created by alan.luo on 2017/10/22.
 */

public abstract class AbstractDefaultRepository<E extends Serializable> extends AbstractDefaultBaseRepository<E> {

    @PersistenceContext
    private EntityManager manager;

    /**
     * get all the data.
     * @param page
     * @param where
     * @param order
     * @param isDesc
     * @return
     */
    @Override
    public List<E> getAll(int page, List<PredicatePojo> where, String order, boolean isDesc) {
        CriteriaBuilder builder = this.manager.getCriteriaBuilder();
        CriteriaQuery<E> query = builder.createQuery(this.entityClass);

        Root<E> root = query.from(this.entityClass);
        CriteriaQuery<E> select = query.select(root);
        if (order != null && !"".equals(order)){
            if (isDesc){
                select.orderBy(builder.desc(root.get(order)));
            }else {
                select.orderBy(builder.asc(root.get(order)));
            }
        }

        select.where(getListPredicate(where,builder,root));

        TypedQuery<E> typedQuery = this.manager.createQuery(select);
        typedQuery.setFirstResult((page - 1) * ConstantInit.PAGE_SIZE);
        typedQuery.setMaxResults(ConstantInit.PAGE_SIZE);
        return typedQuery.getResultList();
    }

    /**
     * search all the data.
     * @param page
     * @param condition
     * @param order
     * @param isDesc
     * @return
     */
    @Override
    public PagePojo<E> search(int page, List<PredicatePojo> condition, String order, boolean isDesc){

        CriteriaBuilder builder = this.manager.getCriteriaBuilder();
        CriteriaQuery<Long> query = builder.createQuery(Long.class);

        Root<E> root = query.from(this.entityClass);
        CriteriaQuery<Long> select = query.select(builder.count(root));

        select.where(getListPredicate(condition,builder,root));

        long total = this.manager.createQuery(select).getSingleResult();

        PagePojo<E> pages = new PagePojo<E>((long) page,total);
        pages.setList(this.getAll(page,condition,order,isDesc));
        return  pages;
    }

    @Override
    public E getRowById(int id) {
        return this.manager.find(this.entityClass,id);
    }

    @Override
    public void add(E o) {
        this.manager.persist(o);
    }

    @Override
    public void update(E o) {
        this.manager.merge(o);
    }

    @Override
    public void remove(E o) {
        this.manager.remove(o);
    }

    @Override
    public int removeById(int id) {
        CriteriaBuilder builder = this.manager.getCriteriaBuilder();
        CriteriaDelete<E> q = builder.createCriteriaDelete(this.entityClass);

        return this.manager
                .createQuery(q.where(builder.equal(q.from(this.entityClass).get("id"),id)))
                .executeUpdate();
    }

    /**
     *
     * List<PredicatePojo> where = new ArrayList<>();
     *  where.add(new PredicatePojo("id","81", Criteria.lt));
     *  where.add(new PredicatePojo("age","10", Criteria.gt));
     *
     * List<PredicatePojo> where = new ArrayList<>();
     * if (userName != null){
     *     userName = "%" + userName + "%";
     *     List<PredicatePojo> like = new ArrayList<>();
     *     like.add(new PredicatePojo("username",userName, Criteria.like));
     *     like.add(new PredicatePojo("mobile",userName, Criteria.like));
     *     like.add(new PredicatePojo("email",userName, Criteria.like));
     *
     *     PredicatePojo pojo = new PredicatePojo();
     *     pojo.setLikeObj(like);
     *     pojo.setCriteria(Criteria.like);
     *     where.add(pojo);
     * }
     *
     *
     *
     * parser predicate.
     * @param where
     * @param builder
     * @param root
     * @return
     */
    protected Predicate[] getListPredicate(List<PredicatePojo> where,CriteriaBuilder builder,Root<E> root){

        Predicate[] predicates = new Predicate[where.size()];
        Predicate[] like;

        if (where != null && where.size() > 0){
            Predicate p = null;

            for (int i = 0; i < where.size();i++){

                Criteria ca = where.get(i).getCriteria();
                switch (ca){
                    case equal:
                        p = builder.equal(root.get(where.get(i).getKey()),where.get(i).getValue());
                        break;
                    case notEqual:
                        p = builder.notEqual(root.get(where.get(i).getKey()),where.get(i).getValue());
                        break;
                    case isNull:
                        p = builder.isNull(root.get(where.get(i).getKey()));
                        break;
                    case isNotNull:
                        p = builder.isNotNull(root.get(where.get(i).getKey()));
                        break;
                    case gt:
                        p = builder.gt(root.get(where.get(i).getKey()),Double.valueOf(where.get(i).getValue()));
                        break;
                    case lt:
                        p = builder.lt(root.get(where.get(i).getKey()),Double.valueOf(where.get(i).getValue()));
                        break;
                    case ge:
                        p = builder.ge(root.get(where.get(i).getKey()),Double.valueOf(where.get(i).getValue()));
                        break;
                    case le:
                        p = builder.le(root.get(where.get(i).getKey()),Double.valueOf(where.get(i).getValue()));
                        break;
                    case isFalse:
                        p = builder.isFalse(root.get(where.get(i).getKey()));
                        break;
                    case isTrue:
                        p = builder.isTrue(root.get(where.get(i).getKey()));
                        break;
                    case not:
                        p = builder.not(root.get(where.get(i).getKey()));
                        break;
                    case like:
                        like = new Predicate[where.get(i).getLikeObj().size()];
                        for (int j = 0; j < where.get(i).getLikeObj().size();j++){
                            like[j] = builder.like(root.get(where.get(i).getLikeObj().get(j).getKey())
                                    ,where.get(i).getLikeObj().get(j).getValue());
                        }
                        p = builder.or(like);
                        break;
                    case notLike:
                        like = new Predicate[where.get(i).getLikeObj().size()];
                        for (int j = 0; j < where.get(i).getLikeObj().size();j++){
                            like[j] = builder.notLike(root.get(where.get(i).getLikeObj().get(j).getKey())
                                    ,where.get(i).getLikeObj().get(j).getValue());
                        }
                        p = builder.and(like);
                        break;
                }

                predicates[i] = p;
            }
        }
        return predicates;
    }

}
1237