在spring中基于字符串参数调用不同的服务

时间:2017-05-06 13:21:31

标签: java spring-boot

在我的控制器中,我收到一个字符串参数,根据该参数我需要决定调用哪个服务,如何使用Spring注释在Spring Boot应用程序中执行相同操作?

例如:我们有不同类型的汽车。现在,根据请求中的参数,我应该能够决定应该拨打哪个特定的汽车服务。

如何在Spring Boot中使用注释工厂,并且应该根据输入从该工厂返回对象。

1 个答案:

答案 0 :(得分:0)

我记得几年前实现了对这种方法的支持,我相信受到启发并使用https://www.captechconsulting.com/blogs/combining-strategy-pattern-and-spring作为我的实用程序库的入口点,在您方便时使用以下代码片段:

<强> Strategy.java

package ...

@Documented
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface Strategy {

    Class<?> type();

    String[] profiles() default {};
}

<强> StrategyFactory.java

package ...

public class StrategyFactory {

    private static final Logger LOG = Logger.getLogger( StrategyFactory.class );

    private Map<Class<?>, Strategy> strategiesCache = new HashMap<Class<?>, Strategy>();

    private String[] packages;

    @PostConstruct
    public void init() {
        if (this.packages != null) {
            Set<Class<?>> annotatedClasses = new HashSet<Class<?>>();
            for (String pack : this.packages) {
                Reflections reflections = new Reflections( pack );
                annotatedClasses.addAll( reflections.getTypesAnnotatedWith( Strategy.class ) );
            }
            this.sanityCheck( annotatedClasses );
        }
    }

    public <T> T getStrategy(Class<T> strategyClass) {
        return this.getStrategy( strategyClass, null );
    }

    @SuppressWarnings("unchecked")
    public <T> T getStrategy(Class<T> strategyClass, String currentProfile) {
        Class<T> clazz = (Class<T>) this.findStrategyMatchingProfile( strategyClass, currentProfile );
        if (clazz == null) {
            throw new StrategyNotFoundException( String.format( "No strategies found of type '%s', are the strategies marked with @Strategy?", strategyClass.getName() ) );
        }
        try {
            return (T) clazz.newInstance();
        } catch (Exception e) {
            throw ExceptionUtils.rethrowAs( e, StrategyException.class );
        }
    }

    /**
     * Checks to make sure there is only one strategy of each type(Interface) annotated for each profile Will throw an exception on startup if multiple strategies are mapped to the same profile.
     * @param annotatedClasses a list of classes
     */
    private void sanityCheck(Set<Class<?>> annotatedClasses) {
        Set<String> usedStrategies = new HashSet<String>();
        for (Class<?> annotatedClass : annotatedClasses) {
            Strategy strategyAnnotation = AnnotationUtils.findAnnotation( annotatedClass, Strategy.class );
            if (!strategyAnnotation.type().isAssignableFrom( annotatedClass )) {
                throw new StrategyProfileViolationException( String.format( "'%s' should be assignable from '%s'", strategyAnnotation.type(), annotatedClass ) );
            }
            this.strategiesCache.put( annotatedClass, strategyAnnotation );

            if (this.isDefault( strategyAnnotation )) {
                this.ifNotExistAdd( strategyAnnotation.type(), "default", usedStrategies );
            } else {
                for (String profile : strategyAnnotation.profiles()) {
                    this.ifNotExistAdd( strategyAnnotation.type(), profile, usedStrategies );
                }
            }
        }
    }

    private void ifNotExistAdd(Class<?> type, String profile, Set<String> usedStrategies) {
        String key = this.createKey( type, profile );
        if (usedStrategies.contains( key )) {
            throw new StrategyProfileViolationException( String.format( "There can only be a single strategy for each type, found multiple for type '%s' and profile '%s'", type, profile ) );
        }
        usedStrategies.add( key );
    }

    private String createKey(Class<?> type, String profile) {
        return String.format( "%s_%s", type, profile ).toLowerCase();
    }

    private boolean isDefault(Strategy strategyAnnotation) {
        return (strategyAnnotation.profiles().length == 0);
    }

    private Class<?> findStrategyMatchingProfile(Class<?> strategyClass, String currentProfile) {
        for (Map.Entry<Class<?>, Strategy> strategyCacheEntry : this.strategiesCache.entrySet()) {
            Strategy strategyCacheEntryValue = strategyCacheEntry.getValue();
            if (strategyCacheEntryValue.type().equals( strategyClass )) {
                if (currentProfile != null) {
                    for (String profile : strategyCacheEntryValue.profiles()) {
                        if (currentProfile.equals( profile )) {
                            Class<?> result = strategyCacheEntry.getKey();
                            if (LOG.isDebugEnabled()) {
                                LOG.debug( String.format( "Found strategy [strategy=%s, profile=%s, strategyImpl=%s]", strategyClass, currentProfile, result ) );
                            }
                            return result;
                        }
                    }
                } else if (this.isDefault( strategyCacheEntryValue )) {
                    Class<?> defaultClass = strategyCacheEntry.getKey();
                    if (LOG.isDebugEnabled()) {
                        LOG.debug( String.format( "Found default strategy [strategy=%s, profile=%s, strategyImpl=%s]", strategyClass, currentProfile, defaultClass ) );
                    }
                    return defaultClass;
                }
            }
        }
        return null;
    }

    public void setPackages(String[] packages) {
        this.packages = packages;
    }
}

<强> StrategyException.java

package ...

public class StrategyException extends RuntimeException {
...
}

<强> StrategyNotFoundException.java

package ...

public class StrategyNotFoundException extends StrategyException {
...
}

<强> StrategyProfileViolationException.java

package ...

public class StrategyProfileViolationException extends StrategyException {
...
}

没有春天的用法

<强> NavigationStrategy.java

package com.asimio.core.test.strategy.strategies.navigation;

public interface NavigationStrategy {

    public String naviateTo();
}

<强> FreeNavigationStrategy.java

package com.asimio.core.test.strategy.strategies.navigation;

@Strategy(type = NavigationStrategy.class)
public class FreeNavigationStrategy implements NavigationStrategy {

    public String naviateTo() {
        return "free";
    }
}

<强> LimitedPremiumNavigationStrategy.java

package com.asimio.core.test.strategy.strategies.navigation;

@Strategy(type = NavigationStrategy.class, profiles = { "limited", "premium" })
public class LimitedPremiumNavigationStrategy implements NavigationStrategy {

    public String naviateTo() {
        return "limited+premium";
    }
}

然后

...
StrategyFactory factory = new StrategyFactory();
factory.setPackages( new String[] { "com.asimio.core.test.strategy.strategies.navigation" } );
this.factory.init();

NavigationStrategy ns = this.factory.getStrategy( NavigationStrategy.class );
String result = ns.naviateTo();
Assert.assertThat( "free", Matchers.is( result ) );
...
Or
...
String result = factory.getStrategy( NavigationStrategy.class, "limited" ).naviateTo();
Assert.assertThat( "limited+premium", Matchers.is( result ) );
...

使用Spring

Spring上下文件

<bean id="strategyFactory" class="com.asimio.core.strategy.StrategyFactory">
    <property name="packages">
        <list>
            <value>com.asimio.jobs.feed.impl</value>
        </list>
    </property>
</bean>

<强> IFeedProcessor.java

package ...

public interface IFeedProcessor {

    void runBatch(String file);
}

<强> CsvRentalsFeedProcessor.java

package ...

@Configurable(dependencyCheck = true)
@Strategy(type = IFeedProcessor.class, profiles = { "csv" })
public class CsvRentalsFeedProcessor implements IFeedProcessor, Serializable {

    @Autowired
    private CsvRentalsBatchReporter batchReporter;
...
}

然后

...
IFeedProcessor feedProcessor = this.strategyFactory.getStrategy( IFeedProcessor.class, feedFileExt );
feedProcessor.runBatch( unzippedFeedDir.getAbsolutePath() + File.separatorChar + feedFileName );
...

注意CsvRentalsBatchReporterCsvRentalsFeedProcessor bean中被“注入”(策略实现),但StrategyFactory使用return (T) clazz.newInstance();实例化策略实现,因此制作此对象需要什么{ {1}} - 知道? 首先使用Spring注释CsvRentalsFeedProcessor,并且在运行Java应用程序时,java命令中需要此参数:@Configurable(dependencyCheck = true)