spring中代理的创建

丶点滴 2021-04-16 16:02:34
代理 spring 创建 中代


[toc]

spring 代理的创建


spring 中创建代理使用了 Jdk 和 Cglib 两种方式创建,JdkDynamicAopProxyObjenesisCglibAopProxy,通过使用配置 AdvisedProxyConfig来管理配置,根据配置决定使用哪种方式创建代理,下面来介绍这几个关键的类。

Advised

​ Advised 是一个管理 AOP 代理工厂配置的接口,在spring中的所有AopProxy都可以转换为 Advised。

ProxyConfig

在 spring 中,使用 ProxyConfig 来配置代理创建属性。

/**
* 代理工厂的超类,用于统一管理代理工厂类的属性。
*/
public class ProxyConfig implements Serializable {
// true:使用子类代理,false:使用接口代理
private boolean proxyTargetClass = false;
// 启动代理优化
private boolean optimize = false;
// 使用该代理工长创建的代理是否可以转换为 Advised,默认为false:表示可以,
// 如果为false,可以将bean转换为Advised:Advised testBean = (Advised) context.getBean("testBean");
boolean opaque = false;
// 将代理暴露出去,绑定到 ThreadLocal 的 currentProxy,用于代理类自己的方法调用自己的场景。
boolean exposeProxy = false;
// 冻结配置,true:不能修改该代理工长的配置。
private boolean frozen = false;
}

实现了ProxyConfig 的直接子类有4个:

ScopedProxyFactoryBean、ProxyProcessorSupport、AbstractSingletonProxyFactoryBean、AdvisedSupport,这几个类使用不同的方式来创建代理,但是最后还是会将创建代理的工作委托给 ProxyFactory,下面来查看4个直接子类的相关代码。

  1. ScopedProxyFactoryBean:用于@Scope 注解,实现bean的作用域控制。他实现了 BeanFactoryBeanFactoryAware接口,具有创建、管理bean的能力。

    这个类生成的代理只会记录类的名称,然后根据作用域获取bean,如果是prototype的,则beanFactory会创建一个新的bean。

    public class ScopedProxyFactoryBean extends ProxyConfig
    implements FactoryBean<Object>, BeanFactoryAware, AopInfrastructureBean{
    // 使用它来管理bean的作用域,他的实现很简答,就是获取对象时,委托给 beanFactory,然后 beanFactory 根据作用域获取对应的bean。
    private final SimpleBeanTargetSource scopedTargetSource = new SimpleBeanTargetSource();
    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
    if (!(beanFactory instanceof ConfigurableBeanFactory)) {
    throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory);
    }
    ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) beanFactory;
    // 将beanFactory 与 scopedTargetSource 关联,获取代理目标类时,从 scopedTargetSource 中获取,
    // SimpleBeanTargetSource 将获取bean的操作委托给了beanFactory
    this.scopedTargetSource.setBeanFactory(beanFactory);
    ProxyFactory pf = new ProxyFactory();
    pf.copyFrom(this);
    pf.setTargetSource(this.scopedTargetSource);
    Assert.notNull(this.targetBeanName, "Property 'targetBeanName' is required");
    Class<?> beanType = beanFactory.getType(this.targetBeanName);
    if (beanType == null) {
    throw new IllegalStateException("Cannot create scoped proxy for bean '" + this.targetBeanName +
    "': Target type could not be determined at the time of proxy creation.");
    }
    // 使用接口代理
    if (!isProxyTargetClass() || beanType.isInterface() || Modifier.isPrivate(beanType.getModifiers())) {
    pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType, cbf.getBeanClassLoader()));
    }
    // 简单的代理增强,在调用代理类时,从 beanFactory 中获取bean进行调用,这样就做到作用域的控制了。
    ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());
    pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject));
    // 标记代理需要不需要被AOP拦截,及时有切入点匹配 
    pf.addInterface(AopInfrastructureBean.class);
    // 创建代理对像:将创建代理交给 ProxyFactory
    this.proxy = pf.getProxy(cbf.getBeanClassLoader());
    }
    }
    
  2. ProxyProcessorSupport:为 ProxyFactory 提供了常用的公共方法。

public class ProxyProcessorSupport extends ProxyConfig implements Ordered, BeanClassLoaderAware, AopInfrastructureBean {
/**
* 可以自定义排序
*/
public void setOrder(int order) { this.order = order; }
@Override
public int getOrder() { return this.order; }
/**
* 当实现了接口时,使用接口代理,没有实现接口则使用类代理。
*/
protected void evaluateProxyInterfaces(Class<?> beanClass, ProxyFactory proxyFactory) {
Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, getProxyClassLoader());
boolean hasReasonableProxyInterface = false;
for (Class<?> ifc : targetInterfaces) {
if (!isConfigurationCallbackInterface(ifc) && !isInternalLanguageInterface(ifc) &&
ifc.getMethods().length > 0) {
hasReasonableProxyInterface = true;
break;
}
}
if (hasReasonableProxyInterface) {
for (Class<?> ifc : targetInterfaces) {
proxyFactory.addInterface(ifc);
}
}
else {
proxyFactory.setProxyTargetClass(true);
}
}
}
  1. AbstractSingletonProxyFactoryBean : 创建单例代理对象,在需要代理的对象实例化后,使用 InitializingBean#afterPropertiesSet()来创建代理,并为其设置前置通知和后置通知。

    public abstract class AbstractSingletonProxyFactoryBean extends ProxyConfig
    implements FactoryBean<Object>, BeanClassLoaderAware, InitializingBean {
    // 代理目标对象
    private Object target;
    // 需要代理的接口
    private Class<?>[] proxyInterfaces;
    // 前置拦截器
    private Object[] preInterceptors;
    // 后置拦截器
    private Object[] postInterceptors;
    // 全局 Advisor 注册器
    private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance();
    // 类加载器
    private transient ClassLoader proxyClassLoader;
    // 代理对象
    private Object proxy;
    // 实例化之后调用
    @Override
    public void afterPropertiesSet() {
    // ....
    // 将代理创建工作委托给 ProxyFactory
    ProxyFactory proxyFactory = new ProxyFactory();
    // 将预处理器、主处理器、后置处理器按顺序添加,可以组成一个处理器链根据添加顺序来执行所有的处理器。
    // 添加预处理器
    if (this.preInterceptors != null) {
    for (Object interceptor : this.preInterceptors) {
    proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(interceptor));
    }
    }
    // 添加主要的处理器,交给子类实现
    proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(createMainInterceptor()));
    // 添加后置处理器
    if (this.postInterceptors != null) {
    for (Object interceptor : this.postInterceptors) {
    proxyFactory.addAdvisor(this.advisorAdapterRegistry.wrap(interceptor));
    }
    }
    // 复制属性
    proxyFactory.copyFrom(this);
    // 创建代理目标源:默认是 SingletonTargetSource 
    TargetSource targetSource = createTargetSource(this.target);
    proxyFactory.setTargetSource(targetSource);
    // 设置代理的接口
    if (this.proxyInterfaces != null) {
    proxyFactory.setInterfaces(this.proxyInterfaces);
    }
    // 如果没有使用类代理的方式,解析目标类的接口。
    else if (!isProxyTargetClass()) {
    // Rely on AOP infrastructure to tell us what interfaces to proxy.
    Class<?> targetClass = targetSource.getTargetClass();
    if (targetClass != null) {
    proxyFactory.setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
    }
    }
    // 代理工厂后置处理方法,由子类实现,更改代理配置
    postProcessProxyFactory(proxyFactory);
    // 创建代理对象,委托给了 ProxyFactory
    this.proxy = proxyFactory.getProxy(this.proxyClassLoader);
    }
    }
    
  2. AdvisedSupport:实现了 Advised,将 ProxyConfigAdvised进行适配,为 Advised 提供了支持,他的唯一子类 ProxyCreatorSupport为创建代理提供了支持。

    public class AdvisedSupport extends ProxyConfig implements Advised {
    // 空代理对象
    public static final TargetSource EMPTY_TARGET_SOURCE = EmptyTargetSource.INSTANCE;
    // 代理目标源:默认为空目标源
    TargetSource targetSource = EMPTY_TARGET_SOURCE;
    // 是否已经对Advisors进行了过虑
    private boolean preFiltered = false;
    // Advisor 调用链工长
    AdvisorChainFactory advisorChainFactory = new DefaultAdvisorChainFactory();
    // 缓存方法对应的 Advisor 调用链。
    private transient Map<MethodCacheKey, List<Object>> methodCache;
    // 要实现的代理接口,按顺序存储。
    private List<Class<?>> interfaces = new ArrayList<>();
    // Advisor 列表
    private List<Advisor> advisors = new ArrayList<>();
    // Advisor 数据,为了方便内部操作。
    private Advisor[] advisorArray = new Advisor[0];
    }
    

    ProxyCreatorSupport 为创建代理提供了支持,他使用了AopProxyFactory 来创建AopProxy,最后ProxyFactory使用 AopProxy来创建代理对象。

    在创建ProxyCreatorSupport 时默认创建 DefaultAopProxyFactory,由他来判断使用接口代理还是子类代理。

    public class ProxyCreatorSupport extends AdvisedSupport {
    private AopProxyFactory aopProxyFactory;
    private final List<AdvisedSupportListener> listeners = new LinkedList<>();
    // 在创建第一个代理后将置为true,表示进入活动状态,将会触发 listeners。
    private boolean active = false;
    /**
    * 无参构造器,将会创建一个默认的aopProxyFactory。
    * DefaultAopProxyFactory 是一个创建代理的工长,用于根据配置创建代理。
    */
    public ProxyCreatorSupport() {
    this.aopProxyFactory = new DefaultAopProxyFactory();
    }
    // 创建AOP代理,根据自身的配置属性觉得使用JDK代理还是Cglib代理。
    protected final synchronized AopProxy createAopProxy() {
    if (!this.active) {
    activate();
    }
    return getAopProxyFactory().createAopProxy(this);
    }
    }
    

    ​ 上面提到使用 DefaultAopProxyFactory 来决定使用 jdk代理还是 Cglib代理,他通过接收一个 AdvisedSupport

    // AopProxy 工厂
    public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    // 启用优化或使用子类代理、没有实现接口,就会使用子类代理方式。
    if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
    Class<?> targetClass = config.getTargetClass();
    if (targetClass == null) {
    throw new AopConfigException("TargetSource cannot determine target class: " +
    "Either an interface or a target is required for proxy creation.");
    }
    // 代理目标是接口,或者也是一个代理对象,使用jdk代理,否则使用Cglib 代理
    if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
    return new JdkDynamicAopProxy(config);
    }
    return new ObjenesisCglibAopProxy(config);
    }
    // 使用接口代理:JDK代理 
    else {
    return new JdkDynamicAopProxy(config);
    }
    }
    }
    

    ProxyFactoryProxyCreatorSupport 的子类,通过调用父类的方法获取AopProxy来创建目标代理对象。

    public class ProxyFactory extends ProxyCreatorSupport {
    public Object getProxy() {
    // 掉用 `ProxyCreatorSupport#createAopProxy` 方法之后根据配置来判断使用 JDK生成代理还是 Cglib生成代理
    return createAopProxy().getProxy();
    }
    // 与上面的方法区别在于传入了类加载器
    public Object getProxy(@Nullable ClassLoader classLoader) {
    return createAopProxy().getProxy(classLoader);
    }
    }
    
JdkDynamicAopProxy
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
/** 代理配置. */
private final AdvisedSupport advised;
/**
* 代理的接口上是否定义了equals方法
*/
private boolean equalsDefined;
/**
* 代理的接口是否定义了hashCode 方法
*/
private boolean hashCodeDefined;
public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
Assert.notNull(config, "AdvisedSupport must not be null");
// 通知不为空,并且目标源不为空。
if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
throw new AopConfigException("No advisors and no TargetSource specified");
}
this.advised = config;
}
// 创建代理
@Override
public Object getProxy() {
// 传入默认类加载器
return getProxy(ClassUtils.getDefaultClassLoader());
}
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}
// 获取代理目标类的所有接口
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
// 检查接口是否实现了equals 和 hashCode 方法
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
// 创建代理对象,这里传入了this对象,因为 JdkDynamicAopProxy 实现了 InvocationHandler,使用这一段代理逻辑进行代理
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
/**
* aop代理使用jdk代理将执行的逻辑
*/
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
// 执行equals方法时,接口未定义 equals 方法 ,执行JdkDynamicAopProxy 的 equals 方法
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
return equals(args[0]);
}
// 执行 hashCode 方法时,接口未定义 hashCode 方法,执行JdkDynamicAopProxy的hashCode方法
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
return hashCode();
}
// 
else if (method.getDeclaringClass() == DecoratingProxy.class) {
return AopProxyUtils.ultimateTargetClass(this.advised);
}
// 能够转换为Advised,将转换为Advised,然后执行
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
// 是否暴露当前的代理,绑定到ThreadLocal中,
if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// 获取目标对象
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// 根据代理目标对象和方法获取切入点、方法拦截器等。
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// 如果与该方法匹配的拦截器或通知为空,则进行直接调用,避免创建MethodInvocation
if (chain.isEmpty()) {
// 找到方法
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
// 直接调用原始对象方法
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// 调用 切入点、方法拦截器,目标类
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();
}
// 
Class<?> returnType = method.getReturnType();
// 如果返回值是目标对象,并且代理对象是返回值类型的一个实例,则将返回值替换为代理对象
// 方法的声明类未实现 RawTargetAccess
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
retVal = proxy;
}
// 如果返回值类型时基础数据类型,并且为null。
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
AopContext.setCurrentProxy(oldProxy);
}
}
}
}

JdkDynamicAopProxy 中,有2处关键代码,1是获取代理目标的接口,2是执行切入点、拦截器。

  1. AopProxyUtils#completeProxiedInterfaces()方法获取代理目标的接口,按照规则添加一部分接口SpringProxy、Advised、DecoratingProxy

    // AopProxyUtils
    static Class<?>[] completeProxiedInterfaces(AdvisedSupport advised, boolean decoratingProxy) {
    // 获取目标类实现的接口接口
    Class<?>[] specifiedInterfaces = advised.getProxiedInterfaces();
    // 目标类的接口为空
    if (specifiedInterfaces.length == 0) {
    // 获取代理目标class
    Class<?> targetClass = advised.getTargetClass();
    if (targetClass != null) {
    // 判断目标类型是否是接口
    if (targetClass.isInterface()) {
    advised.setInterfaces(targetClass);
    }
    // 代理目标类型是代理
    else if (Proxy.isProxyClass(targetClass)) {
    advised.setInterfaces(targetClass.getInterfaces());
    }
    // 重新获取代理对象的接口集
    specifiedInterfaces = advised.getProxiedInterfaces();
    }
    }
    // 如果目标类未实现 SpringProxy 接口,将添加 SpringProxy 到接口集中。
    boolean addSpringProxy = !advised.isInterfaceProxied(SpringProxy.class);
    // 目标类能转换为Advised,并且未实现 Advised 接口,则添加 Advised 到接口集中
    boolean addAdvised = !advised.isOpaque() && !advised.isInterfaceProxied(Advised.class);
    // decoratingProxy 为true,且目标类未实现 DecoratingProxy 接口,将 DecoratingProxy 添加进接口集中
    boolean addDecoratingProxy = (decoratingProxy && !advised.isInterfaceProxied(DecoratingProxy.class));
    // 划分接口数组长度
    int nonUserIfcCount = 0;
    if (addSpringProxy) {
    nonUserIfcCount++;
    }
    if (addAdvised) {
    nonUserIfcCount++;
    }
    if (addDecoratingProxy) {
    nonUserIfcCount++;
    }
    Class<?>[] proxiedInterfaces = new Class<?>[specifiedInterfaces.length + nonUserIfcCount];
    // 拷贝
    System.arraycopy(specifiedInterfaces, 0, proxiedInterfaces, 0, specifiedInterfaces.length);
    // 将接口class设置进对应的数组位置
    int index = specifiedInterfaces.length;
    if (addSpringProxy) {
    proxiedInterfaces[index] = SpringProxy.class;
    index++;
    }
    if (addAdvised) {
    proxiedInterfaces[index] = Advised.class;
    index++;
    }
    if (addDecoratingProxy) {
    proxiedInterfaces[index] = DecoratingProxy.class;
    }
    // 返回需要代理的接口集。
    return proxiedInterfaces;
    }
    
  2. 执行切面和方法拦截器逻辑 ReflectiveMethodInvocation#proceed

    public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {
    public Object proceed() throws Throwable {
    // 执行完后通知或拦截器后,将执行业务方法
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
    return invokeJoinpoint();
    }
    // 获取通知或拦截器
    Object interceptorOrInterceptionAdvice =
    this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    // 通知或拦截器是 InterceptorAndDynamicMethodMatcher 
    // InterceptorAndDynamicMethodMatcher 用于将方法匹配器与拦截器结合,如果方法匹配器匹配了就是用拦截器进行调用
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
    InterceptorAndDynamicMethodMatcher dm =
    (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
    Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
    if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
    return dm.interceptor.invoke(this);
    }
    else {
    // 匹配失败,调用下一个匹配的拦截器
    return proceed();
    }
    }
    // 调用其他拦截器,其他拦截器需要调用,因为传入了this,拦截器链可以使用引用调用本方法,以执行下一个切面或拦截器。
    else {
    return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
    }
    }
    
版权声明
本文为[丶点滴]所创,转载请带上原文链接,感谢
https://my.oschina.net/u/3052009/blog/5021995

  1. 【计算机网络 12(1),尚学堂马士兵Java视频教程
  2. 【程序猿历程,史上最全的Java面试题集锦在这里
  3. 【程序猿历程(1),Javaweb视频教程百度云
  4. Notes on MySQL 45 lectures (1-7)
  5. [computer network 12 (1), Shang Xuetang Ma soldier java video tutorial
  6. The most complete collection of Java interview questions in history is here
  7. [process of program ape (1), JavaWeb video tutorial, baidu cloud
  8. Notes on MySQL 45 lectures (1-7)
  9. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  10. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  11. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  12. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  13. 【递归,Java传智播客笔记
  14. [recursion, Java intelligence podcast notes
  15. [adhere to painting for 386 days] the beginning of spring of 24 solar terms
  16. K8S系列第八篇(Service、EndPoints以及高可用kubeadm部署)
  17. K8s Series Part 8 (service, endpoints and high availability kubeadm deployment)
  18. 【重识 HTML (3),350道Java面试真题分享
  19. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  20. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  21. [re recognize HTML (3) and share 350 real Java interview questions
  22. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  23. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  24. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  25. RPC 1: how to develop RPC framework from scratch
  26. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  27. RPC 1: how to develop RPC framework from scratch
  28. 一次性捋清楚吧,对乱糟糟的,Spring事务扩展机制
  29. 一文彻底弄懂如何选择抽象类还是接口,连续四年百度Java岗必问面试题
  30. Redis常用命令
  31. 一双拖鞋引发的血案,狂神说Java系列笔记
  32. 一、mysql基础安装
  33. 一位程序员的独白:尽管我一生坎坷,Java框架面试基础
  34. Clear it all at once. For the messy, spring transaction extension mechanism
  35. A thorough understanding of how to choose abstract classes or interfaces, baidu Java post must ask interview questions for four consecutive years
  36. Redis common commands
  37. A pair of slippers triggered the murder, crazy God said java series notes
  38. 1、 MySQL basic installation
  39. Monologue of a programmer: despite my ups and downs in my life, Java framework is the foundation of interview
  40. 【大厂面试】三面三问Spring循环依赖,请一定要把这篇看完(建议收藏)
  41. 一线互联网企业中,springboot入门项目
  42. 一篇文带你入门SSM框架Spring开发,帮你快速拿Offer
  43. 【面试资料】Java全集、微服务、大数据、数据结构与算法、机器学习知识最全总结,283页pdf
  44. 【leetcode刷题】24.数组中重复的数字——Java版
  45. 【leetcode刷题】23.对称二叉树——Java版
  46. 【leetcode刷题】22.二叉树的中序遍历——Java版
  47. 【leetcode刷题】21.三数之和——Java版
  48. 【leetcode刷题】20.最长回文子串——Java版
  49. 【leetcode刷题】19.回文链表——Java版
  50. 【leetcode刷题】18.反转链表——Java版
  51. 【leetcode刷题】17.相交链表——Java&python版
  52. 【leetcode刷题】16.环形链表——Java版
  53. 【leetcode刷题】15.汉明距离——Java版
  54. 【leetcode刷题】14.找到所有数组中消失的数字——Java版
  55. 【leetcode刷题】13.比特位计数——Java版
  56. oracle控制用户权限命令
  57. 三年Java开发,继阿里,鲁班二期Java架构师
  58. Oracle必须要启动的服务
  59. 万字长文!深入剖析HashMap,Java基础笔试题大全带答案
  60. 一问Kafka就心慌?我却凭着这份,图灵学院vip课程百度云