You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shardingsphere.apache.org by zh...@apache.org on 2020/10/18 14:26:46 UTC

[shardingsphere-elasticjob] branch master updated: Refactor ElasticJobServiceLoader, remove unused code and use Optional instead of throw JobConfigurationException (#1578)

This is an automated email from the ASF dual-hosted git repository.

zhangliang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere-elasticjob.git


The following commit(s) were added to refs/heads/master by this push:
     new 138a63c  Refactor ElasticJobServiceLoader, remove unused code and use Optional  instead of throw JobConfigurationException (#1578)
138a63c is described below

commit 138a63cb81a15ecbc5d7b710d8e161c21103dbd9
Author: wwj <22...@qq.com>
AuthorDate: Sun Oct 18 22:26:38 2020 +0800

    Refactor ElasticJobServiceLoader, remove unused code and use Optional  instead of throw JobConfigurationException (#1578)
    
    * rebase
    
    * fixed review issues
    
    * Add test case for ElasticJobServiceLoader.
    
    * correct comment.
---
 .../error/handler/JobErrorHandlerFactory.java      |   8 +-
 .../executor/item/JobItemExecutorFactory.java      |   3 +-
 .../sharding/JobShardingStrategyFactory.java       |   6 +-
 .../JobExecutorServiceHandlerFactory.java          |   6 +-
 .../infra/listener/ElasticJobListenerFactory.java  |   2 +-
 .../infra/spi/ElasticJobServiceLoader.java         | 110 ++++++---------------
 .../infra/spi/ElasticJobServiceLoaderTest.java     |  64 ++++++++++++
 .../infra/spi/fixture/TypedFooService.java         |  23 +++++
 .../spi/fixture/UnRegisteredTypedFooService.java   |  23 +++++
 .../spi/fixture/impl/TypedFooServiceImpl.java}     |  31 ++----
 .../impl/UnRegisteredTypedFooServiceImpl.java      |  28 ++++++
 ...re.elasticjob.infra.spi.fixture.TypedFooService |  18 ++++
 ...b.infra.spi.fixture.UnRegisteredTypedFooService |  18 ++++
 13 files changed, 226 insertions(+), 114 deletions(-)

diff --git a/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-general/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/JobErrorHandlerFactory.java b/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-general/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/JobErrorHandlerFactory.java
index b58699c..e023ec5 100644
--- a/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-general/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/JobErrorHandlerFactory.java
+++ b/elasticjob-error-handler/elasticjob-error-handler-type/elasticjob-error-handler-general/src/main/java/org/apache/shardingsphere/elasticjob/error/handler/JobErrorHandlerFactory.java
@@ -44,12 +44,8 @@ public final class JobErrorHandlerFactory {
      */
     public static Optional<JobErrorHandler> createHandler(final String type) {
         if (Strings.isNullOrEmpty(type)) {
-            return newHandlerInstance(DEFAULT_HANDLER);
+            return ElasticJobServiceLoader.newTypedServiceInstance(JobErrorHandler.class, DEFAULT_HANDLER);
         }
-        return newHandlerInstance(type);
-    }
-    
-    private static Optional<JobErrorHandler> newHandlerInstance(final String type) {
-        return Optional.of(ElasticJobServiceLoader.newTypedServiceInstance(JobErrorHandler.class, type));
+        return ElasticJobServiceLoader.newTypedServiceInstance(JobErrorHandler.class, type);
     }
 }
diff --git a/elasticjob-executor/elasticjob-executor-kernel/src/main/java/org/apache/shardingsphere/elasticjob/executor/item/JobItemExecutorFactory.java b/elasticjob-executor/elasticjob-executor-kernel/src/main/java/org/apache/shardingsphere/elasticjob/executor/item/JobItemExecutorFactory.java
index af02b4d..d2792d1 100644
--- a/elasticjob-executor/elasticjob-executor-kernel/src/main/java/org/apache/shardingsphere/elasticjob/executor/item/JobItemExecutorFactory.java
+++ b/elasticjob-executor/elasticjob-executor-kernel/src/main/java/org/apache/shardingsphere/elasticjob/executor/item/JobItemExecutorFactory.java
@@ -67,6 +67,7 @@ public final class JobItemExecutorFactory {
      * @return job item executor
      */
     public static JobItemExecutor getExecutor(final String elasticJobType) {
-        return ElasticJobServiceLoader.getCachedInstance(TypedJobItemExecutor.class, elasticJobType);
+        return ElasticJobServiceLoader.getCachedTypedServiceInstance(TypedJobItemExecutor.class, elasticJobType)
+                .orElseThrow(() -> new JobConfigurationException("Cannot find executor for elastic job type `%s`", elasticJobType));
     }
 }
diff --git a/elasticjob-infra/elasticjob-infra-common/src/main/java/org/apache/shardingsphere/elasticjob/infra/handler/sharding/JobShardingStrategyFactory.java b/elasticjob-infra/elasticjob-infra-common/src/main/java/org/apache/shardingsphere/elasticjob/infra/handler/sharding/JobShardingStrategyFactory.java
index a7fe2e2..4fe65a2 100644
--- a/elasticjob-infra/elasticjob-infra-common/src/main/java/org/apache/shardingsphere/elasticjob/infra/handler/sharding/JobShardingStrategyFactory.java
+++ b/elasticjob-infra/elasticjob-infra-common/src/main/java/org/apache/shardingsphere/elasticjob/infra/handler/sharding/JobShardingStrategyFactory.java
@@ -20,6 +20,7 @@ package org.apache.shardingsphere.elasticjob.infra.handler.sharding;
 import com.google.common.base.Strings;
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
+import org.apache.shardingsphere.elasticjob.infra.exception.JobConfigurationException;
 import org.apache.shardingsphere.elasticjob.infra.spi.ElasticJobServiceLoader;
 
 /**
@@ -42,8 +43,9 @@ public final class JobShardingStrategyFactory {
      */
     public static JobShardingStrategy getStrategy(final String type) {
         if (Strings.isNullOrEmpty(type)) {
-            return ElasticJobServiceLoader.getCachedInstance(JobShardingStrategy.class, DEFAULT_STRATEGY);
+            return ElasticJobServiceLoader.getCachedTypedServiceInstance(JobShardingStrategy.class, DEFAULT_STRATEGY).get();
         }
-        return ElasticJobServiceLoader.getCachedInstance(JobShardingStrategy.class, type);
+        return ElasticJobServiceLoader.getCachedTypedServiceInstance(JobShardingStrategy.class, type)
+                .orElseThrow(() -> new JobConfigurationException("Cannot find sharding strategy using type '%s'.", type));
     }
 }
diff --git a/elasticjob-infra/elasticjob-infra-common/src/main/java/org/apache/shardingsphere/elasticjob/infra/handler/threadpool/JobExecutorServiceHandlerFactory.java b/elasticjob-infra/elasticjob-infra-common/src/main/java/org/apache/shardingsphere/elasticjob/infra/handler/threadpool/JobExecutorServiceHandlerFactory.java
index ff07ced..6987a76 100644
--- a/elasticjob-infra/elasticjob-infra-common/src/main/java/org/apache/shardingsphere/elasticjob/infra/handler/threadpool/JobExecutorServiceHandlerFactory.java
+++ b/elasticjob-infra/elasticjob-infra-common/src/main/java/org/apache/shardingsphere/elasticjob/infra/handler/threadpool/JobExecutorServiceHandlerFactory.java
@@ -20,6 +20,7 @@ package org.apache.shardingsphere.elasticjob.infra.handler.threadpool;
 import com.google.common.base.Strings;
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
+import org.apache.shardingsphere.elasticjob.infra.exception.JobConfigurationException;
 import org.apache.shardingsphere.elasticjob.infra.spi.ElasticJobServiceLoader;
 
 /**
@@ -42,8 +43,9 @@ public final class JobExecutorServiceHandlerFactory {
      */
     public static JobExecutorServiceHandler getHandler(final String type) {
         if (Strings.isNullOrEmpty(type)) {
-            return ElasticJobServiceLoader.getCachedInstance(JobExecutorServiceHandler.class, DEFAULT_HANDLER);
+            return ElasticJobServiceLoader.getCachedTypedServiceInstance(JobExecutorServiceHandler.class, DEFAULT_HANDLER).get();
         }
-        return ElasticJobServiceLoader.getCachedInstance(JobExecutorServiceHandler.class, type);
+        return ElasticJobServiceLoader.getCachedTypedServiceInstance(JobExecutorServiceHandler.class, type)
+                .orElseThrow(() -> new JobConfigurationException("Cannot find executor service handler using type '%s'.", type));
     }
 }
diff --git a/elasticjob-infra/elasticjob-infra-common/src/main/java/org/apache/shardingsphere/elasticjob/infra/listener/ElasticJobListenerFactory.java b/elasticjob-infra/elasticjob-infra-common/src/main/java/org/apache/shardingsphere/elasticjob/infra/listener/ElasticJobListenerFactory.java
index 026c6b1..f7ce3a1 100644
--- a/elasticjob-infra/elasticjob-infra-common/src/main/java/org/apache/shardingsphere/elasticjob/infra/listener/ElasticJobListenerFactory.java
+++ b/elasticjob-infra/elasticjob-infra-common/src/main/java/org/apache/shardingsphere/elasticjob/infra/listener/ElasticJobListenerFactory.java
@@ -40,6 +40,6 @@ public final class ElasticJobListenerFactory {
      * @return optional job listener instance
      */
     public static Optional<ElasticJobListener> createListener(final String type) {
-        return Optional.ofNullable(ElasticJobServiceLoader.newTypedServiceInstance(ElasticJobListener.class, type));
+        return ElasticJobServiceLoader.newTypedServiceInstance(ElasticJobListener.class, type);
     }
 }
diff --git a/elasticjob-infra/elasticjob-infra-common/src/main/java/org/apache/shardingsphere/elasticjob/infra/spi/ElasticJobServiceLoader.java b/elasticjob-infra/elasticjob-infra-common/src/main/java/org/apache/shardingsphere/elasticjob/infra/spi/ElasticJobServiceLoader.java
index e5a478b..e9afcb6 100644
--- a/elasticjob-infra/elasticjob-infra-common/src/main/java/org/apache/shardingsphere/elasticjob/infra/spi/ElasticJobServiceLoader.java
+++ b/elasticjob-infra/elasticjob-infra-common/src/main/java/org/apache/shardingsphere/elasticjob/infra/spi/ElasticJobServiceLoader.java
@@ -19,17 +19,13 @@ package org.apache.shardingsphere.elasticjob.infra.spi;
 
 import lombok.AccessLevel;
 import lombok.NoArgsConstructor;
-import org.apache.shardingsphere.elasticjob.infra.exception.JobConfigurationException;
 import org.apache.shardingsphere.elasticjob.infra.spi.exception.ServiceLoaderInstantiationException;
 
 import java.lang.reflect.InvocationTargetException;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedHashSet;
+import java.util.Optional;
 import java.util.ServiceLoader;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
-import java.util.stream.Collectors;
 
 /**
  * ElasticJob service loader.
@@ -37,103 +33,61 @@ import java.util.stream.Collectors;
 @NoArgsConstructor(access = AccessLevel.PRIVATE)
 public final class ElasticJobServiceLoader {
     
-    private static final ConcurrentMap<Class<?>, Collection<Class<?>>> SERVICES = new ConcurrentHashMap<>();
-    
-    private static final ConcurrentMap<Class<?>, ConcurrentMap<String, TypedSPI>> TYPED_SERVICES = new ConcurrentHashMap<>();
-    
-    private static final ConcurrentMap<Class<?>, ConcurrentMap<String, Class<?>>> TYPED_SERVICE_CLASSES = new ConcurrentHashMap<>();
-    
-    /**
-     * Register SPI service.
-     *
-     * @param service service type
-     * @param <T> type of service
-     */
-    public static <T> void register(final Class<T> service) {
-        if (SERVICES.containsKey(service)) {
-            return;
-        }
-        ServiceLoader.load(service).forEach(each -> registerServiceClass(service, each));
-    }
-    
-    private static <T> void registerServiceClass(final Class<T> service, final T instance) {
-        SERVICES.computeIfAbsent(service, unused -> new LinkedHashSet<>()).add(instance.getClass());
-    }
-    
-    /**
-     * New service instances.
-     *
-     * @param service service class
-     * @param <T> type of service
-     * @return service instances
-     */
-    @SuppressWarnings("unchecked")
-    public static <T> Collection<T> newServiceInstances(final Class<T> service) {
-        return SERVICES.containsKey(service) ? SERVICES.get(service).stream().map(each -> (T) newServiceInstance(each)).collect(Collectors.toList()) : Collections.emptyList();
-    }
-    
-    private static Object newServiceInstance(final Class<?> clazz) {
-        try {
-            return clazz.getConstructor().newInstance();
-        } catch (final InstantiationException | NoSuchMethodException | IllegalAccessException ex) {
-            throw new ServiceLoaderInstantiationException(clazz, ex);
-        } catch (final InvocationTargetException ex) {
-            throw new ServiceLoaderInstantiationException(clazz, ex.getCause());
-        }
-    }
+    private static final ConcurrentMap<Class<? extends TypedSPI>, ConcurrentMap<String, TypedSPI>> TYPED_SERVICES = new ConcurrentHashMap<>();
+
+    private static final ConcurrentMap<Class<? extends TypedSPI>, ConcurrentMap<String, Class<? extends TypedSPI>>> TYPED_SERVICE_CLASSES = new ConcurrentHashMap<>();
     
     /**
      * Register typeSPI service.
      *
-     * @param typedService specific service type
-     * @param <T> type of service
+     * @param typedService typed service
+     * @param <T> class of service
      */
-    public static <T> void registerTypedService(final Class<T> typedService) {
-        if (!TypedSPI.class.isAssignableFrom(typedService)) {
-            throw new IllegalArgumentException("Cannot register @" + typedService.getName() + "as a typed service, because its not a subClass of @" + typedService);
-        }
+    public static <T extends TypedSPI> void registerTypedService(final Class<T> typedService) {
         if (TYPED_SERVICES.containsKey(typedService)) {
             return;
         }
-        ServiceLoader.load(typedService).forEach(each -> registerTypedServiceClass(typedService, (TypedSPI) each));
+        ServiceLoader.load(typedService).forEach(each -> registerTypedServiceClass(typedService, each));
     }
-    
-    private static <T> void registerTypedServiceClass(final Class<T> typedService, final TypedSPI instance) {
+
+    private static <T extends TypedSPI> void registerTypedServiceClass(final Class<T> typedService, final TypedSPI instance) {
         TYPED_SERVICES.computeIfAbsent(typedService, unused -> new ConcurrentHashMap<>()).putIfAbsent(instance.getType(), instance);
         TYPED_SERVICE_CLASSES.computeIfAbsent(typedService, unused -> new ConcurrentHashMap<>()).putIfAbsent(instance.getType(), instance.getClass());
     }
-    
+
     /**
-     * Get cached instance.
+     * Get cached typed instance.
      *
-     * @param typedService typed service
+     * @param typedServiceInterface typed service interface
      * @param type type
      * @param <T> class of service
-     * @return cached service instance
+     * @return cached typed service instance
      */
-    @SuppressWarnings("unchecked")
-    public static <T extends TypedSPI> T getCachedInstance(final Class<T> typedService, final String type) {
-        T result = TYPED_SERVICES.containsKey(typedService) ? (T) TYPED_SERVICES.get(typedService).get(type) : null;
-        if (null == result) {
-            throw new JobConfigurationException("Cannot find a cached typed service instance by the interface: @" + typedService.getName() + "and type: " + type);
-        }
-        return result;
+    public static <T extends TypedSPI> Optional<T> getCachedTypedServiceInstance(final Class<T> typedServiceInterface, final String type) {
+        return Optional.ofNullable(TYPED_SERVICES.get(typedServiceInterface)).map(services -> (T) services.get(type));
     }
-    
+
     /**
      * New typed instance.
      *
-     * @param typedService typed service
+     * @param typedServiceInterface typed service interface
      * @param type type
      * @param <T> class of service
-     * @return new service instance
+     * @return new typed service instance
      */
-    @SuppressWarnings("unchecked")
-    public static <T extends TypedSPI> T newTypedServiceInstance(final Class<T> typedService, final String type) {
-        Class<?> instanceClass = TYPED_SERVICE_CLASSES.containsKey(typedService) ? TYPED_SERVICE_CLASSES.get(typedService).get(type) : null;
-        if (null == instanceClass) {
-            throw new JobConfigurationException("Cannot find a typed service class by the interface: @" + typedService.getName() + "and type: " + type);
+    public static <T extends TypedSPI> Optional<T> newTypedServiceInstance(final Class<T> typedServiceInterface, final String type) {
+        return Optional.ofNullable(TYPED_SERVICE_CLASSES.get(typedServiceInterface))
+                .map(serviceClasses -> serviceClasses.get(type))
+                .map(clazz -> (T) newServiceInstance(clazz));
+    }
+
+    private static Object newServiceInstance(final Class<?> clazz) {
+        try {
+            return clazz.getConstructor().newInstance();
+        } catch (final InstantiationException | NoSuchMethodException | IllegalAccessException ex) {
+            throw new ServiceLoaderInstantiationException(clazz, ex);
+        } catch (final InvocationTargetException ex) {
+            throw new ServiceLoaderInstantiationException(clazz, ex.getCause());
         }
-        return (T) newServiceInstance(instanceClass);
     }
 }
diff --git a/elasticjob-infra/elasticjob-infra-common/src/test/java/org/apache/shardingsphere/elasticjob/infra/spi/ElasticJobServiceLoaderTest.java b/elasticjob-infra/elasticjob-infra-common/src/test/java/org/apache/shardingsphere/elasticjob/infra/spi/ElasticJobServiceLoaderTest.java
new file mode 100644
index 0000000..89e3032
--- /dev/null
+++ b/elasticjob-infra/elasticjob-infra-common/src/test/java/org/apache/shardingsphere/elasticjob/infra/spi/ElasticJobServiceLoaderTest.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.elasticjob.infra.spi;
+
+import org.apache.shardingsphere.elasticjob.infra.spi.fixture.TypedFooService;
+import org.apache.shardingsphere.elasticjob.infra.spi.fixture.UnRegisteredTypedFooService;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.assertThat;
+
+public final class ElasticJobServiceLoaderTest {
+
+    @BeforeClass
+    public static void register() {
+        ElasticJobServiceLoader.registerTypedService(TypedFooService.class);
+    }
+
+    @Test
+    public void assertGetCacheTypedService() {
+        assertThat(ElasticJobServiceLoader.getCachedTypedServiceInstance(TypedFooService.class, "typedFooServiceImpl").orElse(null), instanceOf(TypedFooService.class));
+    }
+
+    @Test
+    public void assertNewTypedServiceInstance() {
+        assertThat(ElasticJobServiceLoader.getCachedTypedServiceInstance(TypedFooService.class, "typedFooServiceImpl").orElse(null), instanceOf(TypedFooService.class));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void assertGetCacheTypedServiceFailureWithUnRegisteredServiceInterface() {
+        ElasticJobServiceLoader.getCachedTypedServiceInstance(UnRegisteredTypedFooService.class, "unRegisteredTypedFooServiceImpl").orElseThrow(() -> new IllegalArgumentException());
+    }
+    
+    @Test(expected = IllegalArgumentException.class)
+    public void assertGetCacheTypedServiceFailureWithInvalidType() {
+        ElasticJobServiceLoader.getCachedTypedServiceInstance(TypedFooService.class, "INVALID").orElseThrow(() -> new IllegalArgumentException());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void assertNewTypedServiceInstanceFailureWithUnRegisteredServiceInterface() {
+        ElasticJobServiceLoader.newTypedServiceInstance(UnRegisteredTypedFooService.class, "unRegisteredTypedFooServiceImpl").orElseThrow(() -> new IllegalArgumentException());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void assertNewTypedServiceInstanceFailureWithInvalidType() {
+        ElasticJobServiceLoader.newTypedServiceInstance(TypedFooService.class, "INVALID").orElseThrow(() -> new IllegalArgumentException());
+    }
+}
diff --git a/elasticjob-infra/elasticjob-infra-common/src/test/java/org/apache/shardingsphere/elasticjob/infra/spi/fixture/TypedFooService.java b/elasticjob-infra/elasticjob-infra-common/src/test/java/org/apache/shardingsphere/elasticjob/infra/spi/fixture/TypedFooService.java
new file mode 100644
index 0000000..89159cf
--- /dev/null
+++ b/elasticjob-infra/elasticjob-infra-common/src/test/java/org/apache/shardingsphere/elasticjob/infra/spi/fixture/TypedFooService.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *  
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.elasticjob.infra.spi.fixture;
+
+import org.apache.shardingsphere.elasticjob.infra.spi.TypedSPI;
+
+public interface TypedFooService extends TypedSPI {
+}
diff --git a/elasticjob-infra/elasticjob-infra-common/src/test/java/org/apache/shardingsphere/elasticjob/infra/spi/fixture/UnRegisteredTypedFooService.java b/elasticjob-infra/elasticjob-infra-common/src/test/java/org/apache/shardingsphere/elasticjob/infra/spi/fixture/UnRegisteredTypedFooService.java
new file mode 100644
index 0000000..f170104
--- /dev/null
+++ b/elasticjob-infra/elasticjob-infra-common/src/test/java/org/apache/shardingsphere/elasticjob/infra/spi/fixture/UnRegisteredTypedFooService.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *  
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.elasticjob.infra.spi.fixture;
+
+import org.apache.shardingsphere.elasticjob.infra.spi.TypedSPI;
+
+public interface UnRegisteredTypedFooService extends TypedSPI {
+}
diff --git a/elasticjob-infra/elasticjob-infra-common/src/main/java/org/apache/shardingsphere/elasticjob/infra/listener/ElasticJobListenerFactory.java b/elasticjob-infra/elasticjob-infra-common/src/test/java/org/apache/shardingsphere/elasticjob/infra/spi/fixture/impl/TypedFooServiceImpl.java
similarity index 50%
copy from elasticjob-infra/elasticjob-infra-common/src/main/java/org/apache/shardingsphere/elasticjob/infra/listener/ElasticJobListenerFactory.java
copy to elasticjob-infra/elasticjob-infra-common/src/test/java/org/apache/shardingsphere/elasticjob/infra/spi/fixture/impl/TypedFooServiceImpl.java
index 026c6b1..3857147 100644
--- a/elasticjob-infra/elasticjob-infra-common/src/main/java/org/apache/shardingsphere/elasticjob/infra/listener/ElasticJobListenerFactory.java
+++ b/elasticjob-infra/elasticjob-infra-common/src/test/java/org/apache/shardingsphere/elasticjob/infra/spi/fixture/impl/TypedFooServiceImpl.java
@@ -5,7 +5,7 @@
  * The ASF licenses this file to You under the Apache License, Version 2.0
  * (the "License"); you may not use this file except in compliance with
  * the License.  You may obtain a copy of the License at
- *
+ *  
  *     http://www.apache.org/licenses/LICENSE-2.0
  *  
  * Unless required by applicable law or agreed to in writing, software
@@ -15,31 +15,14 @@
  * limitations under the License.
  */
 
-package org.apache.shardingsphere.elasticjob.infra.listener;
-
-import lombok.AccessLevel;
-import lombok.NoArgsConstructor;
-import org.apache.shardingsphere.elasticjob.infra.spi.ElasticJobServiceLoader;
+package org.apache.shardingsphere.elasticjob.infra.spi.fixture.impl;
 
-import java.util.Optional;
+import org.apache.shardingsphere.elasticjob.infra.spi.fixture.TypedFooService;
 
-/**
- * Job listener factory.
- */
-@NoArgsConstructor(access = AccessLevel.PRIVATE)
-public final class ElasticJobListenerFactory {
-    
-    static {
-        ElasticJobServiceLoader.registerTypedService(ElasticJobListener.class);
-    }
+public final class TypedFooServiceImpl implements TypedFooService {
     
-    /**
-     * Create a job listener instance.
-     *
-     * @param type job listener type
-     * @return optional job listener instance
-     */
-    public static Optional<ElasticJobListener> createListener(final String type) {
-        return Optional.ofNullable(ElasticJobServiceLoader.newTypedServiceInstance(ElasticJobListener.class, type));
+    @Override
+    public String getType() {
+        return "typedFooServiceImpl";
     }
 }
diff --git a/elasticjob-infra/elasticjob-infra-common/src/test/java/org/apache/shardingsphere/elasticjob/infra/spi/fixture/impl/UnRegisteredTypedFooServiceImpl.java b/elasticjob-infra/elasticjob-infra-common/src/test/java/org/apache/shardingsphere/elasticjob/infra/spi/fixture/impl/UnRegisteredTypedFooServiceImpl.java
new file mode 100644
index 0000000..a0e1401
--- /dev/null
+++ b/elasticjob-infra/elasticjob-infra-common/src/test/java/org/apache/shardingsphere/elasticjob/infra/spi/fixture/impl/UnRegisteredTypedFooServiceImpl.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *  
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.elasticjob.infra.spi.fixture.impl;
+
+import org.apache.shardingsphere.elasticjob.infra.spi.fixture.UnRegisteredTypedFooService;
+
+public final class UnRegisteredTypedFooServiceImpl implements UnRegisteredTypedFooService {
+   
+    @Override
+    public String getType() {
+        return "unRegisteredTypedFooServiceImpl";
+    }
+}
diff --git a/elasticjob-infra/elasticjob-infra-common/src/test/resources/META-INF/services/org.apache.shardingsphere.elasticjob.infra.spi.fixture.TypedFooService b/elasticjob-infra/elasticjob-infra-common/src/test/resources/META-INF/services/org.apache.shardingsphere.elasticjob.infra.spi.fixture.TypedFooService
new file mode 100644
index 0000000..9470ce8
--- /dev/null
+++ b/elasticjob-infra/elasticjob-infra-common/src/test/resources/META-INF/services/org.apache.shardingsphere.elasticjob.infra.spi.fixture.TypedFooService
@@ -0,0 +1,18 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#  
+#     http://www.apache.org/licenses/LICENSE-2.0
+#  
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.apache.shardingsphere.elasticjob.infra.spi.fixture.impl.TypedFooServiceImpl
diff --git a/elasticjob-infra/elasticjob-infra-common/src/test/resources/META-INF/services/org.apache.shardingsphere.elasticjob.infra.spi.fixture.UnRegisteredTypedFooService b/elasticjob-infra/elasticjob-infra-common/src/test/resources/META-INF/services/org.apache.shardingsphere.elasticjob.infra.spi.fixture.UnRegisteredTypedFooService
new file mode 100644
index 0000000..2d955a7
--- /dev/null
+++ b/elasticjob-infra/elasticjob-infra-common/src/test/resources/META-INF/services/org.apache.shardingsphere.elasticjob.infra.spi.fixture.UnRegisteredTypedFooService
@@ -0,0 +1,18 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#  
+#     http://www.apache.org/licenses/LICENSE-2.0
+#  
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.apache.shardingsphere.elasticjob.infra.spi.fixture.impl.UnRegisteredTypedFooServiceImpl