详解Java中自定义注解的使用

目录
  • 什么是注解
    • 注解的注意事项
  • 注解的本质
  • 自定义注解使用
    • 使用方式 1
    • 使用方式 2

什么是注解

在早期的工作的时候 ,自定义注解写的比较多,可大多都只是因为 这样看起来 不会存在一堆代码耦合在一起的情况,所以使用了自定义注解,这样看起来清晰些,

Annontation是Java5开始引入的新特征,中文名称叫注解。

它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。为程序的元素(类、方法、成员变量)加上更直观、更明了的说明,这些说明信息是与程序的业务逻辑无关,并且供指定的工具或框架使用。Annontation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。

Java注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。

一般我们自定义一个注解的操作是这样的:

public @interface MyAnnotation {
}

如果说我们需要给他加上参数,那么大概是这样的

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface MyAnnotation {
    public int age() default 18;
    String name() ;
    String [] books();
}

我们可以关注到上面有些我们不曾见过的注解,而这类注解,统称为元注解 ,我们可以大概来看一下

@Document

是被用来指定自定义注解是否能随着被定义的java文件生成到JavaDoc文档当中。

@Target

是专门用来限定某个自定义注解能够被应用在哪些Java元素上面的,不定义说明可以放在任何元素上。

上面这个 Target这玩意有个枚举,可以清晰的看出来,他的 属性

使用枚举类ElementType来定义

public enum ElementType {
    /** 类,接口(包括注解类型)或枚举的声明 */
    TYPE,
    /** 属性的声明 */
    FIELD,
    /** 方法的声明 */
    METHOD,
    /** 方法形式参数声明 */
    PARAMETER,
    /** 构造方法的声明 */
    CONSTRUCTOR,
    /** 局部变量声明 */
    LOCAL_VARIABLE,
    /** 注解类型声明 */
    ANNOTATION_TYPE,
    /** 包的声明 */
    PACKAGE
}

@Retention

即用来修饰自定义注解的生命周期。

使用了RetentionPolicy枚举类型定义了三个阶段

public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     * (注解将被编译器丢弃)
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     * (注解将被编译器记录在class文件中,但在运行时不会被虚拟机保留,这是一个默认的行为)
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     * (注解将被编译器记录在class文件中,而且在运行时会被虚拟机保留,因此它们能通过反射被读取到)
     * @see java.lang.reflect.AnnotatedElement
     */
    RUNTIME
}

@Inherited

允许子类继承父类中的注解

注解的注意事项

1.访问修饰符必须为public,不写默认为public;

2.该元素的类型只能是基本数据类型、String、Class、枚举类型、注解类型(体现了注解的嵌套效果)以及上述类型的一位数组;

3.该元素的名称一般定义为名词,如果注解中只有一个元素,请把名字起为value(后面使用会带来便利操作);

4.()不是定义方法参数的地方,也不能在括号中定义任何参数,仅仅只是一个特殊的语法;

5.default代表默认值,值必须和第2点定义的类型一致;

6.如果没有默认值,代表后续使用注解时必须给该类型元素赋值。

注解的本质

所有的Java注解都基于Annotation接口。但是,手动定义一个继承自Annotation接口的接口无效。要定义一个有效的Java注解,需要使用@interface关键字来声明注解。Annotation接口本身只是一个普通的接口,并不定义任何注解类型。

public interface Annotation {
    boolean equals(Object obj);
    /**
    * 获取hashCode
    */
    int hashCode();

    String toString();
    /**
     *获取注解类型
     */
    Class<? extends Annotation> annotationType();
}

在Java中,所有的注解都是基于Annotation接口的,但是手动定义一个继承自Annotation接口的接口并不会创建一个有效的注解。要定义有效的注解,需要使用特殊的关键字@interface来声明注解类型。Annotation接口本身只是一个普通的接口,而不是一个定义注解的接口。因此,使用@interface声明注解是定义Java注解的标准方法。

public @interface MyAnnotation1 {
}
public interface MyAnnotation2 extends Annotation  {
}
// javap -c TestAnnotation1.class
Compiled from "MyAnnotation1.java"
public interface com.spirimark.corejava.annotation.MyAnnotation1 extends java.lang.annotation.Annotation {}
​
// javap -c TestAnnotation2.class
Compiled from "MyAnnotation2.java"
public interface com.spirimark.corejava.annotation.MyAnnotation2 extends java.lang.annotation.Annotation {}

虽然Java中的所有注解都是基于Annotation接口,但即使接口本身支持多继承,注解的定义仍无法使用继承关键字来实现。定义注解的正确方式是使用特殊的关键字@interface声明注解类型。

同时需要注意的是,通过@interface声明的注解类型不支持继承其他注解或接口。任何尝试继承注解类型的操作都会导致编译错误。

public @interface MyAnnotation1 {
}
/** 错误的定义,注解不能继承注解 */
@interface MyAnnotation2 extends MyAnnotation1 {
}
/** 错误的定义,注解不能继承接口 */
@interface MyAnnotation3 extends Annotation {
}

自定义注解使用

使用方式 1

自定义注解的玩法有很多,最常见的莫过于

  • 声明注解
  • 通过反射读取

但是上面这种一般现在在开发中不怎么常用,最常用的就是,我们通过 切面去在注解的前后进行加载

创建注解

@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface BussinessLog {

    /**
     * 功能
     */
    BusinessTypeEnum value();

    /**
     * 是否保存请求的参数
     */
    boolean isSaveRequestData() default true;

    /**
     * 是否保存响应的参数
     */
    boolean isSaveResponseData() default true;
}

设置枚举

public enum BusinessTypeEnum {
    /**
     * 其它
     */
    OTHER,

    /**
     * 新增
     */
    INSERT,

    /**
     * 修改
     */
    UPDATE,

    /**
     * 删除
     */
    DELETE,

    /**
     * 授权
     */
    GRANT,

    /**
     * 导出
     */
    EXPORT,

    /**
     * 导入
     */
    IMPORT,
}

创建切面操作

@Slf4j
@Aspect
@Component
public class LogConfig {

    @Autowired
    private IUxmLogService uxmLogService;

   /**
    * 后置通过,⽬标⽅法正常执⾏完毕时执⾏
    *
    */
    @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) {
        handleLog(joinPoint, controllerLog, null, jsonResult);
    }

   /**
    * 异常通知,⽬标⽅法发⽣异常的时候执⾏
    *
    */
    @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) {
        handleLog(joinPoint, controllerLog, e, null);
    }

    protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) {
        try {
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            String title = methodSignature.getMethod().getAnnotation(ApiOperation.class).value();
            // 获取当前的用户
            String userName = CurrentUser.getCurrentUserName();

            // *========数据库日志=========*//
            UxmLog uxmLog = new UxmLog();
            uxmLog.setStatus(BaseConstant.YES);
            // 请求的地址
            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            assert requestAttributes != null;
            HttpServletRequest request = requestAttributes.getRequest();
            String ip = getIpAddr(request);
            // 设置标题
            uxmLog.setTitle(title);
            uxmLog.setOperIp(ip);
            uxmLog.setOperUrl(request.getRequestURI());
            uxmLog.setOperName(userName);

            if (e != null) {
                uxmLog.setStatus(BaseConstant.NO);
                uxmLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
            }
            // 设置方法名称
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            uxmLog.setMethod(className + "." + methodName + "()");
            // 设置请求方式
            uxmLog.setRequestMethod(request.getMethod());
            // 处理设置注解上的参数
            getControllerMethodDescription(joinPoint, controllerLog, uxmLog, jsonResult, request);
            // 保存数据库
            uxmLog.setOperTime(new Date());
            uxmLogService.save(uxmLog);
        } catch (Exception exp) {
            // 记录本地异常日志
            log.error("==前置通知异常==");
            log.error("异常信息:{}", exp.getMessage());
            exp.printStackTrace();
        }
    }

    public static String getIpAddr(HttpServletRequest request) {
        if (request == null) {
            return "unknown";
        }
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Forwarded-For");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }

        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    public void getControllerMethodDescription(JoinPoint joinPoint, Log log, UxmLog uxmLog, Object jsonResult, HttpServletRequest request) throws Exception {
        // 设置action动作
        uxmLog.setBusinessType(log.value().ordinal());
        // 是否需要保存request,参数和值
        if (log.isSaveRequestData()) {
            // 获取参数的信息,传入到数据库中。
            setRequestValue(joinPoint, uxmLog, request);
        }
        // 是否需要保存response,参数和值
        if (log.isSaveResponseData()) {
            uxmLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000));
        }
    }

    private void setRequestValue(JoinPoint joinPoint, UxmLog uxmLog, HttpServletRequest request) throws Exception {
        String requestMethod = uxmLog.getRequestMethod();
        if (RequestMethod.PUT.name().equals(requestMethod) || RequestMethod.POST.name().equals(requestMethod)) {
            String params = argsArrayToString(joinPoint.getArgs());
            uxmLog.setOperParam(StringUtils.substring(params, 0, 2000));
        } else {
            Map<?, ?> paramsMap = (Map<?, ?>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
            uxmLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000));
        }
    }

    private String argsArrayToString(Object[] paramsArray) {
        StringBuilder params = new StringBuilder();
        if (paramsArray != null && paramsArray.length > 0) {
            for (Object o : paramsArray) {
                if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) {
                    try {
                        Object jsonObj = JSON.toJSON(o);
                        params.append(jsonObj.toString()).append(" ");
                    } catch (Exception e) {
                        log.error(e.getMessage());
                    }
                }
            }
        }
        return params.toString().trim();
    }

    @SuppressWarnings("rawtypes")
    public boolean isFilterObject(final Object o) {
        Class<?> clazz = o.getClass();
        if (clazz.isArray()) {
            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
        } else if (Collection.class.isAssignableFrom(clazz)) {
            Collection collection = (Collection) o;
            for (Object value : collection) {
                return value instanceof MultipartFile;
            }
        } else if (Map.class.isAssignableFrom(clazz)) {
            Map map = (Map) o;
            for (Object value : map.entrySet()) {
                Map.Entry entry = (Map.Entry) value;
                return entry.getValue() instanceof MultipartFile;
            }
        }
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
                || o instanceof BindingResult;
    }
}

这样的话,我们就可以 在 项目当中 去在标注注解的前后去进行输出 日志

使用方式 2

我们可能还会在每次请求的时候去输出日志,所以 我们也可以去定义一个 请求的 注解

@HttpLog 自动记录Http日志

在很多时候我们要把一些接口的Http请求信息记录到日志里面。通常原始的做法是利用日志框架如log4j,slf4j等,在方法里面打日志log.info(“xxxx”)。但是这样的工作无疑是单调而又重复的,我们可以采用自定义注解+切面的来简化这一工作。通常的日志记录都在Controller里面进行的比较多,我们可以实现这样的效果:
我们自定义@HttpLog注解,作用域在类上,凡是打上了这个注解的Controller类里面的所有方法都会自动记录Http日志。实现方式也很简单,主要写好切面表达式:

日志切面

下面代码的意思,就是当标注了注解,我们通过 @Pointcut 定义了切入点, 当标注了注解,我们会在标注注解的 前后进行输出 ,当然也包含了 Spring 官方 自带的注解 例如 RestController

// 切面表达式,描述所有所有需要记录log的类,所有有@HttpLog 并且有 @Controller 或 @RestController 类都会被代理
    @Pointcut("@within(com.example.spiritmark.annotation.HttpLog) && (@within(org.springframework.web.bind.annotation.RestController) || @within(org.springframework.stereotype.Controller))")
    public void httpLog() {
    }

    @Before("httpLog()")
    public void preHandler(JoinPoint joinPoint) {
        startTime.set(System.currentTimeMillis());
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest httpServletRequest = servletRequestAttributes.getRequest();
        log.info("Current Url: {}", httpServletRequest.getRequestURI());
        log.info("Current Http Method: {}", httpServletRequest.getMethod());
        log.info("Current IP: {}", httpServletRequest.getRemoteAddr());
        Enumeration<String> headerNames = httpServletRequest.getHeaderNames();
        log.info("=======http headers=======");
        while (headerNames.hasMoreElements()) {
            String nextName = headerNames.nextElement();
            log.info(nextName.toUpperCase() + ": {}", httpServletRequest.getHeader(nextName));
        }
        log.info("======= header end =======");
        log.info("Current Class Method: {}", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        log.info("Parms: {}", null != httpServletRequest.getQueryString() ? JSON.toJSONString(httpServletRequest.getQueryString().split("&")) : "EMPTY");

    }

    @AfterReturning(returning = "response", pointcut = "httpLog()")
    public void afterReturn(Object response) {
        log.info("Response: {}", JSON.toJSONString(response));
        log.info("Spend Time: [ {}", System.currentTimeMillis() - startTime.get() + " ms ]");

    }

@TimeStamp 自动注入时间戳

如果我们想通过自定义注解,在我们每次保存数据的时候,自动的帮我们将标注注解的方法内的时间戳字段转换成 正常日期,我们就需要

我们的很多数据需要记录时间戳,最常见的就是记录created_at和updated_at,通常我们可以通常实体类中的setCreatedAt()方法来写入当前时间,然后通过ORM来插入到数据库里,但是这样的方法比较重复枯燥,给每个需要加上时间戳的类都要写入时间戳很麻烦而且不小心会漏掉。

另一个思路是在数据库里面设置默认值,插入的时候由数据库自动生成当前时间戳,但是理想很丰满,现实很骨感,在MySQL如果时间戳类型是datetime里即使你设置了默认值为当前时间也不会在时间戳为空时插入数据时自动生成,而是会在已有时间戳记录的情况下更新时间戳为当前时间,这并不是我们所需要的,比如我们不希望created_at每次更改记录时都被刷新,另外的方法是将时间戳类型改为timestamp,这样第一个类型为timestamp的字段会在值为空时自动生成,但是多个的话,后面的均不会自动生成。再有一种思路是,直接在sql里面用now()函数生成,比如created_at = now()。

但是这样必须要写sql,如果使用的不是主打sql流的orm不会太方便,比如hibernate之类的,并且也会加大sql语句的复杂度,同时sql的可移植性也会降低,比如sqlServer中就不支持now()函数。为了简化这个问题,我们可以自定义@TimeStamp注解,打上该注解的方法的入参里面的所有对象或者指定对象里面要是有setCreatedAt、setUpdatedAt这样的方法,便会自动注入时间戳,而无需手动注入,同时还可以指定只注入created_at或updated_at。实现主要代码如下:

@Aspect
@Component
public class TimeStampAspect {

    @Pointcut("@annotation(com.example.spiritmark.annotation.TimeStamp)")
    public void timeStampPointcut() {}

    @Before("timeStampPointcut() && @annotation(timeStamp)")
    public void setTimestamp(JoinPoint joinPoint, TimeStamp timeStamp) {
        Long currentTime = System.currentTimeMillis();
        Class<?> type = timeStamp.type();
        Object[] args = joinPoint.getArgs();

        for (Object arg : args) {
            if (type.isInstance(arg)) {
                setTimestampForArg(arg, timeStamp);
            }
        }
    }

    private void setTimestampForArg(Object arg, TimeStamp timeStamp) {
        Date currentDate = new Date(System.currentTimeMillis());
        TimeStampRank rank = timeStamp.rank();
        Method[] methods = arg.getClass().getMethods();

        for (Method method : methods) {
            String methodName = method.getName();
            if (isSetter(methodName) && isRelevantSetter(methodName, rank)) {
                try {
                    method.invoke(arg, currentDate);
                } catch (IllegalAccessException | InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private boolean isSetter(String methodName) {
        return methodName.startsWith("set") && methodName.length() > 3;
    }

    private boolean isRelevantSetter(String methodName, TimeStampRank rank) {
        if (rank.equals(TimeStampRank.FULL)) {
            return methodName.endsWith("At");
        }
        if (rank.equals(TimeStampRank.UPDATE)) {
            return methodName.startsWith("setUpdated");
        }
        if (rank.equals(TimeStampRank.CREATE)) {
            return methodName.startsWith("setCreated");
        }
        return false;
    }
}

1.使用@Aspect和@Component注解分别标注切面和切面类,更符合AOP的实现方式。

2.将pointCut()和before()方法分别改名为timeStampPointcut()和setTimestamp(),更能表达它们的作用。

3.通过Class.isInstance(Object obj)方法,将原先的流操作改为了一个简单的for循环,使代码更加简洁。

4.将原先的setCurrentTime()方法改名为setTimestampForArg(),更能表达它的作用。

5.新增了两个私有方法isSetter()和isRelevantSetter(),将原先在setTimestampForArg()中的逻辑分离出来,提高了代码的可读性和可维护性

到此这篇关于详解Java中自定义注解的使用的文章就介绍到这了,更多相关Java自定义注解内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 浅谈Java自定义注解相关知识

    一.自定义注解格式 分析 Java 中自带的 @Override 注解 , 源码如下 : @Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { } 注解分为两部分 : ① 元注解 ; ② public @interface 注解名称 ; 二.注解本质分析 按照 public @interface 注解名称 格式 , 写出一个注解 , 编译该注解代码生成 Annotat

  • 详解Java如何实现自定义注解

    目录 概念 作用 JDK中预定义的一些注解 注解生成文档案例 自定义注解 格式 本质 属性:接口中的抽象方法 元注解:用于描述注解的注解 在程序使用(解析)注解:获取注解中定义的属性值 案例:通过自定义注解定义一个简单的测试框架 总结 概念 概念:说明程序的.给计算机看的 注释:用文字描述程序的.给程序员看的 定义:注解(Annotation),也叫元数据.一种代码级别的说明.它是JDK1.5及以后版本引入的一个特性,与类.接口.枚举是在同一个层次.它可以声明在包.类.字段.方法.局部变量.方法

  • Java利用自定义注解实现数据校验

    目录 JSR303介绍 引入依赖 常用注解 开启校验 数据校验测试 自定义的封装错误信息 统一异常处理 分组校验 创建分组校验接口 添加校验注解 开启分组校验 自定义校验 编写自定义的校验注解 编写自定义的校验器 关联校验器和校验注解 添加自定义的校验注解 JSR303介绍 在Java中提供了一系列的校验方式 这些校验方式在javax.validation.constraints包中 引入依赖 <dependency> <groupId>org.springframework.bo

  • Java 自定义注解的魅力

    注解是什么? ①.引用自维基百科的内容: Java注解又称Java标注,是JDK5.0版本开始支持加入源代码的特殊语法 元数据 . Java语言中的类.方法.变量.参数和包等都可以被标注.和Javadoc不同,Java标注可以通过反射获取标注内容.在编译器生成类文件时,标注可以被嵌入到字节码中.Java虚拟机可以保留标注内容,在运行时可以获取到标注内容. 当然它也支持自定义Java标注. ②.引用自网络的内容: Java 注解是在 JDK5 时引入的新特性,注解(也被称为 元数据 )为我们在代码

  • Java注解详解及实现自定义注解的方法

    目录 概念 ‍♀️作用 ⛹JDK中预定义的一些注解

  • 详解Java中自定义注解的使用

    目录 什么是注解 注解的注意事项 注解的本质 自定义注解使用 使用方式 1 使用方式 2 什么是注解 在早期的工作的时候 ,自定义注解写的比较多,可大多都只是因为 这样看起来 不会存在一堆代码耦合在一起的情况,所以使用了自定义注解,这样看起来清晰些, Annontation是Java5开始引入的新特征,中文名称叫注解. 它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类.方法.成员变量等)进行关联.为程序的元素(类.方法.成员变量)加上更直观.更明了的说

  • Java中自定义注解介绍与使用场景详解

    注解的概念及分类 1.首先我们来看一下什么是注解: 注解就是某种注解类型的一个实例,我们可以用它在某个类上进行标注,这样编译器在编译我们的文件时,会根据我们自己设定的方法来编译类. 2.注解的分类 注解大体上分为三种:标记注解,一般注解,元注解,@Override用于标识,该方法是继承自超类的.这样,当超类的方法修改后,实现类就可以直接看到了.而@Deprecated注解,则是标识当前方法或者类已经不推荐使用,如果用户还是要使用,会生成编译的警告. 本文主要介绍的是关于Java自定义注解,下面话

  • 详解Java进阶知识注解

    一.注解的概念 1.注解官方解释 注解 叫元数据,一种代码级别的说明,它是JDK1.5及以后版本引入的一个特性,与类.接口.枚举在同一个层次,它可以声明在包.类.字段.局部变量.方法参数等的前面,用来对这些元素进行说明.注释. 注解的作用分类 编写文档:通过代码里表示的元数据生成文档[生成doc文档] 代码分析:通过代码里表示的元数据进行分析[使用反射] 编译检查:通过代码里表示的元数据让编译器能够实现基本的编译检查[Override] 注解按照运行机制分类 源码注解:注解只在源码中存在,编译成

  • 详解Java中的mapstruct插件使用

    实体类的属性映射怎么可以少了它? 我们都知道,随着一个工程的越来越成熟,模块划分会越来越细,其中实体类一般存于 domain 之中,但 domain 工程最好不要被其他工程依赖,所以其他工程想获取实体类数据时就需要在各自工程写 model,自定义 model 可以根据自身业务需要映射相应的实体属性.这样一来,这个映射工程貌似并不简单了.阿森差点就犯难了…… 序 所以阿淼今天就要给大家安利一款叫 mapstruct 的插件,它就是专门用来处理 domin 实体类与 model 类的属性映射的,我们

  • 详解Java如何使用注解来配置Spring容器

    目录 介绍 @Bean and @Configuration AnnotationConfigApplicationContext实例化容器 通过使用 register(Class...) 以编程方式构建容器 @ComponentScan启用组件扫描 Bean的依赖 生命周期回调 Bean指定作用域 自定义bean名称 Bean别名 Bean注入之间的依赖 @Import @ImportResource @PropertySource 支持多个properties文件 ApplicationCo

  • 详解java 中Spring jsonp 跨域请求的实例

    详解java 中Spring jsonp 跨域请求的实例 jsonp介绍 JSONP(JSON with Padding)是JSON的一种"使用模式",可用于解决主流浏览器的跨域数据访问的问题.由于同源策略,一般来说位于 server1.example.com 的网页无法与不是 server1.example.com的服务器沟通,而 HTML 的<script> 元素是一个例外.利用 <script> 元素的这个开放策略,网页可以得到从其他来源动态产生的 JSO

  • 详解Java中Collections.sort排序

    Comparator是个接口,可重写compare()及equals()这两个方法,用于比价功能:如果是null的话,就是使用元素的默认顺序,如a,b,c,d,e,f,g,就是a,b,c,d,e,f,g这样,当然数字也是这样的. compare(a,b)方法:根据第一个参数小于.等于或大于第二个参数分别返回负整数.零或正整数. equals(obj)方法:仅当指定的对象也是一个 Comparator,并且强行实施与此 Comparator 相同的排序时才返回 true. Collections.

  • 详解Java中的 枚举与泛型

    详解Java中的 枚举与泛型 一:首先从枚举开始说起 枚举类型是JDK5.0的新特征.Sun引进了一个全新的关键字enum来定义一个枚举类.下面就是一个典型枚举类型的定义: public enum Color{ RED,BLUE,BLACK,YELLOW,GREEN } 显然,enum很像特殊的class,实际上enum声明定义的类型就是一个类. 而这些类都是类库中Enum类的子类(Java.lang.Enum).它们继承了这个Enum中的许多有用的方法.我们对代码编译之后发现,编译器将 enu

  • 详解Java中AbstractMap抽象类

    jdk1.8.0_144 下载地址:http://www.jb51.net/softs/551512.html AbstractMap抽象类实现了一些简单且通用的方法,本身并不难.但在这个抽象类中有两个方法非常值得关注,keySet和values方法源码的实现可以说是教科书式的典范. 抽象类通常作为一种骨架实现,为各自子类实现公共的方法.上一篇我们讲解了Map接口,此篇对AbstractMap抽象类进行剖析研究. Java中Map类型的数据结构有相当多,AbstractMap作为它们的骨架实现实

  • 详解Java 中的UnitTest 和 PowerMock

    学习一门计算机语言,我觉得除了学习它的语法外,最重要的就是要学习怎么在这个语言环境下进行单元测试,因为单元测试能帮你提早发现错误:同时给你的程序加一道防护网,防止你的修改破坏了原有的功能:单元测试还能指引你写出更好的代码,毕竟不能被测试的代码一定不是好代码:除此之外,它还能增加你的自信,能勇敢的说出「我的程序没有bug」. 每个语言都有其常用的单元测试框架,本文主要介绍在 Java 中,我们如何使用 PowerMock,来解决我们在写单元测试时遇到的问题,从 Mock 这个词可以看出,这类问题主

随机推荐