您的当前位置:首页正文

枚举的高阶用法之枚举里写方法以及注入spring的bean

2024-12-02 来源:个人技术集锦

1、前言

  一般我们使用枚举都是用来定义一些常量。比如我们需要一个表示订单类(pc订单、手机订单)的常量,那我们就可以使用枚举来实现,如下:

@AllArgsConstructor
public enum OrderTypeEnum{

    PC("PC", "电脑端"),
    PHONE("PHONE", "手机端");

    private String name;
    private String value;

}

  接着再写一下根据name查找对应枚举的方法,枚举就算完成了。但比如我们的业务场景是这样的:我们有一个OrderTypeService接口及其实现类。

public interface OrderTypeService {

    String processPc(String order);

    String processPhone(String order);
}
@Service
public class OrderTypeServiceImpl implements OrderTypeService {

    @Override
    public String processPc(String order) {
        //具体的处理pc订单的业务逻辑
        System.out.println("开始处理pc订单:" + order);
        return null;
    }

    @Override
    public String processPhone(String order) {
        //具体的处理phone订单的业务逻辑
        System.out.println("开始处理phone订单:" + order);
        return null;
    }
}

  根据接口的方法名我们一眼就知道,processPc是用来处理PC订单的;processPhone是用来处理PHONE订单的。然后再实际调用这两个方法的地方就需要我们进行if-else的判断。这里的if-else判断的缺点是:代码扩展性很差,如果以后又增加了其它订单类型,我们还需要改这里的if-else逻辑。

        if (OrderTypeEnum.PC.equals(orderTypeEnum)) {
            orderTypeService.processPc("order");
        } else if (OrderTypeEnum.PHONE.equals(orderTypeEnum)) {
            orderTypeService.processPhone("order");
        }

2、思考

  针对以上if-else判断存在的问题,那我们能不能把调用各自业务逻辑的代码挪到枚举里呢?肯定是可以的,本期我们就来实现一下。

2.1、通过@PostConstruct注解

在枚举内定义BeanInjector,再通过PostConstruct注解的方法给枚举的orderTypeService属性注入bean。

@AllArgsConstructor
public enum OrderTypeEnum {

    PC("PC", "电脑端") {
        @Override
        public void handle(String order) {
            orderTypeService.processPc(order);
        }
    },
    PHONE("PHONE", "手机端") {
        @Override
        public void handle(String order) {
            orderTypeService.processPhone(order);
        }
    };
    private String name;
    private String value;


    protected static OrderTypeService orderTypeService;

    public abstract void handle(String order);

    @Component
    public static class BeanInjector {
        @Autowired
        private OrderTypeService orderTypeService;

        @PostConstruct
        public void postConstruct() {
            OrderTypeEnum.orderTypeService = this.orderTypeService;
        }
    }
}

调用代码:

        OrderTypeEnum pcOrderType = OrderTypeEnum.PC;
        pcOrderType.handle("1");

输出结果:

开始处理pc订单:1

2.2、在枚举内实现ApplicationContextAware回调接口,获取到ApplicationContext,再从context里获取我们需要的bean

@AllArgsConstructor
public enum OrderTypeEnum {

    PC("PC", "电脑端") {
        @Override
        public void handle(String order) {
            OrderTypeService orderTypeService = OrderTypeEnum.ApplicationContextProvider.
                    getApplicationContext().getBean(OrderTypeService.class);
            orderTypeService.processPc(order);
        }
    },
    PHONE("PHONE", "手机端") {
        @Override
        public void handle(String order) {
            OrderTypeService orderTypeService = OrderTypeEnum.ApplicationContextProvider.
                    getApplicationContext().getBean(OrderTypeService.class);
            orderTypeService.processPhone(order);
        }
    };
    private String name;
    private String value;


    public abstract void handle(String order);

    @Component
    public static class ApplicationContextProvider implements ApplicationContextAware {
        private static ApplicationContext context;

        @Override
        public void setApplicationContext(ApplicationContext applicationContext) {
            context = applicationContext;
        }

        public static ApplicationContext getApplicationContext() {
            return context;
        }
    }
}

调用代码同2.1
输出结果同2.1

2.3、还是需要实现ApplicationContextAware接口,但这次不是直接使用ApplicationContext,而是把ApplicationContext里的bean值直接注入到我们的枚举字段中。

public enum OrderTypeEnum {

    PC("PC", "电脑端") {
        @Override
        public void handle(String order) {
            orderTypeService.processPc(order);
        }
    },
    PHONE("PHONE", "手机端") {
        @Override
        public void handle(String order) {
            orderTypeService.processPhone(order);
        }
    };
    private String name;
    private String value;

    OrderTypeEnum(String name, String value) {
        this.name = name;
        this.value = value;
    }

    public abstract void handle(String order);

    @Autowired
    OrderTypeService orderTypeService;
}
@Component
public class OrderTypeEnumInjector implements ApplicationContextAware {
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        for (OrderTypeEnum orderTypeEnum : OrderTypeEnum.values()) {
            applicationContext.getAutowireCapableBeanFactory().autowireBean(orderTypeEnum);
        }
    }
}

调用代码同2.1
输出结果同2.1
  这里重点是使用applicationContext.getAutowireCapableBeanFactory().autowireBean。它可以把枚举中使用了@Autowired注解的字段和applicationContext中的bean根据类型和名称做匹配,匹配到之后就会把bean注入到该字段中。从而枚举中的orderTypeService就有值了。

3、延申

  applicationContext.getAutowireCapableBeanFactory().autowireBean能否给普通的类(没有被spring管理)的字段注入bean呢?答案也是可以的,这里最好把这个普通对象定义成单例的,这样我们给字段注入一次bean就可以一直使用了,否则每次new 的时候还需要重新注入bean。

public class OrderTypeManager {

    private OrderTypeManager(){}

    private static OrderTypeManager orderTypeManager = null;

    public synchronized static OrderTypeManager getInstance(){
        if(orderTypeManager == null){
            orderTypeManager = new OrderTypeManager();
        }
        return orderTypeManager;
    }

    @Autowired
    private OrderTypeService orderTypeService;

    public  void test(){
        orderTypeService.processPhone("2");
    }
}

  如上定义了一个单例的OrderTypeManager,但是OrderTypeManager没有被spring所管理,那我们怎么把OrderTypeService这个bean注入到orderTypeService字段里呢?

@Component
public class OrderTypeEnumInjector implements ApplicationContextAware {
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        for (OrderTypeEnum orderTypeEnum : OrderTypeEnum.values()) {
            applicationContext.getAutowireCapableBeanFactory().autowireBean(orderTypeEnum);
        }
        OrderTypeManager orderTypeManager = OrderTypeManager.getInstance();
        applicationContext.getAutowireCapableBeanFactory().autowireBean(orderTypeManager);
    }
}

调用代码:

        OrderTypeManager orderTypeManager = OrderTypeManager.getInstance();
        orderTypeManager.test();

输出结果:

开始处理phone订单:2
显示全文