超多超全的Java专业技能面试盘点(一)

389 阅读34分钟

Java 专业技能面试盘点

具备扎实的 Java 基础,深入理解 OOP 编程思想,具有良好的编码习惯

1、Java 面向对象编程的三大特性

1.1 封装

封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性和方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有供外界访问的方法,那么这个类也就没有什么意义了。

1.2 继承

继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或者新的功能,也可以用父类的功能,但不能选择性的继承父类。通过使用继承我们能够非常方便地复用以前的代码。

关于继承如下三点需要记住

  • 子类拥有父类非 private 的属性和方法。
  • 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
  • 子类可以用自己的方式实现父类的方法。

1.3 多态

所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用,在编程时并不确定,而是在程序运行期间才确定。即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。在 Java 中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中的同一方法)

2、面向对象与面向过程

2.1、面向过程

优点:性能比面向对象强,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。

缺点:没有面向对象易维护、易复用、易扩展。

2.2、面向对象

优点:易维护、易复用、易扩展。由于面向对象有封装、继承、多态的三大特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护。

缺点:性能比面向过程低。

3、Java 语言有哪些特点

  • 简单易学;
  • 面向对象(封装、继承、多态);
  • 平台无关性(Java 虚拟机实现平台无关性);
  • 可靠性;
  • 安全性;
  • 支持多线程;
  • 很方便的支持网络编程;
  • 编译与解释并存;

4、Java 和 C++的区别

没学过 C++,不代表面试官也没学过

  • 都是面向对象的语言,都支持封装、继承和多态
  • Java 不提供指针来直接访问内存,程序内存更加安全
  • Java 的类是单继承的,C++支持多重继承;虽然 Java 的类不可以多继承,但是接口可以多继承。
  • Java 有自动内存管理机制,不需要程序员手动释放无用内存。

掌握 Spring、SpringMVC,Mybatis 等开源框架

1、Spring 简介

1.1、Spring 含义

是于 2003 年兴起的一个轻量级的 Java 开发框架,它是为了解决企业应用开发 的复杂性而创建的。Spring 的核心是控制反转(IoC)和面向切面编程(AOP)。

1.2、Spring 有四大特点:

  • 非侵入式:Spring 框架的 API 不会在业务逻辑上出现,即业务逻辑是 POJO
  • 容器:Spring 作为一个容器,可以管理对象的生命周期、对象与对象之间的依赖关系。可以通过配置文件,来定义对象,以及设置与其他对象的依赖关系。
  • IOC:控制反转(Inversion of Control),即调用者不创建被调用者的实例,而是由 Spring 容器创建被调用者,并注入调用者。 不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。
  • 面向切面编程(AOP,Aspect Orient Programming),是一种编程思想,是面向对象编程 OOP 的补充。Spring 允许通过分离应用的业务逻辑与系统级服务(例如日志和事务管理)进行开发。

1.3、Spring 配置文件的加载

1.3.1 ApplicationContext 接口容器

1、配置文件在类路径下

若 Spring 配置文件存放在项目的类路径下,则使用 ClassPathXmlApplicationContext 实现类进行加载。
//获取容器ApplicationContext context =    new ClassPathXmlApplicationContext("applicationContext.xml");

2、配置文件在本地目录中 若 Spring 配置文件存放在本地磁盘目录中,则使用 FileSystemXmlApplicationContext 实 现类进行加载。

//获取容器ApplicationContext context =    new FileSystemXmlApplicationContext("d:/applicationContext.xm1")

3、配置文件在项目根路径下 若 Spring 配置文件存放在项目的根路径下,同样使用 FileSystemXmlApplicationContext 实现类进行加载。

//获取容器ApplicationContext context =    new FileSystemXmlApplicationContext ("applicationContext.xml")
1.3.2 BeanFactory 接口容器

BeanFactory 接口对象也可作为 Spring 容器出现。**BeanFactory 接口是 ApplicationContext 接口的父类。**若要创建 BeanFactory 容器,需要使用其实现类 XmlBeanFactory, 而 Spring 配置文件以资源 Resouce 的形式出现在 XmlBeanFactory 类的构造器参数中。

注:Resouce 是一个接口,具有两个实现类:

  • ClassPathResource:指定类路径下的资源文件
//获取容器:获取类路径下的配置文件BeanFactory factory =    new XmlBeanFactory(new ClassPathResource( "applicationContext.xml" ))
  • FileSystemResouce:指定项目根路径或本地磁盘路径下的资源文件
//获取容器:获取当前项目根路径下的配置文件BeanFactory factory =    new XmlBeanFactory(new FileSystemResource("applicationContext.xml"));
1.3.3 两个接口容器的区别

虽然这两个接口容器所要加载的 Spring 配置文件是同一个文件,但在代码中的这两个容器对象却不是同一个对象,即不是同一个容器:它们对于容器内对象的装配(创建)时机是不同的。

  1. ApplicationContext 容器中对象的装配时机: ApplicationContext 容器,会在容器对象初始化时,将其中的所有对象一次性全部装配好。以后代码中若要使用到这些对象,只需从内存中直接获取即可。执行效率较高。但占用内存。
  2. BeanFactory 容器中对象的装配时机: BeanFactory 容器,对容器中对象的装配与加载采用延迟加载策略,即在第一次调用 getBean()时,才真正装配该对象

2、Spring 作为容器

2.1、Spring Bean 生命周期!

Spring 生命周期简述:

2.1.1、实例化

实例化一个 Bean,也就是我们常说的 new。

2.1.2 、IOC 依赖注入

按照 Spring 上下文对实例化的 Bean 进行配置,也就是 IOC 注入。

2.1.3、setBeanName 实现

如果这个 Bean 已经实现了 BeanNameAware 接口,会调用它实现的 setBeanName(String) 方法,此处传递 的就是 Spring 配置文件中 Bean 的 id 值。

2.1.4、BeanFactoryAware 实现

如果这个 Bean 已经实现了 BeanFactoryAware 接口,会调用它实现的 setBeanFactory,setBeanFactory(BeanFactory)传递的是 Spring 工厂自身(可以用这个方式来获取其它 Bean,只需在 Spring 配置文件中配置一个普通的 Bean 就可以)。

2.1.5、ApplicationContextAware 实现

如果这个 Bean 已经实现了 ApplicationContextAware 接口,会调用 setApplicationContext(ApplicationContext) 方法,传入 Spring 上下文(同样这个方式也 可以实现步骤 4 的内容,但比 4 更好,因为 ApplicationContext 是 BeanFactory 的子接 口,有更多的实现方法)

2.1.6、postProcessBeforeInitialization 接口实现-初始化预处理

如果这个 Bean 关联了 BeanPostProcessor 接口,将会调用 postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor 经常被用作是 Bean 内容的更改,并且由于这个是在 Bean 初始化结束时调用那个的方法,也可以被应 用于内存或缓存技术。

2.1.7、init-method

如果 Bean 在 Spring 配置文件中配置了 init-method 属性会自动调用其配置的初始化方法。

2.1.8、postProcessAfterInitialization

如果这个 Bean 关联了 BeanPostProcessor 接口,将会调用。

2.1.9、postProcessAfterInitialization(Object obj, String s)方法。

注:以上工作完成以后就可以应用这个 Bean 了,那这个 Bean 是一个 Singleton 的,所以一 般情况下我们调用 同一个 id 的 Bean 会是在内容地址相同的实例,当然在 Spring 配置文件中 也可以配置非 Singleton。

2.1.10、Destroy 过期自动清理阶段

当 Bean 不再需要时,会经过清理阶段,如果 Bean 实现了 DisposableBean 这个接口,会调用那个其实现的 destroy()方法;

2.1.11、destroy-method 自配置清理

最后,如果这个 Bean 的 Spring 配置中配置了 destroy-method 属性,会自动调用其配置的 销毁方法。

注:

  • bean 标签有两个重要的属性(init-method 和 destroy-method)。用它们你可以自己定制初始化和注销方法。它们也有相应的注解(@PostConstruct 和@PreDestroy)。 <bean id="" class="" init-method="初始化方法" destroy-method="销毁方法">
2.1.12、Bean 的生命始末

在配置文件的标签中增加如下属性:

  • init-method:指定初始化方法的方法名
  • destroy-method:指定销毁方法的方法名 destroy-method 的执行结果,需要满足两个条件:
    • Bean 为 singleton,即单例。
    • 要确保容器关闭。接口 ApplicationContext 没有 close()方法,但其实现类有。所以,可以将 ApplicationContext 强转为其实现类对象,或直接创建的就是实现类对象。

2.2、容器中 Bean 的作用域

通过 scope 属性,为 Bean 指定特定的作用域。Spring 支持 5 种作用域。

  1. singleton:单态模式。即在整个 Spring 容器中,使用 singleton 定义的 Bean 将是单例的,只有一个实例。默认为单态的。
  2. prototype:原型模式。即每次使用 getBean 方法获取的同一个的实例都是一个新的实例。
  3. request:对于每次 HTTP 请求,都将会产生一个不同的 Bean 实例。
  4. session:对于每个不同的 HTTP session,都将产生一个不同的 Bean 实例。
  5. global session:每个全局的 HTTP session 对应一个 Bean 实例。典型情况下,仅在使用 portlet 集群时有效,多个 Web 应用共享一个 session。一般应用中,global-session 与 session 是等同的。

注意:

  • 对于 scope 的值 request、session 与 global session,只有在 Web 应用中使用 Spring 时,该作用域才有效。
  • 对于 scope 为 singleton 的单例模式,该 Bean 是在容器被创建时即被装配好了。
  • 对于 scope 为 prototype 的原型模式,Bean 实例是在代码中使用该 Bean 实例时才进行装配的
2.2.1、Spring 中的线程安全

Spring 是没有对 bean 的多线程安全问题作出任何保证和措施的。对于每个 bean 的线程安全问题,根本原因是每个 bean 自身的设计。不要在 bean 中声明任何有状态的实例变量或类变量。

如果必须如此的话,那么就使用 ThreadLoad 把变量变为线程私有的如果 bean 的实例变量或者类变量需要在多个线程之间共享,那么就只用使用 synchronized、lock、CAS 等这些实现线程同步的

2.3、Bean 后处理器

Bean 后处理器是一种特殊的 Bean,容器中所有的 Bean 在初始化时,均会自动执行该类的两个方法。由于该 Bean 是由其它 Bean 自动调用执行,不是程序员手工调用,故此 Bean 无须 id 属性。代码中需要自定义 Bean 后处理器类。该类就是实现了接口 BeanPostProcessor 的类。

2.4、标签的 id 属性与 name 属性

一般情况下,命名使用 id 属性,而不使用 name 属性。

在没有 id 属性的情况下,name 属性与 id 属性作用是相同的。

但当中含有一些特殊字符时,就需要使用 name 属性了。

  • id 的命名需要满足 XML 对 ID 属性命名规范:必须以字母开头,可以包含字母、数字、下划线、连字符、句话、冒号。且要求名称在容器中必须唯一。
  • name 则可以包含各种字符,且对名称没有唯一性要求。若名称不唯一,则后面的会覆盖前面的。

2.5、Spring 常用注解

@Component 和 @Bean 的区别是什么?
  1. 作用对象不同: @Component 注解作用于类,而@Bean注解作用于方法。
  2. @Component通常是通过类路径扫描来自动侦测以及自动装配到 Spring 容器中(我们可以使用 @ComponentScan 注解定义要扫描的路径从中找出标识了需要装配的类自动装配到 Spring 的 bean 容器中)。@Bean 注解通常是我们在标有该注解的方法中定义产生这个 bean,@Bean告诉了 Spring 这是某个类的示例,当我需要用它的时候还给我。
  3. @Bean 注解比 Component 注解的自定义性更强,而且很多地方我们只能通过 @Bean 注解来注册 bean。比如当我们引用第三方库中的类需要装配到 Spring容器时,则只能通过 @Bean来实现。

3、SpringIOC

3.1、IoC 简述:

IoC(Inverse of Control:控制反转)是一种设计思想,就是 将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理。 IoC 在其他语言中也有应用,并非 Spirng 特有。 IoC 容器是 Spring 用来实现 IoC 的载体, IoC 容器实际上就是个 Map(key,value),Map 中存放的是各种对象。

将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。 IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。 在实际项目中一个 Service 类可能有几百甚至上千个类作为它的底层,假如我们需要实例化这个 Service,你可能要每次都要搞清这个 Service 所有底层类的构造函数,这可能会把人逼疯。如果利用 IoC 的话,你只需要配置好,然后在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度。

IoC 是一个概念,是一种思想,其实现方式多种多样。当前比较流行的实现方式有两种:依赖注入和依赖查找。依赖注入方式应用更为广泛。

  • 依赖查找:Dependency Lookup,DL,容器提供回调接口和上下文环境给组件,程序代码则需要提供具体的查找方式。比较典型的是依赖于 JNDI 系统的查找。

  • 依赖注入:Dependency Injection,DI,目前最优秀的解耦方式,是指程序运行过程中,若需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部容器,由外部容器创建后传递给程序:

    Spring DI = 工厂 + 反射 + 配置文件

Spring 通过一个配置文件描述 Bean 及 Bean 之间的依赖关系,利用 Java 语言的反射功能实例化 Bean 并建立 Bean 之间的依赖关系。 Spring 的 IoC 容器在完成这些底层工作的基础上,还提供 了 Bean 实例缓存、生命周期 管理、 Bean 实例代理、事件发布、资源装载等高级服务。

3.2、Spring IOC 过程

Spring 启动时读取应用程序提供的 Bean 配置信息,并在 Spring 容器中生成一份相应的 Bean 配 置注册表,然 后根据这张注册表实例化 Bean,装配好 Bean 之间的依赖关系,为上层应用提供准 备就绪的运行环境。其中 Bean 缓存池为 HashMap 实现。

3.3、Spring 依赖注入的四种方式

  • 构造器注入
  • 静态工厂注入
  • 实例工厂

3.4、五种不同方式的自动装配

Spring 装配包括手动装配和自动装配,手动装配是有基于 xml 装配、构造方法、setter 方法等自动装配有五种自动装配的方式,可以用来指导 Spring 容器用自动装配方式来进行依赖注入。

  1. no:默认的方式是不进行自动装配,通过显式设置 ref 属性来进行装配。
  2. byName:通过参数名 自动装配,Spring 容器在配置文件中发现 bean 的 autowire 属性被设 置成 byname,之后容器试图匹配、装配和该 bean 的属性具有相同名字的 bean。
  3. byType:通过参数类型自动装配,Spring 容器在配置文件中发现 bean 的 autowire 属性被 设置成 byType,之后容器试图匹配、装配和该 bean 的属性具有相同类型的 bean。如果有多个 bean 符合条件,则抛出错误。
  4. constructor:这个方式类似于 byType,但是要提供给构造器参数,如果没有确定的带参数的构造器参 数类型,将会抛出异常。
  5. autodetect:首先尝试使用 constructor 来自动装配,如果无法工作,则使用 byType 方式。

3.5、基于注解的 DI

@Component @Repository @Service @Controller @Scope @Value @Autowired @Autowired(required=false)与@Qualifier @Resource 根据 name 属性有无 @PostConstruct 与@PreDestroy @Configuration

4、Spring AOP

AOP(Aspect-Oriented Programming:面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码降低模块间的耦合度,并有利于未来的可拓展性和可维护性

Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用Cglib ,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理。当然也可以使用 AspectJ,Spring AOP 已经集成了 AspectJ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。

使用 AOP 之后我们可以把一些通用功能抽象出来,在需要用到的地方直接使用即可,这样大大简化了代码量。我们需要增加新功能时也方便,这样也提高了系统扩展性。日志功能、事务管理等等场景都用到了 AOP 。

4.1 AOP 主要应用场景

  1. Authentication 权限

  2. Caching 缓存

  3. Context passing 内容传递

  4. Error handling 错误处理

  5. Lazy loading 懒加载

  6. Debugging 调试

  7. logging, tracing, profiling and monitoring 记录跟踪、优化、校准

  8. Performance optimization 性能优化

  9. Persistence 持久化

  10. Resource pooling 资源池

  11. Synchronization 同步

  12. Transactions 事务

4.2、JDK 与 Cglib 的区别

JDK 动态代理只能对实现了接口的类生成代理,而不能针对类. JDK 动态代理主要涉及到 java.lang.reflect 包中的两个类:Proxy 和 InvocationHandler。 InvocationHandler 是 一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类 的代码,动态将横切逻辑和业务逻辑编制 在一起。Proxy 利用 InvocationHandler 动态创建 一个符合某一接口的实例,生成目标类的代理对象。

CGLIB 是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(继承) CGLib 全称为 Code Generation Library,是一个强大的高性能,高质量的代码生成类库, 可以在运行期扩展 Java 类与实现 Java 接口,CGLib 封装了 asm,可以再运行期动态生成新 的 class。和 JDK 动态代理相比较:JDK 创建代理有一个限制,就是只能为接口创建代理实例, 而对于没有通过接口定义业务方法的类,则可以通过 CGLib 创建动态代理。

4.3、Spring 在选择用 JDK 还是 CGLiB 的依据:

(1)当 Bean 实现接口时,Spring 就会用 JDK 的动态代理

(2)当 Bean 没有实现接口时,Spring 使用 CGlib 是实现

(3)可以强制使用 CGlib(在 spring 配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>

4.4、CGlib 比 JDK 快?

  1. 使用 CGLib 实现动态代理,CGLib 底层采用 ASM 字节码生成框架,使用字节码技术生成代理类,比使用 Java 反射效率要高。唯一需要注意的是,CGLib 不能对声明为 final 的方法进行代理,因为 CGLib 原理是动态生成被代理类的子类。
  2. 在对 JDK 动态代理与 CGlib 动态代理的代码实验中看,1W 次执行下,JDK7 及 8 的动态代理性能比 CGlib 要 好 20%左右。

4.5、Spring AOP 和 AspectJ AOP 有什么区别?

Spring AOP 属于运行时增强,而 AspectJ 是编译时增强。 Spring AOP 基于代理(Proxying),而 AspectJ 基于字节码操作(Bytecode Manipulation)。

Spring AOP 已经集成了 AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。AspectJ 相比于 Spring AOP 功能更加强大,但是 Spring AOP 相对来说更简单,

如果我们的切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择 AspectJ ,它比 Spring AOP 快很多。

4.6、AspectJ 基于注解的 AOP 实现

@Before

@AfterReturning

@Around

@AfterThrowing

@After

@Pointcut

5、SpringAOP 与 IOC 的应用?

5.1、Spring 框架中用到了哪些设计模式?

  • 工厂设计模式 : Spring 使用工厂模式通过 BeanFactoryApplicationContext 创建 bean 对象。
  • 代理设计模式 : Spring AOP 功能的实现。
  • 单例设计模式 : Spring 中的 Bean 默认都是单例的。
  • 模板方法模式 : Spring 中 jdbcTemplatehibernateTemplate 等以 Template 结尾的对数据库操作的类,它们就使用到了模板模式。
  • 包装器设计模式 : 我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
  • 观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。
  • 适配器模式 :Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配Controller

5.2、Spring 与 JDBC 模板(IOC)

5.2.1、数据源的配置

Spring 默认 DriverManagerDataSource

DBCP 数据源 BasicDataSource

C3P0 数据源 ComboPooledDataSource

注:配置 JDBC 模板-使用 class 为 JdbcTemplate

5.2.2、从属性文件读取数据库连接信息

<bean/>方式-使用 class 为 PropertyPlaceholderConfigurer

<context:property-placeholder/>方式

5.2.3、Dao 实现类继承 JdbcDaoSupport 类
	在 Spring 配置文件中,对于 JDBC 模板对象的配置完全可以省去,而是在 Dao 实现类中直接注入数据源对象。这样会让系统自动创建 JDBC 模板对象。
	JdbcTemplate 对象是多例的,即系统会为每一个使用模板对象的线程(方法)创建一个 JdbcTemplate 实例,并且在该线程(方法)结束时,自动释放 JdbcTemplate 实例。所以在每次使用 JdbcTemplate 对象时,都需要通过 getJdbcTemplate() 方法获取。

6、Spring 的事务管理(AOP)

6.1、Spring 管理事务的方式有几种?

  1. 编程式事务,在代码中硬编码。(不推荐使用)
  2. 声明式事务,在配置文件中配置(推荐使用)

声明式事务又分为两种:

  1. 基于 XML 的声明式事务
  2. 基于注解的声明式事务

6.2、Spring 事务管理 API

6.2.1、事务管理接口:

事务管理器是 PlatformTransactionManager 接口对象。其主要用于完成事务的提交、回滚,及获取事务的状态信息。常用的两个实现类:

DataSourceTransactionManager:使用 JDBC 或 iBatis 进行持久化数据时使用。

HibernateTransactionManager:使用 Hibernate 进行持久化数据时使用。

Spring 的回滚方式:发生运行时异常时回滚,发生受查异常 时提交。不过,对于受查异常,程序员也可以 手 工 设置其回滚方式。

6.2.2、Spring 事务中的隔离级别有哪几种?

TransactionDefinition 接口中定义了五个表示隔离级别的常量:

  • DEFAULT: 采用 DB 默认的事务隔离级别。MySql 的默认为 REPEATABLE_READ;Oracle 默认为 READ_COMMITTED。
  • READ_UNCOMMITTED: 最低的隔离级别,读未提交。未解决任何并发问题。
  • READ_COMMITTED: 读已提交。解决脏读,存在不可重复读与幻读
  • REPEATABLE_READ: 可重复读。解决脏读、不可重复读,存在幻读。
  • SERIALIZABLE: 最高的隔离级别,串行化。不存在并发问题。
6.2.3、Spring 事务中哪几种事务传播行为?

支持当前事务的情况:

  • REQUIRED: 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
  • SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
  • MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)

不支持当前事务的情况:

  • REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。
  • NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  • NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。

其他情况:

  • NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 REQUIRED。
6.2.4、默认事务超时时限

常量 TIMEOUT_DEFAULT 定义了事务底层默认的超时时限,及不支持事务超时时限设置 的 none 值。注意,事务的超时时限起作用的条件比较多,且超时的时间计算点较复杂。所以,该 值一般就使用默认值即可。

6.2.5、@Transactional(rollbackFor = Exception.class)注解了解吗?

我们知道:Exception 分为运行时异常 RuntimeException 和非运行时异常。事务管理对于企业应用来说是至关重要的,即使出现异常情况,它也可以保证数据的一致性。

@Transactional注解作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。如果类或者方法加了这个注解,那么这个类里面的方法抛出异常,就会回滚,数据库里面的数据也会回滚。

@Transactional注解中如果不配置rollbackFor属性,那么事物只会在遇到RuntimeException的时候才会回滚,加上rollbackFor=Exception.class,可以让事物在遇到非运行时异常时也回滚。

7、SpringMVC

7.1、原理:

工作原理文字说明

1、用户发送请求至前端控制器 DispatcherServlet。

2、DispatcherServlet 收到请求调用 HandlerMapping 处理器映射器。

3、处理器映射器找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给 DispatcherServlet。

4、DispatcherServlet 调用 HandlerAdapter 处理器适配器。

5、HandlerAdapter 经过适配调用具体的处理器(Controller,也叫后端控制器)方法。

6、处理器执行完成返回 ModelAndView 给 HandlerAdapter。

7、HandlerAdapter 将处理器执行结果 ModelAndView 返回给 DispatcherServlet。

8、DispatcherServlet 将 ModelAndView 传给 ViewReslover 视图解析器。

9、ViewReslover 解析后返回具体 View 给 DispatcherServlet。

10、DispatcherServlet 调用视图解析器根据 View 进行渲染视图(即将模型数据填充至视图 中)。

11、将渲染后的页面返回到前端控制器 DispatcherServlet。

12、DispatcherServlet 将渲染后的页面响应到客户端。

7.2、常用注解:

7.3、SpringMVC 的优点

是基于组件技术的.全部的应用对象,无论控制器和视图,还是业务对象之类的都是 java 组件.并且和 Spring 提供 的其他基础结构紧密集成 与 Spring 框架天生整合,无框架兼容问题 与 Struts2 相比安全性高 配置量小、开发效率高

7.4、SpringMVC 与 Struts2 的区别

springmvc 的入口是一个 servlet 即前端控制器,而 struts2 入口是一个 filter 过虑器。 springmvc 是基于方法开发,传递参数是通过方法形参,可以设计为单例或多例(建议单例),struts2 是基于类开 发,传递参数是通过类的属性,只能设计为多例。 Struts 采用值栈存储请求和响应的数据,通过 OGNL 存取数据。 springmvc 通过参数解析器是将 request 对象内 容进行解析成方法形参,将响应数据和页面封装成 ModelAndView 对象,最后又将模型数据通过 request 对象传 输到页面。 Jsp 视图解析器默认使用 jstl。

8、Mybatis

8.1、什么是 Mybatis?

(1)Mybatis 是一个半 ORM(对象关系映射)框架,它内部封装了 JDBC,开发时只需要关注 SQL 语句本身,不 需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。程序员直接编写原生态 sql,可以严格控制 sql 执行性能,灵活度高。 (2)MyBatis 可以使用 XML 或注解来配置和映射原生信息,将 POJO 映射成数据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。 (3)通过 xml 文件或注解的方式将要执行的各种 statement 配置起来,并通过 java 对象和 statement 中 sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并返回。(从执行 sql 到返回 result 的过程)。

8.2、Mybatis 的优点

(1)基于 SQL 语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响,SQL 写在 XML 里, 解除 sql 与程序代码的耦合,便于统一管理;提供 XML 标签,支持编写动态 SQL 语句,并可重用。 (2)与 JDBC 相比,减少了 50%以上的代码量,消除了 JDBC 大量冗余的代码,不需要手动开关连接; (3)很好的与各种数据库兼容(因为 MyBatis 使用 JDBC 来连接数据库,所以只要 JDBC 支持的数据库 MyBatis 都支持)。 (4)能够与 Spring 很好的集成;

(5)提供映射标签,支持对象与数据库的 ORM 字段关系映射;提供对象关系映射标签,支持对象关系组件维护。

8.3、Mybatis 框架的特点

(1)SQL 语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写 SQL 语句的功底有一定要求。 (2)SQL 语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。

8.4、MyBatis 框架适用场合

(1)MyBatis 专注于 SQL 本身,是一个足够灵活的 DAO 层解决方案。 (2)对性能的要求很高,或者需求变化较多的项目,如互联网项目,MyBatis 将是不错的选择。

8.5、MyBatis 的执行流程

(1)首先,SqlSessionFactoryBuilder 去读取 mybatis 的配置文件,然后 build 一个 DefaultSqlSessionFactory。 (2)当我们获取到 SqlSessionFactory 之后,就可以通过 SqlSessionFactory 去获取 SqlSession 对象 (3)拿到 SqlSession 对象以后就可以调用 SqlSession 中一系列的 select..., insert..., update..., delete...方法轻松自如的进行 CRUD 操作了。 就这样? 那咱配置的映射文件去哪儿了?

8.5.1、MapperProxy

在 mybatis 中,通过 MapperProxy 动态代理咱们的 dao, 也就是说, 当咱们执行自己写的 dao 里面的方法的时候,其实是对应的 mapperProxy 在代理。那么,咱们就看看怎么获取 MapperProxy 对象吧:MapperProxyFactory 的动态代理,咱们就可以方便地使用 dao 接口

8.5.2、Excutor

咱们拿到了 MapperProxy, 每个 MapperProxy 对应一个 dao 接口, 那么咱们在使用的时候,MapperProxy 是怎么做的呢?

8.6、MyBatis 与 Hibernate 有哪些不同?

(1)Mybatis 和 hibernate 不同,它不完全是一个 ORM 框架,因为 MyBatis 需要程序员自己编写 Sql 语句。 (2)Mybatis 直接编写原生态 sql,可以严格控制 sql 执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发,因为这类软件需求变化频繁,一但需求变化要求迅速输出成果。但是灵活的前提是 mybatis 无法做到数据库无关性,如果需要实现支持多种数据库的软件,则需要自定义多套 sql 映射文件,工作量大。 (3)Hibernate 对象/关系映射能力强,数据库无关性好,对于关系模型要求高的软件,如果用 hibernate 开发可以节省很多代码,提高效率。 Hibernate 属于全自动 ORM 映射工具,使用 Hibernate 查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而 Mybatis 在查询关联对象或关联集合对象时,需要手动编写 sql 来完成,所以,称之为半自动 ORM 映射工具。

8.7、#{}和${}的区别是什么?

#{}是预编译处理,${}是字符串替换。

Mybatis 在处理#{}会将 sql 中的#{}替换为?号,调用 PreparedStatement 的 set 方法来赋值。

Mybatis 在处理${}就是把${}替换成变量的值。

使用#{}可以有效的防止 SQL 注入,提高系统安全性。

8.8、当实体类中的属性名和表中的字段名不一样 ,怎么办 ?

第 1 种: 通过在查询的 sql 语句中定义字段名的别名,让字段名的别名和实体类的属性名一致。 第 2 种: 通过来映射字段名和实体类属性名的一一对应的关系。

8.9、关于动态 Mapper 的理解

(通常一个 Xml 映射文件,都会写一个 Dao 接口与之对应,请问,这个 Dao 接口的工作原理是什么?Dao 接口里的方法,参数不同时,方法能重载吗?) Dao 接口即 Mapper 接口。接口的全限名,就是映射文件中的 namespace 的值接口的方法名,就是映射文件中 Mapper 的 Statement 的 id 值接口方法内的参数,就是传递给 sql 的参数。 Mapper 接口是没有实现类的,**当调用接口方法时,接口全限名+方法名拼接字符串作为 key 值,**可唯一定位一个 MapperStatement。在 Mybatis 中,每一个 select、insert、update、delete 标签,都会被解析为一个 MapperStatement 对象。 Mapper 接口里的方法,是不能重载的,因为是使用 全限名+方法名 的保存和寻找策略。Mapper 接口的工作原理 是 JDK 动态代理,Mybatis 运行时会使用 JDK 动态代理为 Mapper 接口生成代理对象 proxy,代理对象会拦截接口方法,转而执行 MapperStatement 所代表的 sql,然后将 sql 执行结果返回。

8.9.1、使用 MyBatis 的 mapper 接口调用时有哪些要求?

① Mapper 接口方法名和 mapper.xml 中定义的每个 sql 的 id 相同; ② Mapper 接口方法的输入参数类型和 mapper.xml 中定义的每个 sql 的 parameterType 的类型相同; ③ Mapper 接口方法的输出参数类型和 mapper.xml 中定义的每个 sql 的 resultType 的类型相同; ④ Mapper.xml 文件中的 namespace 即是 mapper 接口的类路径。

8.10、如何获取自动生成的(主)键值?

insert 方法总是返回一个 int 值 ,这个值代表的是插入的行数。 如果采用自增长策略,自动生成的键值在 insert 方法执行完后可以被设置到传入的参数对象中。

示例:

<insert id=”insertname” usegeneratedkeys=”true” keyproperty=”id”>     insert into names (name) values (#{name})</insert>

8.11、Mybatis 动态 sql 有什么用?执行原理?有哪些动态 sql?

Mybatis 动态 sql 可以在 Xml 映射文件内,以标签的形式编写动态 sql,执行原理是根据表达式的值 完成逻辑判断 并动态拼接 sql 的功能。 Mybatis 提供了 9 种动态 sql 标签:trim | where | set | foreach | if | choose | when | otherwise | bind。

8.12、Mybatis 是否支持延迟加载?如果支持,它的实现原理是什么?

答:Mybatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载,association 指的就是一对一查询,collection 指的就是一对多查询。在 Mybatis 配置文件中,可以配置是否启用延迟加载 lazyLoadingEnabled=true|false。

它的原理是,**使用 CGLIB 创建目标对象的代理对象,**当调用目标方法时,进入拦截器方法,比如调用 a.getB().getName(),拦截器 invoke()方法发现 a.getB()是 null 值,那么就会单独发送事先保存好的查询关联 B 对象的 sql,把 B 查询上来,然后调用 a.setB(b),于是 a 的对象 b 属性就有值了,接着完成 a.getB().getName()方法的调用。这就是延迟加载的基本原理。

当然了,不光是 Mybatis,几乎所有的包括 Hibernate,支持延迟加载的原理都是一样的。

8.13、简述 Mybatis 的插件运行原理,以及如何编写一个插件。

答:Mybatis 仅可以编写针对 ParameterHandler、ResultSetHandler、StatementHandler、Executor 这 4 种接口的插件,**Mybatis 使用 JDK 的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,**每当执行这 4 种接口对象的方法时,就会进入拦截方法,具体就是 InvocationHandler 的 invoke()方法,当然,只会拦截那些你指定需要拦截的方法。 编写插件:实现 Mybatis 的 Interceptor 接口并复写 intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。

8.13.1、Mybatis 是如何进行分页的?分页插件的原理是什么?

Mybatis 使用 RowBounds 对象进行分页它是针对 ResultSet 结果集执行的内存分页,而非物理分页。可以在 sql 内直接书写带有物理分页的参数来完成物理分页功能,也可以使用分页插件来完成物理分页。

分页插件的基本原理是使用 Mybatis 提供的插件接口,实现自定义插件,在插件的拦截方法内拦截待执行的 sql, 然后重写 sql,根据 dialect 方言,添加对应的物理分页语句和物理分页参数。

8.14、Mybatis 缓存

Mybatis 中有一级缓存和二级缓存,默认情况下一级缓存是开启的,而且是不能关闭的。一级缓存 是指 **SqlSession 级别的缓存,**当在同一个 SqlSession 中进行相同的 SQL 语句查询时,第二次以 后的查询不会从 数据库查询,而是直接从缓存中获取,一级缓存最多缓存 1024 条 SQL。二级缓存 是指可以跨 SqlSession 的 缓存。是 mapper 级别的缓存,对于 mapper 级别的缓存不同的 sqlsession 是可以共享的。

8.14.1、Mybatis 的一级、二级缓存:

1)一级缓存: 基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该 Session 中的所有 Cache 就将清空,默认打开一级缓存。

2)二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap 存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。默认不打开二级缓存,要开启二级缓存,使用二级缓存属性类需要实现 Serializable 序列化接口(可用来保存对象的状态),可在它的映射文件中配置 ;

3)对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存 Namespaces)的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear。

8.14.2、Mybatis 的一级缓存原理(sqlsession 级别)

第一次发出一个查询 sql,sql 查询结果写入 sqlsession 的一级缓存中,缓存使用的数据结构是一 个 map。 key:MapperID+offset+limit+Sql+所有的入参 value:用户信息 同一个 sqlsession 再次发出相同的 sql,就从缓存中取出数据。如果两次中间出现 commit 操作 (修改、添加、 删除),本 sqlsession 中的一级缓存区域全部清空,下次再去缓存中查询不到所 以要从数据库查询,从数据库 查询到再写入缓存。

8.15、Mybatis 使用到的设计模式

  • Builder 模式

  • 工厂模式

  • 单例模式

  • 代理模式

  • 组合模式

  • 模板方法模式

  • 适配器模式

  • 装饰者模式

  • 迭代器模式


参考资料:

  • https://juejin.cn/post/6844903982066827277#heading-19
  • https://snailclimb.gitee.io/javaguide/#/
  • https://www.ycbbs.vip/?p=2468