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.
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;
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;
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With