Java框架问题

来源:互联网 发布:php模版生成html 编辑:程序博客网 时间:2024/06/06 10:57

Hibernate和Mybatis

什么是ORM?

对象关系映射(Object-Relational Mapping,简称ORM)是一种为了解决程序的面向对象模型与数据库关系模型互不匹配问题的技术。

Hibernate中SessionFactory是线程安全的吗?Session是线程安全的吗(两个线程能够共享同一个Session吗)?

SessionFactory对应Hibernate的一个数据存储的概念,它是线程安全的,可以被多个线程并发访问。SessionFactory一般只会在启动的时候构建。对于应用程序,最好将SessionFactory通过单例模式进行封装以便于访问。

Session是一个轻量级非线程安全的对象(线程间不能共享session),它表示与数据库进行交互的一个工作单元。
Session是由SessionFactory创建的,在任务完成之后它会被关闭。Session是持久层服务对外提供的主要接口。Session会延迟获取数据库连接(也就是在需要的时候才会获取)。为了避免创建太多的session,可以使用ThreadLocal将session和当前线程绑定在一起,这样可以让同一个线程获得的总是同一个session。

Hibernate中Session的load和get方法的区别是什么?

① 如果没有找到符合条件的记录,get方法返回null,load方法抛出异常。
② get方法直接返回实体类对象,load方法返回实体类对象的代理。
③ 在Hibernate 3之前,get方法只在一级缓存中进行数据查找,如果没有找到对应的数据则越过二级缓存,直接发出SQL语句完成数据读取;load方法则可以从二级缓存中获取数据;从Hibernate 3开始,get方法不再是对二级缓存只写不读,它也是可以访问二级缓存的。

Session的save()、update()、merge()、lock()、saveOrUpdate()和persist()方法分别是做什么的?有什么区别?

Hibernate的对象有三种状态:瞬时态(transient)、持久态(persistent)和游离态(detached)。

  • 瞬时态的实例可以通过调用save()、persist()或者saveOrUpdate()方法变成持久态;
  • 游离态的实例可以通过调用 update()、saveOrUpdate()、lock()或者replicate()变成持久态。

save()和persist()将会引发SQL的INSERT语句,而update()或merge()会引发UPDATE语句。

save()和update()的区别在于一个是将瞬时态对象变成持久态,一个是将游离态对象变为持久态。

merge()方法可以完成save()和update()方法的功能,它的意图是将新的状态合并到已有的持久化对象上或创建新的持久化对象。

persist()方法保证当它在一个事务外部被调用的时候并不触发一个INSERT语句,当需要封装一个长会话流程的时候,persist()方法是很有必要的;save()方法不保证,它要返回标识符,所以它会立即执行INSERT语句,不管是在事务内部还是外部。

update()方法是把一个已经更改过的脱管状态的对象变成持久状态;lock()方法是把一个没有更改过的脱管状态的对象变成持久状态。

Query接口的list方法和iterate方法有什么区别?

① list()方法无法利用一级缓存和二级缓存(对缓存只写不读),它只能在开启查询缓存的前提下使用查询缓存;iterate()方法可以充分利用缓存,如果目标数据只读或者读取频繁,使用iterate()方法可以减少性能开销。

如何理解Hibernate的延迟加载机制?在实际应用中,延迟加载与Session关闭的矛盾是如何处理的?

延迟加载就是并不是在读取的时候就把数据加载进来,而是等到使用时再加载。Hibernate使用了虚拟代理机制实现延迟加载,我们使用Session的load()方法加载数据或者一对多关联映射在使用延迟加载的情况下从一的一方加载多的一方,得到的都是虚拟代理,简单的说返回给用户的并不是实体本身,而是实体对象的代理。代理对象在用户调用getter方法时才会去数据库加载数据。但加载数据就需要数据库连接。而当我们把会话关闭时,数据库连接就同时关闭了。

Hibernate的一级缓存、二级缓存和查询缓存。

Hibernate的Session提供了一级缓存的功能,默认总是有效的,当应用程序保存持久化实体、修改持久化实体时,Session并不会立即把这种改变提交到数据库,而是缓存在当前的Session中,除非显示调用了Session的flush()方法或通过close()方法关闭Session。通过一级缓存,可以减少程序与数据库的交互,从而提高数据库访问性能。

SessionFactory级别的二级缓存是全局性的,所有的Session可以共享这个二级缓存。不过二级缓存默认是关闭的,需要显示开启并指定需要使用哪种二级缓存实现类(可以使用第三方提供的实现)。一旦开启了二级缓存并设置了需要使用二级缓存的实体类,SessionFactory就会缓存访问过的该实体类的每个对象,除非缓存的数据超出了指定的缓存空间。

一级缓存和二级缓存都是对整个实体进行缓存,不会缓存普通属性,如果希望对普通属性进行缓存,可以使用查询缓存。查询缓存是将HQL或SQL语句以及它们的查询结果作为键值对进行缓存,对于同样的查询可以直接从缓存中获取数据。查询缓存默认也是关闭的,需要显示开启。

MyBatis中使用#和$书写占位符有什么区别?

/#将传入的数据都当成一个字符串,会对传入的数据自动加上引号;SQL使占位符可能会导致SQL注射攻击,能用#的地方就不要使用orderby而不是#。

MyBatis中命名空间(namespace)的作用

在MyBatis中,可以为每个映射文件起一个唯一的命名空间,这样定义在这个映射文件中的每个SQL语句就成了定义在这个命名空间中的一个ID。只要我们能够保证每个命名空间中这个ID是唯一的,即使在不同映射文件中的语句ID相同,也不会再产生冲突了。

Spring

Spring Container(容器)

容器是Spring框架的核心,Spring容器就是一个巨大的工厂。Spring容器使用Ioc管理所有组成应用系统的组件。Bean是Spring管理的基本单元,Spring容器是生成Bean实例的工厂,并管理容器中的Bean。

Spring容器会使用XML解析器读取属性值,并利用反射来创建该实现类的实例。

Spring有两个核心接口:BeanFactory和ApplicationContext,其中ApplicationContext是BeanFactory的子接口,它们都可以代表Spring容器。

BeanFactory是IOC容器的核心接口,它定义了IOC的基本功能,
包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。
它只能管理单例(Singleton)Bean的生命周期。它不能管理原型(prototype,非单例)Bean的生命周期,这是因为原型Bean被创建后变换递给了客户端,容器失去了对它们的引用。其中getBean方法是IOC容器获取bean对象和引发依赖注入的起点。

ApplicationContext由BeanFactory派生,提供了更全面的功能,BeanFactory中,很多功能需要编程实现,而在ApplicationContext可以通过配置实现。

BeanFactory接口提供了配置框架及基本功能,但是无法支持Spring的aop功能和web应用。

什么是IoC和DI?DI是如何实现的?

IOC叫控制反转,是Inversion of Control的缩写;
DI(Dependency Injection)叫依赖注入,是对Ioc的诠释。

控制反转是把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。
控制反转就是对组件对象控制权的转移,从程序代码本身转移到外部容器,由容器来创建对象并管理对线之间的依赖关系。

依赖注入的基本原则是应用组件不应该负责查找资源或者其他依赖的协作对象。配置对象、查找资源从应用组件代码中抽取出来,交给容器完成。

DI是对Ioc的准确描述,即组件之间的依赖关系由容器在运行期决定,容器动态将某种依赖关系注入到组件之中。

依赖注入可以通过setter方法注入(设值注入)、构造器注入和接口注入三种方式来实现,Spring支持setter注入和构造器注入,通常使用构造器注入来注入必须的依赖关系,对于可选的依赖关系,则setter注入是更好的选择,setter注入需要类提供无参构造器或者无参的静态工厂方法来创建对象。

IOC或依赖注入减少了应用程序的代码量。它使得应用程序的测试很简单。

Spring中Bean的作用域有哪些?

在Spring的早期版本中,仅有两个作用域:singleton和prototype,前者表示Bean以单例的方式存在;后者表示每次从容器中调用Bean时,都会返回一个新的实例,prototype通常翻译为原型。

Spring 2.x中针对WebApplicationContext新增了3个作用域,分别是:request(每次HTTP请求都会创建一个新的Bean)、session(同一个HttpSession共享同一个Bean,不同的HttpSession使用不同的Bean)和globalSession(同一个全局Session共享一个Bean)。

单例模式和原型模式都是重要的设计模式。一般情况下,无状态或状态不可变的类适合使用单例模式。在传统开发中,由于DAO持有Connection这个非线程安全对象因而没有使用单例模式;但在Spring环境下,所有DAO类对可以采用单例模式,因为Spring利用AOP和java API中的ThreadLocal对非线程安全的对象进行了特殊处理。

ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。ThreadLocal,顾名思义是线程的一个本地化对象,当工作于多线程中的对象使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程分配一个独立的变量副本,所以每一个线程都可以独立的改变自己的副本,而不影响其他线程所对应的副本。从线程的角度看,这个变量就像是线程的本地变量。

在ThreadLocal类中有一个Map,键为线程对象,值是其线程对应的变量的副本。

AOP(面向切面编程)

AOP(Aspect-Oriented Programming)指一种程序设计范型,该范型以一种称为切面(aspect)的语言构造为基础,切面是一种新的模块化机制,用来描述分散在对象、类或方法中的横切关注点。AOP出现是为了降低功能的耦合和代码的冗余,将一些无关主流程的内容(如日志、性能监控等)和主流程分离开的一种思维。

AOP中的连接点(Joinpoint)、切点(Pointcut)、增强(Advice)、织入(Weaving)、切面(Aspect)这些概念?

a. 连接点(Joinpoint):程序执行的某个特定位置。
一个类或一段程序代码拥有一些具有边界性质的特定点,这些代码中的特定点就是连接点。Spring仅支持方法的连接点。
b. 切点(Pointcut):如果连接点相当于数据中的记录,那么切点相当于查询条件,一个切点可以匹配多个连接点。Spring AOP的规则解析引擎负责解析切点所设定的查询条件,找到对应的连接点。
c. 增强(Advice):增强是织入到目标类连接点上的一段程序代码。Spring提供的增强接口都是带方位名的,如:BeforeAdvice、AfterReturningAdvice、ThrowsAdvice等。很多资料上将增强译为“通知”,这明显是个词不达意的翻译,让很多程序员困惑了许久。
e. 织入(Weaving):织入是将增强添加到目标类具体连接点上的过程,AOP有三种织入方式:
①编译期织入:需要特殊的Java编译期(例如AspectJ的ajc);
②装载期织入:要求使用特殊的类加载器,在装载类的时候对类进行增强;
③运行时织入:在运行时为目标类生成代理实现增强。Spring采用了动态代理的方式实现了运行时织入,而AspectJ采用了编译期织入和装载期织入的方式。
f. 切面(Aspect):切面是由切点和增强(引介)组成的,它包括了对横切关注功能的定义,也包括了对连接点的定义。

Spring 中自动装配的方式有哪些?

  • no:不进行自动装配,手动设置Bean的依赖关系。
    • byName:根据Bean的名字进行自动装配。
    • byType:根据Bean的类型进行自动装配。
    • constructor:类似于byType,不过是应用于构造器的参数,如果正好有一个Bean与构造器的参数类型相同则可以自动装配,否则会导致错误。
    • autodetect:如果有默认的构造器,则通过constructor的方式进行自动装配,否则使用byType的方式进行自动装配。

Spring中如何使用注解来配置Bean?有哪些相关的注解?

首先需要在Spring配置文件中增加如下配置:

<context:component-scan base-package="org.example"/>

然后可以用@Component、@Controller、@Service、@Repository注解来标注需要由Spring IoC容器进行对象托管的类。这几个注解没有本质区别,只不过@Controller通常用于控制器,@Service通常用于业务逻辑类,@Repository通常用于仓储类(例如我们的DAO实现类),普通的类用@Component来标注。

Spring支持的事务管理

在Spring中,事务是通过TransactionDefinition接口来定义的。

public interface TransactionDefinition{    int getIsolationLevel();    int getPropagationBehavior();    int getTimeout();    boolean isReadOnly();}

如上的接口只提供了获取实行的方法,没有提供设置属性的方法,因为事务事务的设置完全是程序员控制的,程序员可以自定义任何设置属性的方法。

事务隔离级别

隔离级别是指若干并发的事务之间的隔离程度。TransactionDefinition接口中定义了5个表示隔离级别的常量:

  • TransactionDefinition.ISOLATION_DEFAULT:这是默认值,表示使用底层数据库的默认隔离级别。对于大部分数据库而言,通常这个值就是Transaction.ISOLATION_READ_COMMITTED。
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED:表示一个事务可以读取另一个事务修改单还没有提交的数据。不能防止不可重复读和脏读,很少使用;
  • TransactionDefinition.ISOLATION_READ_COMMITTED:该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该隔离级别可以防止脏读,这是大多数情况下的推荐值。
  • TransactionDefinition.ISOLATION_REPEATABLE_READ:表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回记录都相同。
  • TransactionDefinition.ISOLATION_SERIALIZABLE:所有事务依次逐个执行,这样事务之间就完全不能产生干扰,该级别可以防止脏读、不可重复读和幻读,但严重影响程序性能。

事务传播行为

是指如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下7个表示传播行为的常量:

  • TransactionDefinition.PROPAGATION_REQUIRED(propagation_required):如果当前存在事务,则加入事务,如果没有就新建;
  • TransactionDefinition.PROPAGATION_REQUIRES_NEW(propagation_required_new):创建一个新的事务,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_SUPPORTS(propagation_supports):如果当前存在事务,则加入该事务,如果没有,则以非事务方式运行。
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED(propagataion_not_supported):以非事务方式运行,如果当前存在事务,则把当前事务挂起。
  • TransactionDefinition.PROPAGATION_NEVER(propagation_never):以非事务方式运行,如果当前存在事务,则抛出异常。
  • TransactionDefinition.PROPAGATION_MANDATORY(propagation_mandatory):如果当前存在事务,则加入事务;没有事务,则抛出异常。
  • TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果没有事务,则等价于TrasactionDefinition.PROPAGATION_REQUIRED。

前面的六种事务传播行为是 Spring 从 EJB 中引入的,他们共享相同的概念。而 PROPAGATION_NESTED是 Spring 所特有的。以 PROPAGATION_NESTED 启动的事务内嵌于外部事务中(如果存在外部事务的话),此时,内嵌事务并不是一个独立的事务,它依赖于外部事务的存在,只有通过外部的事务提交,才能引起内部事务的提交,嵌套的子事务不能单独提交(内部不影响外部,但是外部影响内部)。

事务管理的API分析

事务管理最重要的三个:TransactionDefinnition、PlatformTransactionManager、TransactionStatus。所谓事务管理,其实就是“按照给定的事务规则来执行提交或回滚操作”。
“给定的事务规则”就是用TransactionDefinition表示的;
“按照规则执行提交/回滚”是PlatformTransactionManager表示;
“一个运行着的事务的状态”用TransactionStatus表示。

TransactionDefinition

该接口用于定义一个事务。它包含了事务的静态属性,比如:事务传播行为、超时时间等;Spring提供了一个默认的实现类:DefaultTransactionDefinition,该类适用于大多数情况。如果该类不能满足需求,可以通过实现TransactionDefinition接口来实现自己的事务定义。

PlatformTransactionManager

用于执行具体的事务操作。定义的主要方法:

Public interface PlatformTransactionManager{    TransactionStatus getTransacion(TransactionDefinition definition) throws TransactionException;    void commit(TransactionStatus status) throws TransactionException;    void rollback(TransactionStatus status) throws TransactionException;}

根据底层使用的不同持久化API或框架,PlatformTransactionManager主要实现类如下:

  • DataSourceTransactionManager:适用于使用JDBC和iBatis进行数据持久化操作的情况;
  • HibernateTransactionManager:适用于使用Hibernate进行数据持久化操作的情况;
  • JpaTransactionManager:适用于使用JPA进行数据持久化操作的情况;

还有JtaTransactionManager、JdoTransactionManager、JmsTransactionManager等。

如果我们使用JTA进行事务管理,我们可以通过 JNDI 和 Spring 的 JtaTransactionManager 来获取一个容器管理的 DataSource。JtaTransactionManager 不需要知道 DataSource 和其他特定的资源,因为它将使用容器提供的全局事务管理。而对于其他事务管理器,比如DataSourceTransactionManager,在定义时需要提供底层的数据源作为其属性,也就是 DataSource。与 HibernateTransactionManager 对应的是 SessionFactory,与 JpaTransactionManager 对应的是 EntityManagerFactory 等等。

事务分为全局事务和局部事务,全局事务由应用服务器管理,需要底层服务器JTA(Java Transaction API)支持(如WebLogic)(全局事务可以应用于数据库和消息队列的场景)。局部事务和底层采用的持久化方案有关, 使用JDBC进行持久化,需要使用Connection对象操作事务;而采用Hibernate进行持久化,需要使用Session对象来操作事务。

TransactionStatus

PlatformTransactionManager.getTransaction(…)方法返回一个TransactionStatus对象。返回的TransactionStatus对象可能代表一个新的或已经存在的事务。TransactionStatus接口提供了一个简单的控制事务执行和查询事务状态的方法。

public interface TransactionStatus{    boolean isNewTransaction();    void setRollbackOnly();    boolean isRollbackOnly();}

编程式事务和声明式事务

Spring支持编程式事务管理声明式事务管理

编程式事务就是要在代码中显示的调用beginTransaction()、commit()、rollback()等事务管理方法。但是事务管理的代码放在业务逻辑代码中,破坏了程序的条理性。包括底层API事务管理(PlatformTransactionManager、TransactionDefinition、TransactionStatus)和TransactionTemplate事务管理 execute() 。
声明式事务是建立在AOP上的,本质是对方法前后进行拦截,然后在目标方法之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

声明式事务唯一不足地方是,后者的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。

声明式事务

TransactionInterceptor 、TransactionProxyFactoryBean、基于 命名空间、 @Transactional。

SpringMVC的工作原理

  1. 客户端的所有请求都交给前端控制器DispatcherServlet来处理,它会负责调用系统的其他模块来真正处理用户的请求。
  2. DispatcherServlet收到请求后,将根据请求的信息(包括URL、HTTP协议方法、请求头、请求参数、Cookie等)以及HandlerMapping的配置找到处理该请求的Handler(任何一个对象都可以作为请求的Handler)。
  3. 然后Spring会通过HandlerAdapter对该处理器进行封装。HandlerApapter是一个适配器,它用统一的接口对各种Handler中的方法进行调用。
  4. Handler完成对用户请求的处理后,会返回一个ModelAndView对象给DispatcherServlet。
    1. ModelAndView的视图是逻辑视图,DispatcherServlet还要借助ViewResolver完成从逻辑视图到真实视图对象的解析工作。
    2. 当得到真正视图对象后,DispatcherServlet会利用视图对象对模型数据进行渲染。
    3. 客户端得到相应。

Spring Ioc容器配置Bean的方式?

基于XML;
基于注解;
基于Java程序;ConfigurableApplicationContext

Spring中Bean的生命周期?

  1. Spring Ioc容器找到关于Bean的定义并实例化Bean。
  2. Spring Ioc对Bean进行依赖注入。
  3. 如果Bean实现了BeanNameAware接口,则将该Bean的id传给setBeanName方法。
  4. 如果Bean实现了BeanFactoryAware接口,则将BeanFactory对象传给setBeanFactory方法。
  5. 如果Bean实现了BeanPostProcessor接口,则调用其postProcessBeforeInitialization方法。
  6. 如果Bean实现了InitializationBean接口,则调用其afterPropertySet方法。
  7. 如果有和Bean关联的BeanPostProcessors对象,则这些对象的postProcessAfterInitialization方法被调用。
  8. 当Bean销毁时,如果Bean实现了DisposableBean接口,则调用其destroy方法。

依赖注入时如何注入集合属性?

可以在定义Bean属性时,通过 / / / 分别为其注入列表、集合、映射和键值都是字符串的映射属性。

Spring中的自动装配有哪些限制?

  • 如果使用了构造器注入或者setter注入,那么将覆盖自动装配的依赖关系。
  • 基本数据类型的值、字符串字面量、类字面量无法使用自动装配来注入。
  • 优先考虑使用显式的装配来进行更精确的依赖注入而不是使用自动装配。

在Web项目中如何获得Spring的IoC容器?

WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);

Spring框架有哪些模块?

Core module
Bean module
Context module
JDBC module
ORM module
Transaction module
Web module
Web-Servlet module
Web-Struts module

原创粉丝点击