17 января 2012 г.

Вопросы на интервью по технологиям Java Spring и Hibernate

Продолжаем тему интервью. В прошлых статьях мы рассмотрели основные концепции, которые вы должны знать для прохождения интервью по Java, но основ языка недостаточно. Обычно компании предпологают, что вы уже владеете каким-либо популярным фреймворком и способы самостоятельно реализовать реальное приложение. На сегодня самыми популярными фреймворками являются Spring и Hibernate. Возможно скоро к этому must have списку добавиться GWT, а пока что давайте рассмотрим какие вопросы могут быть заданы вам при прохождении интервью на уровень staff developer.



1. Объясните суть паттерна DI или IoC

Dependency injection (DI) - паттерн проектирования и архитектурная модель, так же известная как Inversion of Control (IoC). DI описывает ситуацию, когда один объект реализует свой функционал через другой объект. Например, соединение с базой данных передается конструктору объекта через аргумент, вместо того чтобы конструктор сам устанавливал соединение. Термин "dependency injection" немного неправильный, т.к. внедряются не зависимости, а функциональность или ресурсы. Существуют три формы внедрения (но не типа) зависимостей: сэттер, конструктор и внедрение путем интерфейса.
DI - это способ достижения слабой связанности. IoC предоставляет возможность объекту получать ссылки на свои зависимости. Обычно это реализуется через lookup-метод. Преимущество IoC в том, что эта модель позволяет отделить объекты от реализации механизмов, которые он использует. В результате мы получаем большую гибкость как при разработке приложений, так и при их тестировании.

2. Какие IoC контейнеры вы знаете?

Spring является IoC контейнером. Помимо него существуют HiveMind, Avalon, PicoContainer и т.д.

3. Какие существуют виды DI? Приведите примеры.

Существует два типа DI: через сэттер и через конструктор.
Через сэттер: обычно во всех java beans используются гэттеры и сэттеры для их свойств:
public class NameBean {
    String name;
    public void setName(String a) {
        name = a;
    }
    public String getName() {
        return name;
    }
}
Мы создаем экземпляр бина NameBean (например, bean1) и устанавливаем нужное свойство, например:
bean1.setName("Marfa");
Используя Spring реализация была бы такой:
<bean id="bean1" class="NameBean">
    <property name="name">
        <value>Marfa</value>
    </property>
</bean>
Это и называет DI через сэттер. Пример внедрения зависимости между объектами:
<bean id="bean1" class="bean1impl">
    <property name="game">
        <ref bean="bean2" />
    </property>
</bean>
<bean id="bean2" class="bean2impl" />
Через конструктор: используется конструктор с параметрами. Например:
public class NameBean {
    String name;
    public NameBean(String name) {
        this.name = name;
    }
}
Теперь мы внедряем объект на этапе создания экземпляра класса, т.е.
bean1 = new NameBean("Marfa");
Используя Spring это выглядело бы так:
<bean id="bean1" class="NameBean">
    <constructor-arg>
        <value>Marfa</value>
    </constructor-arg>
</bean>
4. Что такое Spring? Из каких частей состоит Spring Framework?

Spring - фреймворк с открытым исходным кодом, предназначеный для упрощения разработки enterprise-приложений. Одним из главным преимуществом Spring является его слоистая архитектура, позволяющая вам самим определять какие компоненты будут использованы в вашем приложении. Модули Spring построены на базе основного контейнера, который определяет создание, конфигурация и менеджмент бинов. Вот основные модули:
  • Основной контейнер - предоставляет основной функционал Spring. Главным компонентом контейнера является BeanFactory - реализация паттерна Фабрика. BeanFactory позволяет разделить конфигурацию приложения и информацию о зависимостях от кода.
  • Spring context - конфигурационный файл, который предоставляет информация об окружающей среде для Spring. Сюда входят такие enterprise-сервисы, как JNDI, EJB, интернационализация, валиадция и т.п.
  • Spring AOP - отвечает за интеграцию аспектно-ориентированного программирования во фреймворк. Spring AOP обеспечивает сервис управления транзакциями для Spring-приложения.
  • Spring DAO - абстрактный уровень Spring JDBC DAO предоставляет иерархию исключений и множество сообщений об ошибках для разных БД. Эта иерархия упрощает обработку исключений и значительно уменьшает количество кода, которое вам нужно было бы написать для таких операций, как, например, открытие и закрытие соединения.
  • Spring ORM - отвечает за интеграцию Spring и таких популярных ORM-фреймворков, как Hibernate, iBatis и JDO.
  • Spring Web module - отвечает за context web-приложений.
  • Spring MVC framework - реализация паттерна MVC для построения Web-приложений.


5. Что такое AOP? Как это относиться к IoC?

Аспектно-ориентированное программирование (АОП) — парадигма программирования, основанная на идее разделения функциональности для улучшения разбиения программы на модули. AOP и Spring - взаимодополняющие технологии, которые позволяют решать сложные проблемы путем разделения функционала на отдельные модули.
Основные понятия АОП:
  • Аспект (англ. aspect) — модуль или класс, реализующий сквозную функциональность. Аспект изменяет поведение остального кода, применяя совет в точках соединения, определённых некоторым срезом.
  • Совет (англ. advice) — средство оформления кода, который должен быть вызван из точки соединения. Совет может быть выполнен до, после или вместо точки соединения.
  • Точка соединения (англ. join point) — точка в выполняемой программе, где следует применить совет. Многие реализации АОП позволяют использовать вызовы методов и обращения к полям объекта в качестве точек соединения.
  • Срез (англ. pointcut) — набор точек соединения. Срез определяет, подходит ли данная точка соединения к данному совету. Самые удобные реализации АОП используют для определения срезов синтаксис основного языка (например, в AspectJ применяются Java-сигнатуры) и позволяют их повторное использование с помощью переименования и комбинирования.

Внедрение (англ. introduction, введение) — изменение структуры класса и/или изменение иерархии наследования для добавления функциональности аспекта в инородный код. Обычно реализуется с помощью некоторого метаобъектного протокола (англ. metaobject protocol, MOP).

6. Объясните работу BeanFactory в Spring.

BeanFactory - это реализация паттерна Фабрика, его функицональность покрывает создание бинов. Так как эта фабрика знает о многие объектов приложения, то она может создавать связи между объектами на этапе создания экземпляра. Существует несколько реализаций BeanFactory, самая используемся -  "org.springframework.beans.factory.xml.XmlBeanFactory". Она загружает бины на основе конфигурационного XML-файла. Чтобы создать XmlBeanFactory передайте конструктору InputStream, например:
BeanFactory factory = new XmlBeanFactory(new FileInputStream("myBean.xml"));
После этой строки фабрика знает о бинах, но их экземпляры еще не созданы. Чтобы инстанцировать бин нужно вызвать метод getBean(). Например:
myBean bean1 = (myBean)factory.getBean("myBean");
7. В чем роль ApplicationContext в Spring?

В то время, как BeanFactory используется в простых приложениях, Application Context - это более сложный контейнер. Как и BeanFactory он может быть использован для загрузки и связывания бинов, но еще он предоставляет:
  1. возможность получения текстовых сообщений, в том числе поддержку интернационализации;
  2. общий механизм работы с ресурсами;
  3. события для бинов, которые зарегестрированы как слушатели.

Из-за большей функциональности рекомендуется использование Application Context вместо BeanFactory. Последний используется только в случаях нехватки ресурсов, например при разработке для мобильных устройств. Существует 3 основных реализации Application context:
  1. ClassPathXmlApplicationContext - получает информацию из xml-файла, находящегося в classpath. Пример: ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
  2. FileSystemXmlApplicationContext - получает информацию из xml-файла. Например: ApplicationContext context = new FileSystemXmlApplicationContext("bean.xml");
  3. XmlWebApplicationContext - получает информацию из xml-файла за пределами web-приложения.


8. Каким образом Spring поддерживает DAO?

Класс HibernateDaoSupport является подходящим суперклассом для Hibernate DAO. Он содержит методы для получения сессии или фабрики сессий. Самый популярный метод - getHibernateTemplate(), который возвращает HibernateTemplate. Этот темплейт оборачивает checked-исключения Hibernate в runtime-исключения, позволяя вашим DAO оставаться независимыми от исключений Hibernate.
Пример:
public class UserDAOHibernate extends HibernateDaoSupport {
 public User getUser(Long id) {
  return (User) getHibernateTemplate().get(User.class, id);
 }
 public void saveUser(User user) {
  getHibernateTemplate().saveOrUpdate(user);
  if (log.isDebugEnabled()) {
   log.debug(“userId set to: “ + user.getID());
  } 
 } 
 public void removeUser(Long id) {
  Object user = getHibernateTemplate().load(User.class, id);
  getHibernateTemplate().delete(user);
 }
}
9. Какие бывают id generator классы в Hibernate?

increment - генерирует идентификатор типа long, short или int, которые будет уникальным только в том случае, если другой процесс не добавляет запись в эту же таблицу в это же время.
identity - генерирует идентификатор типа long, short или int. Поддерживается в DB2, MySQL, MS SQL Server, Sybase и HypersonicSQL.
sequence - использует последовательности в DB2, PostgreSQL, Oracle, SAP DB, McKoi или генератор Interbase. Возвращает идентификатор типа long, short или int.
hilo - использует алгоритм hi/lo для генерации идентификаторов типа long, short или int. Алгоритм гарантирует генерацию идентификаторов, которые уникальны только в данной базе данных.
seqhilo - использует алгоритм hi/lo для генерации идентификаторов типа long, short или int учитывая последовательность базы данных.
uuid - использует для генерации идентификатора алгоритм 128-bit UUID. Идентификатор будет уникальным в пределах сети. UUID представляется строкой из 32 чисел.
guid - использует сгенерированую БД строку GUID в MS SQL Server и MySQL.
native - использует identity, sequence или hilo в завимисимости от типа БД, с которой работает приложение
assigned - позволяет приложению устанавливать идентификатор объекту, до вызова метода save(). Используется по умолчанию, если тег <generator> не указан.
select - получает первичный ключ, присвоенный триггером БД
foreign - использует идентификатор другого, связанного с данным объекта. Используется в <one-to-one> ассоциации первичных ключей.
sequence-identity - специализированный генератор идентификатора. Используется только с драйевром Oracle 10g дл JDK 1.4.

10. Как выглядит типичная реализция метода используя Spring?

Для типичного Spring-приложения нам необходимы следующие файлы:
1. Интерфейс, описывающий функционал приложения
2. Реализация интерфейса, содержащая свойства, сэттеры-гэттеры, функции и т.п.
3. Конфигурационный XML-файл Spring'а.
4. Клиентское приложение, которое использует функцию.

11. Как задаются файлы маппинга Hibernate в Spring?

Через applicationContext.xml в web/WEB-INF. Например:

 <list>
  <value>org/appfuse/model/User.hbm.xml</value>
 </list>


12. Как добавить поддержку Spring в web-приложение

Достаточно просто указать ContextLoaderListener в web.xml файле приложения:
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
13. Можно ли использовать xyz.xml вместо applicationContext.xml?

ContextLoaderListener - это ServletContextListener, который инициализируется когда ваше web-приложение стартует. По-умолчанию оно загружает файл WEB-INF/applicationContext.xml. Вы можете изменить значение по-умолчанию, указав параметр contextConfigLocation. Пример:
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/xyz.xml</param-value>
        </context-param>
    </listener-class>
</listener>
14. Как настраивается соединение с БД в Spring?

Используя datasource "org.springframework.jdbc.datasource.DriverManagerDataSource". Пример:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName">
        <value>org.hsqldb.jdbcDriver</value>
    </ property>
    <property name="url">
        <value>jdbc:hsqldb:db/appfuse</value>
    </ property>
    <property name="username"><value>sa</value></property>
    <property name="password"><value></value></property>
</bean>
15. Как сконфигурировать JNDI не через datasource в applicationContext.xml?

Используя "org.springframework.jndi.JndiObjectFactoryBean". Пример:
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName">
        <value>java:comp/env/jdbc/appfuse</value>
    </property>
</bean>
16. Какие преимущства от использования Hibernate?

Некоторые из них:
  • прозрачный механизм связи БД и POJO;
  • HQL;
  • автоматическая генерация primary key;
  • кэш: Session Leve, Query и Second level;
  • производительность: ленивая инициализация, выборка outer join.


17. Что такое сесиия и фаблика сессий в Hibernate? Как настроить session factory в конфигурационном файле Spring?

Hibernate сессия - это главный интерфейс взаимодействия Java-приложения и Hibernate. SessionFactory позволяет создавать сессии согласно конфигурации hibernate.cfg.xml. Например:
// Initialize the Hibernate environment
Configuration cfg = new Configuration().configure();
// Create the session factory
SessionFactory factory = cfg.buildSessionFactory();
// Obtain the new session object
Session session = factory.openSession();
При вызове Configuration().configure() загружается файл hibernate.cfg.xml и происходит настройка среды Hibernate. После того, как конфигурация загружена, вы можете сделать дополнительную модификацию настроек уже на программном уровне. Данные корректировки возможны до создания экземпляра фабрики сессий. Экземпляр SessionFactory как правило создается один раз и используется во всем приложении.

Главная задача сессии - обеспечить механизмы создания, чтения и удаления для экземпляров примапленых к БД классов. Экземпляры могут находиться в трёх состояниях:
  1. transient - никогда не сохранялись, не ассоциированы ни с одной сессией;
  2. persistent - ассоциированы с уникальной сессией;
  3. detached - ранее сохраненные, не ассоциированы с сессией.

Объект Hibernate Session представляет одну операцию с БД. Сессию открывает фабрика сессий. Сессия должна быть закрыта, когда все операции с БД совершены. Пример:
Session session = null;
UserInfo user = null;
Transaction tx = null;
try {
 session = factory.openSession();
 tx = session.beginTransaction();
 user = (UserInfo)session.load(UserInfo.class, id);
 tx.commit();
} catch(Exception e) {
 if (tx != null) {
  try {
   tx.rollback();
  } catch (HibernateException e1) {
   throw new DAOException(e1.toString()); }
 } 
 throw new DAOException(e.toString());
} finally {
 if (session != null) {
  try {
   session.close();
  } catch (HibernateException e) { }
 }
}
18. В чем различие методов get() и load()?

Метод load() старее, чем get(), который был добавлен по просьбам пользователей Hibernate. Оба метода предназначены для получения объекта из БД, различие между ними тривиальное: в случае, если объект с заданным ID не будет найден в БД метод load() выбросит исключение, а get() вернет null.
Пример вычитки из БД:
User user = (User) session.get(User.class, userID);

Метод load() может вернуть прокси, вместо реального объекта. Таким образом выбор между load() и get() прост: если вы уверены, что объект существует и его отсутствие - это исключительная ситуация - используйте load(), иначе get().

19. Какие типы менеджмента транзакций поддерживаются в Hibernate?

Hibernate взаимодействует с БД через JDBC-соединение. Таким образом он поддерживает управляемые и не управляемые транзакции.
Неуправляемые транзакции в web-контейнере:
<bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager">
    <property name="sessionFactory">
        <ref local="sessionFactory"/>
    </property>
</bean>
Управляемые транзакции на сервере приложений, использующий JTA:
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager.">
    <property name="sessionFactory">
        <ref local="sessionFactory" />
    </property>
</bean>
20. Что такое ленивая загрузка и как её добиться в Hibernate?

Ленивая загрузка определяет будет ли дочерний объект загружен при загрузке объекта-родителя. Она настраивается свойством Lazy для дочернего объектв в файле маппинга объекта-родителя. По-умолчанию это свойство и так стоит в true.

Hibernate не поддерживает ленивую загрузку для объектов не в сессии.

21. Какие существуют стратегии загрузки объектов в Hibernate?

Существуют следующие типа fetch'a:
  • Join fetching: hibernate получает ассоциированные объекты и коллекции одним SELECT используя OUTER JOIN
  • Select fetching: использует уточняющий SELECT чтобы получить ассоциированные объекты и коллекции. Если вы не установите lazy fetching определив lazy="false", уточняющий SELECT будет выполнен только когда вы запрашиваете доступ к ассоциированным объектам
  • Subselect fetching: поведение такое же, как у предыдущего типа, за тем исключением, что будут загружены ассоциации для все других коллекций, «родительским» для которых является сущность, которую вы загрузили первым SELECT’ом.
  • Batch fetching: оптимизированная стратегия вида select fetching. Получает группу сущностей или коллекций в одном SELECT’е.


22. Какие типы кэша используются в Hibernate?

Hibernate использует 2 типа кэша: кэш первого уровня и кэш второго уровня.

Кэш первого уровня ассоциирован с объектом сесии, в то время, как кэш второго уровня ассоциирован с объектом фабрики сессий. По-умолчанию Hibernate использует кэш первого уровня для каждой операции в транзакции. В первую очередь кэш используется чтобы уменьшить количество SQL-запросов. Например если объект модифицировался несколько раз в одной и той же транзакции, то Hibernate сгенерирует только один UPDATE.

Чтобы уменьшить трафик с БД, Hibernate использует кэш второго уровня, который является общим для всего приложения, а не только для данного конкретного пользователя. Таким образом если результат запроса находится в кэше, мы потенциально уменьшаем количество транзакций к БД.

EHCache - это быстрый и простой кэш. Он поддерживает read-only и read/write кэширование, а так же кэширование в память и на диск. Но не поддерживает кластеризацию.
OSCache - это другая opensource реализация кэша. Помимо всего, что поддерживает EHCache, эта реализация так же поддерживает кластеризацию через JavaGroups или JMS.
SwarmCache - это просто cluster-based решение, базирующееся на JavaGroups. Поддерживает read-only и нестрогое read/write кэширование. Этот тип кэширование полезен, когда количество операций чтения из БД превышает количество операций записи.
JBoss TreeCache - предоставляет полноценный кэш транзакции.

23. Какие существуют типы стратегий кэша?

Read-only: эта стратегия используется когда данные вычитываются, но никогда не обновляется. Самая простая и производительная стратегия
Read/write: может быть использована, когда данные должны обновляться.
Нестрогий read/write: эта стратегия не гарантирует, что две транзакции не модифицируют одни и те же данные синхронно.
Transactional: полноценное кэширование транзакций. Доступно только в JTA окружении.

24. Как настраивается кэш второго уровня в Hibernate?

Чтобы указать кэш второго уровня нужно определить hibernate.cache.provider_class в hibernate.cfg.xml:
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.cache.provider_class">org.hibernate.cache.EHCacheProvider</property>
    </session-factory>
</hibernate-configuration>
По-умолчанию используется EHCache.

Чтобы использвать кэш запросов нужно его включить установив свойство hibernate.cache.use_query_cache в true в hibernate.properties.

25. Какая разница между сортированными и упорядоченными коллекциями в Hibernate?

Сортированные коллекции отсортированы в памяти используя java comparator, в то время как упорядоченные коллекции сортируются БД.

10 комментариев:

  1. Отличная статья!
    Побольше бы таких =)

    ОтветитьУдалить
  2. Спасибо большое за хорошую статью.

    ОтветитьУдалить
  3. Такие статьи помогают мне понять куда двигаться дальше. Спасибо.

    ОтветитьУдалить
  4. Спасибо ОГРОМНОЕ! Отличная статья. Как раз сейчас готовлюсь к собеседованиям.

    ОтветитьУдалить
  5. Спасибо !!! Отличная статья!

    ОтветитьУдалить
  6. Сдается мне уровни Кэша в Hibernate = кругам ада :(

    ОтветитьУдалить
  7. Большое спасибо!!!!!!!
    Пишите ещо.

    ОтветитьУдалить

Примечание. Отправлять комментарии могут только участники этого блога.