Template + strategy mode is used together to experience the benefits of the mode

Let's talk about the background first. Recently, the focus of work is on the hospital, making the hospital system. Because our company is a platform, one system is single sign on, which needs to synchronize all accounts of other systems in the hospital. If the single sign on system account changes, the corresponding account information will be pushed to other systems. For example, if you add or change the information of an account, you need to push the current account information to the corresponding system; Another example: if the role changes, synchronize all account information and permission information related to the role to the corresponding system.

The single sign on system function has long been completed and tested. If it is added to the original function, it needs to be tested again, which is not only time-consuming and laborious, but also does not comply with the opening and closing principle in the software design principle.

In order to realize this function, I think of two ways:

  • Listen to the # requestInitialized # method in # ServletRequestListener # and configure the address to be listened to in the configuration file. Once the request comes in, it will be processed if it meets the requirements.

  • AOP {aspect} implements a aspect function and defines an annotation. Add the defined aspect annotation to each changed interface method.

Both methods have their own advantages and disadvantages. First, they have their own advantages

Advantages of monitoring:

  1. Based on the configuration mode, the addresses to be monitored are placed in the configuration file for easy modification

  2. Simple function implementation

  3. Function expansion is very convenient

Disadvantages of monitoring:

  1. It is a request for monitoring. After each request comes in, it will be filtered here. In fact, only a few addresses are monitored. It feels unnecessary to do so

  2. If the operation on user information fails, user data will also be generated. In the actual scenario, the operation fails and there is no user information data push

  3. If it is a time-consuming synchronization method, it will have a great impact on the QPS of the interface

AOP # section advantages:

  1. Finer granularity, accurate to a specific method

  2. The change of individual user information can be accurately identified

  3. If the interface response fails, data can be generated without generating data (which needs to be selected by the programmer during processing)

AOP # section disadvantages:

  1. The difficulty is relatively increased

  2. Notes need to be added to specific methods

The above two methods have been implemented. I have posted the detailed code. You can choose according to your project needs.

Listening implementation:

Let's talk about the principle of listening first: after a request comes, intercept it according to the address of the request. If it is in the interception queue, implement the # requestInitialized # method of the # ServletRequestListener # interface or the method after the # requestDestroyed # request ends

​//@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) {
    }
}

Note 2:

  1. Using this method requires adding @ WebListener , and @ ServletComponentScan , annotations

  2. It's not too time-consuming here, otherwise it will affect the interface response. It is recommended to use thread pool to process tasks

AOP} aspect implementation

First: define an annotation

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

Again: realize the section function

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();   
             
    //Write your business code here 
    } 
    } 
    }
}

Finally, add @ MyAspect} annotation to the required method

I use "UserRunAble" to represent tasks in my project. The following is the definition:

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("Faceting user information: unknown method"); 
} 
}
}
Defined interfaces:
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;
}​

Final template class:

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 Implementation of:
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;
    }
}

Implementation of 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;
    }
​
}

Implementation of 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;
    }
}

Well, it's done according to the section idea.

Program execution sequence:

SendUserAspect   ->  UserRunAble

AddRoleImpl, ChangeStateImpl, EditImpl , these three are implementation classes of UserRunAblePlate , which are injected into the IOC container. In their respective construction methods, they add themselves to the , HashMap , in the , UserRunAbleInterface , interface

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

In the # UserRunAblePlate # class, two methods defined in the interface # UserRunAbleInterface # are implemented, and an abstract method is defined, which is implemented by subclasses:

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

SubGetMhSendUserToApp is actually the core method. In my program, it processes account information. ​

Here is another point often asked in interviews. AOP is implemented by JDK dynamic agent. It is implemented after the Bean life cycle and after the Bean initialization.

Keywords: Java AOP

Added by Litninfingers63 on Sun, 02 Jan 2022 08:46:39 +0200