如何在Spring Boot中实现Decorator模式

时间:2019-02-23 12:23:26

标签: java spring spring-boot design-patterns

我知道如何在没有Spring的情况下实现和使用装饰器模式。

由于采用这种模式,您可以自己控制创建组件的过程,并且可以执行动态行为添加。

以下是不使用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);
    }
}

我对如何实现相同的示例感兴趣,但需要借助Spring bean。

因为我不太了解如何灵活地包装任意数量的装饰器。

因为据我所知-它将固定在一些单独的组件中实现,因此我将不得不用我需要的装饰器组合来创建数十个各种单独的组件。

2 个答案:

答案 0 :(得分:2)

我还不太了解您在这里遇到的实际问题,但是我还是会尝试的。
假设您有这些课程

UpperCasePrinterDecorator
LowerCasePrinterDecorator
AddAsterisksPrinterDecorator 

每一个都需要一个Printer的实例,可以说它是作为一个Spring @Component提供的。要将每个装饰器用作Spring Bean,您需要注册它。

@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);
}

然后您可以使用@Qualifier批注来获取正确的@Autowired

@Autowired
@Qualifier("lowerCase")
private PrinterDecorator printer;

答案 1 :(得分:1)

使用 BeanPostProcessor。在此示例中,我使用 P6DataSource 作为包装现有 DataSource 的装饰器。使用这种方法,您现有的代码不会发生变化,因为接口保持不变,并且包装类的 @Autowire 按预期工作。

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;
        }
    }
}