目地:
很多程序页面中会用到很多的静态资源文件(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;
}
}