@SuppressWarnings("unchecked")
  private <T> T potentiallyReadOrResolveDbRef(
      DBRef dbref, TypeInformation<?> type, ObjectPath path, Class<?> rawType) {

    if (rawType.equals(DBRef.class)) {
      return (T) dbref;
    }

    Object object =
        dbref == null ? null : path.getPathItem(dbref.getId(), dbref.getCollectionName());

    return (T) (object != null ? object : read(type, readRef(dbref), path));
  }
  private ParameterValueProvider<MongoPersistentProperty> getParameterProvider(
      MongoPersistentEntity<?> entity,
      DBObject source,
      DefaultSpELExpressionEvaluator evaluator,
      ObjectPath path) {

    MongoDbPropertyValueProvider provider =
        new MongoDbPropertyValueProvider(source, evaluator, path);
    PersistentEntityParameterValueProvider<MongoPersistentProperty> parameterProvider =
        new PersistentEntityParameterValueProvider<MongoPersistentProperty>(
            entity, provider, path.getCurrentObject());

    return new ConverterAwareSpELExpressionParameterValueProvider(
        evaluator, conversionService, parameterProvider, path);
  }
  private <S extends Object> S read(
      final MongoPersistentEntity<S> entity, final DBObject dbo, final ObjectPath path) {

    final DefaultSpELExpressionEvaluator evaluator =
        new DefaultSpELExpressionEvaluator(dbo, spELContext);

    ParameterValueProvider<MongoPersistentProperty> provider =
        getParameterProvider(entity, dbo, evaluator, path);
    EntityInstantiator instantiator = instantiators.getInstantiatorFor(entity);
    S instance = instantiator.createInstance(entity, provider);

    final PersistentPropertyAccessor accessor =
        new ConvertingPropertyAccessor(entity.getPropertyAccessor(instance), conversionService);

    final MongoPersistentProperty idProperty = entity.getIdProperty();
    final S result = instance;

    // make sure id property is set before all other properties
    Object idValue = null;

    if (idProperty != null) {
      idValue = getValueInternal(idProperty, dbo, evaluator, path);
      accessor.setProperty(idProperty, idValue);
    }

    final ObjectPath currentPath =
        path.push(result, entity, idValue != null ? dbo.get(idProperty.getFieldName()) : null);

    // Set properties not already set in the constructor
    entity.doWithProperties(
        new PropertyHandler<MongoPersistentProperty>() {
          public void doWithPersistentProperty(MongoPersistentProperty prop) {

            // we skip the id property since it was already set
            if (idProperty != null && idProperty.equals(prop)) {
              return;
            }

            if (!dbo.containsField(prop.getFieldName()) || entity.isConstructorArgument(prop)) {
              return;
            }

            accessor.setProperty(prop, getValueInternal(prop, dbo, evaluator, currentPath));
          }
        });

    // Handle associations
    entity.doWithAssociations(
        new AssociationHandler<MongoPersistentProperty>() {
          public void doWithAssociation(Association<MongoPersistentProperty> association) {

            final MongoPersistentProperty property = association.getInverse();
            Object value = dbo.get(property.getFieldName());

            if (value == null || entity.isConstructorArgument(property)) {
              return;
            }

            DBRef dbref = value instanceof DBRef ? (DBRef) value : null;

            DbRefProxyHandler handler =
                new DefaultDbRefProxyHandler(
                    spELContext, mappingContext, MappingMongoConverter.this);
            DbRefResolverCallback callback =
                new DefaultDbRefResolverCallback(
                    dbo, currentPath, evaluator, MappingMongoConverter.this);

            accessor.setProperty(
                property, dbRefResolver.resolveDbRef(property, dbref, callback, handler));
          }
        });

    return result;
  }