最全穿过拥挤的人潮,Spring已为你制作好高级赛道!,头条三面技术四面HR

独家面经总结,超级精彩

本人面试腾讯,阿里,百度等企业总结下来的面试经历,都是真实的,分享给大家!

image

image

image

image

Java面试准备

准确的说这里又分为两部分:

  1. Java刷题
  2. 算法刷题

Java刷题:此份文档详细记录了千道面试题与详解;

image

image

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

// result.forEach(System.out::println); //stream has already been operated upon or closed
}

运行程序,输出:

----------------StreamConverter使用场景---------------
1

再次特别强调:流只能被读(消费)一次

因为有了ConversionService提供的强大能力,我们就可以在基于Spring/Spring Boot做二次开发时使用它,提高系统的通用性和容错性。如:当方法入参是Stream类型时,你既可以传入Stream类型,也可以是Collection类型、数组类型,是不是瞬间逼格高了起来。

image.png

兜底转换器

按照添加转换器的顺序,Spring在最后添加了4个通用的转换器用于兜底,你可能平时并不关注它,但它实时就在发挥着它的作用。

image.png

ObjectToObjectConverter

将源对象转换为目标类型,非常的通用:Object -> Object:

@Override
public Set getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(Object.class, Object.class));
}

虽然它支持的是Object -> Object,看似没有限制但其实是有约定条件的:

@Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return (sourceType.getType() != targetType.getType() &&
hasConversionMethodOrConstructor(targetType.getType(), sourceType.getType()));
}

是否能够处理的判断逻辑在于hasConversionMethodOrConstructor方法,直译为:是否有转换方法或者构造器。代码详细处理逻辑如下截图:

image.png

此部分逻辑可分为两个part来看:

  • part1:从缓存中拿到Member,直接判断Member的可用性,可用的话迅速返回
  • part2:若part1没有返回,就执行三部曲,尝试找到一个合适的Member,然后放进缓存内(若没有就返回null)
part1:快速返回流程

当不是首次进入处理时,会走快速返回流程。也就是第0步isApplicable判断逻辑,有这几个关注点:

  1. Member包括Method或者Constructor
  2. Method:若是static静态方法,要求方法的第1个入参类型必须是源类型sourceType;若不是static方法,则要求源类型sourceType必须是method.getDeclaringClass()的子类型/相同类型
  3. Constructor:要求构造器的第1个入参类型必须是源类型sourceType

image.png

创建目标对象的实例,此转换器支持两种方式:

  1. 通过工厂方法/实例方法创建实例(method.invoke(source)
  2. 通过构造器创建实例(ctor.newInstance(source)

以上case,在下面均会给出代码示例。

part2:三部曲流程

对于首次处理的转换,就会进入到详细的三部曲逻辑:通过反射尝试找到合适的Member用于创建目标实例,也就是上图的1、2、3步。

step1:determineToMethod,从sourceClass里找实例方法,对方法有如下要求:

  • 方法名必须叫"to" + targetClass.getSimpleName(),如toPerson()
  • 方法的访问权限必须是public
  • 该方法的返回值必须是目标类型或其子类型

step2:determineFactoryMethod,找静态工厂方法,对方法有如下要求:

  • 方法名必须为valueOf(sourceClass) 或者 of(sourceClass) 或者from(sourceClass)
  • 方法的访问权限必须是public

step3:determineFactoryConstructor,找构造器,对构造器有如下要求:

  • 存在一个参数,且参数类型是sourceClass类型的构造器
  • 构造器的访问权限必须是public

特别值得注意的是:此转换器支持Object.toString()方法将sourceType转换为java.lang.String。对于toString()支持,请使用下面介绍的更为兜底的FallbackObjectToStringConverter

代码示例
  • 实例方法

// sourceClass
@Data
public class Customer {
private Long id;
private String address;

public Person toPerson() {
Person person = new Person();
person.setId(getId());
person.setName(“YourBatman-”.concat(getAddress()));
return person;
}

}

// tartgetClass
@Data
public class Person {
private Long id;
private String name;
}

书写测试用例:

@Test
public void test4() {
System.out.println(“----------------ObjectToObjectConverter---------------”);
ConditionalGenericConverter converter = new ObjectToObjectConverter();

Customer customer = new Customer();
customer.setId(1L);
customer.setAddress(“Peking”);

Object convert = converter.convert(customer, TypeDescriptor.forObject(customer), TypeDescriptor.valueOf(Person.class));
System.out.println(convert);

// ConversionService方式(实际使用方式)
ConversionService conversionService = new DefaultConversionService();
Person person = conversionService.convert(customer, Person.class);
System.out.println(person);
}

运行程序,输出:

---------------ObjectToObjectConverter---------------
Person(id=1, name=YourBatman-Peking)
Person(id=1, name=YourBatman-Peking)

  • 静态工厂方法

// sourceClass
@Data
public class Customer {
private Long id;
private String address;
}

// targetClass
@Data
public class Person {

private Long id;
private String name;

/**

  • 方法名称可以是:valueOf、of、from
    */
    public static Person valueOf(Customer customer) {
    Person person = new Person();
    person.setId(customer.getId());
    person.setName(“YourBatman-”.concat(customer.getAddress()));
    return person;
    }
    }

测试用例完全同上,再次运行输出:

----------------ObjectToObjectConverter---------------
Person(id=1, name=YourBatman-Peking)
Person(id=1, name=YourBatman-Peking)

方法名可以为valueOf、of、from任意一种,这种命名方式几乎是业界不成文的规矩,所以遵守起来也会比较容易。但是:建议还是注释写好,防止别人重命名而导致转换生效。

  • 构造器

基本同静态工厂方法示例,略

使用场景

基于本转换器可以完成任意对象 -> 任意对象的转换,只需要遵循方法名/构造器默认的一切约定即可,在我们平时开发书写转换层时是非常有帮助的,借助ConversionService可以解决这一类问题。

对于Object -> Object的转换,另外一种方式是自定义Converter<S,T>,然后注册到注册中心。至于到底选哪种合适,这就看具体应用场景喽,本文只是多给你一种选择

IdToEntityConverter

Id(S) --> Entity(T)。通过调用静态查找方法将实体ID兑换为实体对象。Entity里的该查找方法需要满足如下条件find[EntityName]([IdType])

  1. 必须是static静态方法
  2. 方法名必须为find + entityName。如Person类的话,那么方法名叫findPerson
  3. 方法参数列表必须为1个
  4. 返回值类型必须是Entity类型

说明:此方法可以不必是public,但建议用public。这样即使JVM的Security安全级别开启也能够正常访问

支持的转换Pair如下:ID和Entity都可以是任意类型,能转换就成

@Override
public Set getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(Object.class, Object.class));
}

判断是否能执行准换的条件是:存在符合条件的find方法,且source可以转换为ID类型(注意source能转换成id类型就成,并非目标类型哦)

@Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
Method finder = getFinder(targetType.getType());
return (finder != null
&& this.conversionService.canConvert(sourceType, TypeDescriptor.valueOf(finder.getParameterTypes()[0])));
}

根据ID定位到Entity实体对象简直太太太常用了,运用好此转换器的提供的能力,或许能让你事半功倍,大大减少重复代码,写出更优雅、更简洁、更易于维护的代码。

代码示例

Entity实体:准备好符合条件的findXXX方法

@Data
public class Person {

private Long id;
private String name;

/**

  • 根据ID定位一个Person实例
    */
    public static Person findPerson(Long id) {
    // 一般根据id从数据库查,本处通过new来模拟
    Person person = new Person();
    person.setId(id);
    person.setName(“YourBatman-byFindPerson”);
    return person;
    }

}

应用IdToEntityConverter,书写示例代码:

Test
public void test() {
System.out.println(“----------------IdToEntityConverter---------------”);
ConditionalGenericConverter converter = new IdToEntityConverter(new DefaultConversionService());

TypeDescriptor sourceTypeDesp = TypeDescriptor.valueOf(String.class);
TypeDescriptor targetTypeDesp = TypeDescriptor.valueOf(Person.class);
boolean matches = converter.matches(sourceTypeDesp, targetTypeDesp);
System.out.println(“是否能够转换:” + matches);

// 执行转换
Object convert = converter.convert(“1”, sourceTypeDesp, targetTypeDesp);
System.out.println(convert);
}

运行程序,正常输出:

----------------IdToEntityConverter---------------
是否能够转换:true
Person(id=1, name=YourBatman-byFindPerson)

示例效果为:传入字符串类型的“1”,就能返回得到一个Person实例。可以看到,我们传入的是字符串类型的的1,而方法入参id类型实际为Long类型,但因为它们能完成String -> Long转换,因此最终还是能够得到一个Entity实例的。

使用场景

这个使用场景就比较多了,需要使用到findById()的地方都可以通过它来代替掉。如:

Controller层:

@GetMapping(“/ids/{id}”)
public Object getById(@PathVariable Person id) {
return id;
}

@GetMapping(“/ids”)
public Object getById(@RequestParam Person id) {
return id;
}

Tips:在Controller层这么写我并不建议,因为语义上没有对齐,势必在代码书写过程中带来一定的麻烦。

Service层:

@Autowired
private ConversionService conversionService;

public Object findById(String id){
Person person = conversionService.convert(id, Person.class);

return person;

言尽于此,完结

无论是一个初级的 coder,高级的程序员,还是顶级的系统架构师,应该都有深刻的领会到设计模式的重要性。

  • 第一,设计模式能让专业人之间交流方便,如下:

程序员A:这里我用了XXX设计模式

程序员B:那我大致了解你程序的设计思路了

  • 第二,易维护

项目经理:今天客户有这样一个需求…

程序员:明白了,这里我使用了XXX设计模式,所以改起来很快

  • 第三,设计模式是编程经验的总结

程序员A:B,你怎么想到要这样去构建你的代码

程序员B:在我学习了XXX设计模式之后,好像自然而然就感觉这样写能避免一些问题

  • 第四,学习设计模式并不是必须的

程序员A:B,你这段代码使用的是XXX设计模式对吗?

程序员B:不好意思,我没有学习过设计模式,但是我的经验告诉我是这样写的

image

从设计思想解读开源框架,一步一步到Spring、Spring5、SpringMVC、MyBatis等源码解读,我都已收集整理全套,篇幅有限,这块只是详细的解说了23种设计模式,整理的文件如下图一览无余!

image

搜集费时费力,能看到此处的都是真爱!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

5611212203)]

从设计思想解读开源框架,一步一步到Spring、Spring5、SpringMVC、MyBatis等源码解读,我都已收集整理全套,篇幅有限,这块只是详细的解说了23种设计模式,整理的文件如下图一览无余!

[外链图片转存中…(img-rnjKuFXp-1715611212203)]

搜集费时费力,能看到此处的都是真爱!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 26
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值