جاوا و تکنولوژی های آن

java programming language

در این وبلاگ به بررسی نکات موجود در جاوا و تکنولوژی های آن می پردازیم

طبقه بندی موضوعی


در این قسمت به بررسی اجرای Asynchronous در Spring و Spring MVC میپردازیم. 

به طور ساده متدی در یک Bean که با Async@ مشخص شده باشد در یک ترد جداگانه اجرا خواهد شد و اجرا کننده آن متد منتظر خاتمه یافتن اجرا نخواهد ماند 



نحوه فعال سازی Asynch در Spring :


به دو روش امکانپذیر است روش کد java و xml


کافی است در یک کلاس Configuration@ یک EnableAsync@ به آن کلاس اضافه کنیم 

@Configuration
@EnableAsync
public class SpringAsyncConfig { ... }


برای فعال سازی استفاده از EnableAsync@ کافی است ولی اگر بخواهیم تنظیماتی را اعمال کنیم مانند :


- annotation  : با EnableAsync@ بصورت اتوماتیک Async@ و Asynchronous@ از EJB، موجود در پکیج javax.ejb.Asynchronous  قابل شناسایی هستند ولی ما نیز میتوانیم annotation دلخواه را تعریف کنیم 

- mode : در AOP نوع Advise که مورد استفاده قرار میگیرد را مشخص میکند (JDK Proxy و AspectJ Weaving )

- ProxyTargetClass : نوع پروکسی مورد استفاده در CGLIB یا  JDK. این گزینه موقعی فعال میشود که mode را در حالت AdviceMode.PROXY قرار داده باشیم 

- order : این گزینه مشخص میکند که کدام AsyncAnnotationBeanPostProcessor  مورد تایید قرار گیرد، به طور پیش فرض آخرین حالت تعیین شده اجرا خواهد شد بطوری که proxy های موجود هم از آن استفاده خواهند کرد 



روش فعال سازی Async با تنظیمات XML :

<task:executor id="myexecutor" pool-size="5"  />
<task:annotation-driven executor="myexecutor"/>



Async@ : 


ابتدا دو محدودیت استفاده از Async@ را باید بدانیم :

- فقط روی متد های public قابل اعمال است (چون proxy فقط میتواند با متد های public کار کند)

- کلاس که متد Async@ در آن تعریف شده است نمیتواند آن متد را اجرا کند (چون proxy اعمال نمیشود و مستقیما به آن متد دسترسی نخواهد داشت)



مثالی ساده که یک متد Async@ با مقدار برگشتی void داریم :

@Async
public void asyncMethodWithVoidReturnType() {
    System.out.println("Execute method asynchronously. "
      + Thread.currentThread().getName());
}


و حالتی که مقدار برگشتی دارد :

@Async
public Future<String> asyncMethodWithReturnType() {
    System.out.println("Execute method asynchronously - "
      + Thread.currentThread().getName());
    try {
        Thread.sleep(5000);
        return new AsyncResult<String>("hello world !!!!");
    } catch (InterruptedException e) {
        //
    }
 
    return null;
}


* Spring همچنین کلاس AsyncResult که Future را پیاده سازی کرده است را ارائه میکند که برای رهگیری اجرا و نتیجه اجرا قابل استفاده است 


فراخوانی یک متد Async@ :


public void testAsyncAnnotationForMethodsWithReturnType()
  throws InterruptedException, ExecutionException {
    System.out.println("Invoking an asynchronous method. "
      + Thread.currentThread().getName());
    Future<String> future = asyncAnnotationExample.asyncMethodWithReturnType();
 
    while (true) {
        if (future.isDone()) {
            System.out.println("Result from asynchronous process - " + future.get());
            break;
        }
        System.out.println("Continue doing something else. ");
        Thread.sleep(1000);
    }
}




Executor مورد استفاده جهت اجرا متد های Async@ : 


در Spring بصورت پیش فرض از SimpleAsyncTaskExecutor استفاده میکند که در دو level متواند Override شود : Application Level و Method Level بصورت جداگانه 


Override کردن در Method Level : 


ابتدا  Executor مورد نیاز را در کلاس Configuration@ تعریف میکنیم :

@Configuration
@EnableAsync
public class SpringAsyncConfig {
     
    @Bean(name = "threadPoolTaskExecutor")
    public Executor threadPoolTaskExecutor() {
        return new ThreadPoolTaskExecutor();
    }
}


و بعد برای استفاده از آن کافی است نام Bean ای که مسئول Executor است را به Async@ پاس دهیم  :

@Async("threadPoolTaskExecutor")
public void asyncMethodWithConfiguredExecutor() {
    System.out.println("Execute method with configured executor - "
      + Thread.currentThread().getName());
}



Override کردن در Application Level : 


ابتدا نیاز به کلاس Configuration@ ای داریم که از اینترفیس AsyncConfigurer ارث بری و متد getAsyncExecutor را پیاده سازی کند که مقدار برگشتی آن از نوع Executor خواهد بود و این Executor اجرا کنند پیش فرض متد های Async@ خواهد بود :

@Configuration
@EnableAsync
public class SpringAsyncConfig implements AsyncConfigurer {
     
    @Override
    public Executor getAsyncExecutor() {
        return new ThreadPoolTaskExecutor();
    }
     
}




گرفتن خطا ها و رسیدگی کردن به آنها :


وقتی مقدار برگشتی متد Async@ از نوع Future باشد رسیدگی به خطا کار راحتی خواهد بود کافی است متد ()Future.get را فراخوانی کنیم در صورتی که مقداری در آن نباشد خطا پرتاب خواهد شد 


اما اگر مقدار برگشتی متد Async@ از نوع void باشد خطا ها به تردی که آن متد را اجرا کرده است ارسال نمیشود و باید تنظیماتی را ست کنیم برای این منظور ابتدا یک کلاس مدیریت خطای Async خاص طراحی میکنیم که از اینترفیس AsyncUncaughtExceptionHandler ارث بری کرده است و متد ()handleUncaughtException زمانی فراخوانی میشود که خطایی رخ داده باشد 


public class CustomAsyncExceptionHandler
  implements AsyncUncaughtExceptionHandler {
 
    @Override
    public void handleUncaughtException(
      Throwable throwable, Method method, Object... obj) {
  
        System.out.println("Exception message - " + throwable.getMessage());
        System.out.println("Method name - " + method.getName());
        for (Object param : obj) {
            System.out.println("Parameter value - " + param);
        }
    }
     
}


اگر بالاتر نگاه کنید ما کلاس AsyncConfigurer را پیاده سازی کردیم و تنظیماتی را در ان قرار دادیم برای ست کردن کلاس بالا جهت مدیریت خطا هایی Asynchronous باید متد ()getAsyncUncaughtExceptionHandler را Override کرده و کلاس مدیریت خطای دلخواه را به آن معرفی کنیم 


@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
    return new CustomAsyncExceptionHandler();
}






حالا بریم نگاهی به نحوه کار Asynchronous در Spring MVC بیاندازیم :


در این مبحث به چگونگی پشتیبانی Servlet 3 از درخواست های Asynchronous و نحوه کار آن با Spring Security و Spring MVC میپردازیم

بیشترین کاربرد async در Web مربوط به درخواست هایی است که رسیدگی به آنها مدت زمان زیادی طول خواهد کشید 


اضافه کردن کتابخانه های مورد نیاز به pom پروژه :

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>4.2.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>4.2.1.RELEASE</version>
</dependency>



Spring MVC و Async@ : 


در ابتدا باید اشاره کینم که Spring Security با WebAsyncManager قابلیت ادغام شدن را دارد و بعد باید مطمئن شد که  springSecurityFilterChain قابلیت پردازش Async برایش ست شده باشد که از دو طریق قابل تنظیم است یک روی کلاس تنظیمات servlet :

dispatcher.setAsyncSupported(true);

و دیگری XML :

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <async-supported>true</async-supported>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>ASYNC</dispatcher>
</filter-mapping>

همچنین باید در تنظیمات servlet فایل xml آنرا فعال کنیم :

<servlet>
    ...
    <async-supported>true</async-supported>
    ...
</servlet>



بررسی یکی از کاربرد های مورد استفاده :

@Override
public Callable<Boolean> checkIfPrincipalPropagated() {
    Object before 
      = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    log.info("Before new thread: " + before);
 
    return new Callable<Boolean>() {
        public Boolean call() throws Exception {
            Object after 
              = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            log.info("New thread: " + after);
            return before == after;
        }
    };
}

در این مثال میخواهیم ببینیم که آیا Spring SecurityContext میتواند از ترد جدید ایجاد شده اطلاعات را عبور دهد 

در پایین لاگ مربوط به اجرای متد بالا و نحوه کارکرد آن با SecurityContext را نشان میدهد :

web - 2017-01-02 10:42:19,011 [http-nio-8081-exec-3] INFO
  o.baeldung.web.service.AsyncService - Before new thread:
  org.springframework.security.core.userdetails.User@76507e51:
  Username: temporary; Password: [PROTECTED]; Enabled: true;
  AccountNonExpired: true; credentialsNonExpired: true;
  AccountNonLocked: true; Granted Authorities: ROLE_ADMIN
 
web - 2017-01-02 10:42:19,020 [MvcAsync1] INFO
  o.baeldung.web.service.AsyncService - New thread:
  org.springframework.security.core.userdetails.User@76507e51:
  Username: temporary; Password: [PROTECTED]; Enabled: true;
  AccountNonExpired: true; credentialsNonExpired: true;
  AccountNonLocked: true; Granted Authorities: ROLE_ADMIN



اگر تنظیمات مربوط به Spring SecurityContext را اعمال نمیکردیم فراخوانی دوم مقدار null را دریافت میکند 


کاربرد های دیگر Async در وب که نیاز به همراهی SecurityContext دارد :


- زمانی که ما بخواهیم چندین درخواست بصورت اجرای موازی داشته باشیم که اجرای آنها زمان زیادی را صرف کند 

- درخواست ما نیاز به عملیات زیادی بصورت محلی داشته باشد و درخواست دیگری به موازات آن ارسال شود که باید به موازات عملیات قبلی اجرا شود 

- وقتی که سناریوی Fire-and-Forget داشته باشیم مثل ارسال ایمیل



* باید به این نکته توجه داشت که اگر اجرای متد های Synchronous بصورت زنجیری بودند، تبدیل کردن آنها به حالت Asynchronous ممکن است  نیاز به جوابی Synchronous داشته باشد 






نظرات  (۰)

هیچ نظری هنوز ثبت نشده است

ارسال نظر

ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
شما میتوانید از این تگهای html استفاده کنید:
<b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
تجدید کد امنیتی