Spring-MongoDB 关键类的源码分析

时间:2022-01-25 18:39:11

本文分析的是 spring-data-mongodb-1.9.2.RELEASE.jar 和 mongodb-driver-core-3.2.2.jar。

一、UML Class Diagram

核心类是 MongoTemplate,下面这张 UML 类图涉及了主要的类,省略了次要的类。

涉及的类: MongoTemplate,

MongoOperations,

MongoDbFactory,

SimpleMongoDbFactory,

Mongo,

MongoClient,

MongoCredential,

ServerAddress。

Spring-MongoDB 关键类的源码分析

二、源码分析

(1) MongoTemplate

MongoTemplate 是 Spring-MongoDB 整合的核心类,它实现了 MongoOperations 接口(该接口定义了 CRUD 操作)。

MongoTemplate 有个核心的构造方法(其他重载的构造方法最终都会调用这个构造方法):

    /**
* Constructor used for a basic template configuration.
*
* @param mongoDbFactory must not be {@literal null}.
* @param mongoConverter
*/
public MongoTemplate(MongoDbFactory mongoDbFactory, MongoConverter mongoConverter) { Assert.notNull(mongoDbFactory); this.mongoDbFactory = mongoDbFactory;
this.exceptionTranslator = mongoDbFactory.getExceptionTranslator();
this.mongoConverter = mongoConverter == null ? getDefaultMongoConverter(mongoDbFactory) : mongoConverter;
this.queryMapper = new QueryMapper(this.mongoConverter);
this.updateMapper = new UpdateMapper(this.mongoConverter); // We always have a mapping context in the converter, whether it's a simple one or not
mappingContext = this.mongoConverter.getMappingContext();
// We create indexes based on mapping events
if (null != mappingContext && mappingContext instanceof MongoMappingContext) {
indexCreator = new MongoPersistentEntityIndexCreator((MongoMappingContext) mappingContext, mongoDbFactory);
eventPublisher = new MongoMappingEventPublisher(indexCreator);
if (mappingContext instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) mappingContext).setApplicationEventPublisher(eventPublisher);
}
}
}

可以看到,这个构造方法至少需要一个参数 mongoDbFactory,MongoTemplate 类中其他字段都可以内部构造出来。

(2)MongoDbFactory

MongoDbFactory 是一个接口,用于新建 DB 实例。

它的实现之一是 SimpleMongoDbFactory。

public interface MongoDbFactory {

    /**
* Creates a default {@link DB} instance.
*
* @return
* @throws DataAccessException
*/
DB getDb() throws DataAccessException; /**
* Creates a {@link DB} instance to access the database with the given name.
*
* @param dbName must not be {@literal null} or empty.
* @return
* @throws DataAccessException
*/
DB getDb(String dbName) throws DataAccessException; /**
* Exposes a shared {@link MongoExceptionTranslator}.
*
* @return will never be {@literal null}.
*/
PersistenceExceptionTranslator getExceptionTranslator();
}

(3) SimpleMongoDbFactory

SimpleMongoDbFactory 实现了 MongoDbFactory 接口(以下省略了部分代码):

 public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory {

     private final Mongo mongo;
private final String databaseName;
private final boolean mongoInstanceCreated;
private final UserCredentials credentials;
private final PersistenceExceptionTranslator exceptionTranslator;
private final String authenticationDatabaseName; private WriteConcern writeConcern; /**
* Creates a new {@link SimpleMongoDbFactory} instance from the given {@link MongoClientURI}.
*
* @param uri must not be {@literal null}.
* @throws UnknownHostException
* @since 1.7
*/
public SimpleMongoDbFactory(MongoClientURI uri) throws UnknownHostException {
this(new MongoClient(uri), uri.getDatabase(), true);
} /**
* Creates a new {@link SimpleMongoDbFactory} instance from the given {@link MongoClient}.
*
* @param mongoClient must not be {@literal null}.
* @param databaseName must not be {@literal null}.
* @since 1.7
*/
public SimpleMongoDbFactory(MongoClient mongoClient, String databaseName) {
this(mongoClient, databaseName, false);
} private SimpleMongoDbFactory(Mongo mongo, String databaseName, UserCredentials credentials,
boolean mongoInstanceCreated, String authenticationDatabaseName) { if (mongo instanceof MongoClient && (credentials != null && !UserCredentials.NO_CREDENTIALS.equals(credentials))) {
throw new InvalidDataAccessApiUsageException(
"Usage of 'UserCredentials' with 'MongoClient' is no longer supported. Please use 'MongoCredential' for 'MongoClient' or just 'Mongo'.");
} Assert.notNull(mongo, "Mongo must not be null");
Assert.hasText(databaseName, "Database name must not be empty");
Assert.isTrue(databaseName.matches("[\\w-]+"),
"Database name must only contain letters, numbers, underscores and dashes!"); this.mongo = mongo;
this.databaseName = databaseName;
this.mongoInstanceCreated = mongoInstanceCreated;
this.credentials = credentials == null ? UserCredentials.NO_CREDENTIALS : credentials;
this.exceptionTranslator = new MongoExceptionTranslator();
this.authenticationDatabaseName = StringUtils.hasText(authenticationDatabaseName) ? authenticationDatabaseName
: databaseName; Assert.isTrue(this.authenticationDatabaseName.matches("[\\w-]+"),
"Authentication database name must only contain letters, numbers, underscores and dashes!");
} /**
* @param client
* @param databaseName
* @param mongoInstanceCreated
* @since 1.7
*/
private SimpleMongoDbFactory(MongoClient client, String databaseName, boolean mongoInstanceCreated) { Assert.notNull(client, "MongoClient must not be null!");
Assert.hasText(databaseName, "Database name must not be empty!"); this.mongo = client;
this.databaseName = databaseName;
this.mongoInstanceCreated = mongoInstanceCreated;
this.exceptionTranslator = new MongoExceptionTranslator();
this.credentials = UserCredentials.NO_CREDENTIALS;
this.authenticationDatabaseName = databaseName;
} }

核心构造方法:

/**
* @param client
* @param databaseName
* @param mongoInstanceCreated
* @since 1.7
*/
private SimpleMongoDbFactory(MongoClient client, String databaseName, boolean mongoInstanceCreated) { Assert.notNull(client, "MongoClient must not be null!");
Assert.hasText(databaseName, "Database name must not be empty!"); this.mongo = client;
this.databaseName = databaseName;
this.mongoInstanceCreated = mongoInstanceCreated;
this.exceptionTranslator = new MongoExceptionTranslator();
this.credentials = UserCredentials.NO_CREDENTIALS;
this.authenticationDatabaseName = databaseName;
}

第一个参数 MongoClient client(MongoClient): 包含 host, port, username, password 等信息。

(4)MongoClient 与 Mongo

MongoClient 继承了 Mongo。

 /**
* Creates a Mongo instance based on a (single) mongo node using a given ServerAddress and default options.
*
* @param addr the database address
* @param credentialsList the list of credentials used to authenticate all connections
* @param options default options
* @see com.mongodb.ServerAddress
* @since 2.11.0
*/
public MongoClient(final ServerAddress addr, final List<MongoCredential> credentialsList, final MongoClientOptions options) {
super(addr, credentialsList, options);
}

这个核心构造方法接收三个参数:

final ServerAddress addr:可以由 new ServerAddress(host, port) 构造。

final List<MongoCredential> credentialsList: 包含 username, password 等信息。

final MongoClientOptions options: 包含 MongoDB 数据库的配置信息。

(5) MongoCredential

MongoCredential 包含 username, password 等信息。

 public final class MongoCredential {

     private final AuthenticationMechanism mechanism;
private final String userName;
private final String source;
private final char[] password;
private final Map<String, Object> mechanismProperties; // Other code
}

三、其他

Spring-MongoDB 整合与 Spring-MyBatis 相似。

Spring-MyBatis 的核心类是 SqlSessionTemplate,作用与 MongoTemplate 一样。