Spring-MongoDB 关键类的源码分析
本文分析的是 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。
二、源码分析
(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 接口(以下省略了部分代码):
1 public class SimpleMongoDbFactory implements DisposableBean, MongoDbFactory { 2 3 private final Mongo mongo; 4 private final String databaseName; 5 private final boolean mongoInstanceCreated; 6 private final UserCredentials credentials; 7 private final PersistenceExceptionTranslator exceptionTranslator; 8 private final String authenticationDatabaseName; 9 10 private WriteConcern writeConcern; 11 12 /** 13 * Creates a new {@link SimpleMongoDbFactory} instance from the given {@link MongoClientURI}. 14 * 15 * @param uri must not be {@literal null}. 16 * @throws UnknownHostException 17 * @since 1.7 18 */ 19 public SimpleMongoDbFactory(MongoClientURI uri) throws UnknownHostException { 20 this(new MongoClient(uri), uri.getDatabase(), true); 21 } 22 23 /** 24 * Creates a new {@link SimpleMongoDbFactory} instance from the given {@link MongoClient}. 25 * 26 * @param mongoClient must not be {@literal null}. 27 * @param databaseName must not be {@literal null}. 28 * @since 1.7 29 */ 30 public SimpleMongoDbFactory(MongoClient mongoClient, String databaseName) { 31 this(mongoClient, databaseName, false); 32 } 33 34 private SimpleMongoDbFactory(Mongo mongo, String databaseName, UserCredentials credentials, 35 boolean mongoInstanceCreated, String authenticationDatabaseName) { 36 37 if (mongo instanceof MongoClient && (credentials != null && !UserCredentials.NO_CREDENTIALS.equals(credentials))) { 38 throw new InvalidDataAccessApiUsageException( 39 "Usage of \'UserCredentials\' with \'MongoClient\' is no longer supported. Please use \'MongoCredential\' for \'MongoClient\' or just \'Mongo\'."); 40 } 41 42 Assert.notNull(mongo, "Mongo must not be null"); 43 Assert.hasText(databaseName, "Database name must not be empty"); 44 Assert.isTrue(databaseName.matches("[\\w-]+"), 45 "Database name must only contain letters, numbers, underscores and dashes!"); 46 47 this.mongo = mongo; 48 this.databaseName = databaseName; 49 this.mongoInstanceCreated = mongoInstanceCreated; 50 this.credentials = credentials == null ? UserCredentials.NO_CREDENTIALS : credentials; 51 this.exceptionTranslator = new MongoExceptionTranslator(); 52 this.authenticationDatabaseName = StringUtils.hasText(authenticationDatabaseName) ? authenticationDatabaseName 53 : databaseName; 54 55 Assert.isTrue(this.authenticationDatabaseName.matches("[\\w-]+"), 56 "Authentication database name must only contain letters, numbers, underscores and dashes!"); 57 } 58 59 /** 60 * @param client 61 * @param databaseName 62 * @param mongoInstanceCreated 63 * @since 1.7 64 */ 65 private SimpleMongoDbFactory(MongoClient client, String databaseName, boolean mongoInstanceCreated) { 66 67 Assert.notNull(client, "MongoClient must not be null!"); 68 Assert.hasText(databaseName, "Database name must not be empty!"); 69 70 this.mongo = client; 71 this.databaseName = databaseName; 72 this.mongoInstanceCreated = mongoInstanceCreated; 73 this.exceptionTranslator = new MongoExceptionTranslator(); 74 this.credentials = UserCredentials.NO_CREDENTIALS; 75 this.authenticationDatabaseName = databaseName; 76 } 77 78 }
核心构造方法:
/** * @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。
1 /** 2 * Creates a Mongo instance based on a (single) mongo node using a given ServerAddress and default options. 3 * 4 * @param addr the database address 5 * @param credentialsList the list of credentials used to authenticate all connections 6 * @param options default options 7 * @see com.mongodb.ServerAddress 8 * @since 2.11.0 9 */ 10 public MongoClient(final ServerAddress addr, final List<MongoCredential> credentialsList, final MongoClientOptions options) { 11 super(addr, credentialsList, options); 12 }
这个核心构造方法接收三个参数:
final ServerAddress addr:可以由 new ServerAddress(host, port) 构造。
final List<MongoCredential> credentialsList: 包含 username, password 等信息。
final MongoClientOptions options: 包含 MongoDB 数据库的配置信息。
(5) MongoCredential
MongoCredential 包含 username, password 等信息。
1 public final class MongoCredential { 2 3 private final AuthenticationMechanism mechanism; 4 private final String userName; 5 private final String source; 6 private final char[] password; 7 private final Map<String, Object> mechanismProperties; 8 9 // Other code 10 }
三、其他
Spring-MongoDB 整合与 Spring-MyBatis 相似。
Spring-MyBatis 的核心类是 SqlSessionTemplate,作用与 MongoTemplate 一样。