面试
- 1. spring
- Spring AOP的具体实现
- 核心概念分别指的是什么?
- 基于注解的切面实现主要包括以下几个步骤:
- 两个切面,它们之间的顺序是怎么控制的
- springmvc的工作流程
- 设计模式
- 原则
- Spring 框架中用到了哪些设计模式?
- 2. Java-锁
- 2.1锁的分类
- 可重入锁和不可重入锁
- 乐观锁和悲观锁
- 公平锁和非公平锁
- 互斥锁和共享锁
- 2.2 Java中synchronized 和 ReentrantLock 有什么不同?
- 2.3 ThreadLocal
- 3.JVM调优
1. spring
Spring AOP的具体实现
AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
Spring AOP 就是基于动态代理的,
有实现接口的对象,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象,
而对于没有实现接口的对象, Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理
开发有两种方式,XML 和注解
核心概念分别指的是什么?
Spring的AOP是对一个类的方法在不进行任何修改的前提下实现增强,这些方法我们给起了一个名字叫连接点
需要增强的方法我们给起了一个名字叫切入点
存放共性功能的方法,我们给起了个名字叫通知
通知和切入点之间的关系描述,我们给起了个名字叫切面
方法不能独立存在需要被写在一个类中,这个类我们也给起了个名字叫通知类
基于注解的切面实现主要包括以下几个步骤:
- 定义切面类:在切面类上使用 @Aspect 注解来标识该类为切面类,同时在该类中定义切点和通知等信息。
- 定义切点:在切面类中使用 @Pointcut 注解来定义切点,切点是一个表达式,用于描述哪些连接点应该被拦截。
- 定义通知:在切面类中定义通知方法,通知方法使用 @Before、@After、@AfterReturning、@AfterThrowing 或@Around 等注解来标识通知类型,同时可以使用 JoinPoint 参数来获取连接点的信息。
- 在Spring配置类中开启AOP支持。@EnableAspectJAutoProxy
两个切面,它们之间的顺序是怎么控制的
(1) 通常使用@Order 注解直接定义切面顺序
(2) 实现Ordered 接口重写 getOrder 方法。返回值越小优先级越高
springmvc的工作流程
执行前;当一个请求发来时先进服务器(Tomcat),在服务器中会有拦截器,过滤器啊,等这些功能走完之后,才真正的进入了框架中。
1.用户发来一个请求,首先进入的是前端控制器DispatcherServlet
2.前端控制器将(DispacherServlet)用户发来的请求发送给处理器映射器(HandlerMapping)
3.处理器映射器根据前端控制器发来的用户的请求找到对应符合的控制器(Handler),并且将其封装成处理器执行链,返回给前端控制器。
4.前端控制器DispatcherServlet调用处理适配器(HandlerAdapter)来调用的具体的控制器
5.控制器执行完成后,会返回一个ModelAndView对象给处理器适配器
6.处理器适配器将返回来的ModelAndView对象返回给前端控制器(到这里所有的业务处理过程就要完了,接下就是将结果以页面的的形式相应给用户)
7.前端控制器将返回回来的ModelAndView对象交给视图解析器(ViewResolver),视图解析器根据传过里的View对象解析成对应的页面对象,然后将页面对象和Model对象返回给前端控制器。
8.前端控制器再将返回回来的对象交给视图(View),视图根据传过来的Model对象再一次的对页面进行渲染,然后在返回给前端控制器。
9.前端控制器将完成的结果响应给浏览器,然后浏览器在展现给用户。
设计模式
创建型模式,5 (将对象的创建与使用分离)⼯⼚/抽象⼯⼚/单例/建造者/原型模式
结构型模式,7 关注类和对象的组织 适配器/桥接模式/过滤器/组合/装饰器/外观/享元/代理模式
行为型模式,11 关注对象之间的相互交互 任链/命名/解释器/迭代器/中介者/备忘录/观察者/状态/策略/模板/访问者模式
原则
接一(依)单,开里迪
- 接口隔离原则:将不同功能定义在不同接⼝中实现接⼝隔离
- 依赖倒置原则:程序应该依赖于抽象类或接⼝,⽽不是具体的实现类。
- 单一原则:一个类只负责一项职责
- 开闭原则:对扩展开放,对修改关闭。通过扩展来实现变化,而不是通过修改原来的代码来实现变化
- ⾥⽒替换原则:子类除了新增功能外,尽量不要重写父类的方法。
- 迪⽶特原则:每个模块对其他模块都要尽可能少地了解和依赖,降低代码耦合度。
Spring 框架中用到了哪些设计模式?
-
工厂设计模式 : Spring 使用工厂模式通过 BeanFactory、ApplicationContext 创建 bean 对象。
-
单例设计模式 : Spring 中的 Bean 默认都是单例的。
Spring 中 bean 的默认作用域就是 singleton(单例)的 ,这一类对象只能有一个实例
对于频繁使用的对象,可以省略创建对象所花费的时间
由于 new 操作的次数减少,因而对系统内存的使用频率也会降低, -
代理设计模式 : Spring AOP 功能的实现。
-
模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
-
包装器设计模式 - : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
-
适配器模式 :Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller。
2. Java-锁
2.1锁的分类
JAVA里面主要有ReentrantLock ,synchronized,Lock三种
可重入锁和不可重入锁
可重入锁:当前线程获取到A锁,在获取之后尝试再次获取A锁是可以直接拿到的
java中提供的synchronized,ReentrantLock,ReentrantReadWriteLock都是可重入锁
不可重入:当前线程获取到A锁,在获取之后尝试再次获取A锁,无法获取到的,因为A锁被当前线
程占用着,需要等待自己释放锁再获取锁。
乐观锁和悲观锁
Java中提供的synchronized,ReentrantLock,ReentrantReadWriteLock都是悲观锁。
乐观锁:乐观锁操作数据时不会上锁,获取不到锁资源,可以再次让CPU调度,重新尝试获取锁资源。适合于读操作多的,乐观思想就是认为:当前环境读数据的多,写数据的少,并发读多,并发写少。
悲观锁一个共享数据加了悲观锁,那线程每次想操作这个数据前都会假设其他线程也可能会操作这个数据,所以每次操作前都会上锁,这样其他线程想操作这个数据拿不到锁只能阻塞了。
公平锁和非公平锁
Java中提供的synchronized只能是非公平锁。
Java中提供的ReentrantLock,ReentrantReadWriteLock可以实现公平锁和非公平锁
公平锁:线程A获取到了锁资源,线程B没有拿到,线程B去排队,线程C来了,锁被A持有,同时线
程B在排队。直接排到B的后面,等待B拿到锁资源或者是B取消后,才可以尝试去竞争锁资源。
非公平锁:线程A获取到了锁资源,线程B没有拿到,线程B去排队,线程C来了,先尝试竞争
互斥锁和共享锁
Java中提供的synchronized、ReentrantLock是互斥锁。
Java中提供的ReentrantReadWriteLock,有互斥锁也有共享锁。
互斥锁:同一时间点,只会有一个线程持有者当前互斥锁。
共享锁:同一时间点,当前共享锁可以被多个线程同时持有。
知乎连接讲得很好:
2.2 Java中synchronized 和 ReentrantLock 有什么不同?
(1)功能区别
Synchronized是java语言的关键字,
ReentrantLock 是JDK1.5之后提供的API层面的互斥锁,需要lock和unlock()方法配合try/finally代码块来完成。
相对于 synchronized 它具备如下特点
不同:
- 可中断 :持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待
- 可以设置超时时间
- 锁的细粒度和灵活性:ReentrantLock强于Synchronized;
- 可以设置为公平锁
- 支持多个条件变量
ReentrantLock提供了一个Condition(条件)类,用来实现分组唤醒需要唤醒的线程们,而不是像Synchronized要么随机唤醒一个线程,要么唤醒全部线程。
相同:
- 与 synchronized 一样,都支持可重入
可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁 - 都是阻塞式的同步,也就是说一个线程获得了对象锁,进入代码块,其它访问该同步块的线程都必须阻塞在同步代码块外面等待
2.3 ThreadLocal
文本链接:
为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题,不同的线程之间不会相互干扰
线程隔离:
- 每个THread线程内部都有一个Map(ThreadLocalMap)
- Map里面存储的ThreadLocal对象(key)和线程==变量副本(Value)==也就是存储的值
- Thread内部的Map是由THreadLocal负责向map获取和设置线程变量值
4.别的线程并不能获取当前线程的副本值, 形成了副本的隔离,互不干扰.