“This is the fifth day of my participation in the November Gwen Challenge. See details of the event: The Last Gwen Challenge 2021”.
1 Service Background
The company is to do a lot of hospitals wechat public number, each hospital’s SMS provider is not the same, the call to send verification code SMS platform is not consistent (Tencent SMS, Ali Cloud SMS, China Telecom SMS, China Mobile SMS…) Companies write a lot of if… Else, the previous old code screenshot is as follows, messy and bad, and it is very troublesome to add a SMS provider
2. Code implementation description
Only the policy pattern is described here, and the implementation logic is replaced by a simple output statement
3 Enumeration of user-defined SMS types
import com.ourlang.message.enums.MsgType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** ** where elementType. TYPE means used on classes, * retentionPolicy. RUNTIME means resolved at RUNTIME. * * Custom annotations for SMS types *@author ourlang
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MsgAnnotation {
MsgType type(a);
}
Copy the code
4 Create an enumeration class for short message types
import lombok.AllArgsConstructor;
import lombok.Getter;
/** * Enumeration of SMS classes **@author ourlang
*/
@AllArgsConstructor
@Getter
public enum MsgType {
/** * Tencent SMS */
TENCENT(1."Tencent SMS"),
/** * aliyun SMS */
ALIBABA(2."Aliyun SMS"),
/**
* 中国移动短信
*/
MOBILE(3."China Mobile SMS"),
/** * China telecom SMS */
TELECOM(4."China Telecom SMS"),;/** ** encoding */
private final Integer code;
/** ** Indicates */
private final String desc;
}
Copy the code
5. Construct abstract policies and policy implementation classes
import com.ourlang.message.entity.MsgEntity;
/*** * Build abstract policies and policy implementation classes *@author ourlang
*/
public abstract class AbstractMsgStrategy {
/** * Policy abstract method to send SMS *@paramMsgEntity SMS entity class *@returnReturn */ on successful processing
abstract public String sendMessage(MsgEntity msgEntity);
}
Copy the code
5.1 Tencent SMS Strategy implementation class
import com.ourlang.message.annotation.MsgAnnotation;
import com.ourlang.message.entity.MsgEntity;
import com.ourlang.message.enums.MsgType;
import org.springframework.stereotype.Service;
/** * Tencent SMS strategy *@author ourlang
*/
@Service
@MsgAnnotation( type= MsgType.TENCENT)
public class TencentStrategy extends AbstractMsgStrategy {
@Override
public String sendMessage(MsgEntity msgEntity) {
System.out.println("Tencent SMS sent successfully"+msgEntity.getPhoneNumber()+msgEntity.getContent()+msgEntity.getVerificationCode());
return "Tencent"; }}Copy the code
5.2 Aliyun Policy Implementation class
import com.ourlang.message.annotation.MsgAnnotation;
import com.ourlang.message.entity.MsgEntity;
import com.ourlang.message.enums.MsgType;
import org.springframework.stereotype.Service;
/** * Aliyun SMS strategy *@author ourlang
*/
@Service
@MsgAnnotation( type= MsgType.ALIBABA)
public class AlibabaStrategy extends AbstractMsgStrategy {
@Override
public String sendMessage(MsgEntity msgEntity) {
System.out.println("Ali Cloud SMS sent successfully"+msgEntity.getPhoneNumber()+msgEntity.getContent()+msgEntity.getVerificationCode());
return "Ali Cloud"; }}Copy the code
5.3 SMS strategy of China Mobile
import com.ourlang.message.annotation.MsgAnnotation;
import com.ourlang.message.entity.MsgEntity;
import com.ourlang.message.enums.MsgType;
import org.springframework.stereotype.Service;
/** * China Mobile SMS strategy *@author ourlang
*/
@Service
@MsgAnnotation( type= MsgType.MOBILE)
public class MobileStrategy extends AbstractMsgStrategy {
@Override
public String sendMessage(MsgEntity msgEntity) {
System.out.println("China Mobile SMS sent successfully"+msgEntity.getPhoneNumber()+msgEntity.getContent()+msgEntity.getVerificationCode());
return China Mobile; }}Copy the code
5.4 SMS Policy of China Telecom
import com.ourlang.message.annotation.MsgAnnotation;
import com.ourlang.message.entity.MsgEntity;
import com.ourlang.message.enums.MsgType;
import org.springframework.stereotype.Service;
/** * China telecom SMS policy *@author ourlang
*/
@Service
@MsgAnnotation( type= MsgType.TELECOM)
public class TelecomStrategy extends AbstractMsgStrategy {
@Override
public String sendMessage(MsgEntity msgEntity) {
System.out.println("China Telecom SMS sent successfully"+msgEntity.getPhoneNumber()+msgEntity.getContent()+msgEntity.getVerificationCode());
return "China Telecom"; }}Copy the code
6 SMS entities
import com.ourlang.message.enums.MsgType;
import lombok.Data;
import lombok.experimental.Accessors;
/** * SMS entity class *@author ourlang
*/
@Data
@Accessors(chain = true)
public class MsgEntity {
/** * the unique primary key of the SMS platform */
private String apiKey;
/** * SMS key */
private String secretKey;
/** * Phone number */
private String phoneNumber;
/** * Verification code */
private String verificationCode;
/** * Message content */
private String content;
/** * Used to distinguish the types of short messages */
private MsgType msgType;
}
Copy the code
7 Policy Distribution processing class
import com.ourlang.message.annotation.MsgAnnotation;
import com.ourlang.message.entity.MsgEntity;
import com.ourlang.message.strategy.AbstractMsgStrategy;
import com.ourlang.message.util.SpringBeanUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Map;
/** * This class receives the business request and forwards it to the specific policy class for processing@author ourlang
*/
@Service
@Slf4j
public class StrategyUseService {
/** * handle cancel logic */
public String sendMessage(MsgEntity msgEntity) {
AbstractMsgStrategy // represents all subclasses or interfaces under AbstractMsgStrategy.
Map<String, AbstractMsgStrategy> beanMap = SpringBeanUtils.getBeanMap(AbstractMsgStrategy.class);
String message = "";
try {
for (Map.Entry<String, AbstractMsgStrategy> entry : beanMap.entrySet()) {
// represents getting the specific proxy class
Object real = SpringBeanUtils.getTarget(entry.getValue());
// represents annotation information for the class that has MsgAnnotation on it.
MsgAnnotation annotation = real.getClass().getAnnotation(MsgAnnotation.class);
// Call the corresponding SMS sending class with the type of SMS that is passed
if (msgEntity.getMsgType().getCode().equals(annotation.type().getCode())) {
message = entry.getValue().sendMessage(msgEntity);
break; }}}catch (Exception e) {
log.error("Failed to get target proxy object {}", e);
message = e.getMessage();
}
returnmessage; }}Copy the code
Dynamically get spring-managed bean utility classes
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.framework.AopProxy;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.util.Map;
/ * * * dynamic for spring managed bean instance object tools * https://github.com/ourlang * * * spring context@author ourlang
*/
@Component
public class SpringBeanUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
/** * Implements the ApplicationContextAware interface callback method to set the context */
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
if (applicationContext == null) { applicationContext = context; }}/** * get bean **@paramName service Annotation mode Name is in small hump format *@returnThe instance Object of the Object bean */
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) throws BeansException {
return (T) applicationContext.getBean(name);
}
/** * get bean **@paramCLZ Service corresponding class *@returnThe instance Object of the Object bean */
@SuppressWarnings("unchecked")
public static <T> T getBean(Class
clz) throws BeansException {
return (T) applicationContext.getBean(clz);
}
/** * Get Map ** of type requiredType@param clazz
* @return* /
public static <T> Map<String, T> getBeanMap(Class<T> clazz) {
return applicationContext.getBeansOfType(clazz);
}
/** * get the target object **@paramProxy Proxy object *@returnTarget object *@throws Exception
*/
public static Object getTarget(Object proxy) throws Exception {
if(! AopUtils.isAopProxy(proxy)) {// Not a proxy object, return directly
return proxy;
}
if (AopUtils.isJdkDynamicProxy(proxy)) {
return getJdkDynamicProxyTargetObject(proxy);
} else {
// cglib
returngetCglibProxyTargetObject(proxy); }}private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception {
Field field = proxy.getClass().getSuperclass().getDeclaredField("h");
field.setAccessible(true);
AopProxy aopProxy = (AopProxy) field.get(proxy);
Field advised = aopProxy.getClass().getDeclaredField("advised");
advised.setAccessible(true);
Object target = ((AdvisedSupport) advised.get(aopProxy)).getTargetSource().getTarget();
return target;
}
private static Object getCglibProxyTargetObject(Object proxy) throws Exception {
Field field = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
field.setAccessible(true);
Object dynamicAdvisedInterceptor = field.get(proxy);
Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
advised.setAccessible(true);
Object target = ((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
returntarget; }}Copy the code
9 test
import com.ourlang.message.entity.MsgEntity;
import com.ourlang.message.enums.MsgType;
import com.ourlang.message.service.StrategyUseService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class MessageApplicationTests {
@Autowired
private StrategyUseService strategyUseService;
@Test
void contextLoads(a) {
MsgEntity entity=new MsgEntity();
entity.setContent("Hello apple").setPhoneNumber("13898765432").setVerificationCode("123456").setMsgType(MsgType.ALIBABA);; String message = strategyUseService.sendMessage(entity); System.out.println(message); }}Copy the code
9.1 Output Result
Ali cloud SMS sent successfully 13898765432 Hello Apple123456 Ali cloudCopy the code
10 Complete Code
Gitee.com/ourlang/mes…