模板+策略模式联合使用,切身体会到模式的好处

先讲下背景吧。最近工作重心在医院,做医院系统。因为我们公司是做平台的,其中有一个系统是单点登录,需要同步医院里其它系统的所有帐号。如果单点登录系统帐号变动,会把相应的帐号信息推送到其它系统。比如:添加或者改变某一个帐号的信息,那就需要把当前帐号信息推送到相应的系统;又比如:角色变动了,就把所有与此角色关系的帐号信息及权限信息同步到相应的系统。

单点登录系统功能早就完成并测试完毕,如果在原来的功能上添加,又需要重新测试一次,既费时费力,又不符合软件设计原则中的:开闭原则

为了实现这个功能,我想到了2种方式:

  • 监听    监听 ServletRequestListener 中的 requestInitialized  方法,在配制文件里面配制需要监听的地址,一但请求进来,符合要求就会进入处理。

  • AOP 切面  实现一个切面功能,并定义一个注解,在每个变动的接口方法上加上定义的切面注解即可。

两种方式都各有优缺点,先来说它们各自优点

监听优点:

  1. 基于配制模式,把需要监听的地址都是放在配制文件里,方便修改

  2. 功能实现简单

  3. 功能扩展很方便

监听点:

  1. 是监听的请求,每个请求进来后,都会在此做过滤,其实监听的只是极少数地址,这样做感觉没必要

  2. 如果对用户信息操作失败,也会产生用户数据。实际场景时,操作失败没有用户信息数据推送

  3. 如果是处理比较耗时的同步方法,会对接口的QPS有很大的影响

AOP 切面优点

  1. 粒度更细,精确到了具体的某个方法

  2. 对单个用户信息的变动能精准识别

  3. 如果接口响应失败,可不用产生数据,也可产生数据(需要程序员在处理过程中选择)

AOP 切面

  1. 难度相对监听增大了

  2. 需要在具体的方法上增加注解

以上2种方式都实现了,我都把详细的代码贴出来,大家可根据自己项目需求选择。

监听实现

先说一下监听的原理:一个请求过来后,根据请求的地址做一个拦截,如果是在拦截队列中,实现 ServletRequestListener 接口的 requestInitialized  方法, 或者 requestDestroyed 请求结束后的方法

​//@WebListener
@Slf4j
public class MyServletRequestListener implements ServletRequestListener {
    @SneakyThrows
    @Override
    public void requestInitialized(ServletRequestEvent requestEvent) {
        if (!(requestEvent.getServletRequest() instanceof HttpServletRequest)) {
            throw new IllegalArgumentException(
                    "Request is not an HttpServletRequest: " + requestEvent.getServletRequest());
        }
        HttpServletRequest request = (HttpServletRequest) requestEvent.getServletRequest();
        String url = request.getRequestURI();
        if(StringUtils.hasText(url) && platformProperties.getListenUrl() != null
                && platformProperties.getListenUrl().size() > 0 &&  platformProperties.getListenUrl().contains(url)){
​
            queryUserAble.setClientId(platformProperties.getSendUserListToApp());
            queryUserAble.setRequest(request);
            gftechAutoConfiguration.executorService().execute(queryUserAble);
​
            sendMsgToCenterServer.sendMsg();
        }
    }
​
    @Override
    public void requestDestroyed(ServletRequestEvent requestEvent) {
    }
}

注意2点:

  1. 使用此方法需要添加  @WebListener  和 @ServletComponentScan 注解

  2. 在这里不太耗时,否则对接口响应有影响。建议用线程池处理任务

AOP 切面实现

首先:定义一个注解

package com.gftech.ms.annotation;
​public @interface MyAspect {
}​

再次:实现切面功能

package com.gftech.ms.configuration;​
@Aspect
@Component
@Slf4j
public class SendUserAspect{​ 
@AfterReturning(value = "@annotation(com.gftech.ms.annotation.MyAspect)", returning="result") 
public void computerSendUser(JoinPoint joinPoint, Object result){ 
    if(result instanceof Response<?>){ 
        Response<?> response = (Response<?>) result; 
        if("SUCCESS".equals(response.getMessage())){ 
            HttpServletRequest request =         ((ServletRequestAttributes)Objects.requireNonNull(RequestContextHolder.currentRequestAttributes())).getRequest();   
             
    // 这里写你 业务代码 
    } 
    } 
    }
}

最后:在需要的方法上添加 @MyAspect 注解即可

我在我的项目中使用 UserRunAble 来代表任务,以下是定义:

package com.gftech.ms.runable;
​@Slf4j
@Data
@Component
public class UserRunAble implements Runnable {​ 
private JoinPoint joinPoint; 
private String functionName; 
private HttpServletRequest request; 
private String workStation;​ 

@Resource 
private ChangeStateImpl changeUserStateImpl;​ 

@Resource 
private AddRoleImpl addRoleImpl;​
 
@Resource 
private EditImpl editImpl;​ 

@SneakyThrows 
@Override 
public void run() {​ 
functionName = joinPoint.getSignature().getName(); 
if(StringUtils.isBlank(functionName)){ return; }​ 
Object[] objects = joinPoint.getArgs(); 
if(objects.length <= 0){ return; }​ 
switch (functionName){ 
case "changeState" : 
changeUserStateImpl.setWorkStation(workStation); 
UserRunAbleInterface userRunAbleInterface = UserRunAbleInterface.userRunAbleInterfaceMap.get("changeStateImpl"); 
userRunAbleInterface.saveData(objects[0]);
break; 
case "insert" : 
case "edit" : 
editImpl.setWorkStation(workStation); 
UserRunAbleInterface edit = UserRunAbleInterface.userRunAbleInterfaceMap.get("editImpl"); 
edit.saveData(objects[0]); 
break; 
case "editRole": 
case "addRole": 
addRoleImpl.setHttpServletRequest(request); 
addRoleImpl.setWorkStation(workStation); 
UserRunAbleInterface addRole = UserRunAbleInterface.userRunAbleInterfaceMap.get("addRoleImpl"); 
addRole.saveData(objects[0]); 
break;
default: 
throw new ApiException("切面处理用户信息:未知的方法"); 
} 
}
}
定义的接口:
package com.gftech.ms.runable;​
import com.gftech.ms.runable.vo.MhSendUserToApp;​
import java.io.IOException;
import java.util.HashMap;
import java.util.List;

​public interface UserRunAbleInterface {​ 
    HashMap<String, UserRunAbleInterface> userRunAbleInterfaceMap = new HashMap<>();​ 
    List<MhSendUserToApp> getMhSendUserToApp(Object object) throws IOException;​ 
    void saveData(Object object) throws IOException;
}​

定论的模板类:

package com.gftech.ms.runable;
​
import com.gftech.ms.configuration.PlatformProperties;
​
@Slf4j
public abstract class UserRunAblePlate implements UserRunAbleInterface {
​
    private List<String> appIdList;
    private MhSendUserToAppMapper mhSendUserToAppMapper;
    private RoleManageServiceImpl roleManageService;
    private SendMsgToCenterServer sendMsgToCenterServer;
​
    public UserRunAblePlate(MhSendUserToAppMapper mhSendUserToAppMapper, RoleManageServiceImpl roleManageService, PlatformProperties platformProperties, SendMsgToCenterServer sendMsgToCenterServer){
        this.mhSendUserToAppMapper = mhSendUserToAppMapper;
        this.roleManageService = roleManageService;
        this.sendMsgToCenterServer = sendMsgToCenterServer;
        if(StringUtils.isNotBlank(platformProperties.getSendUserListToApp())){
            this.appIdList = new ArrayList<>(Arrays.asList(platformProperties.getSendUserListToApp().split(",")));
        }
    }
​
    @Override
    public List<MhSendUserToApp> getMhSendUserToApp(Object object) throws IOException {
        return SubGetMhSendUserToApp(object);
    }
​
    @Override
    public void saveData(Object object) throws IOException {
        saveToTable(object);
    }
​
    public abstract List<MhSendUserToApp> SubGetMhSendUserToApp(Object object) throws IOException;
​
    protected HashMap<String, NodeDataExt> getExistHashMap(List<NodeDataExt> nodeDataExtList){
        HashMap<String, NodeDataExt> extHashMap = new HashMap<>();
        for (NodeDataExt item : nodeDataExtList){
        }
​
        return extHashMap;
    }
​
    protected List<NodeDataExt> getNodeDataExt(List<String> roleIds){
        if(roleIds.isEmpty()) return null;
​
        return roleManageService.getAppTreeByRoleIds( roleIds);
    }
​
    private void saveToTable(Object object) throws IOException {
        
    }
}
AddRoleImpl 的实现:
package com.gftech.ms.runable.impl;
​
import java.util.stream.Collectors;
​
@Service("addRoleImpl")
public class AddRoleImpl extends UserRunAblePlate {
    private HttpServletRequest request;
    private List<String> appIdList;
    private String workStation;
​
    @Autowired
    public AddRoleImpl(MhSendUserToAppMapper mhSendUserToAppMapper, RoleManageServiceImpl roleManageService, PlatformProperties platformProperties, SendMsgToCenterServer sendMsgToCenterServer) {
        super(mhSendUserToAppMapper, roleManageService, platformProperties, sendMsgToCenterServer);
​
        if(org.apache.commons.lang3.StringUtils.isNotBlank(platformProperties.getSendUserListToApp())){
            appIdList = new ArrayList<>(Arrays.asList(platformProperties.getSendUserListToApp().split(",")));
        }
​
        UserRunAbleInterface.userRunAbleInterfaceMap.put("addRoleImpl", this);
    }
​
    public void setHttpServletRequest(HttpServletRequest request){
        this.request = request;
    }
​
    public void setWorkStation(String workStation){
        this.workStation = workStation;
    }
​
    @Override
    public List<MhSendUserToApp> SubGetMhSendUserToApp(Object object) throws IOException {
        if(object == null){
            return new ArrayList<>();
        }
        return listUser;
    }
}

ChangeStateImpl 的实现:

package com.gftech.ms.runable.impl;
​
@Slf4j
@Service("changeStateImpl")
public class ChangeStateImpl extends UserRunAblePlate {
    private List<String> appIdList;
    private String workStation;
​
    @Autowired
    public ChangeStateImpl(MhSendUserToAppMapper mhSendUserToAppMapper, RoleManageServiceImpl roleManageService, PlatformProperties platformProperties, SendMsgToCenterServer sendMsgToCenterServer) {
        super(mhSendUserToAppMapper, roleManageService, platformProperties, sendMsgToCenterServer);
​
        if(StringUtils.isNotBlank(platformProperties.getSendUserListToApp())){
            appIdList = new ArrayList<>(Arrays.asList(platformProperties.getSendUserListToApp().split(",")));
        }
​
        UserRunAbleInterface.userRunAbleInterfaceMap.put("changeStateImpl", this);
    }
​
    public void setWorkStation(String workStation){
        this.workStation = workStation;
    }
​
    @Override
    public List<MhSendUserToApp> SubGetMhSendUserToApp(Object object) throws IOException {
        if(object == null){
            return new ArrayList<>();
        }
          return listUser;
    }
​
}

EditImpl 的实现:

package com.gftech.ms.runable.impl;
​
@Service("editImpl")
public class EditImpl extends UserRunAblePlate {
    private String workStation;
    private List<String> appIdList;
​
    @Autowired
    public EditImpl(MhSendUserToAppMapper mhSendUserToAppMapper, RoleManageServiceImpl roleManageService, PlatformProperties platformProperties, SendMsgToCenterServer sendMsgToCenterServer) {
        super(mhSendUserToAppMapper, roleManageService, platformProperties, sendMsgToCenterServer);
​
        if(StringUtils.isNotBlank(platformProperties.getSendUserListToApp())){
            appIdList = new ArrayList<>(Arrays.asList(platformProperties.getSendUserListToApp().split(",")));
        }
​
        UserRunAbleInterface.userRunAbleInterfaceMap.put("editImpl", this);
    }
​
    public void setWorkStation(String workStation){
        this.workStation = workStation;
    }
​
    @Override
    public List<MhSendUserToApp> SubGetMhSendUserToApp(Object object) throws IOException {
        if(object == null){
            return new ArrayList<>();
        }
        return listUser;
    }
}

好了,根据切面思想就完成了。

程序的执行顺序:

SendUserAspect   ->  UserRunAble

AddRoleImpl,ChangeStateImpl,EditImpl 这3个都是UserRunAblePlate 类的实现类,都注入到了IOC容器,并且在各自的构造方法中,把自己添加到了 UserRunAbleInterface 接口中的 HashMap 中

HashMap<String, UserRunAbleInterface> userRunAbleInterfaceMap = new HashMap<>();

在 UserRunAblePlate 类中,实现了接口 UserRunAbleInterface 中定义的2个方法,并且定义了一个抽象方法由子类实现:

public abstract List<MhSendUserToApp> SubGetMhSendUserToApp(Object object) throws IOException;

SubGetMhSendUserToApp    这个方法其实是最核心的方法,在我的程序里,都是它在处理帐号信息​。​

这里再补充一个面试经常被问的点。AOP是JDK动态代理实现的,是在Bean生命周期后置增加环节实现的,也是在Bean初始化之后。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

周周的JAVA技术栈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值