2017-10-19 16 views
0

J'utilise la fonctionnalité @EnableAsync de Spring pour exécuter des méthodes de manière asynchrone. Pour la sécurité, j'utilise Apache Shiro. Dans le code qui est exécuté de manière asynchrone, j'ai besoin d'accéder au sujet Shiro qui a été attaché au thread qui a déclenché l'appel asynchrone.Personnalisation/Extension du support @Async de Spring pour shiro

Shiro prend en charge l'utilisation d'un objet existant dans un autre thread en associant le sujet à l'Callable qui doit être exécuté sur le thread différent (voir here):

Subject.associateWith(Callable) 

Malheureusement, je n'ai pas accès direct au Callable car ce truc est encapsulé par Spring. J'ai trouvé que je devrais étendre le AnnotationAsyncExecutionInterceptor de Spring pour associer mon sujet avec le Callable créé (c'est la partie facile).

Par le problème est maintenant comment faire ressort utiliser mon AnnotationAsyncExecutionInterceptor personnalisé au lieu de celui par défaut. Celui par défaut est créé en AsyncAnnotationAdvisor et AsyncAnnotationBeanPostProcessor. Je peux bien sûr prolonger ces cours aussi, mais cela ne pose que des problèmes car j'ai besoin de faire en sorte que Spring utilise à nouveau mes classes étendues.

Y a-t-il un moyen de réaliser ce que je veux?

Je serais bien d'ajouter une nouvelle annotation asynchrone personnalisée. Mais je ne pense pas que ce serait vraiment utile.


MISE À JOUR: En fait, ma conclusion selon laquelle AnnotationAsyncExecutionInterceptor aurait besoin d'être personnalisé était faux. Par hasard, je suis tombé sur org.apache.shiro.concurrent.SubjectAwareExecutorService qui fait exactement ce que je veux et m'a fait penser que je pouvais simplement fournir un exécuteur personnalisé au lieu de personnaliser l'intercepteur. Voir ma réponse pour plus de détails.

+0

Est-ce une option pour vous de: 1 réécrire vos 'méthodes asynchrones' pour retourner les Callables 2. Pour les lancer manuellement ('call()') manuellement (dans un executePool configuré pour le printemps) – xerx593

Répondre

1

j'ai réussi à obtenir ce que je veux - sujet Shiro est automatiquement lié et non lié à des tâches qui sont exécutées par le soutien async du printemps - en fournissant une version étendue du ThreadPoolTaskExecutor:

public class SubjectAwareTaskExecutor extends ThreadPoolTaskExecutor { 

    @Override 
    public void execute(final Runnable aTask) { 
    final Subject currentSubject = ThreadContext.getSubject(); 
    if (currentSubject != null) { 
     super.execute(currentSubject.associateWith(aTask)); 
    } else { 
     super.execute(aTask); 
    } 
    } 

    ... // override the submit and submitListenable method accordingly 
} 

Pour faire usage de printemps de cette exécuteur testamentaire que je devais mettre en œuvre un AsyncConfigurer qui retourne mon exécuteur testamentaire personnalisé:

@EnableAsync 
public class AsyncConfiguration implements AsyncConfigurer { 

    @Override 
    public Executor getAsyncExecutor() { 
    final SubjectAwareTaskExecutor executor = new SubjectAwareTaskExecutor(); 
    executor.setBeanName("async-executor"); 
    executor.setCorePoolSize(10); 
    executor.setMaxPoolSize(10); 
    executor.initialize(); 
    return executor; 
    } 

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

avec ce changement le parent du sujet de discussion sera automatiquement disponible dans les méthodes qui sont annotées avec @Async et - probablement encore plus important - le sujet sera détaché du thread après l'exécution de la méthode asynchrone.