文章目录
- 引言
- 单例模式简介
- 定义与用途
- 实现方式:
- 饿汉式
- 懒汉式
- UML
- 使用场景
- 优势与劣势
- 单例模式在spring中的应用
- 饿汉式实现
- 懒汉式实现
- 数据库连接示例
- 代码地址
引言
单例模式是一种常用的设计模式,用于确保在一个程序中一个类只有一个实例,并且提供一个全局访问点。这种模式在需要严格控制资源访问和分配的情况下非常有用。
单例模式简介
定义与用途
单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点。这个模式经常用于控制资源的访问,如数据库连接或文件系统。
实现方式:
单例模式通常涉及以下几个关键步骤:
- 将构造函数设置为私有,防止外部通过new关键字创建实例。
- 在类内部创建一个类的实例。
- 提供一个公开的静态方法,供外部获取这个唯一的实例。
饿汉式
在类加载时就创建实例。这种方式是线程安全的,但可能会增加内存负担,因为实例不管是否需要都会被创建。
懒汉式
在第一次需要实例时才创建。这种方式可以节省资源,但需要考虑多线程环境下的线程安全问题。
UML
使用场景
- 资源共享控制:如数据库连接池或配置管理。
- 单一责任对象:当一个对象需要负责系统中的重要操作时。
- 节约资源:避免频繁地创建和销毁对象。
优势与劣势
- 优势
资源节约:减少了对象创建和销毁的开销。
全局访问:提供全局的访问点,易于管理。 - 劣势
全局依赖:过度使用可能导致代码之间的紧密耦合。
多线程问题:在多线程环境下可能会面临同步问题。
单例模式在spring中的应用
单例模式在Spring框架中的应用是其核心功能之一,特别体现在Spring的Bean容器管理中。在Spring框架中,单例模式的应用主要集中在以下几个方面:
Bean的默认作用域:
在Spring中,默认情况下,所有在Spring配置文件中定义的Bean都是以单例模式创建的。这意味着无论应用中有多少次对特定Bean的请求,Spring容器都会返回同一个Bean实例。
这种方式有助于节省资源,因为相同的Bean不会被多次创建。BeanFactory和ApplicationContext:
Spring的BeanFactory和ApplicationContext提供了Bean的创建和管理机制。它们作为Bean的容器,负责实例化、配置和组装Bean。
这些容器自身也是以单例模式运行的,确保整个应用中有一个统一的Bean管理中心。单例Bean的线程安全:
虽然单例Bean在Spring中只实例化一次,但Spring并不保证单例Bean是线程安全的。这意味着Bean的线程安全性依赖于其自身的实现。
在设计单例Bean时,需要考虑其在多线程环境下的行为,确保状态管理的正确性。单例模式与依赖注入(DI):
Spring使用依赖注入(DI)机制来管理Bean之间的依赖关系。在单例模式下,依赖注入确保相同的Bean实例被注入到其他需要它的Bean中。
这种机制简化了对象之间的关系,并提高了代码的可测试性和可维护性。配置与管理:
Spring的单例模式允许集中管理Bean的配置。由于每个Bean只有一个实例,因此其配置和属性只需要设置一次,而无需在每次使用时重新配置。
这对于管理大型应用中的配置和属性尤其有用,可以提高效率和一致性。
饿汉式实现
public class A {private static A obj = new A(); // 类加载时即创建实例private A() {} // 私有构造函数public static A getA() {return obj;}public void doSomething() {// 方法实现}}
懒汉式实现
public class A {private static A obj;private A() {} // 私有构造函数public static synchronized A getA() {if (obj == null) {obj = new A(); // 第一次调用时创建实例}return obj;}public void doSomething() {// 方法实现}
}
数据库连接示例
我们将通过创建一个JDBCSingleton类来展示单例设计模式的一个实际应用。这个类将被用于数据库操作,保证整个应用中只有一个数据库连接实例。
JDBCSingleton 类设计
JDBCSingleton类将包含:
- 一个私有构造函数,防止外部直接实例化。
- 一个私有静态实例jdbc,这是其自身的单一实例。
- 一个公共静态方法,允许外部世界获取这个静态实例。
假设我们已经在MySQL数据库中创建了一个名为userdata的表,它包含三个字段:uid、uname和upassword。数据库名称为ashwinirajput,用户名为root,密码为ashwini。
JDBCSingleton
public class JDBCSingleton {// 静态成员仅持有JDBCSingleton类的一个实例private static JDBCSingleton jdbc;// 私有构造器防止其他类实例化private JDBCSingleton() { }// 提供全局访问点public static JDBCSingleton getInstance() {if (jdbc == null) {jdbc = new JDBCSingleton();}return jdbc;}// 获取连接以进行插入、查看等操作private static Connection getConnection() throws ClassNotFoundException, SQLException {Connection con = null;Class.forName("com.mysql.jdbc.Driver");con = DriverManager.getConnection("jdbc:mysql://localhost:3306/ashwanirajput", "root", "ashwani");return con;}// 向数据库中插入记录public int insert(String name, String pass) throws SQLException {Connection c = null;PreparedStatement ps = null;int recordCounter = 0;try {c = getConnection();ps = c.prepareStatement("insert into userdata(uname, upassword) values(?, ?)");ps.setString(1, name);ps.setString(2, pass);recordCounter = ps.executeUpdate();} catch (Exception e) {e.printStackTrace();} finally {if (ps != null) {ps.close();}if (c != null) {c.close();}}return recordCounter;}// 从数据库中查看数据public void view(String name) throws SQLException {Connection con = null;PreparedStatement ps = null;ResultSet rs = null;try {con = this.getConnection();ps = con.prepareStatement("select * from userdata where uname = ?");ps.setString(1, name);rs = ps.executeQuery();while (rs.next()) {System.out.println("姓名: " + rs.getString(2) + "\t" + "密码: " + rs.getString(3));}} catch (Exception e) {System.out.println(e);} finally {if (rs != null) {rs.close();}if (ps != null) {ps.close();}if (con != null) {con.close();}}}// 更新给定用户名的密码public int update(String name, String password) throws SQLException {Connection c = null;PreparedStatement ps = null;int recordCounter = 0;try {c = this.getConnection();ps = c.prepareStatement("update userdata set upassword = ? where uname = '" + name + "'");ps.setString(1, password);recordCounter = ps.executeUpdate();} catch (Exception e) {e.printStackTrace();} finally {if (ps != null) {ps.close();}if (c != null) {c.close();}}return recordCounter;}// 从数据库删除数据public int delete(int userid) throws SQLException {Connection c = null;PreparedStatement ps = null;int recordCounter = 0;try {c = getConnection();ps = c.prepareStatement("delete from userdata where uid = '" + userid + "'");recordCounter = ps.executeUpdate();} catch (Exception e) {e.printStackTrace();} finally {if (ps != null) {ps.close();}if (c != null) {c.close();}}return recordCounter;}}
JDBCSingletonDemo
public class JDBCSingletonDemo {static int count = 1;static int choice;public static void main(String[] args) throws IOException {JDBCSingleton jdbc = JDBCSingleton.getInstance();BufferedReader br = new BufferedReader(new InputStreamReader(System.in));do {System.out.println("数据库操作");System.out.println("---------------------");System.out.println("1. 插入");System.out.println("2. 查看");System.out.println("3. 删除");System.out.println("4. 更新");System.out.println("5. 退出");System.out.print("\n请输入你想在数据库中执行的操作: ");choice = Integer.parseInt(br.readLine());switch (choice) {case 1: {System.out.print("输入要插入数据库的用户名: ");String username = br.readLine();System.out.print("输入要插入数据库的密码: ");String password = br.readLine();try {int i = jdbc.insert(username, password);if (i > 0) {System.out.println("第 " + (count++) + " 条数据已成功插入");} else {System.out.println("数据未插入");}} catch (Exception e) {System.out.println(e);}System.out.println("按回车键继续...");System.in.read();break;}case 2: {System.out.print("输入要查看的用户名: ");String username = br.readLine();try {jdbc.view(username);} catch (SQLException e) {System.out.println(e);}System.out.println("按回车键继续...");System.in.read();break;}case 3: {System.out.print("输入要删除的用户ID: ");int userid = Integer.parseInt(br.readLine());try {int i = jdbc.delete(userid);if (i > 0) {System.out.println("第 " + (count++) + " 条数据已成功删除");} else {System.out.println("数据未删除");}} catch (Exception e) {System.out.println(e);}System.out.println("按回车键继续...");System.in.read();break;}case 4: {System.out.print("输入要更新的用户名: ");String username = br.readLine();System.out.print("输入新密码: ");String password = br.readLine();try {int i = jdbc.update(username, password);if (i > 0) {System.out.println("第 " + (count++) + " 条数据已成功更新");}} catch (Exception e) {System.out.println(e);}System.out.println("按回车键继续...");System.in.read();break;}default:return;}} while (choice != 5);}
}
这个示例展示了如何在实际应用中使用单例模式来创建和管理数据库连接。通过这种方式,可以确保应用程序中的所有组件都使用相同的数据库连接实例,从而提高了效率和一致性。同时,这也减少了数据库连接的开销,因为连接实例只被创建一次并在整个应用中复用。
运行代码:
代码地址
23种设计模式相关代码后续会逐步提交到github上,方便学习,欢迎指点:
代码地址
https://github.com/RuofeiSun/lf-23Pattern