Article directory
1. Reproducing the error
When executing the quartz
scheduled task today, 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
for and dynamic proxies caused by Job threw an unhandled exception
and No qualifying bean of type 'x' available
think. spring jdk
cglib
2. Analysis errors
Although the error message is Job threw an 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 SpringApplicationContext.getBean
.
Combined withNo qualifying bean of type 'com.xxx.CollectionTaskServiceImpl' available
errors, it can be seen that SpringApplicationContext
cannot get CollectionTaskServiceImpl
this class.
How are youSpringApplicationContext
Source:
@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
Accomplished ApplicationContextAware
Close, 并由@Component
Commentary.
Let’s look down. The error is in the method of the CollectionTaskJob
class, as shown in the following code: execute
@Slf4j
@DisallowConcurrentExecution
public class CollectionTaskJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
CollectionTaskServiceImpl collectionTaskServiceImpl = SpringApplicationContext.getBean(CollectionTaskServiceImpl.class);
//此处省略逻辑代码
}
}
Let’s look at theCollectionTaskServiceImpl
class again, as shown in the following code:
@Service
public class CollectionTaskServiceImpl implements CollectionTaskService {
//此处省略逻辑代码
}
CollectionTaskServiceImpl
AccomplishedCollectionTaskService
Close, 并由@Service
Commentary.
Logically speaking, the CollectionTaskServiceImpl
class is injected into the spring
container and can be obtained through SpringApplicationContext
, but the result is Unreachable.
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 classTest
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.
My general CollectionTaskServiceImpl
Repairs 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 theCollectionTaskServiceImpl
class and did not implement theCollectionTaskService
interface, as shown in the following code:
@Service
public class CollectionTaskServiceImpl {
//此处省略逻辑代码
}
Andrun
method is stillCollectionTaskServiceImpl
, 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, which 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 method of the CollectionTaskJob
class and pass in method >Interface, as shown in the following code:execute
SpringApplicationContext.getBean
CollectionTaskService.class
@Slf4j
@DisallowConcurrentExecution
public class CollectionTaskJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
CollectionTaskServiceImpl collectionTaskServiceImpl = (CollectionTaskServiceImpl) SpringApplicationContext.getBean(CollectionTaskService.class);
//此处省略逻辑代码
}
}
3.2 Solution 2
RepairCollectionTaskServiceImpl
class, unrealisticCollectionTaskService
Available now.
4. Analyze the dynamic proxy of jdk and cglib in spring
4.1 Dynamic proxy comparison
JDK
A dynamic proxy implements the interface implemented by the proxy object, and CGLib
inherits the proxy object.
JDK
and CGLib
both generate bytecode at runtime, and JDK
writes Class
bytecode directly. CGLib
Using ASM
framework Class
bytecode, Cglib
the proxy implementation is more complex and generates proxy classes is less efficient than the JDK
agent.
JDK
calls the proxy method through the reflection mechanism. CGLib
calls the method directly through the FastClass
mechanism. CGLib
executes higher efficiency.
4.2 Differences in principles
java
Dynamic proxy uses the reflection mechanism to generate an anonymous class that implements the proxy interface, and callsInvokeHandler
before calling the specific method for processing. The core is to implement the InvocationHandler
interface, use the invoke()
method to perform aspect-oriented processing, and call the corresponding notifications.
Andcglib
the dynamic proxy uses the asm
open source package to load the class
file of the proxy object class through Modify its bytecode to generate subclasses to handle it.
The core of is to implement the MethodInterceptor
interface, use the intercept()
method for aspect-oriented processing, and call the corresponding notifications.
- If the target object implements the interface, the dynamic proxy implementation of
JDK
will be used by defaultAOP
- If the target object implements the interface, it can be forced to use
CGLIB
implementationAOP
- If the target object does not implement the interface, the
CGLIB
library must be used,spring
will automatically use theJDK
dynamic proxy andCGLIB
Convert between
4.3 Performance differences
-
CGLib
The bottom layer adoptsASM
bytecode generation framework, using bytecode technology to generate proxy classes, which is better than usingjdk6
=3>The reflection efficiency should be high. The only thing to note is that cannot proxy a method declared as , because the principle is to dynamically generate the proxy class subcategory.Java
CGLib
final
CGLib
-
After
jdk6、jdk7、jdk8
gradually optimizing theJDK
dynamic proxy, when the number of calls is small, theJDK
proxy Efficiency is higher thanCGLIB
proxy efficiency, only when a large number of calls are made,jdk6
andjdk7
are better thanCGLIB
The agent efficiency is a little lower, but by the timejdk8
, thejdk
agent efficiency is higher than theCGLIB
agent.
4.4 Respective limitations
-
JDK
The dynamic proxy mechanism of can only proxy classes that implement the interface, and classes that cannot implement the interface cannot implement the dynamic proxy ofJDK
. -
cglib
implements proxy 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, it cannot be used forfinal
The modified class is proxied.
type | mechanism | callback method | Applicable scene | efficiency |
JDK dynamic proxy | Delegation mechanism, both the proxy class and the target class implement the same interface. InvocationHandler holds the target class, and the proxy class entrusts InvocationHandler to call the original method of the target class. | reflection | The target class is the 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 via 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. Calling multiple times is 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 agents and dynamic agents
-
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.
-
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.
-
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.