Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement Decorator pattern in Spring Boot

I know how to implement and use a decorator pattern without Spring.

Because in this pattern you yourself control the process of creating components and you can perform dynamic behavior adding.

Below is an example of implementation without using Spring:

public class SimpleDecoratorApp {
    public static void main(String[] args) {
        SimplePrinter simplePrinter = new SimplePrinter();

        Printer decorated = new UpperCasePrinterDecorator(
                new AddAsterisksPrinterDecorator(simplePrinter)
        );
        decorated.print("hello");   // *** HELLO ***
    }
}

interface Printer {
    void print(String msg);
}

class SimplePrinter implements Printer {
    @Override
    public void print(String msg) {
        System.out.println(msg);
    }
}

abstract class PrinterDecorator implements Printer {
    protected Printer printer;
    public PrinterDecorator(Printer printer) {
        this.printer = printer;
    }
}

class UpperCasePrinterDecorator extends PrinterDecorator {
    public UpperCasePrinterDecorator(Printer printer) {
        super(printer);
    }
    @Override
    public void print(String msg) {
        String s = msg.toUpperCase();
        this.printer.print(s);
    }
}

class AddAsterisksPrinterDecorator extends PrinterDecorator {
    public AddAsterisksPrinterDecorator(Printer printer) {
        super(printer);
    }
    @Override
    public void print(String msg) {
        msg = "*** " + msg + " ***";
        this.printer.print(msg);
    }
}

I am interested in how to implement the same example but with the help of spring beans.

Because I don’t quite understand how to maintain flexibility in the ability to simply wrap with any number of decorators.

Because as I understand it - it will be implemented fixed in some separate component and I will have to create dozens of various separate components with the combinations of decorators I need.

like image 492
FreeOnGoo Avatar asked Feb 23 '19 12:02

FreeOnGoo


2 Answers

I haven't really understood what is your actual problem here, but I'll try anyway.
Say you have these classes

UpperCasePrinterDecorator
LowerCasePrinterDecorator
AddAsterisksPrinterDecorator 

Each of these require an instance of a Printer, which, let's say is provided as a Spring @Component. To use each decorator as Spring Bean you need to register it.

@Bean
@Qualifier("upperCase")
PrinterDecorator upperCasePrinterDecorator(final Printer printer) { // Injected automatically
   return new UpperCasePrinterDecorator(printer);
}

@Bean
@Qualifier("lowerCase")
PrinterDecorator lowerCasePrinterDecorator(final Printer printer) {
   return new LoweCasePrinterDecorator(printer);
}

@Bean
@Qualifier("asterisk")
PrinterDecorator addAsterisksPrinterDecorator(final Printer printer) {
   return new AddAsterisksPrinterDecorator(printer);
}

You can then use the @Qualifier annotation to get the right one @Autowired

@Autowired
@Qualifier("lowerCase")
private PrinterDecorator printer;
like image 137
LppEdd Avatar answered Oct 06 '22 12:10

LppEdd


Use a BeanPostProcessor. In this example I used P6DataSource as a decorator that wraps an existing DataSource. Using this approach, your existing code doesn't change since the interface is kept intact and @Autowire of the wrapped class works as expected.

import com.p6spy.engine.spy.P6DataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.scope.ScopedProxyUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;

import javax.sql.DataSource;

import static org.springframework.core.Ordered.LOWEST_PRECEDENCE;

@Configuration
@Order(LOWEST_PRECEDENCE)
@Slf4j
public class DataSourceBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof DataSource
            && !ScopedProxyUtils.isScopedTarget(beanName)) {
            log.debug("Decorating {} with P6Spy", bean);
            return new P6DataSource((DataSource) bean);
        } else {
            return bean;
        }
    }
}
like image 37
Archimedes Trajano Avatar answered Oct 06 '22 13:10

Archimedes Trajano