Table of Contents
דפוס Singleton הוא אחד דפוסי עיצוב הנפוצים ביותר ב- Java.זה מבטיח כי בכיתה יש בדיוק מקרה אחד ומספק נקודת גישה גלובלית של לדוגמה. ביישומים ארגוניים, דפוס זה הוא בדרך כלל מועסק לנהל חיבורי מסד נתונים - משאב קריטי, שאם לא היה נכון, יכול להוביל להידרדרות ביצועים, דליפות, והתנהגות בלתי צפויה.
מדוע להשתמש בתבנית Singleton לחיבורי מסד נתונים?
חיבורי מסד נתונים יקרים ליצור.כל חיבור צורב זיכרון, שקעים ברשת, ומשאבים בשרת מסד נתונים.ללא ניהול זהיר, יישום יכול במהירות להשמצה את מאגר החיבור, המוביל ל-Timeouts וכישלונות.תבנית Singleton מתייחסת לכך על ידי הבטחת שרק מקרה חיבור אחד קיים לאורך מחזור חיי היישום.
- (FLT:0) ניהול מקורות: FLT:1 מגביל את מספר הקשרים הפעילים, צמצום פניות על הלקוח ועל שרת מסד הנתונים.
- (ב) ⁇ :0) ,(ה) , כל המרכיבים חולקים את אותו הקשר, הימנעות מבדלי מדינה הנגרמים על ידי מספר מקרי חיבור.
- (ב) ⁇ :0) ⁇ : 1FLT 1 , מרכזי יצירת קשר, תצורה, ודמיע בכיתה אחת, פשטו עדכונים ובדיקות.
- (ב) ,0) ,מעגל החיים: ⁇ 1 (ב) נברא החיבור פעם אחת, וניתן להשתמש בו מחדש, תוך מחיקת הצורך באימות חוזר וידיעה.
עם זאת, חשוב לציין כי אחד FLT:0 אינו בטוח על ידי ברירת מחדל.אם חוטים מרובים משתמשים באותו הקשר במקביל, אתה חייב או לסנכרון גישה או להסתמך על מאגר חיבור.
יסודות דפוס ה- Singleton ב- Java
לכיתת יחידון יש שלושה מאפיינים עיקריים:
- (ב) ,0) ,1 ,5 ,1 , , , , , , , ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇
- (ב) ,0) ,1 ,5 ,1 , ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇
- (ב) ,0) , ⁇ ⁇ (הנקרא לעתים קרובות (FLT:1 ), אשר מחזיר את המקרה, יצירתו במידת הצורך.
כאשר החלים על חיבורי מסד נתונים, המחלקה לוטו מנהלת גם את אובייקט החיבור ומספקת שיטה כדי לאחזר אותו (למשל, FLT:2) נתחיל עם היישום הפשוט ביותר ולאחר מכן לשפר אותו עבור בטיחות חוט.
(לא-Thread-Safe) Singleton
היישום הפשוט ביותר הוא היערכות ראשונית, שבו המקרה נוצר כאשר הכיתה עמוסה.זה נמנע סינכרוניזציה לחלוטין אך עשוי לבזבז משאבים אם החיבור לעולם לא נעשה שימוש.
public class DatabaseConnection {
private static final DatabaseConnection INSTANCE = new DatabaseConnection();
private final Connection connection;
private DatabaseConnection() {
this.connection = createConnection();
}
public static DatabaseConnection getInstance() {
return INSTANCE;
}
public Connection getConnection() {
return connection;
}
private Connection createConnection() {
// JDBC connection logic (simplified)
try {
Class.forName("com.mysql.cj.jdbc.Driver");
return DriverManager.getConnection(
"jdbc:mysql://localhost:3306/mydb", "user", "password");
} catch (ClassNotFoundException | SQLException e) {
throw new RuntimeException("Failed to create database connection", e);
}
}
}
גישה עצלנית זו אינה בטוחה כי שני חוטים יכולים לראות בו-זמנית (FLT:4) וכל אחד מהם יוצר מקרה חדש. עבור מערכות ייצור, אתה צריך בטיחות חוט.
המונחים: mit e-Safe Singleton
1. שיטת סינכרונכרו
הוספת מילת המפתח לשיטת ה-FLT:6 מבטיחה שרק חוט אחד יכול לבצע אותה בזמן.זה פשוט אבל מציג עונש ביצועים כי כל קריאה ל-FLT 7 רוכש מנעול, גם לאחר שהדוגמה נוצרה.
public class DatabaseConnection {
private static DatabaseConnection instance;
private Connection connection;
private DatabaseConnection() {
connection = createConnection();
}
public static synchronized DatabaseConnection getInstance() {
if (instance == null) {
instance = new DatabaseConnection();
}
return instance;
}
public synchronized Connection getConnection() {
return connection;
}
private Connection createConnection() { /* ... */ }
}
שימו לב שגם לאחר הזיקוקציה, כל קריאה ל-FLT:9 דורשת סינכרוניזציה.השיטה הזו מקובלת רק אם הטון נוצר לעתים רחוקות מאוד, אך עבור חיבורי מסד נתונים עדיף להשתמש באחת הגישות הבאות.
Double-Checked Locking (DCL)
DCL מפחית סינכרון מעל פני ראש על ידי בדיקת המשתנים הראשונים ללא סינכרוניזציה, ורק סינכרון כאשר המקרה הוא למעשה FLT:10 עם זאת, זה מסובך לשמצה ליישם נכון בגרסאות Java ישנות יותר בשל מודל Java Memory של Java מאז Java 5, באמצעות מילת מפתח FLT:11 מבטיח כי המשתנים נקרא זיכרון הראשי וכי האובייקט בנוי חלקית אינו גלוי חוטים אחרים.
public class DatabaseConnection {
private static volatile DatabaseConnection instance;
private Connection connection;
private DatabaseConnection() {
connection = createConnection();
}
public static DatabaseConnection getInstance() {
if (instance == null) {
synchronized (DatabaseConnection.class) {
if (instance == null) {
instance = new DatabaseConnection();
}
}
}
return instance;
}
public Connection getConnection() {
return connection;
}
private Connection createConnection() { /* ... */ }
}
דפוס זה משמש נרחב ומציע ביצועים טובים כי סינכרוניזציה מתרחשת רק פעם אחת.עם זאת, מילת המפתח של FLT:13 יש מחסום זיכרון קל מעל ראש, אשר הוא רשלנות עבור רוב היישומים.
ביל פוטון (Initialization-on-Demand Holder Idiom)
טכניקה זו משתמשת בכיתה פנימית סטטית פרטית המחזיקה בדוגמה של הטון.המעמד הפנימי אינו טעון עד ל-FLT:14 שנקרא, מה שהופך את העצלן הראשוניזציה.זה בטוח על ידי עיצוב, כי JVM מבטיח כי ההתחלתיזציה המעמדית היא סדרה לא מסונכרן מפורשת או FLT:15 נדרש.
public class DatabaseConnection {
private Connection connection;
private DatabaseConnection() {
connection = createConnection();
}
private static class Holder {
static final DatabaseConnection INSTANCE = new DatabaseConnection();
}
public static DatabaseConnection getInstance() {
return Holder.INSTANCE;
}
public Connection getConnection() {
return connection;
}
private Connection createConnection() {
// JDBC setup
try {
Class.forName("com.mysql.cj.jdbc.Driver");
return DriverManager.getConnection(
"jdbc:mysql://localhost:3306/mydb", "user", "password");
} catch (ClassNotFoundException | SQLException e) {
throw new RuntimeException("Failed to create database connection", e);
}
}
}
דפוס זה מומלץ עבור רוב התרחישים כי הוא משלב אמפתיה עצלנית עם בטיחות חוט מובטח אפס סינכרוניזציה מעל ראש.זה גם עובד נכון עם סידוריזציה אם אתה ליישם FLT:17.
4. Enum Singleton
יהושע בלוך תומך בטורף (FLT:18) מבוסס יחיד בספרו (FLT:0Effective JavaFLT:1 ; Enums הם בלתי ניתנים לסידורים ולספק הגנה מפני התקפות השתקפות; הם גם חס ושלום; עם זאת, באמצעות חיבור מסד נתונים דורש חיבור להיות מוגדר בתוך המבנה של enum, אשר מבוצעת גם כאשר אתה רוצה להיות עצלן נוסף, אם אתה יכול להיות מוגדר מראש, אם אתה יכול להיות מוגדר על ידי הגדרה גבוהה יותר, אם אתה יכול להיות מוגדר על ידי הגדרה זו.
public enum DatabaseConnection {
INSTANCE;
private Connection connection;
DatabaseConnection() {
connection = createConnection();
}
public Connection getConnection() {
return connection;
}
private Connection createConnection() {
// JDBC setup
try {
Class.forName("com.mysql.cj.jdbc.Driver");
return DriverManager.getConnection(
"jdbc:mysql://localhost:3306/mydb", "user", "password");
} catch (ClassNotFoundException | SQLException e) {
throw new RuntimeException("Failed to create database connection", e);
}
}
}
(ב) ,הגישה האנתום היא תמציתית והחזקה, אם כי היא מדגישה את הקשר ברגע שכל התייחסות ל-FLT:21 נעשית.
חיבור: אלטרנטיבה טובה יותר?
בעוד דפוס ה- Singleton מתאים להגבלת חיבור אחד, רוב היישומים בעולם האמיתי דורשים (FLT:0 חיבור הבריכה) 1 [מאגר חיבור שומר על קבוצה של קשרים שניתן לשאול ולשוחזר, מתן יכולת מדרג טובה יותר ושימוש משאבים.תבנית Singleton עדיין ניתן להשתמש כדי לנהל את הבריכה עצמה - לעתים קרובות כמו רקטון - אבל הקשרים האישיים הם מוכנסים.
(ב) , (ב) , ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇ ⁇
public class DataSourceSingleton {
private static final HikariConfig config = new HikariConfig();
private static final HikariDataSource dataSource;
static {
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
config.setUsername("user");
config.setPassword("password");
config.setMaximumPoolSize(10);
config.setConnectionTimeout(30000);
dataSource = new HikariDataSource(config);
}
private DataSourceSingleton() {}
public static DataSource getDataSource() {
return dataSource;
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
}
הטון הזה מספק גלובאלי (FLT:24) כאשר אתה צריך חיבור, אתה קורא ל-FLT:25, אשר ללוות מהברומיאל לאחר השימוש, אתה סוגר את הקשר (אשר מחזיר אותו לבריכה).
מתי להשתמש Singleton לעומת בריכה
- (FLT:0Single Connection: FLT:1 Acceptable only for Databases (למשל, H2 in-memory), יישומים נמוכים מאוד, או כאשר מסד הנתונים הוא חנות ערכים פשוטה ועסקאות אינם מקבילים.
- (ב) לדוגמה של בריכה:0 (בלטינית:0) (בלטינית:0) ,(הגישה המומלצת ליישומים רבים הנקראים: הבריכה היא סינגלטון, אך חיבורים הם מרובים. השתמש בתבנית Singleton כדי להחזיק את ה-FLT:26.
הפרקטיקה הטובה ביותר והשיקולים
- (FLT:0) בטיחות קריאה: 1FLT תמיד להפוך את חוט הטלפוני שלך בטוח. השתמש ביל Pugh מחזיק idiom או enum עבור רוב המקרים.
- (FLT:0)Connection Configuration:FIRLT:1 חיצוניזציה של אישורי מסד הנתונים. השתמש במשתנה הסביבה, קבצי תצורה או ניהול סודות.לעולם לא קודר קוד קוד מקור.
- (FLT:0) Resource ניקוי: 1FLT 1 בסינגלטון אחד של חיבור אחד, עליך להבטיח שהקשר סגור כאשר היישום נסגר.לא ליישם את הסתום או להשתמש במחזור חיים מאוכל.
- (FLT:0)Errir Recovery: חיבורי מסד נתונים 1:1 יכולים ליפול.הטון שלך צריך לזהות קשרים מציפים ולבנות מחדש אותם.
- (ב) אם בקשתך היא מסמנת את הטון (למשל, בסביבה מבוזרת), ליישם את FLT:27 כדי להחזיר את המקרה הקיים.
- (FLT:0) הדגמה לאחור: השתקפות API יכול לקרוא לבונים פרטיים.
מלכודות נפוצות
- (ב) ,0) חיבור אחד ביישום אינטרנט רב-הנקרא: 1FreaLT (הדברים הבאים) מוביל לתכנים ולביצועים עניים במקום, תמיד להשתמש בבריכת חיבור.
- (ב) ,0) ,התקבלו קשרים קרובים: FLT:1 עם חיבור חד-טון, ייתכן שתמנעו ממנו לפתוח לנצח.
- (FLT:0) Improper כפול נבדק ללא תנודתי:Fillo 1: בגרסאות Java ישנות יותר, זה יכול לגרום באגים עדינים. השתמש ב-Sidiom או enum כדי למנוע מורכבות.
- (FLT:0) סדר ההקצאה הראשוני: 1FLT אם המעמד הבודד מתייחס לתחומים סטטיים אחרים שטרם מוקרן, ייתכן שתעמוד בפני תלות מעגלית.
- (FLT:0) קשיים: 1FLT 1 Singletons יכול לעשות בדיקות יחידה קשה כי הם נושאים את המדינה העולמית. השתמש הזרקת תלות להעביר חיבור או מקור נתונים, וללעג את הטון עבור בדיקות. לחלופין, לשקול שימוש דפוס ידידותי מבחן כמו דפוס המפעל בשילוב עם הזרקת תלות.
דוגמה אמיתית ל-Comput Pool
כדי להמחיש גישה לקריאה בייצור, הנה אחדטון שמנהל בריכת חיבור HikariCP באמצעות ביל Pugh מחזיק ב-idiom:
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class ConnectionPoolManager {
private final HikariDataSource dataSource;
private ConnectionPoolManager() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(System.getenv("DB_URL"));
config.setUsername(System.getenv("DB_USER"));
config.setPassword(System.getenv("DB_PASS"));
config.setMaximumPoolSize(10);
config.setMinimumIdle(2);
config.setConnectionTimeout(5000);
config.setIdleTimeout(300000);
config.setMaxLifetime(600000);
config.setPoolName("MyAppPool");
config.addDataSourceProperty("cachePrepStmts", "true");
config.addDataSourceProperty("prepStmtCacheSize", "250");
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
dataSource = new HikariDataSource(config);
}
private static class Holder {
static final ConnectionPoolManager INSTANCE = new ConnectionPoolManager();
}
public static ConnectionPoolManager getInstance() {
return Holder.INSTANCE;
}
public Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
public void shutdown() {
if (dataSource != null && !dataSource.isClosed()) {
dataSource.close();
}
}
// Optional: shutdown hook
public void registerShutdownHook() {
Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown));
}
}
שימוש:
try (Connection conn = ConnectionPoolManager.getInstance().getConnection()) {
// use connection
} catch (SQLException e) {
// handle
}
דפוס זה הוא חזק, חסום-בטוח, ומבחן הייצור.הוא מפריד תצורה של קוד ומבטיח כי קשרים משמשים מחדש ביעילות.
עקבו אחרי Singleton Database Connections
קוד בדיקה תלוי בסינגלטון יכול להיות מאתגר כי הטון שומר על המדינה במבחנים.
- (ה) [ה]הממשק: [ה] [ה] [ה]] [ה]] [ה]] [המילה]] היא ממשק עבור ספק החיבור שלך, לאחר מכן יש את הסימון ליישם אותו.
- (ב) ,0) פתח את הסינגלטון: FLT:1hil הוסף חבילה-פרטית או הגנה שיטה לאפסת את המקרה (רק לבדיקה) השתמש בהשתקפות במידת הצורך, אך להיות מודע לבטיחות חוט.
- (ב) [15] ,0) , זרקה של טוהר התלויות: במקום לקרוא ל-FLT:30 ישירות, להזריק את המקרה באמצעות בונה או סטטר.
public class UserService {
private final Connection connection;
public UserService(Connection connection) {
this.connection = connection;
}
// business methods
}
ביצירה, אתם קוראים ל-FLT:32 במבחנים, אתם יכולים להעביר חיבור לעג.
מסקנה
דפוס Singleton הוא כלי יקר לניהול חיבורי מסד נתונים ב- Java, אבל זה חייב להיות מיושם עם בטיחות חוט וניהול משאבים בראש. עבור רוב היישומים, בריכת חיבור עטוף על ידי יחידטון מספק את האיזון הטוב ביותר של ביצועים ואמינות. לבחור את ביל Pugh idiom או enum עבור הפשטות והבטיחות שלהם, ותמיד לתקן תצורה של חיבור אחד הוא לעתים רחוקות המתאים לייצור מערכות מרובות קרא על ידי תבנית עיצוב זה.
(ב) קרא עוד, נא להתייעץ עם ה-FLT הרשמי:0 (Oracle JDBC הדרכת JDBC) 1 ו- TheFLT:2HikariCP DocumentsFLT 3) עבור צלילה עמוקה יותר לתוך ג'אווה קונפליקציה, מתייחס למדריך מקיף של 4Baeldung:5.