Tomemos el ejemplo más simple: tienes una aplicación y solo usas el cargador de clases predeterminado. Tienes una clase que, por el motivo que sea, decides que no debe tener más de una instancia en la aplicación. (Piense en un escenario en el que varias personas trabajen en partes de la aplicación).
Si no está utilizando el marco Spring, el patrón Singleton asegura que no habrá más de una instancia de una clase en su aplicación. Esto se debe a que no puede crear instancias de la clase haciendo 'nuevo' porque el constructor es privado. La única forma de obtener una instancia de la clase es llamar a algún método estático de la clase (generalmente llamado 'getInstance') que siempre devuelve la misma instancia.
Decir que está utilizando el marco Spring en su aplicación, solo significa que, además de las formas habituales de obtener una instancia de la clase (métodos nuevos o estáticos que devuelven una instancia de la clase), también puede pedirle a Spring que lo obtenga una instancia de esa clase y Spring se asegurarán de que cada vez que le pida una instancia de esa clase, siempre devolverá la misma instancia, incluso si no escribió la clase usando el patrón Singleton. En otras palabras, incluso si la clase tiene un constructor público, si siempre le pide a Spring una instancia de esa clase, Spring solo llamará a ese constructor una vez durante la vida de su aplicación.
Normalmente, si está usando Spring, solo debe usar Spring para crear instancias, y puede tener un constructor público para la clase. Pero si su constructor no es privado, en realidad no está evitando que nadie cree nuevas instancias de la clase directamente, sin pasar por Spring.
Si realmente desea una única instancia de la clase, incluso si usa Spring en su aplicación y define la clase en Spring para que sea un singleton, la única forma de asegurarse de eso es también implementar la clase usando el patrón Singleton. Eso asegura que habrá una sola instancia, ya sea que las personas usen Spring para obtener una instancia o eviten Spring.