Spring系列第19篇:@Import批量注册bean,Java程序员必看

*/

boolean isBeanNameInUse(String beanName);

}

基本上所有bean工厂都实现了这个接口,让bean工厂拥有bean注册的各种能力。

上面我们用到的AnnotationConfigApplicationContext类也实现了这个接口。

BeanNameGenerator接口:bean名称生成器

bean名称生成器,这个接口只有一个方法,用来生成bean的名称:

public interface BeanNameGenerator {

String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry);

}

spring内置了3个实现

DefaultBeanNameGenerator

默认bean名称生成器,xml中bean未指定名称的时候,默认就会使用这个生成器,默认为:完整的类名#bean编号

AnnotationBeanNameGenerator

注解方式的bean名称生成器,比如通过@Component(bean名称)的方式指定bean名称,如果没有通过注解方式指定名称,默认会将完整的类名作为bean名称。

FullyQualifiedAnnotationBeanNameGenerator

将完整的类名作为bean的名称

BeanDefinition接口:bean定义信息

用来表示bean定义信息的接口,我们向容器中注册bean之前,会通过xml或者其他方式定义bean的各种配置信息,bean的所有配置信息都会被转换为一个BeanDefinition对象,然后通过容器中BeanDefinitionRegistry接口中的方法,将BeanDefinition注册到spring容器中,完成bean的注册操作。

这个接口有很多实现类,有兴趣的可以去看看源码,BeanDefinition的各种用法,以后会通过专题细说。

value为ImportBeanDefinitionRegistrar接口类型


用法(4个步骤)

  1. 定义ImportBeanDefinitionRegistrar接口实现类,在registerBeanDefinitions方法中使用registry来注册bean

  2. 使用@Import来导入步骤1中定义的类

  3. 使用步骤2中@Import标注的类作为AnnotationConfigApplicationContext构造参数创建spring容器

  4. 使用AnnotationConfigApplicationContext操作bean

案例

来2个普通的类。

Service1

package com.javacode2018.lesson001.demo24.test4;

public class Service1 {

}

Service2

这个类中需要注入Service1

package com.javacode2018.lesson001.demo24.test4;

public class Service2 {

private Service1 service1;

public Service1 getService1() {

return service1;

}

public void setService1(Service1 service1) {

this.service1 = service1;

}

@Override

public String toString() {

return “Service2{” +

“service1=” + service1 +

‘}’;

}

}

来个类实现ImportBeanDefinitionRegistrar接口,然后在里面实现上面2个类的注册,如下:

MyImportBeanDefinitionRegistrar

package com.javacode2018.lesson001.demo24.test4;

import org.springframework.beans.factory.config.BeanDefinition;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;

import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;

import org.springframework.core.type.AnnotationMetadata;

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

@Override

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

//定义一个bean:Service1

BeanDefinition service1BeanDinition = BeanDefinitionBuilder.genericBeanDefinition(Service1.class).getBeanDefinition();

//注册bean

registry.registerBeanDefinition(“service1”, service1BeanDinition);

//定义一个bean:Service2,通过addPropertyReference注入service1

BeanDefinition service2BeanDinition = BeanDefinitionBuilder.genericBeanDefinition(Service2.class).

addPropertyReference(“service1”, “service1”).

getBeanDefinition();

//注册bean

registry.registerBeanDefinition(“service2”, service2BeanDinition);

}

}

注意上面的registerBeanDefinitions方法,内部注册了2个bean,Service1和Service2。

上面使用了BeanDefinitionBuilder这个类,这个是BeanDefinition的构造器,内部提供了很多静态方法方便构建BeanDefinition对象。

上面定义的2个bean,和下面xml方式效果一样:

来个测试用例

ImportTest中新增个方法

@Test

public void test4() {

//1.通过AnnotationConfigApplicationContext创建spring容器,参数为@Import标注的类

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig4.class);

//2.输出容器中定义的所有bean信息

for (String beanName : context.getBeanDefinitionNames()) {

System.out.println(String.format(“%s->%s”, beanName, context.getBean(beanName)));

}

}

运行输出

service1->com.javacode2018.lesson001.demo24.test4.Service1@62150f9e

service2->Service2{service1=com.javacode2018.lesson001.demo24.test4.Service1@62150f9e}

value为ImportSelector接口类型


先来看一下ImportSelector接口

ImportSelector接口

导入选择器,看一下源码:

public interface ImportSelector {

/**

  • 返回需要导入的类名的数组,可以是任何普通类,配置类(@Configuration、@Bean、@CompontentScan等标注的类)

  • @importingClassMetadata:用来获取被@Import标注的类上面所有的注解信息

*/

String[] selectImports(AnnotationMetadata importingClassMetadata);

}

用法(4个步骤)

  1. 定义ImportSelector接口实现类,在selectImports返回需要导入的类的名称数组

  2. 使用@Import来导入步骤1中定义的类

  3. 使用步骤2中@Import标注的类作为AnnotationConfigApplicationContext构造参数创建spring容器

  4. 使用AnnotationConfigApplicationContext操作bean

案例

来个普通类:Service1

package com.javacode2018.lesson001.demo24.test5;

public class Service1 {

}

来个@Configuration标注的配置类:Module1Config

package com.javacode2018.lesson001.demo24.test5;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

@Configuration

public class Module1Config {

@Bean

public String name() {

return “公众号:路人甲java”;

}

@Bean

public String address() {

return “上海市”;

}

}

上面定义了两个string类型的bean:name和address

下面自定义一个ImportSelector,然后返回上面2个类的名称

package com.javacode2018.lesson001.demo24.test5;

import org.springframework.context.annotation.ImportSelector;

import org.springframework.core.type.AnnotationMetadata;

public class MyImportSelector implements ImportSelector {

@Override

public String[] selectImports(AnnotationMetadata importingClassMetadata) {

return new String[]{

Service1.class.getName(),

Module1Config.class.getName()

};

}

}

来个@Import标注的类,导入MyImportSelector

package com.javacode2018.lesson001.demo24.test5;

import com.javacode2018.lesson001.demo24.test4.MyImportBeanDefinitionRegistrar;

import org.springframework.context.annotation.Import;

/**

  • 通过@Import导入MyImportSelector接口实现类

*/

@Import({MyImportSelector.class})

public class MainConfig5 {

}

新增测试用例

ImportTest中新增个方法

@Test

public void test5() {

//1.通过AnnotationConfigApplicationContext创建spring容器,参数为@Import标注的类

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig5.class);

//2.输出容器中定义的所有bean信息

for (String beanName : context.getBeanDefinitionNames()) {

System.out.println(String.format(“%s->%s”, beanName, context.getBean(beanName)));

}

}

运行输出

部分输出如下:

com.javacode2018.lesson001.demo24.test5.Service1->com.javacode2018.lesson001.demo24.test5.Service1@45b4c3a9

name->公众号:路人甲java

address->上海市

输出中可以看到Service1以及Module1Config中定义的2个bean都有了。

来一个牛逼的案例


需求

凡是类名中包含service的,调用他们内部任何方法,我们希望调用之后能够输出这些方法的耗时。

实现分析

之前我们讲过代理, 此处我们就可以通过代理来实现,bean实例创建的过程中,我们可以给这些bean生成一个代理,在代理中统计方法的耗时,这里面有2点:

  1. 创建一个代理类,通过代理来间接访问需要统计耗时的bean对象

  2. 拦截bean的创建,给bean实例生成代理生成代理

具体实现

先来两个Service类
Service1

package com.javacode2018.lesson001.demo24.test6;

import org.springframework.stereotype.Component;

@Component

public class Service1 {

public void m1() {

System.out.println(this.getClass() + “.m1()”);

}

}

Service2

package com.javacode2018.lesson001.demo24.test6;

import org.springframework.stereotype.Component;

@Component

public class Service2 {

public void m1() {

System.out.println(this.getClass() + “.m1()”);

}

}

创建统计耗时的代理类

下面我们使用cglib来实现一个代理类,如下:

package com.javacode2018.lesson001.demo24.test6;

import org.springframework.cglib.proxy.Enhancer;

import org.springframework.cglib.proxy.MethodInterceptor;

import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CostTimeProxy implements MethodInterceptor {

//目标对象

private Object target;

public CostTimeProxy(Object target) {

this.target = target;

}

@Override

public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

long starTime = System.nanoTime();

//调用被代理对象(即target)的方法,获取结果

Object result = method.invoke(target, objects); //@1

long endTime = System.nanoTime();

System.out.println(method + “,耗时(纳秒):” + (endTime - starTime));

return result;

}

/**

  • 创建任意类的代理对象

  • @param target

  • @param

  • @return

*/

public static T createProxy(T target) {

CostTimeProxy costTimeProxy = new CostTimeProxy(target);

Enhancer enhancer = new Enhancer();

enhancer.setCallback(costTimeProxy);

enhancer.setSuperclass(target.getClass());

return (T) enhancer.create();

}

}

createProxy方法可以用来给某个对象生成代理对象

需要了解cglib的可以看:代理详解(Java动态代理&cglib代理)

拦截bean实例的创建,返回代理对象

这里我们需要用到spring中的一个接口:

org.springframework.beans.factory.config.BeanPostProcessor

public interface BeanPostProcessor {

/**

  • bean初始化之后会调用的方法

*/

@Nullable

default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

return bean;

}

/**

  • bean初始化之后会调用的方法

*/

@Nullable

default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

return bean;

}

}

这个接口是bean处理器,内部有2个方法,分别在bean初始化前后会进行调用,以后讲声明周期的时候还会细说的,这里你只需要知道bean初始化之后会调用postProcessAfterInitialization方法就行,这个方法中我们会给bean创建一个代理对象。

下面我们创建一个BeanPostProcessor实现类:

package com.javacode2018.lesson001.demo24.test6;

import com.javacode2018.lesson001.demo23.test4.CostTimeProxy;

import org.springframework.beans.BeansException;

import org.springframework.beans.factory.config.BeanPostProcessor;

import org.springframework.lang.Nullable;

public class MethodCostTimeProxyBeanPostProcessor implements BeanPostProcessor {

@Nullable

@Override

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

if (bean.getClass().getName().toLowerCase().contains(“service”)) {

return CostTimeProxy.createProxy(bean); //@1

} else {

return bean;

}

}

}

@1:使用上面创建代理类来给当前bean对象创建一个代理

需要将MethodCostTimeProxyBeanPostProcessor注册到容器中才会起作用,下面我们通过@Import结合ImportSelector的方式来导入这个类,将其注册到容器中。

MethodCostTimeImportSelector

package com.javacode2018.lesson001.demo24.test6;

import com.javacode2018.lesson001.demo23.test4.MethodCostTimeProxyBeanPostProcessor;

import org.springframework.context.annotation.ImportSelector;

import org.springframework.core.type.AnnotationMetadata;

public class MethodCostTimeImportSelector implements ImportSelector {

@Override

public String[] selectImports(AnnotationMetadata importingClassMetadata) {

return new String[]{MethodCostTimeProxyBeanPostProcessor.class.getName()};

}

}

来一个@Import来导入MethodCostTimeImportSelector

下面我们使用注解的方式,在注解上使用@Import,如下:

package com.javacode2018.lesson001.demo24.test6;

import com.javacode2018.lesson001.demo23.test4.MethodCostTimeImportSelector;

import org.springframework.context.annotation.Import;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Import(MethodCostTimeImportSelector.class)

public @interface EnableMethodCostTime {

}

来一个总的配置类

package com.javacode2018.lesson001.demo24.test6;

import org.springframework.context.annotation.ComponentScan;

@ComponentScan

@EnableMethodCostTime //@1

public class MainConfig6 {

}

上面使用了@CompontentScan注解,此时会将Servce1和Service2这两个类注册到容器中。

@1:此处使用了@EnableMethodCostTime注解,而@EnableMethodCostTime注解上使用了@Import(MethodCostTimeImportSelector.class),此时MethodCostTimeImportSelector类中的MethodCostTimeProxyBeanPostProcessor会被注册到容器,会拦截bean的创建,创建耗时代理对象。

来个测试用例

ImportTest中新增个方法

@Test

public void test6() {

//1.通过AnnotationConfigApplicationContext创建spring容器,参数为@Import标注的类

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig6.class);

Service1 service1 = context.getBean(Service1.class);

Service2 service2 = context.getBean(Service2.class);

service1.m1();

service2.m1();

}

上面会调用service1和service2的方法

运行输出

class com.javacode2018.lesson001.demo24.test6.Service1.m1()

public void com.javacode2018.lesson001.demo24.test6.Service1.m1(),耗时(纳秒):74200

class com.javacode2018.lesson001.demo24.test6.Service2.m1()

public void com.javacode2018.lesson001.demo24.test6.Service2.m1(),耗时(纳秒):33800

太牛逼了,需求实现了。

如果我们不想开启方法耗时统计,只需要将MainConfig6上的@EnableMethodCostTime去掉就可以了,用起来是不是特别爽。

spring中有很多类似的注解,以@EnableXXX开头的注解,基本上都是通过上面这种方式实现的,如:

@EnableAspectJAutoProxy

@EnableCaching

@EnableAsync

继续向下看,还有一个更牛逼的接口DeferredImportSelector。

DeferredImportSelector接口


先给你透露一下,springboot中的核心功能@EnableAutoConfiguration就是靠DeferredImportSelector来实现的。

DeferredImportSelector是ImportSelector的子接口,既然是ImportSelector的子接口,所以也可以通过@Import进行导入,这个接口和ImportSelector不同地方有两点:

  1. 延迟导入

  2. 指定导入的类的处理顺序

延迟导入

比如@Import的value包含了多个普通类、多个@Configuration标注的配置类、多个ImportSelector接口的实现类,多个ImportBeanDefinitionRegistrar接口的实现类,还有DeferredImportSelector接口实现类,此时spring处理这些被导入的类的时候,会将DeferredImportSelector类型的放在最后处理,会先处理其他被导入的类,其他类会按照value所在的前后顺序进行处理

那么我们是可以做很多事情的,比如我们可以在DeferredImportSelector导入的类中判断一下容器中是否已经注册了某个bean,如果没有注册过,那么再来注册。

以后我们会讲到另外一个注解@Conditional,这个注解可以按条件来注册bean,比如可以判断某个bean不存在的时候才进行注册,某个类存在的时候才进行注册等等各种条件判断,通过@Conditional来结合DeferredImportSelector可以做很多事情。

来个延迟导入的案例

来3个配置类,每个配置类中都通过@Bean定一个string类型的bean,内部输出一句文字。

Configuration1

package com.javacode2018.lesson001.demo24.test7;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

@Configuration

public class Configuration1 {

@Bean

public String name1() {

System.out.println(“name1”);

return “name1”;

}

}

Configuration2

package com.javacode2018.lesson001.demo24.test7;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

@Configuration

public class Configuration2 {

@Bean

public String name2() {

System.out.println(“name2”);

return “name2”;

}

}

Configuration3

package com.javacode2018.lesson001.demo24.test7;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

@Configuration

public class Configuration3 {

@Bean

public String name3() {

System.out.println(“name3”);

return “name3”;

}

}

来一个ImportSelector实现类,导入Configuration1

package com.javacode2018.lesson001.demo24.test7;

import org.springframework.context.annotation.ImportSelector;

import org.springframework.core.type.AnnotationMetadata;

public class ImportSelector1 implements ImportSelector {

@Override

public String[] selectImports(AnnotationMetadata importingClassMetadata) {

return new String[]{

Configuration1.class.getName()

};

}

}

来一个DeferredImportSelector实现类,导入Configuration2

package com.javacode2018.lesson001.demo24.test7;

import org.springframework.context.annotation.DeferredImportSelector;

import org.springframework.core.type.AnnotationMetadata;

public class DeferredImportSelector1 implements DeferredImportSelector {

@Override

public String[] selectImports(AnnotationMetadata importingClassMetadata) {

return new String[]{Configuration2.class.getName()};

}

}

来一个总的配置类

package com.javacode2018.lesson001.demo24.test7;

import org.springframework.context.annotation.Import;

@Import({

DeferredImportSelector1.class,

Configuration3.class,

ImportSelector1.class,

})

public class MainConfig7 {

}

注意上面的@Import中被导入类的顺序:

DeferredImportSelector1->Configuration3->ImportSelector1

下面来个测试用例,看一下3个配置文件中@Bean标注的方法被执行的先后顺序。

测试用例

ImportTest中新增个方法

@Test

public void test7() {

//1.通过AnnotationConfigApplicationContext创建spring容器,参数为@Import标注的类

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig7.class);

}

运行输出

name3

name1

name2

输出的结果结合一下@Import中被导入的3个类的顺序,可以看出DeferredImportSelector1是被最后处理的,其他2个是按照value中所在的先后顺序处理的。

指定导入的类的处理顺序

当@Import中有多个DeferredImportSelector接口的实现类时候,可以指定他们的顺序,指定顺序常见2种方式

实现Ordered接口的方式

org.springframework.core.Ordered

public interface Ordered {

int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;

int LOWEST_PRECEDENCE = Integer.MAX_VALUE;

int getOrder();

}

value的值越小,优先级越高。

实现Order注解的方式

org.springframework.core.annotation.Order

@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})

@Documented

public @interface Order {

int value() default Ordered.LOWEST_PRECEDENCE;

}

value的值越小,优先级越高。

下面我们来个案例感受一下。

来个指定导入类处理顺序的案例

来2个配置类,内部都有一个@Bean标注的方法,用来注册一个bean,方法内部输出一行文字

Configuration1

package com.javacode2018.lesson001.demo24.test8;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

@Configuration

public class Configuration1 {

@Bean

public String name1() {

System.out.println(“name1”);

return “name1”;

}

}

Configuration2

package com.javacode2018.lesson001.demo24.test8;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

@Configuration

public class Configuration2 {

@Bean

public String name2() {

System.out.println(“name2”);

return “name2”;

}

}

来2个DeferredImportSelector实现类,分别来导入上面2个配置文件,顺便通过Ordered接口指定一下顺序

DeferredImportSelector1

package com.javacode2018.lesson001.demo24.test8;

import org.springframework.context.annotation.DeferredImportSelector;

import org.springframework.core.Ordered;

import org.springframework.core.type.AnnotationMetadata;

public class DeferredImportSelector1 implements DeferredImportSelector, Ordered {

@Override

public String[] selectImports(AnnotationMetadata importingClassMetadata) {

return new String[]{Configuration1.class.getName()};

}

@Override

public int getOrder() {

return 2;

}

}

DeferredImportSelector2

package com.javacode2018.lesson001.demo24.test8;

import com.javacode2018.lesson001.demo24.test7.Configuration2;

import org.springframework.context.annotation.DeferredImportSelector;

import org.springframework.core.Ordered;

import org.springframework.core.type.AnnotationMetadata;

public class DeferredImportSelector2 implements DeferredImportSelector, Ordered {

@Override

public String[] selectImports(AnnotationMetadata importingClassMetadata) {

return new String[]{Configuration2.class.getName()};

}

@Override

public int getOrder() {

return 1;

}

}

DeferredImportSelector1的order为2,DeferredImportSelector2的order为1,order值越小优先级越高。

来个总的配置类,引入上面两个ImportSelector

MainConfig8

package com.javacode2018.lesson001.demo24.test8;

import org.springframework.context.annotation.Import;

@Import({

DeferredImportSelector1.class,

DeferredImportSelector2.class,

})

public class MainConfig8 {

}

测试用例

ImportTest中新增个方法

@Test

public void test8() {

//1.通过AnnotationConfigApplicationContext创建spring容器,参数为@Import标注的类

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig8.class);

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
img

最后

总而言之,面试官问来问去,问的那些Redis知识点也就这么多吧,复习的不够到位,知识点掌握不够熟练,所以面试才会卡壳。将这些Redis面试知识解析以及我整理的一些学习笔记分享出来给大家参考学习

还有更多学习笔记面试资料也分享如下:

都是“Redis惹的祸”,害我差点挂在美团三面,真是“虚惊一场”

e

public String[] selectImports(AnnotationMetadata importingClassMetadata) {

return new String[]{Configuration2.class.getName()};

}

@Override

public int getOrder() {

return 1;

}

}

DeferredImportSelector1的order为2,DeferredImportSelector2的order为1,order值越小优先级越高。

来个总的配置类,引入上面两个ImportSelector

MainConfig8

package com.javacode2018.lesson001.demo24.test8;

import org.springframework.context.annotation.Import;

@Import({

DeferredImportSelector1.class,

DeferredImportSelector2.class,

})

public class MainConfig8 {

}

测试用例

ImportTest中新增个方法

@Test

public void test8() {

//1.通过AnnotationConfigApplicationContext创建spring容器,参数为@Import标注的类

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig8.class);

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-e9Ad1QQc-1711997318921)]
[外链图片转存中…(img-S17Pqr7O-1711997318922)]
[外链图片转存中…(img-93raTpTZ-1711997318922)]
[外链图片转存中…(img-uf6WzLmd-1711997318922)]
[外链图片转存中…(img-xxGVR5zj-1711997318923)]
[外链图片转存中…(img-ZdQLK74L-1711997318923)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-wxRLtZ9g-1711997318923)]

最后

总而言之,面试官问来问去,问的那些Redis知识点也就这么多吧,复习的不够到位,知识点掌握不够熟练,所以面试才会卡壳。将这些Redis面试知识解析以及我整理的一些学习笔记分享出来给大家参考学习

还有更多学习笔记面试资料也分享如下:

[外链图片转存中…(img-FXkwaF9A-1711997318924)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值