Vamos pegar o exemplo mais simples: você tem um aplicativo e apenas usa o carregador de classe padrão. Você tem uma classe que, por qualquer motivo, decide que não deve haver mais de uma instância no aplicativo. (Pense em um cenário onde várias pessoas trabalham em partes do aplicativo).
Se você não estiver usando a estrutura Spring, o padrão Singleton garante que não haverá mais de uma instância de uma classe em seu aplicativo. Isso ocorre porque você não pode instanciar instâncias da classe fazendo 'novo' porque o construtor é privado. A única maneira de obter uma instância da classe é chamar algum método estático da classe (geralmente chamado de 'getInstance') que sempre retorna a mesma instância.
Dizer que você está usando o framework Spring em seu aplicativo, significa apenas que além das formas regulares de obter uma instância da classe (métodos novos ou estáticos que retornam uma instância da classe), você também pode pedir ao Spring para obter uma instância dessa classe e o Spring garantirão que sempre que você solicitar uma instância dessa classe, ela sempre retornará a mesma instância, mesmo que você não tenha escrito a classe usando o padrão Singleton. Em outras palavras, mesmo se a classe tiver um construtor público, se você sempre pedir ao Spring uma instância dessa classe, o Spring só chamará esse construtor uma vez durante a vida de seu aplicativo.
Normalmente, se você estiver usando o Spring, deve usar o Spring apenas para criar instâncias e pode ter um construtor público para a classe. Mas se seu construtor não for privado, você não está realmente impedindo ninguém de criar novas instâncias da classe diretamente, ignorando o Spring.
Se você realmente deseja uma única instância da classe, mesmo se usar Spring em seu aplicativo e definir a classe em Spring como um singleton, a única maneira de garantir isso também é implementar a classe usando o padrão Singleton. Isso garante que haverá uma única instância, quer as pessoas usem Spring para obter uma instância ou contornar o Spring.