如何在 Spring Boot 中实现装饰模式

Fre*_*Goo 7 java spring design-patterns spring-boot

我知道如何在没有 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我对如何在 spring bean 的帮助下实现相同的示例感兴趣。

因为我不太明白如何在简单地包装任意数量的装饰器的能力方面保持灵活性。

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

Lpp*_*Edd 6

我还没有真正理解您在这里的实际问题是什么,但无论如何我都会尝试。
假设你有这些课程

UpperCasePrinterDecorator
LowerCasePrinterDecorator
AddAsterisksPrinterDecorator 
Run Code Online (Sandbox Code Playgroud)

每一个都需要一个 a 的实例,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);
}
Run Code Online (Sandbox Code Playgroud)

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

@Autowired
@Qualifier("lowerCase")
private PrinterDecorator printer;
Run Code Online (Sandbox Code Playgroud)

  • 问题是我不需要单个组件——我需要它们的组合。并且可以有数百个装饰器 (2认同)

Arc*_*ano 6

用一个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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)