0%

Java部分

  1. java比较 icompare
  2. tomcat 热部署 加载方式与双亲委派模型?
  3. java io api 过滤器模式?
  4. threadLocal 实现原理?
  5. tcp ip协议
  6. 服务端如何确定seesion是同一个?
  7. 内存屏障(Memory Barriers)
  8. lock synchronized ReentrantLock
  9. jvm JVM的年轻代分为哪几代?年轻代什么时候会进入老年代?
  10. jvm JVM 垃圾回收算法?(注意年轻代与老年代是不一样的)?
  11. jvm内存模型 一个变量初始化 怎么分配内存 分配到什么地方?
  12. 不使用双亲委派模型的缺点?
  13. java 开源序列化框架有哪些?彼此之间有什么区别(优缺点)?
  14. java.util.concurrent hashmap 相关问题
  15. JAVA线程sleep和wait方法区别
    https://jiankunking.blog.csdn.net/article/details/79824353
  16. PriorityQueue(优先级队列) 堆相关问题
  17. 常见的负载均衡算法
  18. java 阻塞队列 相关问题,阻塞具体是如何实现的?
  19. 静态代码块. 构造代码块. 构造函数以及Java类初始化顺序
  20. java 枚举的实现,内部如何进行存储的?
  21. 静态内部类与普通内部类,在用法. 初始化方面的区别?
  22. java 原子性 可见性 顺序性是通过什么来保证的?
  23. java 多线程内共享的模型
  24. 阻塞非阻塞与同步异步
  25. java nio原理
  26. 读写锁 自旋锁 尝试锁(cas) cas如何保证,查询到修改这个过程是原子的?
  27. 一个类中的静态变量是在类加载的哪个步骤加载的?
  28. synchronized与ReentrantLock 实现原理区别?
  29. threadlocal 实现原理?应用场景?
  30. 常见的设计模式
  31. 分布式事务
  32. 线程池工作原理及机制
  33. 线程挂了 保活
  34. keepalive 保活策略?
  35. Protocol Buffers 适用场景?
  36. http tcp 相比多了些什么?有什么不一样的地方?
  37. http与https区别?加密算法是?
  38. wait 是释放锁?为什么释放了锁,线程就挂起了。为什么线程wait了就挂起了?
  39. CMS 垃圾回收
  40. hashmap 线程不安全 什么时候会出现?会出现什么问题?(hashmap为啥线程不安全?)
  41. equals 比较原理?
  42. jvm 内存分布
  43. arraylist linklist
  44. interger 为null 转int 会发生什么?
  45. hashmap与hashset的关系?
  46. 线程与协程的区别?协程的优势?
  47. JDK8 如何实现协程?
  48. java lambda 实现原理
  49. java stream 实现原理
  50. 永久代(permanent generation)与Metaspace
  51. 如何保证GC ROOTS找的全?(比如中G1中)
  52. G1清理老年代. 年轻代是遍历所有吗?
  53. 可重入锁和不可重入锁?不可重入锁有啥缺陷?
  54. CPU密集型 Java线程池大小为何会大多被设置成CPU核心数+1?
  55. 什么情况下会出现ClassNotFoundException?
  56. 线程有几种状态?
  57. 如何动态上报JVM信息,以便后期排查OOM等问题?
  58. ConcurrentHashMap put的时候加锁的是数组上的元素 还是啥?
  59. Concurrenthashmap中用到的优化技巧?
  60. LRU如何实现?
  61. 为什么Concurrenthashmap扩容是安全的?
  62. LinkedHashMap和HashMap 区别?
  63. CompletableFuture get(long timeout, TimeUnit unit) throws TimeoutException, ExecutionException实现
    https://medium.com/@sergeykuptsov/how-it-works-in-java-completablefuture-3031dbbca66d
    64、Java time-based map/cache with expiring keys
    https://stackoverflow.com/questions/3802370/java-time-based-map-cache-with-expiring-keys
    65、jmap 其实是多个线程 他们之间是怎么通信 dump出数据的?(jmap命令的实现原理)
    66、GC的年轻代Survivor区,为什么是2个,而不是1个?
    https://stackoverflow.com/questions/10695298/java-gc-why-two-survivor-regions
    简单来说2个Survivor区,就是整理内存碎片的时候方便。
    67、类加载器及类加载机制

MySQL部分

  1. MySQL 时间 比较无效 原因?
  2. MySQL 数据库 索引 是以什么数据结构形式存储的?
  3. MySQL与sql server 异同点? 原理上?
  4. 索引顺序对于索引效果的影响?
  5. 数据库索引如何优化(从哪几个方面)?
  6. MySQL优化有哪些?
  7. 比如一个表中有100条数据,a字段的值,是从1到100,我要更新其中的数据,where条件时a>10
    MySQL通过innodb引擎的话,是通过表锁还是行锁?
  8. MySQL mvcc多版本并发控制
  9. MySQL为什么选中B+ TREE而不是B TREE?两种数据结构有什么区别?

B+ 树继承于 B 树,都限定了节点中数据数目和子节点的数目。B 树所有节点都可以映射数据,B+ 树只有叶子节点可以映射数据。
单独看这部分设计,看不出 B+ 树的优势。为了只有叶子节点可以映射数据,B+ 树创造了很多冗余的索引(所有非叶子节点都是冗余索引),这些冗余索引让 B+ 树在插入、删除的效率都更高,而且可以自动平衡,因此 B+ 树的所有叶子节点总是在一个层级上。所以 B+ 树可以用一条链表串联所有的叶子节点,也就是索引数据,这让 B+ 树的范围查找和聚合运算更快。

  1. MySQL 范围查询?索引的数据结构是如何处理的?
  2. MySQL事务提交原理?
  3. 聚集索引 非聚集索引 查询效率?
  4. MySQL 乐观锁 悲观锁
  5. 数据库分库分表

基础

  1. 进程间通信方式有哪些?
  2. 有些信号你能捕获,有些信号你是捕获不了的,捕获不了的信号有哪些?
  3. zookeeper 可以通过watch,用来做进程间通信,那么zk底层是使用什么方式来实现进程间通信的?依赖操作系统如何实现的?
  4. socket通信
  5. keepalive时间限制
  6. tcp 如何处理粘包问题
  7. http协议 如何区分header头还有body体
  8. tcp 协议网络段 协议簇
  9. 一次完整的http请求
  10. http code 302 304含义

线上

  1. 如何线上debug?比如线上的cpu爆了,这个步骤是?
  2. 线上fd耗光了,如何排查?
  3. 如何定位OOM 问题?

Kakfa

  1. kafka某个broker上是否可以有无限个topic?或者万级别的topic?
  2. kafka 设计,还有broker上文件存储
  3. kakfa是否支持顺序消费消息?
  4. zk在kafka集群中的作用
  5. kafka 消费时候可以批量拉取?
  6. 消息队列 选型 为什么选择kafka?
  7. kafka增加. 删除节点时如何迁移数据?新的数据如何分配?
  8. kafka写入消息 如何保证回滚或者保证不被消费
  9. kafka 如何确保消息消费且只消费一次?
  10. kafka 大批量写入 是怎么传输的?
    对象缓存池
    https://www.sohu.com/a/346950666_100123073
    https://github.com/a0x8o/kafka/blob/master/clients/src/main/java/org/apache/kafka/clients/producer/internals/BufferPool.java
  11. Kafka和RocketMQ存储区别
    https://www.cnblogs.com/lewis09/p/11168902.html

ElasticSearch

  1. 在ElasticSearch中,集群(Cluster),节点(Node),分片(Shard),Indices(索引),replicas(备份)之间是什么关系?
  2. elasticsearch整个建索引. 查询的过程
  3. elasticsearch如何选举
  4. ik 是如何进行分词的?
  5. es Scroll 原理? Search After原理?
  6. es 副本作用?
  7. MySQL elasticsearch 查询对比?(比如整个搜索流程)
  8. elasticsearch match filter 差异点?
  9. es 评分机制/原理

OpenTSDB

  1. OpenTSDB与HBase 关系

脑经急转弯

  1. 判断一个整数是2的N次方?
  2. 二叉树拷贝(非递归)
  3. BitMap算法(应用)

其他

  1. 分布式锁有哪些实现方式?
  2. 分布式事务
  3. 异地多活
  4. zookeeper集群 当一个节点挂了一天,当再次启动的时候,如何识别哪个是leader?
  5. 有什么知名的开源apm(Application Performance Management)工具吗?
  6. pinpoint 原理?
  7. consul template作用?如何与prometheus交互的?

金融

  1. 同业拆借
  2. 信用卡消费一笔钱,是如何到收款人的账户的?(整个流转过程)
  3. 复式记账

Spring

  1. spring 注入 接口即如何注入一个接口的多个实现类?
  2. spring 中用到的设计模式?spring中一次完整的http请求链路?
  3. 手写stater
  4. spring 类自动加载机制
  5. spring fegin 接口相互调用异常问题解决,有没有熔断啥的配置?

一、什么是代理?

代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。

代理模式UML图:


简单结构示意图:


为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。Java 动态代理机制以巧妙的方式近乎完美地实践了代理模式的设计理念。

二、Java 动态代理类

Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:

(1)Interface InvocationHandler:该接口中仅定义了一个方法

1
public object invoke(Object obj,Method method, Object[] args)

在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组。这个抽象方法在代理类中动态实现。

(2)Proxy:该类即为动态代理类,其中主要包含以下内容:

protected Proxy(InvocationHandler h):构造函数,用于给内部的h赋值。

static Class getProxyClass(

ClassLoader loader,

Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。

static Object newProxyInstance(ClassLoaderloader, Class[] interfaces,InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)

所谓DynamicProxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然,这个DynamicProxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。

在使用动态代理类时,我们必须实现InvocationHandler接口

通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口(Subject接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态改变,从而实现了非常灵活的动态代理关系。

动态代理步骤:

  1. 创建一个实现接口InvocationHandler的类,它必须实现invoke方法

  2. 创建被代理的类以及接口

  3. 通过Proxy的静态方法

newProxyInstance(ClassLoaderloader,Class[]interfaces,InvocationHandler h)创建一个代理

  1. 通过代理调用方法

三、JDK的动态代理怎么使用?

1、需要动态代理的接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package jiankunking;

/**
* 需要动态代理的接口
*/
public interface Subject {
/**
* 你好
*
* @param name
* @return
*/
public String SayHello(String name);

/**
* 再见
*
* @return
*/
public String SayGoodBye();
}

2、需要代理的实际对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

package jiankunking;

/**
* 实际对象
*/
public class RealSubject implements Subject {

/**
* 你好
*
* @param name
* @return
*/
@Override
public String SayHello(String name) {
return "hello " + name;
}

/**
* 再见
*
* @return
*/
@Override
public String SayGoodBye() {
return " good bye ";
}
}

3、调用处理器实现类(有木有感觉这里就是传说中的AOP啊)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package jiankunking;

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


/**
* 调用处理器实现类
* 每次生成动态代理类对象时都需要指定一个实现了该接口的调用处理器对象
*/
public class InvocationHandlerImpl implements InvocationHandler {

/**
* 这个就是我们要代理的真实对象
*/
private Object subject;

/**
* 构造方法,给我们要代理的真实对象赋初值
*
* @param subject
*/
public InvocationHandlerImpl(Object subject) {
this.subject = subject;
}

/**
* 该方法负责集中处理动态代理类上的所有方法调用。
* 调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行
*
* @param proxy 代理类实例
* @param method 被调用的方法对象
* @param args 调用参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//在代理真实对象前我们可以添加一些自己的操作
System.out.println("在调用之前,我要干点啥呢?");
System.out.println("Method:" + method);
//当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
Object returnValue = method.invoke(subject, args);
//在代理真实对象后我们也可以添加一些自己的操作
System.out.println("在调用之后,我要干点啥呢?");
return returnValue;
}
}

4、测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package jiankunking;

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

/**
* 动态代理演示
*/
public class DynamicProxyDemonstration {
public static void main(String[] args) {
//代理的真实对象
Subject realSubject = new RealSubject();
/**
* InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
* 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用.
* 即:要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法
*/
InvocationHandler handler = new InvocationHandlerImpl(realSubject);

ClassLoader loader = handler.getClass().getClassLoader();
Class<?>[] interfaces = realSubject.getClass().getInterfaces();
/**
* 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
*/
Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);

System.out.println("动态代理对象的类型:" + subject.getClass().getName());

String hello = subject.SayHello("jiankunking");
System.out.println(hello);
// String goodbye = subject.SayGoodBye();
// System.out.println(goodbye);
}
}

5、输出结果如下:

四、动态代理怎么实现的?

从使用代码中可以看出,关键点在:

1
Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);

通过跟踪提示代码可以看出:当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用。

也就是说,当代码执行到:subject.SayHello(“jiankunking”)这句话时,会自动调用InvocationHandlerImpl的invoke方法。这是为啥呢?

下面是代码跟分析的过程,不想看的朋友可以直接看结论

以下代码来自:JDK1.8.0_92

既然生成代理对象是用的Proxy类的静态方newProxyInstance,那么我们就去它的源码里看一下它到底都做了些什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/**
* Returns an instance of a proxy class for the specified interfaces
* that dispatches method invocations to the specified invocation
* handler.
*
* <p>{@code Proxy.newProxyInstance} throws
* {@code IllegalArgumentException} for the same reasons that
* {@code Proxy.getProxyClass} does.
*
* @param loader the class loader to define the proxy class
* @param interfaces the list of interfaces for the proxy class
* to implement
* @param h the invocation handler to dispatch method invocations to
* @return a proxy instance with the specified invocation handler of a
* proxy class that is defined by the specified class loader
* and that implements the specified interfaces
* @throws IllegalArgumentException if any of the restrictions on the
* parameters that may be passed to {@code getProxyClass}
* are violated
* @throws SecurityException if a security manager, <em>s</em>, is present
* and any of the following conditions is met:
* <ul>
* <li> the given {@code loader} is {@code null} and
* the caller's class loader is not {@code null} and the
* invocation of {@link SecurityManager#checkPermission
* s.checkPermission} with
* {@code RuntimePermission("getClassLoader")} permission
* denies access;</li>
* <li> for each proxy interface, {@code intf},
* the caller's class loader is not the same as or an
* ancestor of the class loader for {@code intf} and
* invocation of {@link SecurityManager#checkPackageAccess
* s.checkPackageAccess()} denies access to {@code intf};</li>
* <li> any of the given proxy interfaces is non-public and the
* caller class is not in the same {@linkplain Package runtime package}
* as the non-public interface and the invocation of
* {@link SecurityManager#checkPermission s.checkPermission} with
* {@code ReflectPermission("newProxyInPackage.{package name}")}
* permission denies access.</li>
* </ul>
* @throws NullPointerException if the {@code interfaces} array
* argument or any of its elements are {@code null}, or
* if the invocation handler, {@code h}, is
* {@code null}
*/
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) throws IllegalArgumentException {
//检查h 不为空,否则抛异常
Objects.requireNonNull(h);

final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}

/*
* 获得与指定类装载器和一组接口相关的代理类类型对象
*/
Class<?> cl = getProxyClass0(loader, intfs);

/*
* 通过反射获取构造函数对象并生成代理类实例
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//获取代理对象的构造方法(也就是$Proxy0(InvocationHandler h))
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//生成代理类的实例并把InvocationHandlerImpl的实例传给它的构造方法
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}

我们再进去getProxyClass0方法看一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* Generate a proxy class. Must call the checkProxyAccess method
* to perform permission checks before calling this.
*/
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}

// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}

真相还是没有来到,继续,看一下 proxyClassCache

1
2
3
4
 /**
* a cache of proxy classes
*/
private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

奥,原来用了一下缓存啊

那么它对应的get方法啥样呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/**
* Look-up the value through the cache. This always evaluates the
* {@code subKeyFactory} function and optionally evaluates
* {@code valueFactory} function if there is no entry in the cache for given
* pair of (key, subKey) or the entry has already been cleared.
*
* @param key possibly null key
* @param parameter parameter used together with key to create sub-key and
* value (should not be null)
* @return the cached value (never null)
* @throws NullPointerException if {@code parameter} passed in or
* {@code sub-key} calculated by
* {@code subKeyFactory} or {@code value}
* calculated by {@code valueFactory} is null.
*/
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
expungeStaleEntries();
Object cacheKey = CacheKey.valueOf(key, refQueue);
// lazily install the 2nd level valuesMap for the particular cacheKey
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
if (valuesMap == null) {
//putIfAbsent这个方法在key不存在的时候加入一个值,如果key存在就不放入
ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey,valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}

// create subKey and retrieve the possible Supplier<V> stored by that
// subKey from valuesMap
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;

while (true) {
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
V value = supplier.get();
if (value != null) {
return value;
}
}
// else no supplier in cache
// or a supplier that returned null (could be a cleared CacheValue
// or a Factory that wasn't successful in installing the CacheValue)

// lazily construct a Factory
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}

if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
supplier = factory;
}
// else retry with winning supplier
} else {
if (valuesMap.replace(subKey, supplier, factory)) {
// successfully replaced
// cleared CacheEntry / unsuccessful Factory
// with our Factory
supplier = factory;
} else {
// retry with current supplier
supplier = valuesMap.get(subKey);
}
}
}
}

我们可以看到它调用了 supplier.get(); 获取动态代理类,其中supplier是Factory,这个类定义在WeakCach的内部。

来瞅瞅,get里面又做了什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public synchronized V get() { // serialize access
// re-check
Supplier<V> supplier = valuesMap.get(subKey);
if (supplier != this) {
// something changed while we were waiting:
// might be that we were replaced by a CacheValue
// or were removed because of failure ->
// return null to signal WeakCache.get() to retry
// the loop
return null;
}
// else still us (supplier == this)

// create new value
V value = null;
try {
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) { // remove us on failure
valuesMap.remove(subKey, this);
}
}
// the only path to reach here is with non-null value
assert value != null;

// wrap value with CacheValue (WeakReference)
CacheValue<V> cacheValue = new CacheValue<>(value);

// try replacing us with CacheValue (this should always succeed)
if (valuesMap.replace(subKey, this, cacheValue)) {
// put also in reverseMap
reverseMap.put(cacheValue, Boolean.TRUE);
} else {
throw new AssertionError("Should not reach here");
}

// successfully replaced us with new CacheValue -> return the value
// wrapped by it
return value;
}
}

发现重点还是木有出现,但我们可以看到它调用了valueFactory.apply(key, parameter)方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
 /**
* A factory function that generates, defines and returns the proxy class given
* the ClassLoader and array of interfaces.
*/
private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";

// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();

@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}

String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}

if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}

/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;

/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
}

通过看代码终于找到了重点:

1
2
//生成字节码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);

那么接下来我们也使用测试一下,使用这个方法生成的字节码是个什么样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package jiankunking;

import sun.misc.ProxyGenerator;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/**
* 动态代理演示
*/
public class DynamicProxyDemonstration {
public static void main(String[] args) {
//代理的真实对象
Subject realSubject = new RealSubject();

/**
* InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发
* 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用.
* 即:要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法
*/
InvocationHandler handler = new InvocationHandlerImpl(realSubject);

ClassLoader loader = handler.getClass().getClassLoader();
Class[] interfaces = realSubject.getClass().getInterfaces();
/**
* 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
*/
Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);
System.out.println("动态代理对象的类型:"+subject.getClass().getName());

String hello = subject.SayHello("jiankunking");
System.out.println(hello);
// 将生成的字节码保存到本地,
createProxyClassFile();
}
private static void createProxyClassFile(){
String name = "ProxySubject";
byte[] data = ProxyGenerator.generateProxyClass(name,new Class[]{Subject.class});
FileOutputStream out =null;
try {
out = new FileOutputStream(name+".class");
System.out.println((new File("hello")).getAbsolutePath());
out.write(data);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(null!=out) try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

可以看一下这里代理对象的类型:


我们用jd-jui 工具将生成的字节码反编译:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import jiankunking.Subject;

public final class ProxySubject extends Proxy implements Subject {
private static Method m1;
private static Method m3;
private static Method m4;
private static Method m2;
private static Method m0;

public ProxySubject(InvocationHandler paramInvocationHandler) {
super(paramInvocationHandler);
}

public final boolean equals(Object paramObject){
try {
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
} catch (Error|RuntimeException localError){
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}

public final String SayGoodBye() {
try{
return (String)this.h.invoke(this, m3, null);
} catch (Error|RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}

public final String SayHello(String paramString) {
try {
return (String)this.h.invoke(this, m4, new Object[] { paramString });
} catch (Error|RuntimeException localError){
throw localError;
} catch (Throwable localThrowable){
throw new UndeclaredThrowableException(localThrowable);
}
}

public final String toString() {
try {
return (String)this.h.invoke(this, m2, null);
} catch (Error|RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}

public final int hashCode() {
try {
return ((Integer)this.h.invoke(this, m0, null)).intValue();
} catch (Error|RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}

static
{
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("jiankunking.Subject").getMethod("SayGoodBye", new Class[0]);
m4 = Class.forName("jiankunking.Subject").getMethod("SayHello", new Class[] { Class.forName("java.lang.String") });
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
} catch (NoSuchMethodException localNoSuchMethodException) {
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
} catch (ClassNotFoundException localClassNotFoundException) {
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}

这就是最终真正的代理类,它继承自Proxy并实现了我们定义的Subject接口,也就是说:

1
Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);

这里的subject实际是这个类的一个实例,那么我们调用它的:

1
public final String SayHello(String paramString)

就是调用我们定义的InvocationHandlerImpl的 invoke方法:

上面是代码跟分析的过程,不想看的朋友可以直接看结论

五、结论

到了这里,终于解答了:

subject.SayHello(“jiankunking”)这句话时,为什么会自动调用InvocationHandlerImpl的invoke方法?

因为JDK生成的最终真正的代理类,它继承自Proxy并实现了我们定义的Subject接口,在实现Subject接口方法的内部,通过反射调用了InvocationHandlerImpl的invoke方法。

通过分析代码可以看出Java 动态代理,具体有如下四步骤:

  1. 通过实现 InvocationHandler 接口创建自己的调用处理器;

  2. 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;

  3. 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;

  4. 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。

演示代码下载:
https://github.com/jiankunking/DynamicProxyDemo

一直以来都是对于事件与委托比较混淆,而且不太会用。找了个时间,总结了一下,感觉清晰了很多。

先说一下个人理解的结论吧:

delegate是C#中的一种类型,它实际上是一个能够持有对某个方法的引用的类。

delegate声明的变量与delegate声明的事件,并没有本质的区别,事件是在delegate声明变量的基础上包装而成的,类似于变量与属性的关系(在IL代码中可以看到每一个delegate声明的事件都对应是私有的delegate声明的变量),提升了安全性。

Action 与Func:这两个其实说白了就是系统定义好的Delegate,他有很多重载的方法,便于各种应用情况下的调用。他在系统的System命名空间下,因此全局可见。

首先了解一下, ILDasm中图标含义:  

该图来自:http://www.cnblogs.com/zery/p/3366175.html

委托创建步骤:

  1. 用delegate关键字创建一个委托,包括声明返回值和参数类型。
  2. 使用的地方接收这个委托。
  3. 创建这个委托的实例并指定一个返回值和参数类型匹配的方法传递过去。

一、事件与委托

新建一个事件委托测试项目:EventDelegateTest。

具体代码如下:

1
2
3
4
5
6
7
8
9
namespace EventDelegateTest
{
public class TestClass
{
public delegate int delegateAction();
public event delegateAction OnActionEvent;
public delegateAction daNew;
}
}

编译代码后,使用 Visual Studio 2010自带的ILDASM.EXE:

打开该dll,可以看到如下信息:

从上图可以看出如下几点信息:

1、delegate

委托 public delegate int delegateAction();在IL中是以类(delegateAction)的形式存在的
.NET将委托定义为一个密封类,派生自基类System.MulticastDelegate,并继承了基类的三个方法:

2、event

public event delegateAction OnActionEvent;在IL中不仅仅对应event OnActionEvent而且还对应一个field OnActionEvent;而field OnActionEvent与 public delegateAction daNew生成的field daNew是一样的.


都是以字段(field )的形式存在的。
双击event OnActionEvent可以看到如下信息:

在IL中事件被封装成了包含一个add_前缀和一个remove_前缀的的代码段。
其中,add_前缀的方法其实是通过调用Delegate.Combine()方法来实现的,组成了一个多播委托;remove_就是调用Delegate.Remove()方法,用于移除多播委托中的某个委托。

也就是说:事件其实就是一个特殊的多播委托。

那么对于事件进行这一次封装有什么好处呢?
1、因为delegate可以支持的操作非常多,比如我们可以写onXXXChanged += aaaFunc,把某个函数指针挂载到这个委托上面,但是我们也可以简单粗暴地直接写onXXXChanged = aaaFunc,让这个委托只包含这一个函数指针。不过这样一来会产生一个安全问题:如果我们用onXXXChanged = aaaFunc这样的写法,那么会把这个委托已拥有的其他函数指针给覆盖掉,这大概不是定义onXXXChanged的程序员想要看到的结果。

小注:虽然事件不能直接=某个函数,也不可以直接=null

2、还有一个问题就是onXXXChanged这个委托应该什么时候触发(即调用它所包含的函数指针)。从面向对象的角度来说,XXX改变了这个事实(即onXXXChaned的字面含义)应该由包含它的那个对象来决定。但实际上我们可以从这个对象的外部环境调用onXXXChanged,这既产生了安全问题也不符合面向对象的初衷。 
说到这里对于事件与委托的管理算是说明白了,那么平时常用的Action与Func,与委托又有什么关系呢?

二、Action 与Func

Action 委托:封装一个方法,该方法具有参数(0到16个参数)并且不返回值。
具体形式如下:https://msdn.microsoft.com/zh-cn/library/system.action(v=vs.110).aspx

Func<T, TResult> 委托:封装一个具有参数(0到16个参数)并返回 TResult 参数指定的类型值的方法。
具体形式如下:https://msdn.microsoft.com/zh-cn/library/bb534960(v=vs.110).aspx

那么这Action与Func是怎么实现的呢?
1、Action(以Action<T1, T2> 委托:封装一个方法,该方法具有两个参数并且不返回值为例)
从微软公布的源码中,可以看到,如下实现:

1
public Action<bool,bool>  ac;

上面这个声明就是:该方法具有两个参数并且不返回值的委托。
其余使用方式与委托变量一样。
2、Func(以Func<T1, T2, TResult> 委托:封装一个具有两个参数并返回 TResult 参数指定的类型值的方法为例)
从微软公布的源码中,可以看到,如下实现:

此处,可以看出Func与Action是类似的,唯一的区别就是,Func必须指定返回值的类型,使用方式与委托咱们自己使用委托变量是一样的,直接使用相应参数的Func或者Action声明变量,=或者+=挂载函数(方法即可)
这两个其实说白了就是系统定义好的Delegate,他有很多重载的方法,便于各种应用情况下的调用。他在系统的System命名空间下,因此全局可见。

三、Predicate

是返回bool型的泛型委托,Predicate有且只有一个参数,返回值固定为bool。表示定义一组条件并确定指定对象是否符合这些条件的方法。

此方法常在集合(Array 和 List)的查找中被用到,如:数组,正则拼配的结果集中被用到。

官方文档:
https://docs.microsoft.com/zh-cn/dotnet/api/system.predicate-1?redirectedfrom=MSDN&view=netframework-4.8

具体用法demo如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace IconTest
{
public partial class Form2 : Form
{
Predicate<int> myPredicate;
int[] myNum = new int[8] { 12, 33, 89, 21, 15, 29, 40, 52 };
public int[] myResult;
public Form2()
{
InitializeComponent();
myPredicate = delegate(int curNum)             
{
if (curNum % 2 == 0)
{
return true;
}
else
{
return false;
}
};
}
private void Form2_Load(object sender, EventArgs e)
{
myResult = Array.FindAll(myNum, myPredicate);
}
}
}

上例中说明了Predicate的使用,FindAll方法中,参数2即是一个Predicate,在具体的执行中,每一个数组的元素都会执行指定的方法,如果满足要求返回true,并会被存放在结果集中,不符合的则被剔除,最终返回的集合,即是结果判断后想要的集合。
Array.FindAll 泛型方法:
https://docs.microsoft.com/zh-cn/dotnet/api/system.array.findall?redirectedfrom=MSDN&view=netframework-4.8#System_Array_FindAll__1___0___System_Predicate___0__
以上代码执行结果为:

那么Predicate与委托又有什么关系呢?

从微软源码中可以看出Predicate是返回bool型的泛型委托,从本质上来说与Func、Action、事件、委托变量并无本质区别。

四、资料

参考文章:
http://www.zhihu.com/question/28932542

关于事件部分应用注意可以参考:
http://www.cnblogs.com/buptzym/archive/2013/03/15/2962300.html

.NET Framework 源码:
https://referencesource.microsoft.com
Delegate 类:
https://docs.microsoft.com/zh-cn/dotnet/api/system.delegate?redirectedfrom=MSDN&view=netframework-4.8