衡水网站建设怎么做网站建设与推广销售户话术
- 作者: 五速梦信息网
- 时间: 2026年03月21日 10:53
当前位置: 首页 > news >正文
衡水网站建设怎么做,网站建设与推广销售户话术,网站建设 计入哪个科目,建设门户网站的公司背景#xff1a;
本文作为Spring系列的第九篇#xff0c;介绍Async注解的使用、注意事项和实现原理#xff0c;原理部分会结合Spring框架代码进行。 本文可以和Spring系列-8 AOP原理进行比较阅读 1.使用方式
Async一般注解在方法上#xff0c;用于实现方法的异步#xf…背景
本文作为Spring系列的第九篇介绍Async注解的使用、注意事项和实现原理原理部分会结合Spring框架代码进行。 本文可以和Spring系列-8 AOP原理进行比较阅读 1.使用方式
Async一般注解在方法上用于实现方法的异步方法调用者立即返回待调用的方法提交给Spring的线程池执行。Async也可以注解在类上等价于在类中的所有方法上添加该注解。需要注意Async只对Spring管理的对象生效。
案例介绍
1.1 基本用例
在配置类或者启动类上注解EnableAsync用于开启异步功能
Configuration
ComponentScan(basePackages com.seong.async)
EnableAsync
public class AsyncConfiguration {
}在方法上添加Async注解
package com.seong.async;Component
public class ComponentA {Asyncpublic void print() {System.out.println(Thread.currentThread().getName(): test Async call.);}
}用例如下
Slf4j
public class AsyncApplication {public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext new AnnotationConfigApplicationContext(AsyncConfiguration.class);//用id取出业务逻辑类的beanComponentA componentA (ComponentA) applicationContext.getBean(componentA);LOGGER.info(test begin…);componentA.print();LOGGER.info(test end!);}
}得到如下结果 结果显示ComponentA对象的print()方法被异步执行了且DEBUG日志抛出了No qualifying bean of type org.springframework.core.task.TaskExecutor available异常这是因为业务代码没有配置TaskExecutor类型的Bean对象导致。
1.2 配置线程池
被Async注解的方法会提交给线程池执行这里可以手动指定线程池或者使用默认的线程池: (1) 手动指定线程池 通过Async的value属性指定线程池Bean对象(通过beanName指定)如
Component
public class ComponentA {Async(myTaskExecutor)public void print() {System.out.println(Thread.currentThread().getName(): test Async call.);}
}Configuration
EnableAsync
public class AsyncConfiguration {Beanpublic Executor myTaskExecutor() {return Executors.newFixedThreadPool(1);}
}此时运行任务会提交给myTaskExecutor线程池对象执行。
(2) 配置默认的线程池
Bean
public TaskExecutor myTaskExecutor() {return new SimpleAsyncTaskExecutor();
}Spring为Async提供了默认线程池配置可通过向IOC中注册TaskExecutor类型的Bean对象实现。 也可使用如下配置注册默认线程池
Bean
public Executor taskExecutor() {return Executors.newFixedThreadPool(1);
}Spring框架获取TaskExecutor类型的Bean对象失败时会尝试获取BeanName为taskExecutor的线程池对象但执行时日志中会给出异常信息。
(3) 使用Spring框架默认的SimpleAsyncTaskExecutor线程池. 若业务未配置默认线程池默认使用Spring生成的SimpleAsyncTaskExecutor对象 但执行时日志中会给出异常信息。
1.3 获取返回值
由于Async注解的方法为异步执行因此可以通过Future来获取返回值案例如下 修改ComponentA的方法
Component
Slf4j
public class ComponentA {Asyncpublic FutureString print() {LOGGER.info(Thread.currentThread().getName() : test Async call.);return new AsyncResult(ComponentA finished.);}
}适配修改案例
Slf4j
public class AsyncApplication {public static void main(String[] args) throws ExecutionException, InterruptedException {AnnotationConfigApplicationContext applicationContext new AnnotationConfigApplicationContext(AsyncConfiguration.class);ComponentA componentA (ComponentA) applicationContext.getBean(componentA);LOGGER.info(test begin…);final FutureString resultFuture componentA.print();doOtherBusiness();LOGGER.info(test end!);// 这一步执行时main线程会等待异步方法返回的结果LOGGER.info(result is {}, resultFuture.get());}private static void doOtherBusiness() {LOGGER.info(do other business…);}
}运行得到如下结果
2.原理
本质上Async注解的实现依赖于动态代理代理过程中将任务提交给线程池存在以下流程
如上图所示Spring为使用Async注解Bean对象生产一个动态代理对象当Async注解的方法被调用时会进入代理流程选择线程池、封装调用逻辑并提交给线程池执行返回执行结果。 注意未被Async注解的方法则不会执行上述流程(static方法也不会) 由于异步的本质是基于代理实现所以同一个类中的方法调用会导致被调用方的异步作用失效该场景与Spring的事务失效原因相同可参考事务-1 事务隔离级别和Spring事务传播机制.
2.1 EnableAsync注解
2.1.1 EnableAsync注解定义
EnableAsync注解核心作用是向容器中注册AsyncAnnotationBeanPostProcessor对象。
Target(ElementType.TYPE)
Retention(RetentionPolicy.RUNTIME)
Documented
Import(AsyncConfigurationSelector.class)
public interface EnableAsync {Class? extends Annotation annotation() default Annotation.class;boolean proxyTargetClass() default false;AdviceMode mode() default AdviceMode.PROXY;int order() default Ordered.LOWEST_PRECEDENCE;
}EnableAsync注解提过了以下属性用于自定义配置 [1] annotation属性 用于指定生效的注解默认为Async和EJB的Asynchronous注解。当指定注解时仅指定的注解生效
Target({ElementType.TYPE, ElementType.METHOD})
Retention(RetentionPolicy.RUNTIME)
Documented
public interface TestAnnotation {
}EnableAsync(annotation TestAnnotation.class)
public class AsyncConfiguration {
}TestAnnotation
public void print() {System.out.println(Thread.currentThread().getName() : test Async call);
}但是仅Async中可以通过value属性指定需要的线程池Bean对象因为硬编码获取beanName的逻辑来源必须为Async注解的value属性
protected String getExecutorQualifier(Method method) {Async async AnnotatedElementUtils.findMergedAnnotation(method, Async.class);if (async null) {async AnnotatedElementUtils.findMergedAnnotation(method.getDeclaringClass(), Async.class);}return (async ! null ? async.value() : null);
}[2] proxyTargetClass属性 可用于配置代理类型true时表示强制使用CGLIB动态代理false时表示根据被代理类情况进行确定默认为false。仅当模式设置为AdviceMode#PROXY时有效。
[3] mode属性 指出应该如何应用异步通知, 默认值是AdviceMode.PROXY。
[4] order属性 指示应该Bean对象在BPP阶段应用AsyncAnnotationBeanPostProcessor的顺序默认值是Ordered.LOWEST_PRECEDENCE以便在所有其他后处理器之后运行这样它就可以向现有代理添加一个advisor而不是双代理。
2.1.2 EnableAsync注解作用
通过Import注解向IOC中注入了AsyncConfigurationSelector对象, 进入AsyncConfigurationSelector对象
public final String[] selectImports(AnnotationMetadata importingClassMetadata) {//…AdviceMode adviceMode attributes.getEnum(getAdviceModeAttributeName());String[] imports selectImports(adviceMode);//…return imports;
}public String[] selectImports(AdviceMode adviceMode) {switch (adviceMode) {case PROXY:// EnableAsync注解的mode属性的默认值为AdviceMode.PROXYreturn new String[] {ProxyAsyncConfiguration.class.getName()};case ASPECTJ:return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};default:return null;}
}
由于加载的AsyncConfigurationSelector对象为ImportSelector类型Spring继续完成ProxyAsyncConfiguration类型的注入。 进入ProxyAsyncConfiguration代码逻辑发现是一个配置类用于向容器中注入一个AsyncAnnotationBeanPostProcessor对象
Configuration
Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {Bean(nameorg.springframework.context.annotation.internalAsyncAnnotationProcessor)Role(BeanDefinition.ROLE_INFRASTRUCTURE)public AsyncAnnotationBeanPostProcessor asyncAdvisor() {AsyncAnnotationBeanPostProcessor bpp new AsyncAnnotationBeanPostProcessor();bpp.configure(this.executor, this.exceptionHandler);Class? extends Annotation customAsyncAnnotation this.enableAsync.getClass(annotation);if (customAsyncAnnotation ! AnnotationUtils.getDefaultValue(EnableAsync.class, annotation)) {bpp.setAsyncAnnotationType(customAsyncAnnotation);}bpp.setProxyTargetClass(this.enableAsync.getBoolean(proxyTargetClass));bpp.setOrder(this.enableAsync.IntegergetNumber(order));return bpp;}
}总之EnableAsync后IOC容器中会增加一个AsyncAnnotationBeanPostProcessor类型的对象beanName为org.springframework.context.annotation.internalAsyncAnnotationProcessor.
2.2 AsyncAnnotationBeanPostProcessor:
AsyncAnnotationBeanPostProcessor是一个后置处理器且实现了BeanFactoryAware接口核心逻辑在两处Aware阶段和BPP-After阶段. [1] Aware阶段准备好Advisor对象(内部包含一个拦截器)
public void setBeanFactory(BeanFactory beanFactory) {super.setBeanFactory(beanFactory);// 构造AsyncAnnotationAdvisor对象注入到this.advisor属性中AsyncAnnotationAdvisor advisor new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);advisor.setBeanFactory(beanFactory);this.advisor advisor;
}
上述方法完成了BeanFactory属性的注入以及this.advisor属性的设置属性类型为AsyncAnnotationAdvisor进入AsyncAnnotationAdvisor的构造函数
public AsyncAnnotationAdvisor( executor, exceptionHandler) {this.advice buildAdvice(executor, exceptionHandler);this.pointcut buildPointcut(asyncAnnotationTypes);
}protected Advice buildAdvice( executor, exceptionHandler) {AnnotationAsyncExecutionInterceptor interceptor new AnnotationAsyncExecutionInterceptor(null);// 设置线程池和异常处理器interceptor.configure(executor, exceptionHandler);return interceptor;
}
注意AnnotationAsyncExecutionInterceptor是一个MethodInterceptor接口的实现类
// MethodInterceptor拦截器接口的invoke方法
public Object invoke(final MethodInvocation invocation) throws Throwable {//…
}总之在aware阶段为AsyncAnnotationBeanPostProcessor对象注入this.advisor一个AsyncAnnotationAdvisor类型的对象该对象包含BeanFactory和advice属性其中advice属性包含一个MethodInterceptor拦截器。
[2] BPP-After阶段为Bean对象生成代理 在AsyncAnnotationBeanPostProcessor的初始化过程完成后当IOC初始化单例Bean对象时会在初始化的后置处理器阶段调用AsyncAnnotationBeanPostProcessor的postProcessAfterInitialization方法
Override
public Object postProcessAfterInitialization(Object bean, String beanName) {// this.advisor在AsyncAnnotationBeanPostProcessor构建阶段已设值if (this.advisor null || bean instanceof AopInfrastructureBean) {return bean;}// 是否已进行了AOP代理if (bean instanceof Advised) {Advised advised (Advised) bean;if (!advised.isFrozen() isEligible(AopUtils.getTargetClass(bean))) {// Add our local Advisor to the existing proxys Advisor chain…if (this.beforeExistingAdvisors) {advised.addAdvisor(0, this.advisor);}else {advised.addAdvisor(this.advisor);}return bean;}}// 类中是否注解了Asyncif (isEligible(bean, beanName)) {// 将bean对象存在了targetSource属性中以便后续取用ProxyFactory proxyFactory prepareProxyFactory(bean, beanName);if (!proxyFactory.isProxyTargetClass()) {evaluateProxyInterfaces(bean.getClass(), proxyFactory);}proxyFactory.addAdvisor(this.advisor);customizeProxyFactory(proxyFactory);return proxyFactory.getProxy(getProxyClassLoader());}return bean;
}
如上述代码所示 已完成AOP代理的不需要再次代理将AsyncAnnotationBeanPostProcessor的this.advisor属性添加到代理对象的ListAdvisor列表中未经过代理且类中注解了Async的对象Spring为其创建一个代理对象并将this.advisor属性信息保存到代理对象中。【代理过程参考Spring系列-8 AOP原理】
【3】接口调用 Spring提供了两种代理方式JDK动态代理和CGLIB代理由于底层代理细节对实现原理并无影响这里以JDK动态代理为例进行介绍。 被代理的对象在方法被调用时进入JdkDynamicAopProxy的invoke拦截方法注意每个代理对象对应一个JdkDynamicAopProxy对象。
[插图解释ProxyFactory、ProxyConfig、Advised的关系以及JdkDynamicAopProxy的传参路径]
涉及代码如下所示(保留主线逻辑)
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// hash/equals/toString check…TargetSource targetSource this.advised.targetSource;Object target targetSource.getTarget();Class? targetClass (target ! null ? target.getClass() : null);// 根据方法和字节码对象获取调用链如果该方法未被Async注解则返回的集合为空否则返回一个包含AnnotationAsyncExecutionInterceptor对象的单元素集合。ListObject chain this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);Object retVal;if (chain.isEmpty()) {Object[] argsToUse AopProxyUtils.adaptArgumentsIfNecessary(method, args);// 直接用过反射调用目标方法retVal AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);} else {MethodInvocation invocation new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);retVal invocation.proceed();}// 返回结果类型校验…return retVal;
}invoke方法的主线逻辑 [1] 从targetSource属性中取出被代理的对象; [2] 从 advised属性中获取拦截器链; [3] 判断拦截器链是否为空为空则直接通过反射调用该方法否则进入拦截器对象 [4] 进行结果类型的校验并返回结果对象。
ReflectiveMethodInvocation介绍
ReflectiveMethodInvocation是对调用链进行的一层封装
public Object proceed() throws Throwable {// We start with an index of -1 and increment early.if (this.currentInterceptorIndex this.interceptorsAndDynamicMethodMatchers.size() - 1) {return invokeJoinpoint();}Object interceptorOrInterceptionAdvice this.interceptorsAndDynamicMethodMatchers.get(this.currentInterceptorIndex);return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
interceptorsAndDynamicMethodMatchers为构建ReflectiveMethodInvocation对象时传入的chain参数即依次调用拦截器的拦截invoke方法最后执行invokeJoinpoint()方法。 注意调用拦截器时将this作为入参(该ReflectiveMethodInvocation对象)拦截器中可通过调用该对象的proceed()实现回调。
2.3 AnnotationAsyncExecutionInterceptor介绍
当异步方法被调用时堆栈逻辑进入到AnnotationAsyncExecutionInterceptor的invoke方法
public Object invoke(final MethodInvocation invocation) throws Throwable {Class? targetClass (invocation.getThis() ! null ? AopUtils.getTargetClass(invocation.getThis()) : null);Method specificMethod ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);final Method userDeclaredMethod BridgeMethodResolver.findBridgedMethod(specificMethod);// 1.确定待执行的任务的线程池AsyncTaskExecutor executor determineAsyncExecutor(userDeclaredMethod);// 2.封装执行逻辑成一个任务CallableObject task () - {try {// 回调ReflectiveMethodInvocation的proceed方法Object result invocation.proceed();if (result instanceof Future) {return ((Future?) result).get();}}catch (ExecutionException ex) {handleError(ex.getCause(), userDeclaredMethod, invocation.getArguments());}catch (Throwable ex) {handleError(ex, userDeclaredMethod, invocation.getArguments());}return null;};// 3.将任务提交给线程池return doSubmit(task, executor, invocation.getMethod().getReturnType());
}AnnotationAsyncExecutionInterceptor的invoke方法主线逻辑如下 [1] 获取待执行的任务的线程池; [2] 回调ReflectiveMethodInvocation的逻辑(将invocation.proceed())封装为一个任务 [3] 将任务提交给线程池执行。
当提交给线程池的任务被执行时即invocation.proceed()执行时进入到ReflectiveMethodInvocation的invokeJoinpoint()方法反射调用目标方法。
获取执行Async方法的线程池
protected AsyncTaskExecutor determineAsyncExecutor(Method method) {AsyncTaskExecutor executor this.executors.get(method);if (executor null) {Executor targetExecutor;String qualifier getExecutorQualifier(method);if (StringUtils.hasLength(qualifier)) {targetExecutor findQualifiedExecutor(this.beanFactory, qualifier);}else {targetExecutor this.defaultExecutor.get();}if (targetExecutor null) {return null;}executor (targetExecutor instanceof AsyncListenableTaskExecutor ?(AsyncListenableTaskExecutor) targetExecutor : new TaskExecutorAdapter(targetExecutor));this.executors.put(method, executor);}return executor;
}其中this.executors对象用于缓存方法与线程池的关系减少查询次数。
主线逻辑为 如果Async注解的value有值则根据该值从beanFactory中获取对应的线程池Bean对象否则获取默认的线程池获取步骤如下
protected Executor getDefaultExecutor(Nullable BeanFactory beanFactory) {Executor defaultExecutor super.getDefaultExecutor(beanFactory);return (defaultExecutor ! null ? defaultExecutor : new SimpleAsyncTaskExecutor());
}逻辑较为简单通过父类的getDefaultExecutor方法获取默认的线程池获取失败则使用SimpleAsyncTaskExecutor线程池对象。
父类getDefaultExecutor方法逻辑如下
protected Executor getDefaultExecutor(Nullable BeanFactory beanFactory) {if (beanFactory ! null) {try {return beanFactory.getBean(TaskExecutor.class);} catch (NoUniqueBeanDefinitionException ex) {return beanFactory.getBean(taskExecutor, Executor.class);} catch (NoSuchBeanDefinitionException ex) {return beanFactory.getBean(taskExecutor, Executor.class);}}return null;
}先尝试根据TaskExecutor类型从IOC中获取Bean对象获取失败再次根据taskExecutor名称获取Executor类型的Bean对象都获取失败时返回null当从父类的getDefaultExecutor方法获取线程池结果为空时会使用 SimpleAsyncTaskExecutor线程池对象。
注意 SimpleAsyncTaskExecutor对象每次执行任务都会创建一个新的线程(且没有最大线程数设置)当并发量较大时可能导致严重的性能问题建议使用Async的项目自定义beanName为 taskExecutor且类型为TaskExecutor的线程池。
2.4 代理类型
Spring提供了两种动态代理类型JDK动态代理和CGLIB动态代理(可参考AVASE-14 静态代理与动态代理)并支持通过配置对其进行指定。 AopProxy工厂(DefaultAopProxyFactory)创建AopProxy对象的过程如下
Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {if (!NativeDetector.inNativeImage() (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {Class? targetClass config.getTargetClass();if (targetClass null) {throw new AopConfigException(TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.);}if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {return new JdkDynamicAopProxy(config);}return new ObjenesisCglibAopProxy(config);} else {return new JdkDynamicAopProxy(config);}
}NativeDetector.inNativeImage()和config.isOptimize() 在整个代理和方法调用过程(未涉及配置)取默认值可直接忽略且通过Class? targetClass config.getTargetClass();得到的对象为(已被实例化的)Bean对象的字节码类型不可能为接口类型或空对象可直接忽略。[框架代码不同于业务代码需要考虑多种场景的适配] 因此上述代码可以简化为
Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {if (config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {Class? targetClass config.getTargetClass();if (Proxy.isProxyClass(targetClass)) {return new JdkDynamicAopProxy(config);}return new ObjenesisCglibAopProxy(config);} else {return new JdkDynamicAopProxy(config);}
}// 目标类是否继承了接口除SpringProxy外
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {Class?[] ifcs config.getProxiedInterfaces();return (ifcs.length 0 || (ifcs.length 1 SpringProxy.class.isAssignableFrom(ifcs[0])));
}结果比较清晰 【1】如果目标对象已被JDK动态代理过则选择JDK动态代理 【2】如果ProxyTarget属性为true 或者没有实现接口使用CGBLIB代理否则使用JDK动态代理。 顺便提一下JDK动态代理生成代理类的速度较快(相对CGLLIB快8倍)但是运行速度较慢(比CGLIB慢10倍)。
- 上一篇: 衡水网站建设在哪里程序员培训机构
- 下一篇: 衡水营销网站建设wordpress 知名网站
相关文章
-
衡水网站建设在哪里程序员培训机构
衡水网站建设在哪里程序员培训机构
- 技术栈
- 2026年03月21日
-
衡水网站建设哪家专业大连网站如何制作
衡水网站建设哪家专业大连网站如何制作
- 技术栈
- 2026年03月21日
-
衡水网站建设服务哪有深圳设计公司
衡水网站建设服务哪有深圳设计公司
- 技术栈
- 2026年03月21日
-
衡水营销网站建设wordpress 知名网站
衡水营销网站建设wordpress 知名网站
- 技术栈
- 2026年03月21日
-
衡水做网站开发的湖南最新消息今天
衡水做网站开发的湖南最新消息今天
- 技术栈
- 2026年03月21日
-
衡阳企业网站中企动力是干嘛的
衡阳企业网站中企动力是干嘛的
- 技术栈
- 2026年03月21日
