独家面经总结,超级精彩
本人面试腾讯,阿里,百度等企业总结下来的面试经历,都是真实的,分享给大家!
Java面试准备
准确的说这里又分为两部分:
- Java刷题
- 算法刷题
Java刷题:此份文档详细记录了千道面试题与详解;
}
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
// 1.拿到节点ele的命名空间,例如常见的:
// 节点对应命名空间: http://www.springframework.org/schema/context
// 节点对应命名空间: http://www.springframework.org/schema/aop
String namespaceUri = getNamespaceURI(ele);
// 2.拿到命名空间对应的的handler, 例如:http://www.springframework.org/schema/context 对应 ContextNameSpaceHandler
// 2.1 getNamespaceHandlerResolver: 拿到namespaceHandlerResolver
// 2.2 resolve: 使用namespaceHandlerResolver解析namespaceUri, 拿到namespaceUri对应的NamespaceHandler
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error(“Unable to locate Spring NamespaceHandler for XML schema namespace [” + namespaceUri + “]”, ele);
return null;
}
// 3.使用拿到的handler解析节点(ParserContext用于存放解析需要的一些上下文信息)
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
2.1 在Spring IoC:obtainFreshBeanFactory详解 中的代码块10,我们构建 XmlReaderContext 时会创建一个默认的DefaultNamespaceHandlerResolver,这边拿到的就是当时创建的 DefaultNamespaceHandlerResolver 对象,例如下图。
2.2 使用 DefaultNamespaceHandlerResolver 解析 namespaceUri,拿到对应的 handler,见代码块1详解。
3.使用拿到的 handler 解析 ele 节点,见代码块5详解。
代码块1:DefaultNamespaceHandlerResolver.resolve
@Override
public NamespaceHandler resolve(String namespaceUri) {
// 1.拿到配置文件的所有命名空间和对应的handler
// 例如:“http://www.springframework.org/schema/aop” -> “org.springframework.aop.config.AopNamespaceHandler”
Map<String, Object> handlerMappings = getHandlerMappings();
// 2.拿到当前命名空间对应的handler (可能是handler的className,也可能是已经实例化的handler)
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
// 2.1 如果不存在namespaceUri对应的handler,则返回null
return null;
}
else if (handlerOrClassName instanceof NamespaceHandler) {
// 2.2 如果是已经实例化的handler,则直接强转返回
return (NamespaceHandler) handlerOrClassName;
}
else {
// 2.3 如果是handler的className
String className = (String) handlerOrClassName;
try {
// 2.3.1 根据className,使用类加载器拿到该类
Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
// 2.3.2 校验是否是继承自NamespaceHandler(所有的handler都继承自NamespaceHandler)
if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
throw new FatalBeanException(“Class [” + className + “] for namespace [” + namespaceUri +
“] does not implement the [” + NamespaceHandler.class.getName() + “] interface”);
}
// 2.3.3 使用无参构造函数实例化handlerClass类
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
// 2.3.4 调用handler类的初始化方法(将命名空间下的节点名和对应的解析器注册到parsers缓存中)
namespaceHandler.init();
// 2.3.5 将实例化的handler放到缓存,替换原来的className
// 原来为: namespaceUri -> handler的className,会被覆盖成: namespaceUri -> 实例化的handler
handlerMappings.put(namespaceUri, namespaceHandler);
// 返回实例化后的handler对象
return namespaceHandler;
}
catch (ClassNotFoundException ex) {
throw new FatalBeanException(“NamespaceHandler class [” + className + “] for namespace [” +
namespaceUri + “] not found”, ex);
}
catch (LinkageError err) {
throw new FatalBeanException(“Invalid NamespaceHandler class [” + className + “] for namespace [” +
namespaceUri + “]: problem with handler class file or dependent class”, err);
}
}
}
1.拿到配置文件的所有命名空间和对应的 handler,见代码块2详解。
2.3.4 调用 handler 类的初始化方法,见代码块3详解。
代码块2:getHandlerMappings
private Map<String, Object> getHandlerMappings() {
// 1.如果handlerMappings已经加载过,则直接返回
if (this.handlerMappings == null) {
synchronized (this) {
// 2.如果handlerMappings还没加载过,则进行加载
if (this.handlerMappings == null) {
try {
// 2.1 使用给定的类加载器从指定的类路径资源加载所有属性
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isDebugEnabled()) {
logger.debug("Loaded NamespaceHandler mappings: " + mappings);
}
Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>(mappings.size());
// 2.2 将Properties转换成Map, mappings -> handlerMappings
CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
// 2.3 将加载到的所有命名空间映射放到缓存
this.handlerMappings = handlerMappings;
}
catch (IOException ex) {
throw new IllegalStateException(
“Unable to load NamespaceHandler mappings from location [” + this.handlerMappingsLocation + “]”, ex);
}
}
}
}
return this.handlerMappings;
}
2.1 通过Spring IoC:obtainFreshBeanFactory详解 中的代码块10,我们知道 handlerMappingsLocation 的默认值为 “META-INF/spring.handlers”,因此在这边会使用指定的 classLoader 从所有类路径资源(META-INF/spring.handlers)加载所有属性,并使用 Properties 来存放 spring.handlers 文件中的内容(命名空间和 handler 的键值对,例如下图)。
2.2 将 Properties 转成 Map。其中 key 为命名空间,例如:http://www.springframework.org/schema/context;value 为命名空间对应的 handler,例如:org.springframework.context.config.ContextNamespaceHandler,所有的handler都需要自己实现。
2.3 最后将转换后的 handlerMappings 放到缓存。
代码块3:namespaceHandler#init
点击该方法,我们可以看到有很多个实现类,分别对应了不同的命名空间 Handler。
我们拿最常见的命名空间:http://www.springframework.org/schema/context 来举例,对应的 handler 为 ContextNamespaceHandler。
内容很简单,就是给 context 命名空间下的不同节点指定了不同的 BeanDefinition 解析器,并将节点名和对应的解析器注册到缓存中,见代码块4详解。例如,最常用的 component-scan 对应 ComponentScanBeanDefinitionParser 。
代码块4:registerBeanDefinitionParser
private final Map<String, BeanDefinitionParser> parsers =
new HashMap<String, BeanDefinitionParser>();
// … …
protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
this.parsers.put(elementName, parser);
}
将节点名和对应的解析器放到 parsers 缓存中。
代码块5:parse
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 1.findParserForElement: 给element寻找对应的BeanDefinition解析器
// 2.使用BeanDefinition解析器解析element节点
return findParserForElement(element, parserContext).parse(element, parserContext);
}
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
// 1.1 拿到节点的localName,例如:annotation-config、component-scan
String localName = parserContext.getDelegate().getLocalName(element);
// 1.2 从parsers缓存中,拿到localName对应的解析器, 例如: component-scan -> ComponentScanBeanDefinitionParser
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
“Cannot locate BeanDefinitionParser for element [” + localName + “]”, element);
}
return parser;
}
1.从 parsers 缓存中(见代码块4),查找 element 节点对应的 BeanDefinition 解析器。parsers缓存如下:
2.使用拿到的 BeanDefinition 解析器解析 element 节点。例如:使用 ComponentScanBeanDefinitionParser 解析器解析 component-scan 节点。
到此为此,自定义命名空间的解析操作基本完成,只剩下具体解析器解析对应节点的内容没有写。
自定义命名空间节点常见的有:<context:component-scan />、<context:annotation-config />、aop:aspectj-autoproxy/、<tx:annotation-driven /> 等,下一篇文章将对最常见的 <context:component-scan /> 进行解析。
自定义一个命名空间
=========
看完上面的流程,我们可以参考 Spring 的自定义命名空间,尝试写一个自己的命名空间。
1.新建一个空项目,在 resources/META-INF 目录下新建一个 spring.handler 文件,内容如下:
http://open.joonwhee.com/schema/dog=com.joonwhee.open.annotation.spring.DogNamespaceHandler
文件内容为一个键值对。
key 为自定义命名空间:http://open.joonwhee.com/schema/dog,
value 为自定义命名空间对应的 NameSpaceHandler :com.joonwhee.open.annotation.spring.DogNamespaceHandler
2.在 resources/META-INF 目录下新建一个 spring.schemas 文件,内容如下:
http://open.joonwhee.com/schema/dog/dog-1.0.xsd=./com/joonwhee/open/dog/config/dog-1.0.xsd
文件内容为一个键值对。
key 为 schema 的 Url:http://open.joonwhee.com/schema/dog/dog-1.0.xsd,
value 为本地 schema 路径:./com/joonwhee/open/dog/config/dog-1.0.xsd
3.对应第1步 spring.handler 内容中的 value,在 com.joonwhee.open.annotation.spring 目录下新建 DogNamespaceHandler.java,内容如下:
package com.joonwhee.open.annotation.spring;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
/**
-
@author joonwhee
-
@date 2019/2/16
*/
public class DogNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
// 注册一个自定义的解析器, 用于解析命名空间下的annotation节点
registerBeanDefinitionParser(“annotation”, new DogAnnotationDefinitionParser());
}
}
新建 DogAnnotationDefinitionParser.java,内容如下:
package com.joonwhee.open.annotation.spring;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;
/**
-
@author joonwhee
-
@date 2019/2/16
*/
public class DogAnnotationDefinitionParser implements BeanDefinitionParser {
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClass(DogBean.class);
MutablePropertyValues mutablePropertyValues = beanDefinition.getPropertyValues();
// 添加name属性
if (element.hasAttribute(“name”)) {
mutablePropertyValues.addPropertyValue(“name”, element.getAttribute(“name”));
}
// 添加package属性
if (element.hasAttribute(“package”)) {
mutablePropertyValues.addPropertyValue(“package”, element.getAttribute(“package”));
}
String id = element.getAttribute(“id”);
// 拿到注册表, 注册BeanDefinition
parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
return beanDefinition;
}
}
其中用到的 DogBean 代码如下:
package com.joonwhee.open.annotation.spring;
/**
-
@author joonwhee
-
@date 2019/2/16
*/
public class DogBean {
private String name;
private String basePackage;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPackage() {
return basePackage;
}
public void setPackage(String basePackage) {
this.basePackage = basePackage;
总结
虽然面试套路众多,但对于技术面试来说,主要还是考察一个人的技术能力和沟通能力。不同类型的面试官根据自身的理解问的问题也不尽相同,没有规律可循。
上面提到的关于这些JAVA基础、三大框架、项目经验、并发编程、JVM及调优、网络、设计模式、spring+mybatis源码解读、Mysql调优、分布式监控、消息队列、分布式存储等等面试题笔记及资料
有些面试官喜欢问自己擅长的问题,比如在实际编程中遇到的或者他自己一直在琢磨的这方面的问题,还有些面试官,尤其是大厂的比如 BAT 的面试官喜欢问面试者认为自己擅长的,然后通过提问的方式深挖细节,刨根到底。
(String basePackage) {
this.basePackage = basePackage;
总结
虽然面试套路众多,但对于技术面试来说,主要还是考察一个人的技术能力和沟通能力。不同类型的面试官根据自身的理解问的问题也不尽相同,没有规律可循。
[外链图片转存中…(img-r4b3NLBF-1715648520634)]
[外链图片转存中…(img-EI1Wa16Z-1715648520634)]
上面提到的关于这些JAVA基础、三大框架、项目经验、并发编程、JVM及调优、网络、设计模式、spring+mybatis源码解读、Mysql调优、分布式监控、消息队列、分布式存储等等面试题笔记及资料
有些面试官喜欢问自己擅长的问题,比如在实际编程中遇到的或者他自己一直在琢磨的这方面的问题,还有些面试官,尤其是大厂的比如 BAT 的面试官喜欢问面试者认为自己擅长的,然后通过提问的方式深挖细节,刨根到底。