Spring asynchronous programming your @Async really asynchronous it? Seven foot pit of the most fashionable asynchronous ~

Introduction little long

The front end of the baby will use ajax, asynchronous programming with the joy of not - we have asynchronous java, but also use them happier than they are - we bia ~ ji ~ a 注(gǒupí)解(gāoyào), is a happy man ... Let us look at the following wind of chestnuts:

A registered user, initialization points to his account (also can be imagined as registration incentives), to give the user send a registration notification message, and then send a message, ( 只是举栗子,切莫牛角大法), such a process, for example, we add a transaction ( 一损俱损的那种), SMS Mail and I think we can split out, no need to prop up the primary process:

Today this business, I thought to myself, this is not a note pad to engage the Well ~ ~ ~ ~ ~ ha ha ha

Pit circular dependencies

Look code:

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    UserService userService;
    
     @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public int save(UserDTO userDTO) {
        User user = new User();
        BeanCopyUtils.copy(userDTO, user);
        int insert = userMapper.insert(user);
        System.out.println("User 保存用户成功:" + user);
        userService.senMsg(user);
        userService.senEmail(user);
        return insert;
    }


    @Async
    @Override
    public Boolean senMsg(User user) {
        try {
            TimeUnit.SECONDS.sleep(2);
            System.out.println("发送短信中:.....");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName() + "给用户id:" + user.getId() + ",手机号:" + user.getMobile() + "发送短信成功");
        return true;
    }

    @Async
    @Override
    public Boolean senEmail(User user) {
        try {
            TimeUnit.SECONDS.sleep(3);
            System.out.println("发送邮件中:.....");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "给用户id:" + user.getId() + ",邮箱:" + user.getEmail() + "发送邮件成功");
        return true;
    }
复制代码

Result: not start up, Spring circular dependency problem. Perhaps just said that, some of the junior partner will be shocked. Spring is not the problem it solves the cycle of dependence, which is supported by a circular dependency of it? how could be?

Admittedly, before I was so convinced, but each time was also tested. If you currently strong and I have the same idea, let anomaly UnsatisfiedDependencyException, has been injected into other beans [userServiceImpl] in its raw version as part of a circular reference,to encourage you, hug you, is so not to face, naked circular reference.

Speaking circular dependency Spring Bean, and some small partners may be unfamiliar, after all, the development process seems to rely on the concept of free circulation perception. In actual fact, you have this illusion, right because you work in the Spring of infancy , thus let you " peace of mind " - in fact, our code can be given that we write a cycle of dependence, like this:

@Service
public class AServiceImpl implements AService {
    @Autowired
    private BService bService;
    ...
}
@Service
public class BServiceImpl implements BService {
    @Autowired
    private AService aService;
    ...
}
复制代码

Summed up by experiments , using @Async appear necessary conditions leading to a circular dependency issues:

  1. We turned on the support @EnableAsync
  2. Bean @Async notes where the cycle is dependent on the

Pit asynchronous failure

Well, since the cycle of dependence can not, we will not circular dependencies, so we come:

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    UserMapper userMapper;
    @Autowired
    SendService sendService;
    
    @Override
    @Transactional()
    public int save(UserDTO userDTO) {
        User user = new User();
        BeanCopyUtils.copy(userDTO, user);
        int insert = userMapper.insert(user);
        System.out.println("User 保存用户成功:" + user);
        this.senMsg(user);
        this.senEmail(user);
        return insert;
    }


    @Async
    @Override
    public Boolean senMsg(User user) {
        System.out.println(Thread.currentThread().getName() + "给用户id:" + user.getId() + ",手机号:" + user.getMobile() + "发送短信成功");
        return true;
    }

    @Async
    @Override
    public Boolean senEmail(User user) {
        System.out.println(Thread.currentThread().getName() + "给用户id:" + user.getId() + ",邮箱:" + user.getEmail() + "发送邮件成功");
        return true;
    }
复制代码

Results We tested a few, I printed at the results:

2019-08-05 21:59:32.304  INFO 14360 --- [nio-8080-exec-3] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
2019-08-05 21:59:32.346 DEBUG 14360 --- [nio-8080-exec-3] c.b.lea.mybot.mapper.UserMapper.insert   : ==>  Preparing: insert into t_user (username, sex, mobile,email) values (?, ?, ?,?) 
2019-08-05 21:59:32.454 DEBUG 14360 --- [nio-8080-exec-3] c.b.lea.mybot.mapper.UserMapper.insert   : ==> Parameters: 王麻子(String), 男(String), 18820158833(String), [email protected](String)
2019-08-05 21:59:32.463 DEBUG 14360 --- [nio-8080-exec-3] c.b.lea.mybot.mapper.UserMapper.insert   : <==    Updates: 1
User 保存用户成功:User(id=101, username=王麻子, mobile=18820158833, [email protected], sex=男, password=123435, createTime=Mon Aug 05 12:20:51 CST 2019, updateTime=null)
发送短信中:.....
http-nio-8080-exec-3给用户id:101,手机号:18820158833发送短信成功
发送邮件中:.....
http-nio-8080-exec-3给用户id:101,邮箱:[email protected]发送邮件成功
复制代码

This is not white blind do not perceive my love, white writing, the thread is still uncomfortable ~ ~? http-nio-8080-exec-3, Then why does the following will speak Oh, start with the conclusion?:

Summed up by experiments , reason for using asynchronous @Async lead to failure occurs:

  1. Using asynchronous does not support asynchronous in this class
  2. The caller actually is this, it is the current object, not the real proxy object userService, spring can not be intercepted by this method is called so not not in this class to call, online solutions have applicationContext.getBean(AInterface.class)and AopContext.currentProxy(), but I do not currently test proved successful, still in error stage, When I study successfully added here先占位...

Three basic types of pit

I wanted to write a text message to see ah, mailing sign of success, to go on,

Fourth pit transaction failure

What if this short message interface (most of us are using the three-way interfaces), mail a little uncomfortable (an anomaly ...)

Accident

You need to understand the principles of asynchronous

@Async asynchronous :

  • Actually contains notes @Async spring in the bean when scanning method scans, if included, spring will be a sub-class for the bean dynamically generated, we call proxy class (jdkProxy), the proxy class is inherited us written by the bean, and then injected into the proxy class came in, that at this time, in the implementation of this approach will be to the proxy class, proxy class to determine if this method should execute asynchronously, they will not call the parent class (we originally wrote the, the bean) corresponding method.
  • spring maintains a queue himself, the way he would need to perform, placed in the queue, waiting to read this thread pool queue, complete the implementation process, thus completing the asynchronous function.
  • We can focus on the task of re-configuration when there is a parameter allows us to configure the number of thread pool. Because of this implementation, the method in the same class called to add @Async comment is invalid! Because when you're in the same class, method invocation is performed in the class body, spring can not intercept the method call (Why, this is talking about below ... 奸笑...嘻嘻嘻嘻...).
  • In that one step further, Spring provides us with AOP, aspect-oriented features. His principle and the principle of asynchronous annotations are similar, spring start when the container is scanned as defined class section. When these classes are injected, the injected agent class is, when you call these methods, the proxy class is called nature. The method of implementation of the parent class to go through the corresponding proxy class that spring just before and after the call to execute a piece of code to complete the implementation of the AOP!

SpringBoot environment, to use @Async notes, we need to add @EnableAsync comment on the startup class. The use @Scheduled with annotations SpringBoot need to add the startup class @EnableScheduling is the same reason (of course, you use the old XML configurations are possible, but in SpringBoot environment, we recommend the development of the whole notes), specific principles We will analyze below. After adding @EnableAsync comment, if we want to turn in to call a method when a new thread starts asynchronous operation, we only need to add @Async comment on this approach, of course, the premise is that this method where the class must be in the Spring Environment.

示例:非spingboot项目
<task:annotation-driven executor="annotationExecutor" />
<!-- 支持 @Async 注解 -->
<task:executor id="annotationExecutor" pool-size="20"/>
复制代码

Implementation process:

  1. Scanning is turned on notes EnableAsync, there is a comment on @ EnableAsync @Import(AsyncConfigurationSelector.class), springboot injected into the old routine
  2. Please re venue AsyncConfigurationSelector, see the selectImportsmethod yet, here are using the default using ProxyAsyncConfigurationthis configuration class
  3. Continue to observe the ProxyAsyncConfigurationsuccession AbstractAsyncConfiguration, inside it's setConfigurersshows that we can achieve AsyncConfigurerto complete the thread pool as well as an interface to configure exception handler, but in a Spring environment can have only one implementation class, otherwise it will throw an exception. On a bit of code:
    /**
	 * Collect any {@link AsyncConfigurer} beans through autowiring.
	 */
	@Autowired(required = false)
	void setConfigurers(Collection<AsyncConfigurer> configurers) {
      
		if (CollectionUtils.isEmpty(configurers)) {
			return;
		}
      //AsyncConfigurer用来配置线程池配置以及异常处理器,而且在Spring环境中最多只能有一个,在这里我们知道了,如果想要自己去配置线程池,只需要实现AsyncConfigurer接口,并且不可以在Spring环境中有多个实现AsyncConfigurer的类。
		if (configurers.size() > 1) {
			throw new IllegalStateException("Only one AsyncConfigurer may exist");
		}
		AsyncConfigurer configurer = configurers.iterator().next();
		this.executor = configurer.getAsyncExecutor();
		this.exceptionHandler = configurer.getAsyncUncaughtExceptionHandler();
	}
复制代码
  1. ProxyAsyncConfigurationInjected bean AsyncAnnotationBeanPostProcessor, this BeanPostBeanPostProcessorobviously would be annotated can trigger asynchronous operation (for example @Async) the processing Bean
  2. We note that AsyncAnnotationBeanPostProcessorthere is rewriting the parent class setBeanFactory, this method is a bit familiar with it, it is the BeanFactoryAwareinterface method, AsyncAnnotationBeanPostProcessorthe parent class implements this interface, we analyzed a long time before initialization Bean's, is mentioned this interface implements Bean Aware types of interfaces, will call the appropriate initialization method during initialization of Bean, you can view the specific AbstractAutowireCapableBeanFactory#initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd)method
  3. The Bean process postProcessAfterInitializationmethod in the ancestor class AbstractAdvisingBeanPostProcessorof. We can see from the source code. AsyncAnnotationBeanPostProcessorBean is a post-process theBeanPostProcessor
  4. The last agent to JdkDynamicAopProxythe invoke method is the use of a chain of responsibility pattern: List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);the proxy intercepts to perform, inform the chain will include setBeanFactory () method to generate the notification, execution chain will be used to create ReflectiveMethodInvocationobjects, and ultimately call ReflectiveMethodInvocationthe proceed()to complete the enhancement processing method, proceed()a method where a branch will perform the last
  5. The actual implementation is AsyncExecutionInterceptortheinvoke()
  6. Note : Although the above Spring environment can have only one AsyncConfigurerimplementation class, but it does not mean that only one thread pool configuration in a Spring environment, in a Spring environment can configure multiple thread pools, and we can use @Async when annotations for asynchronous operations, specified by the value in the thread pool BeanName properties, so that you can specify the task thread pool as a carrier, see:determineAsyncExecutor

Long-winded brothers:

When we want to facilitate the use @Async annotation enables asynchronous operation in SpringBoot in, only to realize AsyncConfigurer Interface (thus configured with the default thread pool configuration, of course, need the class in a Spring environment, because it is the default, so only can have one, no more implementation classes to prioritize the argument), for configuration of the thread pool, plus @EnableAsync comment on startup class, you can make @Async annotations take effect.

We can not even explicitly implement AsyncConfigurer, we can configure multiple types of Executor Bean in a Spring environment, when used @Async annotation, the annotation value specified as your Executor type of BeanName, you can use the specified thread pool as a carrier task, so that you use the thread pool is also more flexible.

Small dessert

So: we will use Spring's asynchronous programming dear, asynchronous programming there are many ways: for example, common Future的sync, CompletableFuture.supplyAsync,, @Async, ha ha really are inseparable Thread.start () ..., and so I said a joke:

Dad had two children: Alice and Bob. Dad wanted to drink, let him buy red wine, red out. Then dad suddenly want to smoke, so let Dad Xiao Ming to buy cigarettes. In the face of the object of thought, in general, will buy things, then bought it as a method, according to the order structure or multi-thread synchronization, then Xiao Ming to go buy cigarettes would have to buy things such as red this operation It is finished. This will increase the cost of time (in case of urinary father hold it?). Asynchronous is to solve this problem. You can separately issued a directive to the red Xiao Ming, let them buy things, then you can do your own thing their own, so they bought when receiving the results of it.

package com.boot.lea.mybot.futrue;

/**
 * @ClassName: TestFuture
 * @Description: 演示异步编程
 * @author LiJing
 * @date 2019/8/5 15:16
 */
@SuppressWarnings("all")
public class TestFuture {
    static ExecutorService executor = Executors.newFixedThreadPool(2);

    public static void main(String[] args) throws InterruptedException {
        //两个线程的线程池
        //小红买酒任务,这里的future2代表的是小红未来发生的操作,返回小红买东西这个操作的结果
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("爸:小红你去买瓶酒!");
            try {
                System.out.println("小红出去买酒了,女孩子跑的比较慢,估计5s后才会回来...");
                Thread.sleep(5000);
                return "我买回来了!";
            } catch (InterruptedException e) {
                System.err.println("小红路上遭遇了不测");
                return "来世再见!";
            }
        }, executor);

        //小明买烟任务,这里的future1代表的是小明未来买东西会发生的事,返回值是小明买东西的结果
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("爸:小明你去买包烟!");
            try {
                System.out.println("小明出去买烟了,可能要3s后回来...");
                Thread.sleep(3000);

                throw new InterruptedException();
//                return "我买回来了!";
            } catch (InterruptedException e) {
                System.out.println("小明路上遭遇了不测!");
                return "这是我托人带来的口信,我已经不在了。";
            }
        }, executor);

        //获取小红买酒结果,从小红的操作中获取结果,把结果打印
        future2.thenAccept((e) -> {
            System.out.println("小红说:" + e);
        });
        //获取小明买烟的结果
        future1.thenAccept((e) -> {
            System.out.println("小明说:" + e);
        });

        System.out.println("爸:等啊等 西湖美景三月天嘞......");
        System.out.println("爸: 我觉得无聊甚至去了趟厕所。");
        Thread.currentThread().join(9 * 1000);
        System.out.println("爸:终于给老子买来了......huo 酒");
        //关闭线程池
        executor.shutdown();
    }
}
复制代码

operation result:

Guess you like

Origin juejin.im/post/5d47a80a6fb9a06ad3470f9a