一.问题的延续与解决思路
我们发现在寻找策略类的时候,还是在根据loginType去判断使用什么策略,那么依然逃避不了IF,ElSE这样的代码。那我们此章采用注解的方式来解决这个问题。
解决思路
- loginType与策略的关系为一一对应,那么我们可以将策略类使用注解标记,并且绑定一个loginType值。
- 通过反射我们识别到loginType与策略的关系,然后通过Spring来获取具体的策略的实例。
二.具体实现
1. 定义登陆方式的枚举
/**
* 登陆方式
*/
public enum LoginType {
ACCOUNT_PWD("1", "账号密码登陆"),
ACCOUNT_SMS("2", "账号+短信验证码登陆"), ;
/**
* 登陆方式
*/
private String loginType;
/**
* 描述信息
*/
private String loginDesc;
LoginType(String loginType, String loginDesc) {
this.loginType = loginType;
this.loginDesc = loginDesc;
}
public String getLoginType() {
return loginType;
}
public String getLoginDesc() {
return loginDesc;
}
}
2. 定义登陆策略注解
/**
* 登陆策略注解
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginInstance {
//登陆方式
LoginType loginType();
}
3. 标记策略类
@Service
@LoginInstance(loginType = LoginType.ACCOUNT_PWD)
public class AccountSmsLogProcessor extends LoginTemplate
@Service
@LoginInstance(loginType = LoginType.ACCOUNT_PWD)
public class AccountPwdLogProcessor extends LoginTemplate
4.实现通过注解获取策略(reflections)
4.1 加入反射工具依赖
implementation 'org.reflections:reflections:0.9.11'
4.2 增加注解解析
/**
* 登陆工具类
*/
public class LoginTypeUtil{
public static Map<String, String> map = new ConcurrentHashMap<String, String>();
static {
//反射工具包,指明扫描路径
Reflections reflections = new Reflections("com.demo");
//获取带LoginInstance注解的类
Set<Class<?>> classList = reflections.getTypesAnnotatedWith(LoginInstance.class);
for (Class classes : classList) {
Annotation annotation = classes.getAnnotation(LoginInstance.class);
if (annotation != null) {
LoginInstance loginInstance = (LoginInstance) annotation;
map.put(loginInstance.loginType().getLoginType(), normalizeFirstWord(classes.getSimpleName()));
}
}
}
/**
* 首字母转为小写
* @param name
* @return
*/
public static String normalizeFirstWord(String name) {
if (name == null || name.length() == 0) {
return name;
}
if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
Character.isUpperCase(name.charAt(0))){
return name;
}
char chars[] = name.toCharArray();
chars[0] = Character.toLowerCase(chars[0]);
return new String(chars);
}
/**
* 通过loginType获取策略实例
* @param loginType
* @return
*/
public static LoginAdapter getInstanceByLoginType(String loginType) {
return (LoginAdapter)SpringBeanUtil.getBean(map.get(loginType));
}
}
4.3 改造策略获取方式
@Component
public class LoginProcessor {
/**
* 根据loginType获取登录的策略
* @param loginRo
* @return
*/
public LoginAdapter userLogin(LoginRo loginRo) {
return LoginTypeUtil.getInstanceByLoginType(loginRo.getLoginType());
}
}
4.4 测试登陆
5.实现通过注解获取策略(Spring,Holder)
5.1 编写HolderConfig
@Configuration
public class HolderConfig {
@Bean
public LoginProcessorHolder orderPayPostProcessorHolder(List<LoginAdapter> list){
return new LoginProcessorHolder(list);
}
}
5.2 编写策略装配
public class LoginProcessorHolder {
private static final Map<String, Class> HOLDER = new HashMap<>();
public LoginProcessorHolder(List<LoginAdapter> list) {
if (list != null && list.size() > 0){
for (LoginAdapter processAdapter : list) {
LoginInstance processor = AnnotationUtils.findAnnotation(processAdapter.getClass(), LoginInstance.class);
if (processor != null){
LoginType loginType = processor.loginType();
HOLDER.put(loginType.getLoginType(), processAdapter.getClass());
}
}
}
}
public LoginAdapter getInstanceByLoginType(String loginType){
Class adapter = HOLDER.get(loginType);
return (LoginAdapter) SpringBeanUtil.getBean(adapter);
}
}
5.3 修改策略获取方法
@Component
public class LoginProcessor {
@Resource
private LoginProcessorHolder loginProcessorHolder;
/**
* 根据loginType获取登录的策略
* @param loginRo
* @return
*/
public LoginAdapter userLogin(LoginRo loginRo) {
//return LoginTypeUtil.getInstanceByLoginType(loginRo.getLoginType());
return loginProcessorHolder.getInstanceByLoginType(loginRo.getLoginType());
}
}
这里使用了reflections以及Holder来解决策略实现类的获取,根据个人喜欢可以选择不同的模式,我更人更倾向于Holder模式。