Hello! I’ve made changes to allow the customization of the banner printer.

The Banner class was originally intended for printing a banner programmatically, but I needed a way to customize it more flexibly. Here is the code before my contribution:

class MyBanner(private val plugin: Plugin) : Banner {
    override fun printBanner(environment: Environment?, sourceClass: Class<*>?, out: PrintStream?) {
        with(plugin.logger) {
            info("██╗  ██╗██╗████████╗")
            info("██║ ██╔╝██║╚══██╔══╝")
            info("█████╔╝ ██║   ██║   ")
            info("██╔═██╗ ██║   ██║   ")
            info("██║  ██╗██║   ██║   ")
            info("╚═╝  ╚═╝╚═╝   ╚═╝   ")
        }
    }
}

So, I changed the printer to be changeable. Now I can create a banner more programmatically!

class MyBanner(private val plugin: Plugin) : Banner {
    override fun printBanner(environment: Environment?, sourceClass: Class<*>?, printStream: PrintStream) {
        with(printStream) {
            println("██╗  ██╗██╗████████╗")
            println("██║ ██╔╝██║╚══██╔══╝")
            println("█████╔╝ ██║   ██║   ")
            println("██╔═██╗ ██║   ██║   ")
            println("██║  ██╗██║   ██║   ")
            println("╚═╝  ╚═╝╚═╝   ╚═╝   ")
        }
    }
}
class MyBannerPrinter(private val plugin: Plugin) : SpringApplicationBannerPrinter {
    override fun print(environment: Environment?, sourceClass: Class<*>?, bannerMode: Banner.Mode): Banner {
        return when (bannerMode) {
            Banner.Mode.CONSOLE -> print(environment, sourceClass, System.out)
            Banner.Mode.LOG -> print(environment, sourceClass, plugin.logger)
            else -> PrintedBanner(getBanner(environment), sourceClass)
        }
    }
}

I tried to follow the existing code style, but if any parts need to be revised, I would appreciate it if you could make corrections. Thank you!

Comment From: philwebb

@vjh0107 Can you provide some more details about what you're trying to achieve. I'm having a bit of a hard time following the example code you've posted (probably because my Kotlin knowledge isn't that good).

What is the aim of the SpringApplicationBannerPrinter as opposed to the Banner? Does calling SpringApplication.setBanner(...) not work for your needs?

Comment From: vjh0107

@philwebb The Banner in SpringBoot was designed to focus solely on rendering the Banner itself, without depending on PrintStream, regardless of what PrintStream is used. However, there was a limitation in extending beyond Console and SpringApplication logger types for PrintStream, so I made it possible to replace the SpringApplicationBannerPrinter. This allows the Banner to focus solely on rendering, while the Printer handles rendering the printed values through PrintStream. My original code had the issue of being tightly coupled to a logger, printing directly through it instead of using PrintStream for the Banner!

Comment From: philwebb

I'm afraid I'm still not totally following. Perhaps a sample application might help (ideally in Java). Are you saying you want to be able to plug in a different PrintStream for the banner to use? Can you not do that already by having a Banner implementation that ignores the passed in PrintStream and prints to something else?

Comment From: vjh0107

That’s right, I’ve already been using it that way, but when someone else uses the banner I already made (or it can be SpringBootBanner), there’s a problem where they can’t set the PrintStream.

Comment From: philwebb

I wonder if you could use a decorator pattern to do this? I'm not too keen to introduce a new API just for this use-case.

I'm thinking something like:

public class BannerWrapper implements Banner {

    // Fields

    public BannerWrapper(Banner delegate, PrintStream out) {
       this.delegate = delegate;
       this.out = out;
    }

    public void printBanner(Environment environment, Class<?> sourceClass, PrintStream out) {
        this.delegate.printBanner(environment, sourceClass, this.out);
    }

}

If that doesn't work, I'd really like to see a sample application so we totally understand the requirements.

Comment From: vjh0107

Sadly, the wrapper method doesn’t change only the printing method of the SpringBootBanner. Here is my sample application that has only changed the way the banner is printed regardless of the banner.

https://github.com/vjh0107/spring-boot-pr-42284

Screenshot 2024-09-13 at 05 42 02

I designed it so that there are no changes to the public API!

Comment From: philwebb

Unfortunately there is new public API which increases the surface area of code we'd need to support. Looking at the sample, I'm afraid I don't see a use-case that will be popular enough for us to warrant the changes. If the decorator approach does not work, the other option you might consider is using the springBootBanner to reprint the banner after the application has started.

Thanks anyway for the suggestion.