自定义注解拦截web请求对返回结果进行有针对的替换

154 阅读3分钟

目地:

很多程序页面中会用到很多的静态资源文件(image/css/js),有些是页面中初始化加载的,有些是通过后台接口返回给页面动态加载的。这些静态资源文件的域名是写死的(www.xxx.com/ss.xxx.com)…

方案:

后台的接口,在返回结果时对有包含指定的域名进行替换。这里考虑使用AOP拦截器,对@StaticResourceMapping的注解进行切面拦截,通过后置通知获取返回的对象,遍历对象中的属性进行域名替换。

1,定义一个模型,包含要替换的域名和替换后的域名,存放在redis中

package com.bxm.hcmony.sc.domain;

import org.apache.commons.collections.CollectionUtils;

import java.net.URI;
import java.util.List;

/**
 * <h3>替换模型</h3>
 * <p></p>
 *
 * @author hcmony
 * @since V1.0.0, 2019/01/23 11:32
 */
public class ResourceModel {

	/**
	 * 源目标
	 */
	private List<String> sourceList;
	/**
	 * 替换目标
	 */
	private String targetUrl;

	public List<String> getSourceList() {
		return sourceList;
	}

	public void setSourceList(List<String> sourceList) {
		this.sourceList = sourceList;
	}

	public String getTargetUrl() {
		return targetUrl;
	}

	public void setTargetUrl(String targetUrl) {
		this.targetUrl = targetUrl;
	}

	public boolean isContains(String url){

		if(CollectionUtils.isEmpty(sourceList)){
			return false;
		}

		if(sourceList.contains(getHost(url))){
			return true;
		}

		return false;
	}


	private String getHost(String url){
		URI uri;
		try {
			uri = URI.create(url);
			return uri.getHost();
		} catch (Exception e) {
			return url;
		}
	}
}

2,定义一个注解,标注在方法上面,对有些标注的方面进行拦截

package com.bxm.hcmony.sc.domain;

import java.lang.annotation.*;

/**
 * <h3>主要应用于静态资源拦截 {@link StaticResourceReplaceHandler}</h3>
 *
 * @author hcmony
 * @since V1.0.0, 2019/01/24 14:22
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface StaticResourceMapping {

}

3,通知aop拦截,获取到具体的对象,递归遍历处理。

package com.hcmony.sc.domain;

import com.hcmony.integration.Redis;
import com.hcmony.utils.PathUtils;
import com.google.common.base.Preconditions;
import org.apache.commons.beanutils.MethodUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.reflect.FieldUtils;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.Map;

/**
 * 返回结果替换处理器
 *
 * @see StaticResource
 * @see StaticResourceMapping
 * @author hcmony
 * @date 2019/1/21
 * @since 1.0.0
 */
@Aspect
public class StaticResourceReplaceHandler {

    private static final String DOMAIN_KEY = "STATIC_RESOURCE_DOMAIN";
    private final Redis redis;

    public StaticResourceReplaceHandler(Redis redis) {
        Preconditions.checkNotNull(redis);
        this.pair = pair;
    }

    @Pointcut("@annotation(com.hcmony.sc.domain.StaticResourceMapping)")
    public void pointcut() {}

    @AfterReturning(value = "pointcut()", returning = "returning")
    public void afterRetuning(Object returning) {
        if (null == returning) {
            return;
        }
        if (Iterable.class.isAssignableFrom(returning.getClass())) {
            this.replaceRootForIterable(returning);
        } else if (returning.getClass().isArray()) {
            this.replaceRootForArray(returning);
        } else {
            this.replaceForObject(returning);
        }
    }

    private void replaceRootForIterable(Object retuning) {
        Iterable iterable = (Iterable) retuning;
        for (Object o : iterable) {
            this.replaceForObject(o);
        }
    }
    private void replaceRootForArray(Object retuning) {
        Object[] array = (Object[]) retuning;
        for (Object o : array) {
            this.replaceForObject(o);
        }
    }

    private void replaceObject(Object retuning) {
        Class<?> cls = retuning.getClass();
        while (null != cls) {
            Field[] declaredFields = cls.getDeclaredFields();
            for (Field declaredField : declaredFields) {

                String fieldName = declaredField.getName();
                Class<?> fieldClass = declaredField.getType();

                Object value = getObject(retuning, fieldName);
                if (null == value) {
                    continue;
                }

                if (value instanceof Number){
                    continue;
                }

                if (value instanceof Boolean){
                    continue;
                }

                if (value instanceof String) {
                    this.replaceForString(retuning, fieldName);
                    continue;
                }
                if (value instanceof Map) {
                    this.replaceForMap(value);
                    continue;
                }

                if (value instanceof Iterable) {
                    this.replaceForIterable(value);
                    continue;
                }

                if (value instanceof Array) {
                    this.replaceForArray(value);
                    continue;
                }

                //if (fieldClass.getClassLoader()!=null) {
                //}
                if (Object.class.isAssignableFrom(fieldClass)){

                    this.replaceObject(value);
                }
            }
            cls = cls.getSuperclass();
        }
    }

    private  void replaceForObject(Object value){
        if (null == value) {
            return;
        }

        if (value instanceof Number){
            return;
        }

        if (value instanceof Boolean){
            return;
        }
        this.replaceObject(value);
    }

    private void replaceForArray(Object value) {
        Array[] array = (Array[]) value;
        for (Object o : array) {
            this.replaceForObject(o);
        }
    }

    private void replaceForIterable(Object value) {
        Iterable<?> iterable = (Iterable) value;
        for (Object o : iterable) {
            this.replaceForObject(o);
        }
    }

    private void replaceForMap(Object value) {
        disposeMap(value);
    }

    private void  disposeMap(Object value){
        Map<Object, Object> map = (Map) value;
        for (Map.Entry<?, ?> entry : map.entrySet()) {
            Object mv = entry.getValue();
            if(mv == null){
               continue;
            }
            if (mv instanceof String) {
                String url = replace(mv.toString());
                Object key = entry.getKey();
                map.put(key, url);
            }else if(Object.class.isAssignableFrom(mv.getClass())){
                replaceForObject(mv);
            }
        }
    }

    private void replaceForString(Object retuning, String fieldName) {
        Object value = getObject(retuning, fieldName);

        if (null == value) {
            return;
        }
        String newUrl = replace(value.toString());

        try {
            FieldUtils.writeField(retuning, fieldName, newUrl, true);
        } catch (IllegalAccessException | IllegalArgumentException e) {
            this.setValue(retuning, fieldName, newUrl);
        }
    }

    private Object getObject(Object retuning, String fieldName) {
        Object value;
        try {
            value = FieldUtils.readDeclaredField(retuning, fieldName, true);
        } catch (Exception e) {
            value = getValue(retuning, fieldName);
        }
        return value;
    }

    private Object getValue(Object object, String fieldName) {
        String methodName = StringUtils.join(new String[]{"get", StringUtils.capitalize(fieldName)});
        try {
            return MethodUtils.invokeMethod(object, methodName, new Object[0]);
        } catch (Exception e) {
            return null;
        }
    }

    private void setValue(Object object, String fieldName, Object value) {
        String methodName = StringUtils.join(new String[]{"set", StringUtils.capitalize(fieldName)});
        try {
            MethodUtils.invokeMethod(object, methodName, value);
        } catch (Exception ignored) {
        }
    }

    private String replace(String url) {
        ResourceModel resourceModel = redis.get(DOMAIN_KEY).toObject(ResourceModel.class);

        if (resourceModel == null ){
            return url;
        }

        if ( CollectionUtils.isEmpty(resourceModel.getSourceList())
                ||  StringUtils.isBlank(resourceModel.getTargetUrl())){
            return url;
        }

        if (resourceModel.isContains(url)){
            return PathUtils.replaceDomain(url, resourceModel.getTargetUrl());
        }

        return url;
    }
}