Voilà ce qui s'est passé.,En ce moment, je suis impliqué dans XXXX Construction du projet,Besoin d'interfaces d'amarrage avec des tiers.Il y a plusieurs notifications asynchrones dans l'interface de l'autre,Pour la sécurité de l'interface,Les paramètres de l'interface doivent être vérifiés et signés.
Pour faciliter le traitement des paramètres de retour des notifications asynchrones,Z Le collègue propose d'encapsuler uniformément la fonction d'inspection des étiquettes,Tout ce que vous devez faire, c'est vous concentrer sur votre logique d'entreprise.
Z Les collègues ont choisi“Analyseur de paramètres personnalisé”Solutions pour,Ensuite, regardons le Code.
Commentaires personnalisés
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public @interface RsaVerify {
/**
* Si la fonction de vérification des signatures est activée,Vérification par défaut
*/
boolean verifySign() default true;
}
Analyseur de paramètres de méthode personnalisé
@AllArgsConstructor
@Component
//Réalisation HandlerMethodArgumentResolver Interface
public class RsaVerifyArgumentResolver implements HandlerMethodArgumentResolver {
private final SecurityService securityService;
/**
* Cette méthode est utilisée pour déterminer si l'interface demandée doit analyser les paramètres,
* Retour si nécessaire true,Puis appelez ce qui suit resolveArgument Méthodes,
* Si le retour n'est pas nécessaire false
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RsaVerify.class);
}
/**
* La vraie méthode d'analyse,Résoudre la valeur du paramètre dans la requête à un objet
* parameter Paramètres de la méthode à analyser
* mavContainer Demande actuelle ModelAndViewContainer(Fournir l'accès au modèle pour la demande)
* webRequest Demande actuelle
* WebDataBinderFactory Pour créer WebDataBinder L'usine de
*/
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
RsaVerify parameterAnnotation = parameter.getParameterAnnotation(RsaVerify.class);
if (!parameterAnnotation.verifySign()) {
return mavContainer.getModel();
}
//Logique de traitement et de vérification des paramètres
......
//Renvoie le paramètre de classe d'entité traité
return ObjectMapperFactory
.getDateTimeObjectMapper("yyyyMMddHHmmss")
.readValue(StringUtil.queryParamsToJson(sb.toString()), parameter.getParameterType());
}
}
Créer une classe de configuration
@Configuration
@AllArgsConstructor
public class PayTenantWebConfig implements WebMvcConfigurer {
private final RsaVerifyArgumentResolver rsaVerifyArgumentResolver;
/**
* Ajouter un analyseur de paramètres de méthode personnalisé à la classe de configuration
*/
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(rsaVerifyArgumentResolver);
}
}
Utiliser
La méthode d'utilisation est très simple,Il suffit d'introduire des annotations sur les paramètres
@RestController
@Slf4j
@RequestMapping("/xxx")
public class XxxCallbackController {
/**
* @param params
* @return
*/
@PostMapping("/callback")
public String callback(@RsaVerify CallbackReq params) {
log.info("receive callback req={}", params);
//Traitement de la logique d'entreprise
.....
return "success";
}
}
Questions
Question 1
Regarde ça.,Un ami prudent devrait avoir des questions:Maintenant que des annotations personnalisées sont utilisées ici,Pourquoi pas?TangentePour réaliser,Au lieu de cela, utilisez un analyseur de paramètres personnalisé?Very Good!C'est pareil.QQuestions soulevées,Les collègues disent que c'est parce que jackson
L'action de désrialisation deSupérieur àPriorité de la section,Donc vous avez signalé une erreur de désrialisation qui a échoué avant d'entrer dans la section.
Deuxième question
Pourquoi? controller
Note moyenne @RequestBody
Il a disparu?
Pour répondre à cette question,Il faut qu'on comprenne.HandlerMethodArgumentResolverComposite
C'est la classe.,Ci - après dénomméeComposite
.SpringMVC
Au démarrage, tous les Analyseurs de paramètres sont placés dans Composite
Moyenne,Composite
Est une collection de tous les paramètres.Lorsqu'un paramètre est analysé, une paire de support est sélectionnée dans cette collection d'Analyseurs de paramètres parameter
Analyseur de paramètres analysé,L'analyseur est ensuite utilisé pour l'analyse des paramètres.
Encore parce que@RequestBody
Donc l'analyseur de paramètres utiliséRequestResponseBodyMethodProcessor
Priorité sur notre analyseur de paramètres personnalisé,Donc si le partage est intercepté par le premier,Donc pour une utilisation normale,Nous devons@RequestBody
Note enlevée.
/**
* Find a registered {@link HandlerMethodArgumentResolver} that supports
* the given method parameter.
*/
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
En haut Z Le plan du collègue peut déjà résoudre le problème,Mais le programme présente encore deux lacunes:
Chaque rappel est nécessaire pour créer son propre controller
Couche,Il n'y a pas d'entrée unifiée vers l'extérieur;
Une annotation personnalisée doit être ajoutée à la méthode,Très intrusif;
C'est pourquoi nous avons discuté,Décision d'abandonner le programme,Mais l'idée du programme mérite d'être étudiée.Ensuite, analysons la nouvelle solution:
Définir les classes d'interface d'affaires
La classe d'interface d'affaires contient deux méthodes:Types de processus opérationnels spécifiques;Traitement spécifique des affaires.
public interface INotifyService {
/**
* Type de traitement
*/
public String handleType();
/**
* Traiter des affaires spécifiques
*/
Integer handle(String notifyBody);
}
Notification asynchrone entrée unifiée
@AllArgsConstructor
@RestController
@RequestMapping(value = "/notify")
public class NotifyController {
private IService service;
@PostMapping(value = "/receive")
public String receive(@RequestBody String body) {
//Traitement des notifications
Integer status = service.handle(body);
return "success";
}
}
In Iservice En deux étapes:
In spring Après le démarrage,Collecter tous les types comme INotifyService
Et mettremap
Moyenne;
Transformation des paramètres de traitement,Traitement de l'inspection et de la signature;
private ApplicationContext applicationContext;
private Map<String,INotifyService> notifyServiceMap;
/**
* Charge de démarrage
*/
@PostConstruct
public void init(){
Map<String,INotifyService> map = applicationContext.getBeansOfType(INotifyService.class);
Collection<INotifyService> services = map.values();
if(CollectionUtils.isEmpty(services)){
return;
}
notifyServiceMap = services.stream().collect(Collectors.toMap(INotifyService::handleType, x -> x));
}
@Override
public Map<String, INotifyService> getNotifyServiceMap() {
return notifyServiceMap;
}
@Override
public Integer handle(String body) {
//Traitement des paramètres+Logique de vérification des signatures
......
//Obtenir des classes de mise en œuvre d'entreprise spécifiques
INotifyService notifyService=notifyServiceMap.get(notifyType);
Integer status=null;
if(Objects.nonNull(notifyService)) {
//Exécution des activités spécifiques
try {
status=notifyService.handle(JSON.toJSONString(requestParameter));
} catch (Exception e) {
e.printStackTrace();
}
}
//Traitement logique ultérieur
......
return status;
}
Réalisation concrète de l'entreprise
@Service
public class NotifySignServiceImpl implements INotifyService {
@Override
public String handleType() {
return "type_sign";
}
@Override
@Transactional
public Integer handle(String notifyBody) {
//Traitement spécifique des affaires
......
}
}
Résumé
Ce système fournit une entrée de notification asynchrone unifiée,Séparer la logique commune de traitement des paramètres et de vérification des signatures de la logique opérationnelle.
Utilisation java Propriétés de la classe de chargement dynamique,Collecte des classes d'implémentation par type.
Utilisation java Propriétés polymorphes,Gérer différentes logiques d'affaires par différentes classes d'implémentation.
Regarde ça.,Je crois que vous avez une certaine compréhension de ces deux options de mise en oeuvre,Vous pouvez essayer de l'appliquer à un projet ultérieur,Une expérience!