【开发提效神器】分享Java常用的组件

前言

在软件开发的过程会用到很多称为Framework的组件。但这些组件并不能完全覆盖所有场景,这种情况下,就需要根据实际场景抽取、封闭一些特定的Framework或组件。
有了这些称手的工具,就可以加快开发效率。下面分享几个工具类。

获取访问接口的Http客户端IP

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;

/**
 * @Description:
 */
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());
    }

    /**
     * todo:  user-agent长度范围,如何是合适的区间?
     *
     * @return
     */
    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;
        }
    }

    /**
     * 对trace/debug/info级别的日志输出,必须使用条件输出形式或者使用占位符的方式
     * LOGGER.info("symbol:"+symbol);
     * 日志级别是WARN,上述日志不会打印,但是会执行字符串拼接操作,
     * 如果symbol是对象,会执行toString()方法,浪费系统资源,执行了上述操作,最终日志却没有打印
     *
     * @param request
     * @return
     * @throws IOException
     */
    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")) {
            /**
             * win7下使用localhost访问时没有经过路由
             */
            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;
    }

    /**
     * 获取applicationContext
     *
     * @return
     */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    /**
     * 通过name获取Bean
     *
     * @param name
     * @return
     */
    public static Object getBean(String name) {
        return getApplicationContext().getBean(name);
    }

    /**
     * 通过class获取Bean
     *
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(Class<T> clazz) {
        return getApplicationContext().getBean(clazz);
    }

    /**
     * 通过name以及clazz返回指定的Bean
     *
     * @param name
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(String name, Class<T> 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;

/**
 * Spring的BeanUtils.copyProperties会把null的字段值也copy过去,这点觉得与预期不一致
 */
public class TreeBeanUtils {

    /**
     * 将source的属性copy到目标类的实例中。
     * 避免Spring的BeanUtils.copyProperties会把null的字段值也copy过去的问题
     *
     * @param source
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T copyProperties(Object source, Class<T> 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;
    }


    /**
     * 基于cglib进行集合间copy
     *
     * @param sourceList
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> List<T> copyByList(List<?> sourceList, Class<T> clazz) {
        if (ObjectUtils.isEmpty(sourceList) || clazz == null) {
            return new ArrayList<>(8);
        }
        List<T> result = new ArrayList<>(sourceList.size());
        for (Object data : sourceList) {
            result.add(copyProperties(data, clazz));
        }
        return result;
    }


    /**
     * 使用fastjson进行集合间copy【使用fastjson进行序列化,再反序列化。间接使用了深度copy】
     *
     * @param sourceList
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> List<T> deepCopyByList(List<?> sourceList, Class<T> clazz) {
        if (ObjectUtils.isEmpty(sourceList) || clazz == null) {
            return new ArrayList<>(8);
        }
        return JSON.parseArray(JSON.toJSONString(sourceList), clazz);
    }


    /**
     * 实例化一个class
     *
     * @param clazz
     * @param <T>
     * @return
     */
    private static <T> T instantiate(Class<T> 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);
        }
    }


    /**
     * Copy the property values of the given source bean into the target bean.
     * <p>Note: The source and target classes do not have to match or even be derived
     * from each other, as long as the properties match. Any bean properties that the
     * source bean exposes but the target bean does not will silently be ignored.
     * <p>This is just a convenience method. For more complex transfer needs,
     * consider using a full BeanWrapper.
     *
     * @param source the source bean
     * @param target the target bean
     * @throws BeansException if the copying failed
     * @see BeanWrapper
     */
    public static void copyProperties(Object source, Object target) throws BeansException {
        copyProperties(source, target, null, (String[]) null);
    }

    /**
     * Copy the property values of the given source bean into the given target bean,
     * only setting properties defined in the given "editable" class (or interface).
     * <p>Note: The source and target classes do not have to match or even be derived
     * from each other, as long as the properties match. Any bean properties that the
     * source bean exposes but the target bean does not will silently be ignored.
     * <p>This is just a convenience method. For more complex transfer needs,
     * consider using a full BeanWrapper.
     *
     * @param source   the source bean
     * @param target   the target bean
     * @param editable the class (or interface) to restrict property setting to
     * @throws BeansException if the copying failed
     * @see BeanWrapper
     */
    public static void copyProperties(Object source, Object target, Class<?> editable) throws BeansException {
        copyProperties(source, target, editable, (String[]) null);
    }

    /**
     * Copy the property values of the given source bean into the given target bean,
     * ignoring the given "ignoreProperties".
     * <p>Note: The source and target classes do not have to match or even be derived
     * from each other, as long as the properties match. Any bean properties that the
     * source bean exposes but the target bean does not will silently be ignored.
     * <p>This is just a convenience method. For more complex transfer needs,
     * consider using a full BeanWrapper.
     *
     * @param source           the source bean
     * @param target           the target bean
     * @param ignoreProperties array of property names to ignore
     * @throws BeansException if the copying failed
     * @see BeanWrapper
     */
    public static void copyProperties(Object source, Object target, String... ignoreProperties) throws BeansException {
        copyProperties(source, target, null, ignoreProperties);
    }

    /**
     * Copy the property values of the given source bean into the given target bean.
     * <p>Note: The source and target classes do not have to match or even be derived
     * from each other, as long as the properties match. Any bean properties that the
     * source bean exposes but the target bean does not will silently be ignored.
     *
     * @param source           the source bean
     * @param target           the target bean
     * @param editable         the class (or interface) to restrict property setting to
     * @param ignoreProperties array of property names to ignore
     * @throws BeansException if the copying failed
     * @see BeanWrapper
     */
    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<String> 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一样。

欢迎大家一块交流

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

也和光同尘

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

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

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

打赏作者

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

抵扣说明:

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

余额充值