Dynamic call when there are multiple implementation classes in the interface

Posted by Tarsonis21 on Mon, 01 Nov 2021 06:43:36 +0100

Copyright notice: This article is the original article of CSDN blogger "Emine Wang", which follows the CC 4.0 BY-SA copyright agreement. Please attach the original source link and this notice for reprint.
Original link: https://blog.csdn.net/a718515028/article/details/72457436

Raising questions

In practice, we often encounter one interface and multiple implementation classes, and different implementation classes will be used under different conditions. In terms of usage, it is similar to SPI,
However, since the use of SPI is not very convenient, what should we do? We can use getBeansOfType of ApplicationContext to achieve the results we need.

First, let's look at the signature of this method

<T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException;

From the above code, we can see that this method can return all implementation classes of an interface (provided that all implementation classes must be managed by the Spring IoC container).

org.springframework.beans and org.springframework.context are the foundation of Spring IoC container,
One of the important classes is BeanFactory. BeanFactory is the core interface of IoC container, and its responsibilities include instantiating, locating and configuring applications
Objects and establishing dependencies between these objects.

As a subclass of BeanFactory, ApplicationContext has been greatly enhanced in the function of Bean management and is easier to integrate with Spring AOP.
ApplicationContext is a practical application method.

example

The design is as follows: there is an interface for traffic mode. The interface has two modes: one is to query the cost and the other is to query the type of the traffic mode. At the same time, we can use an enumeration type class to identify the traffic type.

We also need a factory class to find the Bean instance of the traffic type according to the traffic type ID, so as to use the instance to obtain the details of the traffic type and the operation of the traffic type.

The code is as follows:
Interface:

/**
 * mode of transportation
 */
public interface TrafficMode {
 
    /**
     * Query traffic mode code
     * @return code
     */
    TrafficCode getCode();
 
    /**
     * Query the cost of transportation mode, unit: minute
     * @return cost
     */
    Integer getFee();
 
}
/**
 * Traffic type enumeration
 */
public enum TrafficCode {
 
    TRAIN,
    BUS
 
}

Interface has two implementation classes:

/**
 * Automobile mode
 */
@Component
public class BusMode implements TrafficMode {
 
    @Override
    public TrafficCode getCode() {
        return TrafficCode.BUS;
    }
 
    @Override
    public Integer getFee() {
        return 10000;
    }
 
}
/**
 * Train mode
 */
@Component
public class TrainMode implements TrafficMode {
 
    @Override
    public TrafficCode getCode() {
        return TrafficCode.TRAIN;
    }
 
    @Override
    public Integer getFee() {
        return 9000;
    }
 
}

Factory type:

/**
 * Transportation mode factory
 */
@Component
public class TrafficModeFactory implements ApplicationContextAware {
 
    private static Map<TrafficCode, TrafficMode> trafficBeanMap;
 
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, TrafficMode> map = applicationContext.getBeansOfType(TrafficMode.class);
        trafficBeanMap = new HashMap<>();
        map.forEach((key, value) -> trafficBeanMap.put(value.getCode(), value));
    }
 
    public static <T extends TrafficMode> T getTrafficMode(TrafficCode code) {
        return (T)trafficBeanMap.get(code);
    }
 
}

verification
After having the above code, let's see the effect through unit test. The code fragment of unit test is as follows:

    @Test
    public void testGetTrafficMode() {
        TrafficMode mode = TrafficModeFactory.getTrafficMode(TrafficCode.BUS);
        Assert.assertEquals(mode.getFee().intValue(), 10000);
 
        mode = TrafficModeFactory.getTrafficMode(TrafficCode.TRAIN);
        Assert.assertEquals(mode.getFee().intValue(), 9000);
    }

Topics: Java Spring Back-end