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>