策略+享元

约 643 字大约 2 分钟

策略+享元

问题

本章使用 Spring 的容器管理 Service,在处理业务时候根据映射进行转换,从 ApplicationContext 中获取到策略,极大减少了手动编写代码管理 Bean 的问题。 来看一下目录:

.
├── ApplicationContextProvider.java
└── ApplicationContextProviderSetter.java

0 directories, 2 files

.
├── dict
│   └── FourPolicyNameEnum.java
├── service
│   ├── FourPolicyService.java
│   └── impl
│       ├── HelloFourPolicyServiceImpl.java
│       ├── NilFourPolicyServiceImpl.java
│       └── WorldFourPolicyServiceImpl.java
└── web
    ├── FourPolicyController.http
    └── FourPolicyController.java

4 directories, 7 file

让我门来看一下 Controller 代码:


@Slf4j
@RequestMapping("/four")
@RestController
public class FourPolicyController {

    @GetMapping("/policy")
    public String policy(HelloRequest request) {
        FourPolicyService fourPolicyService = (FourPolicyService) ApplicationContextProvider.getBean(FourPolicyNameEnum.getPolicyName(request.getPolicy()));
        String print = fourPolicyService.print(request);
        log.info("result:{}", print);
        return print;
    }

}

让我门来看一下 Service 代码:

public interface FourPolicyService {

    /**
     * @param helloRequest 请求
     */
    String print(HelloRequest helloRequest);
}

@Service
public class HelloFourPolicyServiceImpl implements FourPolicyService {

    @Override
    public String print(HelloRequest helloRequest) {
        return helloRequest.getName() + "hello";
    }

}

@Service
public class WorldFourPolicyServiceImpl implements FourPolicyService {
    @Override
    public String print(HelloRequest helloRequest) {
        return helloRequest.getName() + "world";
    }
}

@Service
public class NilFourPolicyServiceImpl implements FourPolicyService {

    @Override
    public String print(HelloRequest helloRequest) {
        return ThreePolicyManager.NIL;
    }

}

Bean 如果实现了 BeanNameAware ,在 Bean 生命周期中可以设定 Bean 的名称( void setBeanName(String name) ),这里不建议使用, 使用 Spring 规约(类似 Springboot 规约大于配置一样),这样的话 Service 名称为 Class 名称第一个字母小写,根据这一规则创建映射:


@Getter
@AllArgsConstructor
public enum FourPolicyNameEnum {

    HELLO("hello", "helloFourPolicyServiceImpl"),

    WORLD("world", "worldFourPolicyServiceImpl"),

    NIL("Nil", "nilFourPolicyServiceImpl"),

    ;

    private final String code;
    private final String policyName;

    public static String getPolicyName(String code) {
        if (StringUtils.isEmpty(code)) {
            return NIL.getPolicyName();
        }

        for (FourPolicyNameEnum value : FourPolicyNameEnum.values()) {
            if (value.getCode().equals(code)) {
                return value.getPolicyName();
            }
        }

        return NIL.getPolicyName();
    }
}

ApplicationContext 保存和获取方式如下:


public final class ApplicationContextProvider {

    private static ApplicationContext applicationContext;

    public static void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ApplicationContextProvider.applicationContext = applicationContext;
    }

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    public static Object getBean(String beanName) {
        return applicationContext.getBean(beanName);
    }

    public static <T> T getBean(Class<T> requiredType) {
        return applicationContext.getBean(requiredType);
    }

}

@Component
public class ApplicationContextProviderSetter implements ApplicationContextAware {

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ApplicationContextProvider.setApplicationContext(applicationContext);
    }
}

然后进行测试:


 Testing started at 09:57 ...

 HTTP/1.1 200
 Content-Type: text/plain;charset=UTF-8
 Content-Length: 8
 Date: Sun, 25 Apr 2020 01:57:36 GMT
 Keep-Alive: timeout=60
 Connection: keep-alive

 > 2020-04-26T095736.200.txt > renhello

 Response code: 200; Time: 347ms; Content length: 8 bytes
 HTTP/1.1 200
 Content-Type: text/plain;charset=UTF-8
 Content-Length: 8
 Date: Sun, 25 Apr 2020 01:57:36 GMT
 Keep-Alive: timeout=60
 Connection: keep-alive

 > 2020-04-26T095737.200.txt > renworld

总结

优点: 不需要编写额外的代码对 Bean 进行保存,使用的时候按照规约从 ApplicationContext 获取。 缺点: 使用 BeanName 获取 Bean 的时候需要进行强转,不如自己编写容器获取看起来更加优雅。 需要在测试阶段测试出 BeanName-helloFourPolicyServiceImpl(HELLO("hello", "helloFourPolicyServiceImpl"))是否正确。

提示: 如果在创表初期,可以直接将 ServiceImpl 名称存到数据库,如果业务已经开展,多方使用,字断不可更改,而且不想增加字断的话,需要在代码中增加映射,请大家酌情处理。

Github 地址open in new window