Solve org.quartz.SchedulerException: Job threw an unhandled exception.

Article directory

  • 1. Reproducing the error
  • 2. Analysis errors
  • 3. Solve the problem
    • 3.1 Solution 1
    • 3.2 Solution 2
  • 4. Analyze the dynamic proxy of jdk and cglib in spring
    • 4.1 Dynamic proxy comparison
    • 4.2 Differences in principles
    • 4.3 Performance differences
    • 4.4 Respective limitations
    • 4.5 The essential difference between static agents and dynamic agents

1. Reproduction error

Today, when executing the quartz scheduled task, the following error was reported:

org.quartz.SchedulerException: Job threw an unhandled exception.
at org.quartz.core.JobRunShell.run(JobRunShell.java:213)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.xxx.CollectionTaskServiceImpl' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:351)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1127)
at com.xxx.SpringApplicationContext.getBean(SpringApplicationContext.java:19)
at com.xxx.quartz.CollectionTaskJob.execute(CollectionTaskJob.java:27)
at org.quartz.core.JobRunShell.run(JobRunShell.java:202)
... 1 common frames omitted

org.quartz.SchedulerException: Job threw an unhandled exception.

2. Analysis error

Translate org.quartz.SchedulerException: Job threw an unhandled exception. into Chinese, that is, org.quartz.SchedulerException: Job threw an unhandled exception.

What is this unhandled exception? We followed the error and found this error: No qualifying bean of type 'com.xxx.CollectionTaskServiceImpl' available.

Let’s continue to look at the error. The error occurs in the method of SpringApplicationContext.getBean.

Combined with the No qualifying bean of type 'com.xxx.CollectionTaskServiceImpl' available error, it can be seen that SpringApplicationContext cannot get the CollectionTaskServiceImpl class.

For example, the source code of SpringApplicationContext:

@Component
public class SpringApplicationContext implements ApplicationContextAware {<!-- -->

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {<!-- -->
        SpringApplicationContext.applicationContext = applicationContext;
    }

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

SpringApplicationContext implements the ApplicationContextAware interface and is annotated by @Component.

Let’s look further down. The error is in the execute method of the CollectionTaskJob class, as shown in the following code:

@Slf4j
@DisallowConcurrentExecution
public class CollectionTaskJob implements Job {<!-- -->

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {<!-- -->
        CollectionTaskServiceImpl collectionTaskServiceImpl = SpringApplicationContext.getBean(CollectionTaskServiceImpl.class);
        //Omit logic code here
   }
}

Let’s look at the CollectionTaskServiceImpl class again, as shown in the following code:

@Service
public class CollectionTaskServiceImpl implements CollectionTaskService {<!-- -->
//Omit logic code here
}

CollectionTaskServiceImpl implements the CollectionTaskService interface and is annotated by @Service.

Logically speaking, the CollectionTaskServiceImpl class is injected into the spring container and can be obtained through SpringApplicationContext, but the result cannot be obtained.

But why can’t I get it? We need to write a test class, as shown in the following code:

@Component
public class Test implements CommandLineRunner, ApplicationContextAware {<!-- -->

  private ApplicationContext applicationContext;

  @Override
  public void run(String... args) throws Exception {<!-- -->
    Map<String, CollectionTaskServiceImpl> beansOfType =
        applicationContext.getBeansOfType(CollectionTaskServiceImpl.class);
    System.out.println();
  }

  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {<!-- -->
    this.applicationContext = applicationContext;
  }
}

The test class Test implements the CommandLineRunner and ApplicationContextAware interfaces. At this time, we run the code:

You will clearly see that the container of beansOfType is 0, and it is indeed not obtained.

We modify CollectionTaskServiceImpl to CollectionTaskService:

 @Override
  public void run(String... args) throws Exception {<!-- -->
    Map<String, CollectionTaskService> beansOfType =
        applicationContext.getBeansOfType(CollectionTaskService.class);
    System.out.println();
  }

Rerun:

At this time, the object of CollectionTaskServiceImpl is obtained, but pay attention to the red box. It uses the dynamic proxy of jdk aop .

Then, I modified the CollectionTaskServiceImpl class and did not implement the CollectionTaskService interface, as shown in the following code:

@Service
public class CollectionTaskServiceImpl {<!-- -->
//Omit logic code here
}

The run method is still CollectionTaskServiceImpl, as shown in the following code:

@Override
  public void run(String... args) throws Exception {<!-- -->
    Map<String, CollectionTaskServiceImpl> beansOfType =
        applicationContext.getBeansOfType(CollectionTaskServiceImpl.class);
    System.out.println();
  }

Rerun the code:

In this way, you can also get the object of CollectionTaskServiceImpl, but pay attention to the red box. It uses the dynamic proxy of spring cglib.

After analyzing this point, we can roughly understand that there are two solutions as follows.

3. Solve the problem

3.1 Solution 1

Modify the execute method of the CollectionTaskJob class and pass in the CollectionTaskService.class interface in the SpringApplicationContext.getBean method, as shown in the following code Shown:

@Slf4j
@DisallowConcurrentExecution
public class CollectionTaskJob implements Job {<!-- -->

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {<!-- -->
        CollectionTaskServiceImpl collectionTaskServiceImpl = (CollectionTaskServiceImpl) SpringApplicationContext.getBean(CollectionTaskService.class);
        //Omit logic code here
   }
}

3.2 Solution 2

Modify the CollectionTaskServiceImpl class so that it does not implement CollectionTaskService.

4. Analyze the dynamic proxy of jdk and cglib in spring

4.1 Dynamic proxy comparison

JDKDynamic proxy implements the interface implemented by the proxy object, and CGLib inherits the proxy object.

JDK and CGLib both generate bytecode during runtime, and JDK directly writes Class bytecode. CGLib uses the ASM framework Class bytecode. Cglib proxy implementation is more complex and the efficiency of generating proxy classes is less than code>JDKAgent is low.

JDK calls the proxy method through the reflection mechanism. CGLib calls the method directly through the FastClass mechanism. CGLib executes higher efficiency.

4.2 Principle difference

javaDynamic proxy uses the reflection mechanism to generate an anonymous class that implements the proxy interface, and calls InvokeHandler to process it before calling specific methods. The core is to implement the InvocationHandler interface, use the invoke() method to perform aspect-oriented processing, and call corresponding notifications.

The cglib dynamic proxy uses the asm open source package to load the class file of the proxy object class and generate subclasses by modifying its bytecode. to handle.

The core is to implement the MethodInterceptor interface, use the intercept() method to perform aspect-oriented processing, and call corresponding notifications.

  1. If the target object implements the interface, the dynamic proxy implementation of JDK will be used by default to implement AOP
  2. If the target object implements the interface, you can force the use of CGLIB to implement AOP
  3. If the target object does not implement the interface, the CGLIB library must be used. spring will automatically use JDK dynamic proxy and CGLIB Convert between

4.3 Performance differences

  1. The bottom layer of CGLib adopts the ASM bytecode generation framework and uses bytecode technology to generate proxy classes. Before jdk6, it was better than using JavaReflection efficiency should be high. The only thing to note is that CGLib cannot proxy methods declared as final, because the principle of CGLib is to dynamically generate subclasses of the proxy class .

  2. After jdk6, jdk7, jdk8 gradually optimized the JDK dynamic proxy, when the number of calls was small, the JDK proxy efficiency was higher than CGLIB proxy efficiency, only when a large number of calls are made, jdk6 and jdk7 are slightly less efficient than CGLIB proxy, but By the time of jdk8, the jdk proxy is more efficient than the CGLIB proxy.

4.4 Respective limitations

  1. JDK‘s dynamic proxy mechanism can only proxy classes that implement interfaces, and classes that cannot implement interfaces cannot implement JDK‘s dynamic proxy.

  2. cglib implements proxies for classes. Its principle is to generate a subclass for the specified target class and overwrite its methods to achieve enhancement. However, because inheritance is used, cannot be Classes modified with final are proxied.

Type Mechanism Callback method Applicable scenarios Efficiency
JDK dynamic proxy Delegation mechanism, the proxy class and the target class implement the same interface, the InvocationHandler holds the target class, and the proxy class entrusts the InvocationHandler to call the target class Original method Reflection The target class is an interface class The efficiency bottleneck is that the reflection call is slightly slower
CGLIB dynamic proxy Inheritance mechanism, the proxy class inherits the target class and rewrites the target method, and calls the parent class method through the callback function MethodInterceptor to execute the original logic Called through the FastClass method index Non-interface class, non-final class, non-final method The first call requires generating multiple Class objects, which is slower than the JDK method. Multiple calls are faster than reflection because there are method indexes. If there are too many methods and too many switch cases, the efficiency needs to be tested

4.5 The essential difference between static proxy and dynamic proxy

  1. Static proxies can only complete proxy operations manually. If the proxied class adds new methods, the proxy class needs to be added simultaneously, which violates the opening and closing principle.

  2. Dynamic proxy uses the method of dynamically generating code at runtime, cancels the expansion restrictions on the proxy class, and follows the opening and closing principle.

  3. If the dynamic agent wants to enhance the logical extension of the target class, combined with the strategy mode, it can be completed by adding a new strategy class without modifying the code of the agent class.