🎨
application-framework
  • Introduction
  • 设计模式篇
    • 设计原则
      • 单一职责原则
      • 里氏替换原则
      • 依赖倒置原则
      • 接口隔离原则
      • 迪米特法则
      • 开闭原则
    • 代理模式
    • 工厂模式
    • 策略模式
    • 等等..设计模式
    • 常用设计模式在开源软件的应用
    • Template设计模式介绍
  • SpringBoot篇
    • SpringBoot官方教程结构图
    • SpringBoot启动过程源码分析
    • SpringBoot启动过程定制化
    • SpringBoot实现自动配置的基础
    • SpringBoot实现自动配置的原理
    • SpringBoot启动类源码分析以及@EnableAutoConfiguration和@SpringBootApplication讲解
    • EnableAutoConfigurationImportSelector 是如何工作的 ?
    • ConfigurationClassParser 是如何工作的 ?
    • SpringBoot源码分析之Spring上下文refresh(重点)
    • SpringBoot中的ApplicationContext - 执行ApplicationContextInitializer初始化器
    • SpringBoot常用配置-Profile
    • Spring Boot API 版本权限控制
  • Mybatis篇
    • Mybatis基础教程
    • Mybatis-Spring基础教程
    • Sqlsession原理
    • Mybatis代码架构分析
    • Mybatis事务
    • Mybatis与Spring集成事务相关问题
    • 结果/参数绑定
    • Mybatis插件拓展/插件原理
    • Mybatis 使用Ehcache缓存机制//自带缓存与Spring结合使用
    • 使用代码生成器快速开发
    • Mybatis使用时的一些注意事项
    • Mybatis配置打印SQL语句
    • 持久层框架mybatis如何防止sql注入
    • SqlSessionTemplate与SqlSessionDaoSupport讲解
    • MapperFactoryBean与MapperScannerConfigurer讲解
    • Spring+MyBatis多数据源配置实现
    • Mybatis与Spring集成事务相关问题
  • Spring源码解读篇
    • Spring 架构图
    • Spring核心结构及组件分析
    • Spring5 Framework体系结构
    • Spring源码剖析
      • BeanFactory
      • BeanPostProcessor源码讲解
      • BeanFactoryPostProcessor源码讲解
      • BeanDefinition源码解析
      • RootBeanDefinition源码解析
      • AnnotatedBeanDefinition源码解析
      • ApplicationContext源码讲解
      • IoC容器的初始化?
      • @Configuration源码讲解
      • Bean的注解(annotation)
      • @ImportSelector、@Import、ImportResource工作原理分析
      • Bean的生命周期
    • IOC机制从设计理念/实现原理到源码解读
    • AOP实现原理
      • aop编程思想
      • aop在Spring中的应用
      • cglib和jdk动态代理
        • java/jdk代理实现与原理详细分析
        • cglib实现动态代理
    • Transaction事务处理源码分析及高级特性
      • 事务概念
      • Spring事务传播
      • 事务隔离级别
      • 事务实现源码分析
      • Spring事物应用实战(一)
      • Spring事务应用实战(二)之spring+hibernate+JTA 分布式事务的例子
    • SpringMVC源码解读
      • DispatcherServlet说明
      • 核心流程剖析及原理分析
      • 请求映射机制
      • 参数绑定与转换机制
      • 页面渲染机制
      • ContextLoader加载过程
      • web.xml 中的listener、 filter、servlet 加载顺序及其详解
      • Spring中WebApplicationContext、DispatcherServlet与web容器的ServletContext关系梳理
    • Spring新版本特性解读
  • JPA篇
    • 简单叙述
    • 基础教程
    • SpringData Jpa、Hibernate、Jpa 三者之间的关系
    • Spring data jpa 全面解析
    • 数据库schema含义
    • 数据库schema与catalog简介
    • Jpa关联映射以及字段映射注解讲解
      • @Entity、@Table、@id
      • @GeneratedValue
      • @Basic、@Column、@Transient
      • @MappedSuperclass、@Embedded、@OrderBy、@Lob、@Data
      • @OneToOne级联配置
      • @OneToMany、@ManyToOne级联配置
      • 更新的同时删除多的一方的旧数据
      • cascade级联属性讲解
      • JpaSpecificationExecutor接口
      • @Query 创建查询
      • @NamedQueries创建查询
      • @CreateDate @LastModifiedDate @EntityListeners、@SQLDelete、@Where
      • 注解关联时报错总结
      • JPA 多对多关联 中间表带有属性 两个外键作为中间表的联合主键时 直接操作中间表查询修改的方法
    • Jpa 使用@Query查询时 (参数可能为空)语句
    • Jpa校验/验证注解
      • Jpa的list校验方式
      • Jpa的基础校验/验证注解
  • Hibernate篇
    • Hibernate基础教程
    • Hibernate主键生成策略
    • Hibernate的体系结构
    • Hibernate面试题
    • 自定义一个方言类——Hibernate Dialect
    • Hibernate 不同数据库的连接及SQL方言
    • Hibernate中一级缓存和二级缓存的具体区别是什么?
    • Hibernate中,对象有三种状态:
Powered by GitBook
On this page
  • 前言
  • 代理模式
  • 静态代理
  • 动态代理

Was this helpful?

  1. Spring源码解读篇
  2. AOP实现原理
  3. cglib和jdk动态代理

java/jdk代理实现与原理详细分析

Previouscglib和jdk动态代理Nextcglib实现动态代理

Last updated 5 years ago

Was this helpful?

前言

关于Java中的代理,我们首先需要了解的是一种常用的设计模式--代理模式,而对于代理,根据创建代理类的时间点,又可以分为静态代理和动态代理。

代理模式

代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。在后面我会解释这种间接性带来的好处。代理模式结构图(图片来自《大话设计模式》):

静态代理

1、静态代理

静态代理:由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。

2、静态代理简单实现

根据上面代理模式的类图,来写一个简单的静态代理的例子。

比如西门庆找潘金莲,那潘金莲不好意思答复呀,咋办,找那个王婆做代理,表现在程序上时这样的:

先定义一种类型的女人:

package com.master.proxy;


/**
 *  @Author tuonioooo
 *  @Date 2018-8-22 10:35
 *  @Info 定义一种类型的女人,王婆和潘金莲都属于这个类型的女人
 *  @Blog https://blog.csdn.net/tuoni123
 */
public interface KindWomen {

    //这种类型的女人能做什么事情呢?
    public void makeEyesWithMan(); //抛媚眼
    public void happyWithMan(); //happy what? You know that!
    public void dragonEnteredShuangFeng();//龙入双峰

}

一种类型嘛,那肯定是接口,然后定义潘金莲:

package com.master.proxy;

/**
*  @Author tuonioooo
*  @Date 2018-8-22 10:37
*  @Info 潘金莲
*  @Blog https://blog.csdn.net/tuoni123
*/
public class PanJinLian implements KindWomen {

    @Override
    public void makeEyesWithMan() {
        System.out.println("makeEyesWithMan:makeEyesWithMan");
    }

    @Override
    public void happyWithMan() {
        System.out.println("happyWithMan:papapa");
    }

    @Override
    public void dragonEnteredShuangFeng() {
        System.out.println("dragonEnteredShuangFeng:hahaha");
    }
}

再定一个丑陋的王婆:

package com.master.proxy;

/**
*  @Author tuonioooo
*  @Date 2018-8-22 10:40
*  @Info 王婆
*  @Blog https://blog.csdn.net/tuoni123
*/
public class WangPo implements KindWomen {

    private KindWomen kindWomen;

    public WangPo() { //默认的话,是潘金莲的代理
        this.kindWomen = new PanJinLian();
    }

    //她可以是KindWomen的任何一个女人的代理,只要你是这一类型
    public WangPo(KindWomen kindWomen) {
        this.kindWomen = kindWomen;
    }

    public void happyWithMan() {
        this.kindWomen.happyWithMan(); //自己老了,干不了,可以让年轻的代替
    }

    @Override
    public void dragonEnteredShuangFeng() {
        this.kindWomen.dragonEnteredShuangFeng(); //下垂了,让胸大的来干吧
    }

    public void makeEyesWithMan() {
        this.kindWomen.makeEyesWithMan(); //王婆这么大年龄了,谁看她抛媚眼?!
    }
}

两个女主角都上场了,男主角也该出现了:

package com.master.proxy;

/**
 * @Author tuonioooo
 * @Date 2018-8-22 10:43
 * @Info 西门庆(这人色中饿鬼)
 * @Blog https://blog.csdn.net/tuoni123
 */
public class XiMenQing {
    /*
     * 水浒里是这样写的:西门庆被潘金莲用竹竿敲了一下难道,痴迷了,
     * 被王婆看到了, 就开始撮合两人好事,王婆作为潘金莲的代理人
     * 收了不少好处费,那我们假设一下:
     * 如果没有王婆在中间牵线,这两个不要脸的能成吗?难说的很!
     */
    public static void main(String[] args) {
        //把王婆叫出来
        WangPo wangPo = new WangPo();
        //然后西门庆就说,我要和潘金莲happy,然后王婆就安排了西门庆丢筷子的那出戏:
        wangPo.makeEyesWithMan(); //看到没,虽然表面上时王婆在做,实际上爽的是潘金莲
        wangPo.happyWithMan();
    }
}

那这就是活生生的一个例子,通过代理人实现了某种目的,如果真去掉王婆这个中间环节,直接是西 门庆和潘金莲勾搭,估计很难成就武松杀嫂事件。

那我们再考虑一下,水浒里还有没有这类型的女人?有,卢俊义的老婆贾氏(就是和那个固管家苟合

的那个),这名字起的:“假使”,那我们也让王婆做她的代理:

package com.master.proxy;

/**
 * @Author tuonioooo
 * @Date 2018-8-22 10:48
 * @Info 贾氏
 * @Blog https://blog.csdn.net/tuoni123
 */
public class JiaShi implements KindWomen {

    public void happyWithMan() {
        System.out.println("贾氏正在Happy中......");

    }

    @Override
    public void dragonEnteredShuangFeng() {
        System.out.println("dragonEnteredShuangFeng:hahaha");
    }

    public void makeEyesWithMan() {
        System.out.println("贾氏抛媚眼");
    }
}

西门庆勾贾氏:

package com.master.proxy;

/**
 * @Author tuonioooo
 * @Date 2018-8-22 10:43
 * @Info 西门庆(这人色中饿鬼)
 * @Blog https://blog.csdn.net/tuoni123
 */
public class XiMenQing {
    /*
     * 水浒里是这样写的:西门庆被潘金莲用竹竿敲了一下难道,痴迷了,
     * 被王婆看到了, 就开始撮合两人好事,王婆作为潘金莲的代理人
     * 收了不少好处费,那我们假设一下:
     * 如果没有王婆在中间牵线,这两个不要脸的能成吗?难说的很!
     */
    public static void main(String[] args) {
        //把王婆叫出来
        WangPo wangPo = new WangPo();
        //然后西门庆就说,我要和潘金莲happy,然后王婆就安排了西门庆丢筷子的那出戏:
        wangPo.makeEyesWithMan(); //看到没,虽然表面上时王婆在做,实际上爽的是潘金莲
        wangPo.happyWithMan();


        //改编一下历史,贾氏被西门庆勾走:
        JiaShi jiaShi = new JiaShi();
        WangPo wangPo1 = new WangPo(jiaShi); //让王婆作为贾氏的代理人
        wangPo.makeEyesWithMan();
        wangPo.happyWithMan();
    }
}

说完这个故事,那额总结一下,代理模式主要使用了 Java 的多态,干活的是被代理类,代理类主要是接活,你让我干活,好,我交给幕后的类去干,你满意就成,那怎么知道被代理类能不能干呢?同根就成,大家知根知底,你能做啥,我能做啥都清楚的很,同一个接口呗。

动态代理

1.动态代理

代理类在程序运行时创建的代理方式被成为动态代理。 我们上面静态代理的例子中,代理类(studentProxy)是自己定义好的,在程序运行之前就已经编译完成。然而动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。

实现原理:在程序运行时,运用反射机制动态创建而成。

比如,想要在每个代理的方法前都加上一个处理方法:

public void happyWithMan() {
    giveMoney(10);
    System.out.println("贾氏正在Happy中......");

}

这里有几个代理方法,就需要写几次giveMoney的方法,如果有多个静态代理,需要写很多次,非常麻烦。然而动态代理就是为了解决这个问题而生的。

2、动态代理简单实现

在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。

  • InvocationHandler接口:

public interface InvocationHandler { 
    public Object invoke(Object proxy,Method method,Object[] args) throws Throwable; 
}

参数说明:

Object proxy:指被代理的对象/目标对象/委托对象。

Method method:要调用的方法

Object[] args:方法调用时所需要的参数

可以将InvocationHandler接口的子类想象成一个代理的最终操作类,替换掉ProxySubject。

  • Proxy类:

Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, 
InvocationHandler h) 
throws IllegalArgumentException

参数说明:

ClassLoader loader:类加载器

Class<?>[] interfaces:得到全部的接口

InvocationHandler h:得到InvocationHandler接口的子类实例

类加载器说明:

在Proxy类中的newProxyInstance()方法中需要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器,在Java中主要有一下三种类加载器;

Booststrap ClassLoader:此加载器采用C++编写,一般开发中是看不到的;

Extendsion ClassLoader:用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类;

AppClassLoader:(默认)加载classpath指定的类,是最常使用的是一种加载器。

还是以王婆、潘金莲、西门庆为示例:

先定义一个王婆动态代理类,他可以代理所有的实现KindWomen这种类型的人(JDK动态代理类)

package com.master.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * Created by daizhao.
 * User: tony
 * Date: 2018-8-22
 * Time: 11:09
 * info: 定义一个王婆动态代理类,他可以代理所有的实现KindWomen这种类型的人(JDK动态代理类)
 */
public class WangPoInvocationHandler implements InvocationHandler {

    private Object target;// 目标对象/委托对象/被代理对象

    /**
     *  @Author daizhao
     *  @Date 2018-8-22 11:13
     *  @Params [target]
     *  @Return java.lang.Object
     *  @Info   绑定委托对象并返回一个代理类
     */
    public Object bind(Object target) {
        this.target = target;
        //取得代理对象
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);   //要绑定接口(这是一个缺陷,cglib弥补了这一缺陷)
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("WangPoInvocationHandler:invoke");
        //执行方法
        return method.invoke(target, args);


    }
}

潘金莲的实现步骤,参考上面的

定义一个西门庆的示例:

package com.master.proxy;

/**
 * @Author tuonioooo
 * @Date 2018-8-22 10:43
 * @Info 西门庆(这人色中饿鬼)
 * @Blog https://blog.csdn.net/tuoni123
 */
public class XiMenQing {
    /*
     * 水浒里是这样写的:西门庆被潘金莲用竹竿敲了一下难道,痴迷了,
     * 被王婆看到了, 就开始撮合两人好事,王婆作为潘金莲的代理人
     * 收了不少好处费,那我们假设一下:
     * 如果没有王婆在中间牵线,这两个不要脸的能成吗?难说的很!
     */
    public static void main(String[] args) {

        //JDK动态代理实现
        WangPoInvocationHandler wangPoInvocationHandler = new WangPoInvocationHandler();
        KindWomen kindWomen = (KindWomen) wangPoInvocationHandler.bind(new PanJinLian());
        kindWomen.happyWithMan();


    }
}