JDK解构 – Java中的引用和动态代理的实现

我们知道,动态代理(这里指JDK的动态代理)与静态代理的区别在于,其真实的代理类是动态生成的。但具体是怎么生成,生成的代理类包含了哪些内容,以什么形式存在,它为什么一定要以接口为基础?

如果去看动态代理的源代码(java.lang.reflect.Proxy),会发现其原理很简单(真正二进制类文件的生成是在本地方法中完成,源代码中没有),但其中用到了一个缓冲类java.lang.reflect.WeakCache<ClassLoader, Class<?>[], Class<?>>,这个类用到了弱引用来构建。

在JDK的3个特殊引用中,弱引用是使用范围最广的,它的特性也最清晰,相对而言,其他两种逻辑稍显晦涩,源码中的注释也语焉不详。本文将简单介绍几种引用的行为特征,然后分析一下弱引用的一些实际应用场景,其中包含了动态代理中的实现。本文将包含以下内容:

  1. JDK中的引用类型
  2. 不同引用类型对GC行为的影响
  3. 引用类型的实现
  4. ThreadLocal对弱引用的使用
  5. 动态代理对弱引用的实现
  6. 虚引用如何导致内存泄漏

KK笔记:kknotes.com
本文链接地址: JDK解构 – Java中的引用和动态代理的实现

转载须以超链接形式标明文章原始出处和作者信息及版权声明

Continue Reading

Java线程池的理论与实践

Doug Lea

前段时间公司里有个项目需要进行重构,目标是提高吞吐量和可用性,在这个过程中对原有的线程模型和处理逻辑进行了修改,发现有很多基础的多线程的知识已经模糊不清,如底层线程的运行情况、现有的线程池的策略和逻辑、池中线程的健康状况的监控等,这次重新回顾了一下,其中涉及大量java.util.concurrent包中的类。本文将会包含以下内容:

  1. Java中的Thread与操作系统中的线程的关系
  2. 线程切换的各种开销
  3. ThreadGroup存在的意义
  4. 使用线程池减少线程开销
  5. Executor的概念
  6. ThreadPoolExecutor中的一些具体实现
  7. 如何监控线程的健康

KK笔记:kknotes.com
本文链接地址: Java线程池的理论与实践

转载须以超链接形式标明文章原始出处和作者信息及版权声明

Continue Reading

从I/O模型到Netty(三)

Netty

零、写在前面

本文虽然是讲Netty,但实际更关注的是Netty中的NIO的实现,所以对于Netty中的OIO(Old I/O)并没有做过多的描述,或者说根本只字未提,所以本文中所述的所有实现细节都是基于NIO版本的。

Netty作为一个已经发展了十多年的框架,已然非常成熟了,其中有大量的细节是普通使用者不知道或者不关心的,所以本文难免有遗漏或者纰漏的地方,如果你发现了请告知。

本文不涉及Netty5的部分。

虽然这一节叫「写在前面」,但实际上上最后写的。

KK笔记:kknotes.com
本文链接地址: 从I/O模型到Netty(三)

转载须以超链接形式标明文章原始出处和作者信息及版权声明

Continue Reading

从I/O模型到Netty(二)

旧瓶装新酒,Java NIO

在上一篇文章中对于I/O模型已经讲的比较清楚了,在I/O密集型应用中使用Reactor模式可以明显提高系统的性能(我们这里谈到的性能很大程度上指的是吞吐量),但是在具体的开发过程中模式还是要落地成真实的代码,使用传统的I/O库肯定是不行的,在Java中需要使用java.nio包下的库。

虽然是讲NIO的实现,但本文将不会把所有Java NIO中的主要API全部过一遍,而是通过例子理清NIO到底可以做什么事情。

本文中提到的JDK源代码都可以在%JAVA_HOME%/jre/lib/rt.jar中看到。

KK笔记:kknotes.com
本文链接地址: 从I/O模型到Netty(二)

转载须以超链接形式标明文章原始出处和作者信息及版权声明

Continue Reading

从I/O模型到Netty(一)

如何使用咖啡读文件
I/O是任何一个程序设计者都无法忽略的存在,很多高级编程语言都在尝试使用巧妙的设计屏蔽I/O的实际存在,减小它对程序的影响,但是要真正的理解并更好运用这些语言,还是要搞清楚I/O的一些基本理念。本文将从最基本的I/O概念开始,试图理清当前I/O处理存在的问题和与之对应一些手段及背后的思想。

本来这是上个月在公司内部做的一次关于NIO的分享,发现很多概念可能当时理解的很清楚,过了一段时间就会感到模糊了。在这里整理一下,以备以后查看,同时也将作为另一个系列的开端。

由于篇幅限制,本文将只包含I/O模型到Reactor的部分,下一篇会继续讲到Netty和Dubbo中的I/O。本文包含以下内容:

  1. 五种典型的I/O模型
  2. 同步&异步、阻塞&非阻塞的概念
  3. Reactor & Proactor
  4. Reactor的启发

KK笔记:kknotes.com
本文链接地址: 从I/O模型到Netty(一)

转载须以超链接形式标明文章原始出处和作者信息及版权声明

Continue Reading

MyBatis在Spring环境下的事务管理

MyBatis & Spring

MyBatis的设计思想很简单,可以看做是对JDBC的一次封装,并提供强大的动态SQL映射功能。但是由于它本身也有一些缓存、事务管理等功能,所以实际使用中还是会碰到一些问题——另外,最近接触了JFinal,其思想和Hibernate类似,但要更简洁,和MyBatis的设计思想不同,但有一点相同:都是想通过简洁的设计最大限度地简化开发和提升性能——说到性能,前段时间碰到两个问题:

  1. 在一个上层方法(DAO方法的上层)内删除一条记录,然后再插入一条相同主键的记录时,会报主键冲突的错误。
  2. 某些项目中的DAO方法平均执行时间会是其他一些项目中的 2倍 。

第一个问题是偶尔会出现,在实验环境无论如何也重现不了,经过分析MyBatis的逻辑,估计是两个DAO分别拿到了两个不同的Connection,第二个语句比第一个更早的被提交,导致了主键冲突,有待进一步的分析和验证。对于第二个问题,本文将尝试通过分析源代码和实验找到它的root cause,主要涉及到以下内容:

  1. 问题描述与分析
  2. MyBatis在Spring环境下的载入过程
  3. MyBatis在Spring环境下事务的管理
  4. 实验验证

KK笔记:kknotes.com
本文链接地址: MyBatis在Spring环境下的事务管理

转载须以超链接形式标明文章原始出处和作者信息及版权声明

Continue Reading

MyBatis完全使用指南

MyBatis

MyBatis作为一个轻量的SQL映射框架,确实很简单,但是知识点挺多,实际使用中还是会有时想不起来某个标签该怎么写,所以整理了这篇文章,以备查询。由于MyBatis如此简单,使得这一篇文章基本把实际使用中常碰到的事情都涵盖了,包括:

  1. MyBatis中的一些概念
  2. MyBatis包含的内容
  3. SQL映射
  4. 动态SQL

KK笔记:kknotes.com
本文链接地址: MyBatis完全使用指南

转载须以超链接形式标明文章原始出处和作者信息及版权声明

Continue Reading

如何写出优雅的函数(Clean Code读书笔记之二)

第三章讲了在写函数时应该注意的事情,作者首先拿一个开源的测试工具(Fitnesse)来举了一个例子,来说明好的函数该是什么样子。原则上其实和上一篇中讲到的命名的一些原则很相似,就是一个名字要是能够自解释的,当然这一章还会讲到很多新的东西,这里拿这个函数作为一个引子。

//代码2-1
public static String renderPageWithSetupsAndTeardowns(PageData pageData, boolean isSuite)throws Exception{
    boolean isTestPage = pageData.hasAttribute("Test");
    if(isTestPage){
        WikiPage testPage = pageData.getWikiPage();
        StringBuffer newPageContent = new StringBuffer();
        includeSetupPages(test Page, newPageContent, isSuite);
        newPageContent.append(pageData.getContent());
    }
}

KK笔记:kknotes.com
本文链接地址: 如何写出优雅的函数(Clean Code读书笔记之二)

转载须以超链接形式标明文章原始出处和作者信息及版权声明

Continue Reading

命名的艺术(clean code阅读笔记之一)

本文是「Clean Code」(英文版)第二章的读书笔记。

第二章简单地列举了一些命名规则,我们在coding的时候会不断地对我们的变量、函数、参数、类、package,甚至源文件、和包含源文件的目录等等进行命名,这里是简单的几个命名规则能帮助你更好地对这些命名。

1. 使用名副其实(Intention-Revealing)的名字

  • 不要定义无意义的名字
  • 不要使用magic numbers
  • 例子:
    // 坏代码例子
    public List<int[]> getThem() {
        List<int[]> list1 = new ArrayList<int[]>();
        for (int[] x : theList)
            if (x[0] == 4) list1.add(x);
        return list1;
    }
    
    //不是很坏的代码
    public List<int[]> getFlaggedCells() {
        List<int[]> flaggedCells = new ArrayList<int[]>();
        for (int[] cell : gameBoard)
            if (cell[STATUS_VALUE] == FLAGGED)
                flaggedCells.add(cell);
        return flaggedCells;
    }
    
    //好代码
    public List<Cell> getFlaggedCells() {
        List<Cell> flaggedCells = new ArrayList<Cell>();
        for (Cell cell : gameBoard)
            if (cell.isFlagged())
                flaggedCells.add(cell);
        return flaggedCells;
    }
    

KK笔记:kknotes.com
本文链接地址: 命名的艺术(clean code阅读笔记之一)

转载须以超链接形式标明文章原始出处和作者信息及版权声明

Continue Reading

在PlayFramework下隐藏后台管理员登录界面的两种方法(限制IP和HTTPS)

最近在做一个网站,网站的内容有些是要管理员修改的,比如招聘信息等,但要修改的信息不是很多,因为想要尽可能的提高安全性,对管理后台的登录做了一些限制,首先想到的是对登录IP做限制,但是现在我们使用的宽带基本上都是动态IP,所以会给用户带来很多不便,于是想到了使用HTTPS,本文讲对这两种方法进行讨论,并给出相应实现。我是在Play framework的环境中开发,但是这个配置流程是普适的。

    1. 绑定IP


这个方法相当简单,在Play的Controller中使用request().remoteAddress()获取远程IP,然后对用户名进行匹配。

如果用户访问/admin/login这种地址,先判断他的IP是否在允许的IP范围内,如果不在,直接重定向到主页,入侵者是不知道这个地址是不存在,还是他的权限不能访问。

    2. 使用HTTPS双向验证


使用HTTPS双向验证并不能够达到管理登录的地址完全对用户透明,使用“绑定IP”的方法,当用户不在允许的IP地址访问/admin/login这种地址,后台可以直接把他重定向到/index, 但是使用HTTPS需要重定向到HTTPS的地址,大致的步骤如下:

转载须以超链接形式标明文章原始出处和作者信息及版权声明

Continue Reading