Morphia — это библиотека ODM для MongoDB. Это может рассматриваться как облегченная альтернатива Spring Data MongoDB или как более богатая версия Jongo . По сравнению с Spring Data MongoDB, Morphia проще настроить в среде, отличной от Spring, и у нее гораздо меньше зависимостей. С другой стороны, ему не хватает унифицированного интерфейса репозитория Spring Data и подхода, основанного на соглашении о конфигурации, который может повысить производительность после настройки базовой конфигурации.
JAR ядра Morphia готов к OSGi. Однако включение разрешения класса сущностей в Morphia при работе в OSGi требует некоторых дополнительных шагов.
Класс погрузки в Морфию
Подход Morphia к разрешению классов сущностей является расширяемым. Он делегирует конструкцию объекта экземпляру, который реализует интерфейс org.mongodb.morphia.ObjectFactory . Morphia также предоставляет реализацию по умолчанию — org.mongodb.morphia.mapping.DefaultCreator , которая с помощью необработанного объекта com.mongodb.DBObject читает свое поле ‘className’, разрешает класс и создает соответствующий POJO.
DefaultCreator делегирует разрешение класса отдельному защищенному методу, который использует загрузчик классов контекста потока (TCCL). Хотя TCCL обычно должен знать о классах сущностей, поскольку он обычно вызывается с уровня DAO, это не всегда так. Например, в Karaf во время создания экземпляра контейнера Blueprint TCCL является не инициализатором загрузчика класса связки, а загрузчиком класса начальной загрузки Karaf. Переопределение метода загрузки классов и делегирование загрузчику классов с поддержкой OSGi обеспечивает более согласованное решение.
Разрешение класса сущностей в OSGi с Morphia
Вот настроенная версия DefaultCreator, которая заменяет загрузчик классов TCCL на загрузчик классов контекста пакета:
public class BundleObjectFactory extends DefaultCreator {
private BundleContext bundleContext;
@Override
protected ClassLoader getClassLoaderForClass() {
ClassLoader cl = ((BundleWiring)bundleContext.getBundle().adapt(
BundleWiring.class)).getClassLoader();
return cl;
}
public BundleObjectFactory(BundleContext bundleContext) {
super();
this.bundleContext = bundleContext;
}
}
Настроенный экземпляр класса Morphia можно создать с помощью фабрики:
public class MorphiaFactory {
private BundleContext bundleContext;
private List<String> classes;
private Class<?> loadClass(String className) throws ClassNotFoundException {
ClassLoader cl = ((BundleWiring)bundleContext.getBundle().adapt(
BundleWiring.class)).getClassLoader();
return cl.loadClass(className);
}
private Mapper getMapper() {
Mapper mapper = new Mapper();
mapper.getOptions().objectFactory = new BundleObjectFactory(bundleContext);
return mapper;
}
private List<Class<?>> getClassObjects(List<String> classNames)
throws ClassNotFoundException {
if (classNames == null || classNames.size() == 0)
return null;
List<Class<?>> classObjs = new ArrayList<>();
for (String className:classNames) {
classObjs.add(loadClass(className));
}
return classObjs;
}
@SuppressWarnings("rawtypes")
public Morphia get() throws ClassNotFoundException {
Mapper mapper = getMapper();
List<Class<?>> classObjs = getClassObjects(classes);
Morphia morphia = new Morphia(mapper, new HashSet<Class>(classObjs));
return morphia;
}
public void setBundleContext(BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
public void setClasses(List<String> classes) {
this.classes = classes;
}
}
Настройка Blueprint
Собрав все это вместе, используя Blueprint для создания экземпляров и подключения, получаем следующую конфигурацию:
<blueprint default-activation="eager"
xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.0.0">
<cm:property-placeholder persistent-id="io.modio.blog.osgi.morphia"/>
<bean id="mongoClient" class="com.mongodb.MongoClient">
<argument value="${io.modio.blog.osgi.morphia.mongodb.host}"/>
<argument value="${io.modio.blog.osgi.morphia.mongodb.port}"/>
</bean>
<bean id="morphiaFactory" class="io.modio.blog.osgi.morphia.MorphiaFactory">
<property name="bundleContext" ref="blueprintBundleContext"/>
<property name="classes">
<list>
<value>io.modio.blog.osgi.morphia.entity.Acl</value>
<value>io.modio.blog.osgi.morphia.Principal</value>
</list>
</property>
</bean>
<bean id="morphia" factory-ref="morphiaFactory" factory-method="get"/>
<bean id="datastoreFactory" class="io.modio.blog.osgi.morphia.DatastoreFactory">
<property name="mongoClient" ref="mongoClient"/>
<property name="morphia" ref="morphia"/>
<property name="database" value="${io.modio.blog.osgi.morphia.mongodb.db}"/>
</bean>
<bean id="datastore" factory-ref="datastoreFactory" factory-method="get"/>
<!-- DAOs -->
<bean id="aclDao" class="io.modio.blog.osgi.morphia.dao.AclDaoImpl">
<argument ref="datastore"/>
</bean>
<bean id="principalDao" class="io.modio.blog.osgi.morphia.dao.PrincipalDaoImpl">
<argument ref="datastore"/>
</bean>
<!-- Services -->
<service ref="aclDao" interface="io.modio.blog.osgi.morphia.dao.AclDao"/>
<service ref="principalDao" interface="io.modio.blog.osgi.morphia.dao.PrincipalDao"/>
</blueprint>
Приведенная выше конфигурация включает в себя два примера DAO, которые реализуют простую службу аутентификации / авторизации с использованием MongoDB в качестве постоянного хранилища.
Реализация DatastoreFactory проста:
public class DatastoreFactory {
private MongoClient mongoClient;
private Morphia morphia;
private String database;
public Datastore get() {
return morphia.createDatastore(mongoClient, database);
}
/** get/set methods */
...
}
Особенность Morphia Karaf
При развертывании в Karaf полезно сгруппировать все зависимости Morphia в одну повторно используемую функцию, на которую затем можно ссылаться из других функций:
<feature name="morphia" version="0.108" resolver="(obr)"> <feature version="[1.9.13,2)">jackson</feature> <bundle dependency="true">mvn:org.mongodb/mongo-java-driver/2.12.2</bundle> <bundle>wrap:mvn:com.thoughtworks.proxytoys/proxytoys/1.0</bundle> <bundle>mvn:org.mongodb.morphia/morphia/0.108</bundle> </feature>