电话面。这一面,项目为主,基础知识为辅,重点是需要将自己做的项目用比较清楚的话语表述清楚,让面试官能够最短时间了解到你做的项目,同时切忌注意,自己不了解不深入的知识点尽量不要提及,这是大忌。
- 当将业务水平分库后,转账业务如何保证事务的一致性?
这是典型的分布式事务,理论有 CAP 「C (一致性),A (可用性),P (分区容错性),只能选择 AP or CP」,BASE「Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent (最终一致性)三个短语的缩写。是对CAP中AP的一个扩展」 常见的分布式解决方案:
- 2PC,两阶段提交,事务管理器来协调,全部okay了才okay,这样效率很低,因为是如果没成功就一直阻塞。
- TCC(Try-Confirm-Cancel)最大努力交付,在更新多个资源时,将多个资源的提交尽量延后到最后一刻处理,这样的话,如果业务流程出现问题,则所有的资源更新都可以回滚,事务仍然保持一致。唯一可能出现问题的情况是在提交多个资源时发生了系统问题,比如网络问题等,但是这种情况是非常罕见的,一旦出现这种情况,就需要进行实时补偿,将已提交的事务进行回滚。
- 事务补偿机制。在数据库分库分表后,如果涉及的多个更新操作在某一个数据库范围内完成,则可以使用数据库内的本地事务保证一致性;对于跨库的多个操作,可通过补偿和重试,使其在一定的时间窗口内完成操作,这样就可以实现事务的最终一致性,突破事务遇到问题就滚回的传统思路。
参考:https://juejin.im/post/5b5a0bf9f265da0f6523913b#heading-16 https://cloud.tencent.com/developer/news/200316 说实话…还没看太懂…
- zookeeper中的两阶段提交是怎么去做的?
CAP理论中,zookeeper就是CP,放弃可用性,追求一致性和分区容错性,追求的是强一致。
- 在项目中假如一级调度器挂了,怎么处理?
我说没处理…正常应该是类似于 Mysql 一样有个主备切换的机制。
- SpringBoot中的 AOP 分为几类,
AOP 主要是两种方式,一种是直接通过 JDK 动态代理,一种是通过cglib。 先来回顾一下 Spring 中 AOP 的流程:
- 代理的创建。
注意:创建代理对象时,同时会创建一个外层拦截器,这个拦截器就是 Spring 内核的拦截器。用于控制整个 AOP 的流程。
- 需要创建代理工厂,代理工厂需要 3 个重要的信息:拦截器数组,目标对象接口数组,目标对象。
- 创建代理工厂时,默认会在拦截器数组尾部再增加一个默认拦截器 —— 用于最终的调用目标方法。
- 当调用 getProxy 方法的时候,会根据接口数量大余 0 条件返回一个代理对象(JDK or Cglib)。
- 代理的调用
- 当对代理对象进行调用时,就会触发外层拦截器。
- 外层拦截器根据代理配置信息,创建内层拦截器链。创建的过程中,会根据表达式判断当前拦截是否匹配这个拦截器。而这个拦截器链设计模式就是职责链模式。
- 当整个链条执行到最后时,就会触发创建代理时那个尾部的默认拦截器,从而调用目标方法。最后返回。
AOPAOP过程[1] 参考:https://www.jianshu.com/p/e18fd44964eb
- 讲讲 cglib 如何使用并实现的?
动态代理再熟悉不过了,是只能代理接口,cglib现在我们来具体看一下。 我们知道,动态代理是代理类实现被代理类的接口,而cglib则是代理类继承被代理类,也就是子类增强父类的手段。cglib其实也就是字节码增强类库。 具体如何使用 cglib:https://zhuanlan.zhihu.com/p/37886319
- 动态代理和cglib的区别?
- 一个是代理类实现接口,一个是代理类继承类,我觉得差不太多。
- 动态代理用到的接口有 InnvocationHandler,通过实现其 invoke 方法增强方法,并且通过 Proxy.newInstace() 实现代理过程。而cglib则使用 MethodInterceptor 接口,通过实现其 intercept() 方法增强方法,并且通过 Enhancer.create() 方法实现代理过程。
下面我放一下自己写的动态代理和 cglib 的实现
代码语言:javascript复制// 动态代理,总共有四个类
// 1. 接口
package AOP.Proxy;
public interface Person {
void play();
void dance();
}
// 2. 实现类,也就是要被代理的类
package AOP.Proxy;
public class Universities implements Person {
@Override
public void play() {
System.out.println("I like play computer");
}
@Override
public void dance() {
System.out.println("I like dance");
}
}
// 3. 实现 InnocationHandler 接口,完成代理的任务
package AOP.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Dynamic implements InvocationHandler {
// 整容的人是谁,我得知道
private Object obj;
public Dynamic(Object obj){
this.obj = obj;
}
// 整容的过程
public Object myDynamic(){
return Proxy.newProxyInstance(this.obj.getClass().getClassLoader(),this.obj.getClass().getInterfaces(),this);
}
// 整容的地方交代清楚
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始为" method.getName() "方法进行代理");
Object result = method.invoke(obj,args);
System.out.println("结束" method.getName() "方法的代理");
return result;
}
}
// 4. 测试类
// 这里我采用了两种方法测试,一种是直接在 test 类中写出整容的过程
// 另外一种是直接调用我在 Dynamic 已经包装好整容过程的方法,建议使用这种
// 因为这样代码就少了,下面 cglib 就是采用方法二哈
package AOP.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class test {
public static void main(String[] args) {
// 相当于做生意的过程,比如我去医院整容,首先我要确保我有钱(也就是有接口)
Universities person = new Universities();
// 其次,我得去医院找到相应的医生,也就是把我想代理的对象,即我本人,告知给医生
// 医生肯定得有能整容的技术,那就是得继承 InvocationHandler
InvocationHandler dynamic_person = new Dynamic(person);
// 进行交易的过程,一手交钱一手交换,医生拿到钱,会返回一个有钱的处理好的美女,也就是代理完成了
// 这里必须强调代理返回的是 接口对象,也就是医生只会对有钱人进行代理,没钱的代理就失败了
// 如果最开始我没钱,我就去找医生了,那在这一步交易的过程就会出错,因为医生只会处理有钱人,并且返回有钱人的代理好的对象
// 有钱人得到代理后的对象,就可以为所欲为了
Person pp = (Person) Proxy.newProxyInstance(person.getClass().getClassLoader(),person.getClass().getInterfaces(),dynamic_person);
pp.dance();
System.out.println("-----------");
Person pp2 = (Person) new Dynamic(person).myDynamic();
pp2.dance();
}
}
cglib 实现
代码语言:javascript复制// cglib 实现
//1. 导包,在pom.xml 导包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.0</version>
</dependency>
// 2. 需要被代理的类
package AOP.Cglib;
public class SomeService {
public String doFirst() {
System.out.println("执行doFirst()方法");
return "abc";
}
public void doSecond() {
System.out.println("doSecond()方法");
}
}
// 3. 进行代理过程
package AOP.Cglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibFactory implements MethodInterceptor {
private Object object;
public CglibFactory(Object object) {
this.object = object;
}
public Object myCglibCreator() {
Enhancer enhancer = new Enhancer();
//将目标类设置为父类,cglib动态代理增强的原理就是子类增强父类,cglib不能增强目标类为final的类
//因为final类不能有子类
enhancer.setSuperclass(this.object.getClass());
//设置回调接口,这里的MethodInterceptor实现类回调接口,而我们又实现了MethodInterceptor,其实
//这里的回调接口就是本类对象,调用的方法其实就是intercept()方法
enhancer.setCallback(this);
//create()方法用于创建cglib动态代理对象
return enhancer.create();
}
//回调接口的方法
//回调接口的方法执行的条件是:代理对象执行目标方法时会调用回调接口的方法
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object result = methodProxy.invokeSuper(o, objects);
//这里实现将返回值字符串变为大写的逻辑
if(result != null) {
result = ((String) result).toUpperCase();
}
return result;
}
}
// 4. 测试
package AOP.Cglib;
public class Test {
public static void main(String[] args) {
SomeService target = new SomeService();
SomeService proxy = (SomeService) new CglibFactory(target).myCglibCreator();
String result = proxy.doFirst();
System.out.println(result);
proxy.doSecond();
}
}
- Spring中 Ioc 和 DI 讲一下?
参考:https://juejin.im/post/5df5bab0e51d45582427104e https://www.jianshu.com/p/17b66e6390fd
IoC(Inversion of Control 控制反转):是一种面向对象编程中的一种设计原则,用来减低计算机代码之间的耦合度。其基本思想是:借助于“第三方”实现具有依赖关系的对象之间的解耦。 DI(Dependence Injection 依赖注入):将实例变量传入到一个对象中去(Dependency injection means giving an object its instance variables)。 也就是说 DI 是 Ioc 的 实现,Ioc 是一种设计原则。 Spring 作者 Rod Johnson 设计了两个接口用以表示容器。
- BeanFactory
- ApplicationContext
BeanFactory 粗暴简单,可以理解为就是个 HashMap,Key 是 BeanName,Value 是 Bean 实例。通常只提供注册(put),获取(get)这两个功能。我们可以称之为 “低级容器”。 ApplicationContext 可以称之为 “高级容器”。因为他比 BeanFactory 多了更多的功能。他继承了多个接口。因此具备了更多的功能。例如资源的获取,支持多种消息(例如 JSP tag 的支持),对 BeanFactory 多了工具级别的支持等待。所以你看他的名字,已经不是 BeanFactory 之类的工厂了,而是 “应用上下文”, 代表着整个大容器的所有功能。该接口定义了一个 refresh 方法,此方法是所有阅读 Spring 源码的人的最熟悉的方法,用于刷新整个容器,即重新加载/刷新所有的 bean。 Ioc 的过程: a. 加载配置文件,解析成 BeanDefinition 放在 Map 里。 b. 调用 getBean 的时候,从 BeanDefinition 所属的 Map 里,拿出 Class 对象进行实例化,同时,如果有依赖关系,将递归调用 getBean 方法 —— 完成依赖注入。
- ORM框架有哪些?有什么好处?什么是mysql注入?$ 和 # 有什么区别
JPA是orm框架标准,主流的orm框架都实现了这个标准。 MyBatis没有实现JPA,他和orm框架的设计思路完全不一样。MyBatis是拥抱sql,而orm则更靠近面向对象,不建议写sql,实在要写推荐你写hql代替。 Mybatis是sql mapping框架而不是orm框架,当然orm和Mybatis都是持久层框架。 所以说hibernate是典型的ORM框架,好处就是我们可以用面向对象的思想去对数据库进行操作,我觉得主要就是比较省代码,解决面向对象的设计方式和关系型数据库之间的关联,Java主要面向对象设计,因此在分析业务的时候会以对象的角度来看待问题。然而数据库是关系型的,对于Java程序员而言是不符合面向对象设计的,因此才会出现ORM这种东西。有了ORM,Java开发人员在整个代码设计都将遵循对象的思维模式,这就是好处。 mysql注入:参数进行转义与过滤 如何解决:使用 Prepare Statement $ 和 # 的区别: Sql: delete from student where name=${name} 假如 name = jerome OR 1 = 1 在 Mybatis 中,如果写 ${name},那就是直接将 name 拼接到 sql 中,结局就是全删; 如果写 #{name},则 sql 变成 delete from student where name= ’jerome OR 1 = 1‘,这样只有name = jerome OR 1 = 1 的才会删除,否则不会删除任何东西。
阿里三面
从这一面开始面试官就是阿里 P9 了,所以说实话压力还是非常大的,这一面还是跟前面一样是电话面。这一面基本上没有问任何基础,全部在讲项目,建议大家一定要有一个讲的很溜的项目,我因为提及了一下毕设,然后就被一直抓着问,而自己本身其实是没有好好准备这个项目的,所以有些问题竟然被问了后,答得不是特别理想。但是还好,答得也没有很差,后面的项目介绍的还是让面试官很满意的。
介绍完后,面试官可能觉得才半小时,于是就问了几个问题,只是探寻一下我知识的广度吧,全部没有深入。
例如:
- 了解 tomcat 吗?「当然了这是我在讲双亲委派模型引申出来的」
- 用过 nginx 吗?
- spring 中用过吗?
这些,我因为说不太了解 or 用过没深入,所以面试官也就索性没有问下去。
最后,花了10分钟介绍了下自己的部门,然后就到了反转环节了,我觉得大家可以好好抓住反转环节,因为我们往往可以从这个环节探到面试官对本次面试的看法,比如,我问的第一个问题就是:
”我觉得今天的表现不是很好,第一个项目没讲的非常清楚,您后面问的几个知识点我也不太会,没深入了解过。“
很让我意外的是,面试官给我的回答让我备受鼓舞:
”前面可能是你太着急太紧张了,所以讲的不是很好,但是后面讲的很清楚了,也能听得出来全是自己用心做了的,我也听明白了,所以不用担心,至于后面的知识点不会也非常正常,你还年轻的很,不会是非常正常的,你要都会了我还需要在这吗?你还年轻,是非常有潜力的。“
至此,开始期待四面。