diff --git a/src/main/java/cc/ryanc/halo/model/dto/CommentViewOutputDTO.java b/src/main/java/cc/ryanc/halo/model/dto/CommentViewOutputDTO.java index db51f8c56..6a0e65c98 100644 --- a/src/main/java/cc/ryanc/halo/model/dto/CommentViewOutputDTO.java +++ b/src/main/java/cc/ryanc/halo/model/dto/CommentViewOutputDTO.java @@ -2,7 +2,7 @@ package cc.ryanc.halo.model.dto; import cc.ryanc.halo.model.domain.Comment; import cc.ryanc.halo.model.domain.Post; -import cc.ryanc.halo.model.dto.base.AbstractOutputConverter; +import cc.ryanc.halo.model.dto.base.OutputConverter; import lombok.Data; import java.util.Date; @@ -12,7 +12,7 @@ import java.util.Date; * @date : 2019-03-09 */ @Data -public class CommentViewOutputDTO extends AbstractOutputConverter { +public class CommentViewOutputDTO implements OutputConverter { private Long commentId; diff --git a/src/main/java/cc/ryanc/halo/model/dto/PageAdminOutputDTO.java b/src/main/java/cc/ryanc/halo/model/dto/PageAdminOutputDTO.java index d0c35850a..ac48baad5 100644 --- a/src/main/java/cc/ryanc/halo/model/dto/PageAdminOutputDTO.java +++ b/src/main/java/cc/ryanc/halo/model/dto/PageAdminOutputDTO.java @@ -2,7 +2,7 @@ package cc.ryanc.halo.model.dto; import cc.ryanc.halo.model.domain.Comment; import cc.ryanc.halo.model.domain.Post; -import cc.ryanc.halo.model.dto.base.AbstractOutputConverter; +import cc.ryanc.halo.model.dto.base.OutputConverter; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; @@ -14,7 +14,7 @@ import java.util.List; * @date : 2019-03-09 */ @Data -public class PageAdminOutputDTO extends AbstractOutputConverter { +public class PageAdminOutputDTO implements OutputConverter { private Long postId; diff --git a/src/main/java/cc/ryanc/halo/model/dto/PostDetailOutputDTO.java b/src/main/java/cc/ryanc/halo/model/dto/PostDetailOutputDTO.java index b598f3003..4948eef6c 100644 --- a/src/main/java/cc/ryanc/halo/model/dto/PostDetailOutputDTO.java +++ b/src/main/java/cc/ryanc/halo/model/dto/PostDetailOutputDTO.java @@ -1,7 +1,7 @@ package cc.ryanc.halo.model.dto; import cc.ryanc.halo.model.domain.*; -import cc.ryanc.halo.model.dto.base.AbstractOutputConverter; +import cc.ryanc.halo.model.dto.base.OutputConverter; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; @@ -14,7 +14,7 @@ import java.util.List; * @author johnniang */ @Data -public class PostDetailOutputDTO extends AbstractOutputConverter { +public class PostDetailOutputDTO implements OutputConverter { private Long postId; diff --git a/src/main/java/cc/ryanc/halo/model/dto/PostSimpleOutputDTO.java b/src/main/java/cc/ryanc/halo/model/dto/PostSimpleOutputDTO.java index ef6c5d103..99d32f6a2 100644 --- a/src/main/java/cc/ryanc/halo/model/dto/PostSimpleOutputDTO.java +++ b/src/main/java/cc/ryanc/halo/model/dto/PostSimpleOutputDTO.java @@ -1,7 +1,9 @@ package cc.ryanc.halo.model.dto; -import cc.ryanc.halo.model.domain.*; -import cc.ryanc.halo.model.dto.base.AbstractOutputConverter; +import cc.ryanc.halo.model.domain.Category; +import cc.ryanc.halo.model.domain.Post; +import cc.ryanc.halo.model.domain.Tag; +import cc.ryanc.halo.model.dto.base.OutputConverter; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; @@ -14,7 +16,7 @@ import java.util.List; * @author johnniang */ @Data -public class PostSimpleOutputDTO extends AbstractOutputConverter { +public class PostSimpleOutputDTO implements OutputConverter { private Long postId; diff --git a/src/main/java/cc/ryanc/halo/model/dto/PostViewOutputDTO.java b/src/main/java/cc/ryanc/halo/model/dto/PostViewOutputDTO.java index 131e0c6d5..725ced1e4 100644 --- a/src/main/java/cc/ryanc/halo/model/dto/PostViewOutputDTO.java +++ b/src/main/java/cc/ryanc/halo/model/dto/PostViewOutputDTO.java @@ -1,7 +1,7 @@ package cc.ryanc.halo.model.dto; import cc.ryanc.halo.model.domain.Post; -import cc.ryanc.halo.model.dto.base.AbstractOutputConverter; +import cc.ryanc.halo.model.dto.base.OutputConverter; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; @@ -12,7 +12,7 @@ import java.util.Date; * @date : 2019-03-09 */ @Data -public class PostViewOutputDTO extends AbstractOutputConverter { +public class PostViewOutputDTO implements OutputConverter { private Long postId; diff --git a/src/main/java/cc/ryanc/halo/model/dto/base/AbstractInputConverter.java b/src/main/java/cc/ryanc/halo/model/dto/base/AbstractInputConverter.java index 4bd1c9171..d3c2c8518 100644 --- a/src/main/java/cc/ryanc/halo/model/dto/base/AbstractInputConverter.java +++ b/src/main/java/cc/ryanc/halo/model/dto/base/AbstractInputConverter.java @@ -11,6 +11,7 @@ import static cc.ryanc.halo.utils.BeanUtils.updateProperties; * * @author johnniang */ +@Deprecated public abstract class AbstractInputConverter implements InputConverter { @SuppressWarnings("unchecked") diff --git a/src/main/java/cc/ryanc/halo/model/dto/base/AbstractOutputConverter.java b/src/main/java/cc/ryanc/halo/model/dto/base/AbstractOutputConverter.java index 5fe3535da..f68896089 100644 --- a/src/main/java/cc/ryanc/halo/model/dto/base/AbstractOutputConverter.java +++ b/src/main/java/cc/ryanc/halo/model/dto/base/AbstractOutputConverter.java @@ -12,7 +12,8 @@ import static cc.ryanc.halo.utils.BeanUtils.updateProperties; * * @author johnniang */ -public abstract class AbstractOutputConverter implements OutputConverter { +@Deprecated +public abstract class AbstractOutputConverter, DOMAIN> implements OutputConverter { @SuppressWarnings("unchecked") private final Class dtoType = (Class) fetchType(0); @@ -23,9 +24,9 @@ public abstract class AbstractOutputConverter implements OutputConv @Override @SuppressWarnings("unchecked") - public DTO convertFrom(DOMAIN domain) { + public T convertFrom(DOMAIN domain) { updateProperties(domain, this); - return (DTO) this; + return (T) this; } /** diff --git a/src/main/java/cc/ryanc/halo/model/dto/base/InputConverter.java b/src/main/java/cc/ryanc/halo/model/dto/base/InputConverter.java index f244bcf87..dbf09fb9c 100644 --- a/src/main/java/cc/ryanc/halo/model/dto/base/InputConverter.java +++ b/src/main/java/cc/ryanc/halo/model/dto/base/InputConverter.java @@ -1,5 +1,13 @@ package cc.ryanc.halo.model.dto.base; +import cc.ryanc.halo.utils.ReflectionUtils; + +import java.lang.reflect.ParameterizedType; +import java.util.Objects; + +import static cc.ryanc.halo.utils.BeanUtils.transformFrom; +import static cc.ryanc.halo.utils.BeanUtils.updateProperties; + /** * Converter interface for input DTO. * @@ -12,13 +20,26 @@ public interface InputConverter { * * @return new domain with same value(not null) */ - DOMAIN convertTo(); + @SuppressWarnings("unchecked") + default DOMAIN convertTo() { + // Get parameterized type + ParameterizedType currentType = ReflectionUtils.getParameterizedType(InputConverter.class, this.getClass()); + + // Assert not equal + Objects.requireNonNull(currentType, "Cannot fetch actual type because parameterized type is null"); + + Class domainClass = (Class) currentType.getActualTypeArguments()[0]; + + return transformFrom(this, domainClass); + } /** * Update a domain by dto.(shallow) * * @param domain updated domain */ - void update(DOMAIN domain); + default void update(DOMAIN domain) { + updateProperties(this, domain); + } } diff --git a/src/main/java/cc/ryanc/halo/model/dto/base/OutputConverter.java b/src/main/java/cc/ryanc/halo/model/dto/base/OutputConverter.java index 2a6b59710..b88c73ee1 100644 --- a/src/main/java/cc/ryanc/halo/model/dto/base/OutputConverter.java +++ b/src/main/java/cc/ryanc/halo/model/dto/base/OutputConverter.java @@ -1,11 +1,17 @@ package cc.ryanc.halo.model.dto.base; +import static cc.ryanc.halo.utils.BeanUtils.updateProperties; + /** * Converter interface for output DTO. * + * The implementation type must be equal to DTO type + * + * @param the implementation class type + * @param doamin type * @author johnniang */ -public interface OutputConverter { +public interface OutputConverter, DOMAIN> { /** * Convert from domain.(shallow) @@ -13,5 +19,11 @@ public interface OutputConverter { * @param domain domain data * @return converted dto data */ - DTO convertFrom(DOMAIN domain); + @SuppressWarnings("unchecked") + default T convertFrom(DOMAIN domain) { + + updateProperties(domain, this); + + return (T) this; + } } diff --git a/src/main/java/cc/ryanc/halo/model/params/JournalParam.java b/src/main/java/cc/ryanc/halo/model/params/JournalParam.java index 6c0d13ca1..bfc86ff17 100644 --- a/src/main/java/cc/ryanc/halo/model/params/JournalParam.java +++ b/src/main/java/cc/ryanc/halo/model/params/JournalParam.java @@ -1,7 +1,7 @@ package cc.ryanc.halo.model.params; import cc.ryanc.halo.model.domain.Post; -import cc.ryanc.halo.model.dto.base.AbstractInputConverter; +import cc.ryanc.halo.model.dto.base.InputConverter; import cc.ryanc.halo.model.enums.PostTypeEnum; import cc.ryanc.halo.utils.MarkdownUtils; import lombok.Data; @@ -13,7 +13,7 @@ import lombok.Data; * @date : 2019/03/04 */ @Data -public class JournalParam extends AbstractInputConverter { +public class JournalParam implements InputConverter { /** * 标题 diff --git a/src/main/java/cc/ryanc/halo/utils/BeanUtils.java b/src/main/java/cc/ryanc/halo/utils/BeanUtils.java index 21ea19391..446dbc7e3 100644 --- a/src/main/java/cc/ryanc/halo/utils/BeanUtils.java +++ b/src/main/java/cc/ryanc/halo/utils/BeanUtils.java @@ -52,7 +52,7 @@ public class BeanUtils { // Return the target instance return targetInstance; } catch (Exception e) { - throw new BeanUtilsException("Failed to new " + targetClass.getName() + "instance or copy properties", e); + throw new BeanUtilsException("Failed to new " + targetClass.getName() + " instance or copy properties", e); } } diff --git a/src/main/java/cc/ryanc/halo/utils/ReflectionUtils.java b/src/main/java/cc/ryanc/halo/utils/ReflectionUtils.java new file mode 100644 index 000000000..bcc17d8ad --- /dev/null +++ b/src/main/java/cc/ryanc/halo/utils/ReflectionUtils.java @@ -0,0 +1,74 @@ +package cc.ryanc.halo.utils; + +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +/** + * Reflection utilities. + * + * @author johnniang + */ +public class ReflectionUtils { + + private ReflectionUtils() { + } + + /** + * Gets parameterized type. + * + * @param interfaceType interface type must not be null + * @param genericTypes generic type array + * @return parameterized type of the interface or null if it is mismatch + */ + @Nullable + public static ParameterizedType getParameterizedType(@NonNull Class interfaceType, Type... genericTypes) { + Assert.notNull(interfaceType, "Interface type must not be null"); + + ParameterizedType currentType = null; + + for (Type genericType : genericTypes) { + if (genericType instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) genericType; + if (parameterizedType.getRawType().getTypeName().equals(interfaceType.getTypeName())) { + currentType = parameterizedType; + break; + } + } + } + + return currentType; + } + + /** + * Gets parameterized type. + * + * @param interfaceType interface type must not be null + * @param implementationClass implementation class of the interface must not be null + * @return parameterized type of the interface or null if it is mismatch + */ + @Nullable + public static ParameterizedType getParameterizedType(@NonNull Class interfaceType, Class implementationClass) { + Assert.notNull(interfaceType, "Interface type must not be null"); + + if (implementationClass == null) { + // If the super class is Object parent then return null + return null; + } + + // Get parameterized type + ParameterizedType currentType = getParameterizedType(interfaceType, implementationClass.getGenericInterfaces()); + + if (currentType != null) { + // return the current type + return currentType; + } + + Class superclass = implementationClass.getSuperclass(); + + return getParameterizedType(interfaceType, superclass); + } +} diff --git a/src/main/java/cc/ryanc/halo/web/controller/admin/AdminController.java b/src/main/java/cc/ryanc/halo/web/controller/admin/AdminController.java index 9c549439e..be47e4523 100755 --- a/src/main/java/cc/ryanc/halo/web/controller/admin/AdminController.java +++ b/src/main/java/cc/ryanc/halo/web/controller/admin/AdminController.java @@ -110,7 +110,7 @@ public class AdminController extends BaseController { //查询最新的文章 final List postsLatest = postService.findPostLatest() .stream() - .map(post -> new PostViewOutputDTO().convertFrom(post)) + .map(post -> (PostViewOutputDTO) new PostViewOutputDTO().convertFrom(post)) .collect(Collectors.toList()); model.addAttribute("postsLatest", postsLatest); @@ -121,7 +121,7 @@ public class AdminController extends BaseController { //查询最新的评论 final List commentsLatest = commentService.findCommentsLatest() .stream() - .map(comment -> new CommentViewOutputDTO().convertFrom(comment)) + .map(comment -> (CommentViewOutputDTO) new CommentViewOutputDTO().convertFrom(comment)) .collect(Collectors.toList()); model.addAttribute("commentsLatest", commentsLatest); diff --git a/src/main/java/cc/ryanc/halo/web/controller/admin/MenuController.java b/src/main/java/cc/ryanc/halo/web/controller/admin/MenuController.java index 9518dcb99..d3b80cf23 100644 --- a/src/main/java/cc/ryanc/halo/web/controller/admin/MenuController.java +++ b/src/main/java/cc/ryanc/halo/web/controller/admin/MenuController.java @@ -48,9 +48,9 @@ public class MenuController { public String menus(Model model) { List posts = postService.findAll(PostTypeEnum.POST_TYPE_PAGE.getDesc()) .stream() - .map(post -> new PostViewOutputDTO().convertFrom(post)) + .map(post -> (PostViewOutputDTO) new PostViewOutputDTO().convertFrom(post)) .collect(Collectors.toList()); - model.addAttribute("posts",posts); + model.addAttribute("posts", posts); return "admin/admin_menu"; } diff --git a/src/main/java/cc/ryanc/halo/web/controller/admin/PageController.java b/src/main/java/cc/ryanc/halo/web/controller/admin/PageController.java index 6f5bf41d5..a485ec6ff 100755 --- a/src/main/java/cc/ryanc/halo/web/controller/admin/PageController.java +++ b/src/main/java/cc/ryanc/halo/web/controller/admin/PageController.java @@ -87,8 +87,10 @@ public class PageController { public String pages(Model model) { final List posts = postService.findAll(PostTypeEnum.POST_TYPE_PAGE.getDesc()) .stream() - .map(post -> new PageAdminOutputDTO().convertFrom(post)) + .map(post -> (PageAdminOutputDTO) new PageAdminOutputDTO().convertFrom(post)) .collect(Collectors.toList()); + + model.addAttribute("pages", posts); return "admin/admin_page"; } diff --git a/src/main/java/cc/ryanc/halo/web/controller/admin/PostController.java b/src/main/java/cc/ryanc/halo/web/controller/admin/PostController.java index 5d1c83fee..1ded78839 100755 --- a/src/main/java/cc/ryanc/halo/web/controller/admin/PostController.java +++ b/src/main/java/cc/ryanc/halo/web/controller/admin/PostController.java @@ -94,6 +94,9 @@ public class PostController extends BaseController { @RequestParam(value = "status", defaultValue = "0") Integer status, @PageableDefault(sort = "postDate", direction = DESC) Pageable pageable) { final Page posts = postService.findPostByStatus(status, PostTypeEnum.POST_TYPE_POST.getDesc(), pageable); + + Page postViewOutputDTOS = posts.map(post -> new PostViewOutputDTO().convertFrom(post)); + model.addAttribute("posts", posts); model.addAttribute("publishCount", postService.getCountByStatus(PostStatusEnum.PUBLISHED.getCode())); model.addAttribute("draftCount", postService.getCountByStatus(PostStatusEnum.DRAFT.getCode())); @@ -180,7 +183,7 @@ public class PostController extends BaseController { @RequestParam("tagList") String tagList) { //old data final Post oldPost = postService.fetchById(post.getPostId()).orElse(new Post()); - BeanUtils.updateProperties(oldPost,post); + BeanUtils.updateProperties(oldPost, post); post.setPostContent(MarkdownUtils.renderMarkdown(post.getPostContentMd())); if (null == post.getPostDate()) { post.setPostDate(new Date()); @@ -308,7 +311,7 @@ public class PostController extends BaseController { final String blogUrl = OPTIONS.get(BlogPropertiesEnum.BLOG_URL.getProp()); final List posts = postService.findAll(PostTypeEnum.POST_TYPE_POST.getDesc()) .stream() - .map(post -> new PostViewOutputDTO().convertFrom(post)) + .map(post -> (PostViewOutputDTO) new PostViewOutputDTO().convertFrom(post)) .collect(Collectors.toList()); final StringBuilder urls = new StringBuilder(); for (PostViewOutputDTO post : posts) { diff --git a/src/main/java/cc/ryanc/halo/web/controller/api/ApiJournalController.java b/src/main/java/cc/ryanc/halo/web/controller/api/ApiJournalController.java index 8b8a097ec..e2658a39e 100644 --- a/src/main/java/cc/ryanc/halo/web/controller/api/ApiJournalController.java +++ b/src/main/java/cc/ryanc/halo/web/controller/api/ApiJournalController.java @@ -1,12 +1,18 @@ package cc.ryanc.halo.web.controller.api; import cc.ryanc.halo.model.domain.Post; +import cc.ryanc.halo.model.domain.User; import cc.ryanc.halo.model.dto.PostDetailOutputDTO; import cc.ryanc.halo.model.params.JournalParam; import cc.ryanc.halo.service.PostService; +import cc.ryanc.halo.service.UserService; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; +import javax.servlet.http.HttpSession; + +import static cc.ryanc.halo.model.support.HaloConst.USER_SESSION_KEY; + /** *
  *     日志 API
@@ -21,8 +27,12 @@ public class ApiJournalController {
 
     private final PostService postService;
 
-    public ApiJournalController(PostService postService) {
+    private final UserService userService;
+
+    public ApiJournalController(PostService postService,
+                                UserService userService) {
         this.postService = postService;
+        this.userService = userService;
     }
 
     /**
@@ -33,9 +43,12 @@ public class ApiJournalController {
      */
     @PostMapping
     @ResponseStatus(HttpStatus.CREATED)
-    public PostDetailOutputDTO save(@RequestBody JournalParam journalParam) {
+    public PostDetailOutputDTO save(@RequestBody JournalParam journalParam, HttpSession session) {
+
+        User user = userService.findUser();
 
         Post post = journalParam.convertTo();
+        post.setUser(user);
 
         return new PostDetailOutputDTO().convertFrom(postService.create(post));
     }
diff --git a/src/test/java/cc/ryanc/halo/model/dto/base/InputConverterTest.java b/src/test/java/cc/ryanc/halo/model/dto/base/InputConverterTest.java
new file mode 100644
index 000000000..88a9ad07b
--- /dev/null
+++ b/src/test/java/cc/ryanc/halo/model/dto/base/InputConverterTest.java
@@ -0,0 +1,74 @@
+package cc.ryanc.halo.model.dto.base;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.junit.Test;
+
+import java.io.Serializable;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+
+public class InputConverterTest {
+
+    @Test
+    public void convertToTest() {
+        TestInputDTO inputDTO = new TestInputDTO("test_name");
+
+        TestDomain domain = inputDTO.convertTo();
+        assertThat(domain.getName(), equalTo("test_name"));
+        assertNull(domain.getAge());
+    }
+
+    @Test
+    public void updateTest() {
+        TestInputDTO inputDTO = new TestInputDTO("test_input_dto_name");
+
+        TestDomain domain = new TestDomain("test_domain_name", 10);
+
+        inputDTO.update(domain);
+
+        assertThat(domain.getName(), equalTo("test_input_dto_name"));
+        assertThat(domain.getAge(), equalTo(10));
+    }
+
+    @Test
+    public void subConvertToTest() {
+        SubTestInputDTO subTestInputDTO = new SubTestInputDTO();
+        subTestInputDTO.setName("test_name");
+        subTestInputDTO.setAge(10);
+
+        TestDomain domain = subTestInputDTO.convertTo();
+
+        assertThat(domain.getName(), equalTo("test_name"));
+        assertThat(domain.getAge(), equalTo(10));
+    }
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class TestDomain {
+
+        private String name;
+
+        private Integer age;
+    }
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class TestInputDTO implements InputConverter, Serializable {
+
+        private String name;
+    }
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class SubTestInputDTO extends TestInputDTO {
+
+        private Integer age;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/cc/ryanc/halo/model/dto/base/OutputConverterTest.java b/src/test/java/cc/ryanc/halo/model/dto/base/OutputConverterTest.java
new file mode 100644
index 000000000..813ddb990
--- /dev/null
+++ b/src/test/java/cc/ryanc/halo/model/dto/base/OutputConverterTest.java
@@ -0,0 +1,61 @@
+package cc.ryanc.halo.model.dto.base;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Output converter test.
+ *
+ * @author johnniang
+ */
+public class OutputConverterTest {
+
+    @Test
+    public void convertFromTest() {
+        TestDomain domain = new TestDomain("test_domain_name", 10);
+
+        TestOutputDTO testOutputDTO = new TestOutputDTO().convertFrom(domain);
+
+        assertThat(testOutputDTO.getName(), equalTo("test_domain_name"));
+    }
+
+    @Test
+    public void convertFromSubTest() {
+        TestDomain domain = new TestDomain("test_domain_name", 10);
+
+        SubTestOutputDTO subTestOutputDTO = new SubTestOutputDTO().convertFrom(domain);
+
+        assertThat(subTestOutputDTO.getName(), equalTo("test_domain_name"));
+        assertThat(subTestOutputDTO.getAge(), equalTo(10));
+    }
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class TestDomain {
+
+        private String name;
+
+        private Integer age;
+    }
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class TestOutputDTO implements OutputConverter {
+
+        private String name;
+    }
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    public static class SubTestOutputDTO extends TestOutputDTO {
+        private Integer age;
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/cc/ryanc/halo/utils/ReflectionUtilsTest.java b/src/test/java/cc/ryanc/halo/utils/ReflectionUtilsTest.java
new file mode 100644
index 000000000..13d166b4e
--- /dev/null
+++ b/src/test/java/cc/ryanc/halo/utils/ReflectionUtilsTest.java
@@ -0,0 +1,10 @@
+package cc.ryanc.halo.utils;
+
+/**
+ * Reflection utils test.
+ *
+ * @author johnniang
+ */
+public class ReflectionUtilsTest {
+
+}
\ No newline at end of file