- 前言
- 获取访问接口的Http客户端IP
- 获取Spring容器中的数据,方便排查问题
- Bean Copy
- 总结
在软件开发的过程会用到很多称为Framework的组件。但这些组件并不能完全覆盖所有场景,这种情况下,就需要根据实际场景抽取、封闭一些特定的Framework或组件。
有了这些称手的工具,就可以加快开发效率。下面分享几个工具类。
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.InetAddress;
public class NetworkUtil {
private static Logger LOGGER = LoggerFactory.getLogger(NetworkUtil.class);
public static final String UNKNOWN = "unknown";
public static String getRemoteIp() {
return getRemoteIp(RequestHolder.getRequestFacade());
}
public static String getUserAgent() {
return RequestHolder.getRequestFacade().getHeader(HttpHeaders.USER_AGENT);
}
public static String getRemoteIp(HttpServletRequest request) {
try {
return NetworkUtil.getIpAddress(request);
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
return UNKNOWN;
}
}
private static String getIpAddress(HttpServletRequest request) throws IOException {
String ip = request.getHeader("X-Forwarded-For");
LOGGER.debug("X-Forwarded-For {}", ip);
if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
LOGGER.debug("Proxy-Client-IP {}", ip);
}
if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
LOGGER.debug("WL-Proxy-Client-IP {}", ip);
}
if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
LOGGER.info("HTTP_CLIENT_IP {}", ip);
}
if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
LOGGER.debug("HTTP_X_FORWARDED_FOR {}", ip);
}
if (StringUtils.isBlank(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
LOGGER.debug("getRemoteAddr {}", ip);
}
} else if (ip.contains(",")) {
String[] ips = ip.split(",");
for (int index = ips.length - 1; index >= 0; index--) {
if (!(UNKNOWN.equalsIgnoreCase(ips[index]))) {
ip = ips[index];
break;
}
}
}
if (ip.equals("0:0:0:0:0:0:0:1")) {
return InetAddress.getLocalHost().getHostAddress();
}
if (StringUtils.isBlank(ip)) {
return UNKNOWN;
}
return ip;
}
}
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
public class RequestHolder {
public static HttpServletRequest getRequestFacade() {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
if (servletRequestAttributes == null) {
log.warn("不是SpringHttp请求");
throw new RuntimeException("不是SpringHttp请求");
}
return servletRequestAttributes.getRequest();
}
public static HttpServletResponse getResponseFacade() {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
return servletRequestAttributes.getResponse();
}
public static String getLastAccessUrl() {
HttpServletRequest httpServletRequest = getRequestFacade();
String requestURI = httpServletRequest.getRequestURI();
String queryString = httpServletRequest.getQueryString();
if (StringUtils.isBlank(queryString)) {
return String.format("[%s] %s", httpServletRequest.getMethod(), requestURI);
}
return String.format("[%s] %s?%s", httpServletRequest.getMethod(), requestURI, queryString);
}
}
获取Spring容器中的数据,方便排查问题
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
@Component("troubleShootingTool")
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextUtil.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
public static T getBean(Class clazz) {
return getApplicationContext().getBean(clazz);
}
public static T getBean(String name, Class clazz) {
return getApplicationContext().getBean(name, clazz);
}
public static String getActiveProfile() {
Environment environment = getApplicationContext().getEnvironment();
String[] activeProfiles = environment.getActiveProfiles();
if (ArrayUtils.isEmpty(activeProfiles)) {
return "default[activeProfiles is empty]";
}
return activeProfiles[0];
}
}
Bean Copy
import com.alibaba.fastjson.JSON;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class TreeBeanUtils {
public static T copyProperties(Object source, Class clazz) {
if (source == null || clazz == null) {
return null;
}
T target = instantiate(clazz);
BeanCopier copier = BeanCopier.create(source.getClass(), clazz, false);
copier.copy(source, target, null);
return target;
}
public static List copyByList(List> sourceList, Class clazz) {
if (ObjectUtils.isEmpty(sourceList) || clazz == null) {
return new ArrayList<>(8);
}
List result = new ArrayList<>(sourceList.size());
for (Object data : sourceList) {
result.add(copyProperties(data, clazz));
}
return result;
}
public static List deepCopyByList(List> sourceList, Class clazz) {
if (ObjectUtils.isEmpty(sourceList) || clazz == null) {
return new ArrayList<>(8);
}
return JSON.parseArray(JSON.toJSONString(sourceList), clazz);
}
private static T instantiate(Class clazz) {
Assert.notNull(clazz, "Clazz must not be null");
try {
return clazz.newInstance();
} catch (InstantiationException ex) {
throw new IllegalArgumentException(clazz.getName() + "is an abstract class?", ex);
} catch (IllegalAccessException ex) {
throw new IllegalArgumentException(clazz.getName() + "is the constructor accessible?", ex);
}
}
public static void copyProperties(Object source, Object target) throws BeansException {
copyProperties(source, target, null, (String[]) null);
}
public static void copyProperties(Object source, Object target, Class> editable) throws BeansException {
copyProperties(source, target, editable, (String[]) null);
}
public static void copyProperties(Object source, Object target, String... ignoreProperties) throws BeansException {
copyProperties(source, target, null, ignoreProperties);
}
private static void copyProperties(Object source, Object target, Class> editable, String... ignoreProperties)
throws BeansException {
if (source == null || target == null) {
return;
}
Class> actualEditable = target.getClass();
if (editable != null) {
if (!editable.isInstance(target)) {
throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
"] not assignable to Editable class [" + editable.getName() + "]");
}
actualEditable = editable;
}
PropertyDescriptor[] targetPds = org.springframework.beans.BeanUtils.getPropertyDescriptors(actualEditable);
List ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);
for (PropertyDescriptor targetPd : targetPds) {
Method writeMethod = targetPd.getWriteMethod();
if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
PropertyDescriptor sourcePd = org.springframework.beans.BeanUtils.getPropertyDescriptor(source.getClass(), targetPd.getName());
if (sourcePd != null) {
Method readMethod = sourcePd.getReadMethod();
if (readMethod != null &&
ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
try {
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
Object value = readMethod.invoke(source);
if (value != null) {//与Spring BeanUtil.copyProperties相比,多了校验:value如果为null则跳过。蛋疼:如果对象使用了其它对象,则会跳过这个校验
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
writeMethod.invoke(target, value);
}
} catch (Throwable ex) {
throw new FatalBeanException(
"Could not copy property '" + targetPd.getName() + "' from source to target", ex);
}
}
}
}
}
}
}
总结
别人开源的好东西,你今天看着不爽,自己造的可能2年就没人维护了。但是开源的还有无数人在增加新特性和修复bug,这就是open的力量。
在抽象项目中公共的基础组件【造轮子】时,要判断,什么东西应该站在巨人的肩膀上,而什么东西应该分享出去,具有更强的生命力。
造轮子没有最好,只有更好。只要从一个时间尺度上,譬如3个月来看,节省的开发时间能大于投入的时间,就可以着手做起来。毕竟,重复是万恶之源。我们在实际开发过程中,要不断识别并提取模型。在代码层面,分支规则化,规则插件化,插件配置化。
就像k8s目前提供的声明式API一样。



