兩個模塊, App 與 Admin, App 模塊提供增長用戶(/add?name=${name})與查詢用戶(/query/${id}), Admin 模塊提供列出全部用戶(/listAllUser), 用戶共有兩個屬性, id(由系統自動分配) 與 name(由增長用戶時的 name 參數提供)java
開發環境爲 IDEA ULTIMATE 2018.3.5, JDK 1.8, Tomcat 8, Maven Java Web 項目, RESTful 傳輸數據時只支持 JSON 格式, Group Id 爲 com.seliote
, Artifact Id 爲 RESTfulDemo
web
爲 pom.xml 添加如下配置spring
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.targer>1.8</maven.compiler.targer> </properties> <packaging>war</packaging> <build> <sourceDirectory>src/main/java</sourceDirectory> <resources> <resource> <directory>src/main/resources</directory> </resource> </resources> <testSourceDirectory>src/test/java</testSourceDirectory> <testResources> <testResource> <directory>src/test/resources</directory> </testResource> </testResources> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.3</version> <configuration> <warSourceDirectory>web</warSourceDirectory> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build>
添加 Spring 依賴, 先在 pom.xml 的 properties
標籤中添加屬性 <spring.version>5.1.5.RELEASE</spring.version>
用於 Spring 版本, 再新增 Spring MVC 依賴apache
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> <scope>compile</scope> </dependency>
建立類 com.seliote.restfuldemo.config.RootContextConfig
json
package com.seliote.restfuldemo.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.stereotype.Controller; /** * @author seliote * @date 2019-03-24 * @description Spring 根應用上下文配置類 */ @Configuration @ComponentScan( basePackages = "com.seliote.restfuldemo", excludeFilters = @ComponentScan.Filter( type = FilterType.ANNOTATION, classes = {Configuration.class, Controller.class} ) ) public class RootContextConfig { }
此配置類如今只配置了組建掃描, 暫沒有添加其餘配置, 以後會一點點添加進來api
建立 App 模塊 Controller
所歸屬的包 com.seliote.restfuldemo.app
以及 Admin 模塊 Controller
所歸屬的包 com.seliote.restfuldemo.admin
建立 App 上下文配置類 com.seliote.restfuldemo.config.AppContextConfig
瀏覽器
package com.seliote.restfuldemo.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.stereotype.Controller; import org.springframework.web.servlet.config.annotation.EnableWebMvc; /** * @author seliote * @date 2019-03-24 * @description App 模塊的 Spring 上下文配置 */ @Configuration @EnableWebMvc @ComponentScan( basePackages = "com.seliote.restfuldemo.app", useDefaultFilters = false, includeFilters = @ComponentScan.Filter( type = FilterType.ANNOTATION, classes = Controller.class ) ) public class AppContextConfig { }
建立 Admin 上下文配置類 com.seliote.restfuldemo.config.AdminContextConfig
restful
package com.seliote.restfuldemo.config; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.stereotype.Controller; import org.springframework.web.servlet.config.annotation.EnableWebMvc; /** * @author seliote * @date 2019-03-24 * @description Admin 上下文配置類 */ @Configuration @EnableWebMvc @ComponentScan( basePackages = "com.seliote.restfuldemo.admin", useDefaultFilters = false, includeFilters = @ComponentScan.Filter( type = FilterType.ANNOTATION, classes = Controller.class ) ) public class AdminContextConfig { }
下面的配置會用到 Servlet, 因此添加 Servlet 依賴mvc
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency>
建立 Spring 啓動類 com.seliote.restfuldemo.config.Bootstrap
app
package com.seliote.restfuldemo.config; import org.springframework.web.WebApplicationInitializer; import org.springframework.web.context.ContextLoaderListener; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.servlet.DispatcherServlet; import javax.servlet.ServletRegistration; /** * @author seliote * @date 2019-03-24 * @description Spring 啓動類 */ public class Bootstrap implements WebApplicationInitializer { @Override public void onStartup(javax.servlet.ServletContext aServletContext) { // 根應用上下文 AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext(); rootContext.register(RootContextConfig.class); aServletContext.addListener(new ContextLoaderListener(rootContext)); // 靜態資源直接處理 aServletContext.getServletRegistration("default").addMapping("/resources/*"); // App 模塊應用上下文 AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext(); appContext.register(AppContextConfig.class); ServletRegistration.Dynamic appServletRegistration = aServletContext.addServlet("appServlet", new DispatcherServlet(appContext)); appServletRegistration.setLoadOnStartup(1); // 該 Servlet 映射至 / 則能夠響應全部請求, 可是更具體的 URL 映射會優先進行處理 appServletRegistration.addMapping("/"); AnnotationConfigWebApplicationContext adminContext = new AnnotationConfigWebApplicationContext(); adminContext.register(AdminContextConfig.class); DispatcherServlet adminDispatcherServlet = new DispatcherServlet(adminContext); // 禁用 OPTIONS 請求 adminDispatcherServlet.setDispatchOptionsRequest(false); ServletRegistration.Dynamic adminServletRegistration = aServletContext.addServlet("adminServlet", adminDispatcherServlet); adminServletRegistration.setLoadOnStartup(2); // URL 映射模式必定不要少末尾的 * adminServletRegistration.addMapping("/admin/*"); } }
爲 IDEA 設置上下文配置以便檢測依賴注入, Project Structure -> Modules -> Spring -> 右側加號 -> 依次添加上下文並設置父上下文
自頂向下進行建立, 首先建立一個控制器 com.seliote.restfuldemo.app.AppController
package com.seliote.restfuldemo.app; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; /** * @author seliote * @date 2019-03-24 * @description App 模塊的 Controller */ @Controller public class AppController { private UserService mUserService; @Autowired public void setUserService(UserService aUserService) { mUserService = aUserService; } }
建立其所用的 Service 接口 com.seliote.restfuldemo.service.UserService
package com.seliote.restfuldemo.service; import java.util.Set; /** * @author seliote * @date 2019-03-24 * @description User 相關的服務 */ public interface UserService { void addUser(String aName); User queryUser(long aId); Set<User> listAllUser(); }
建立用戶 POJO 類, com.seliote.restfuldemo.pojo.User
package com.seliote.restfuldemo.pojo; /** * @author seliote * @date 2019-03-24 * @description 用戶的 POJO 類 */ public class User { private static volatile long sNextId = 0; private long mId; private String mName; private static synchronized long getNextId() { return ++sNextId; } public User(String aName) { mId = getNextId(); mName = aName; } public long getId() { return mId; } public void setId(long aId) { mId = aId; } public String getName() { return mName; } public void setName(String aName) { mName = aName; } }
此處未作約束限制, 好比 id 應大於 0, name 不爲 null 或空字符串且長度小於 16 個字符, 下面添加約束
先增長約束所用的依賴, properties
標籤中先增長 hibernate 依賴版本 <hibernate.validator.version>6.0.16.Final</hibernate.validator.version>
, 添加依賴
<dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>2.0.1.Final</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>${hibernate.validator.version}</version> <scope>runtime</scope> <exclusions> <exclusion> <groupId>org.jboss.logging</groupId> <artifactId>jboss-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator-annotation-processor</artifactId> <version>${hibernate.validator.version}</version> <scope>compile</scope> <optional>true</optional> </dependency>
建立自定義約束, 判斷字符串不爲 null 且不爲空且長度小於 16, com.seliote.restfuldemo.annotation.UserNameValidator
package com.seliote.restfuldemo.annotation; import javax.validation.Constraint; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import javax.validation.Payload; import javax.validation.ReportAsSingleViolation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author seliote * @date 2019-03-24 * @description 用於驗證用戶名不爲 null 且不爲空且長度小於 16 */ @Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.CONSTRUCTOR , ElementType.METHOD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented @Constraint(validatedBy = {UserNameValidatorConstraint.class}) @ReportAsSingleViolation public @interface UserNameValidator { String message() default "{validator.usernamevalidator}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; @Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.CONSTRUCTOR , ElementType.METHOD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented @interface List { UserNameValidator[] value(); } } class UserNameValidatorConstraint implements ConstraintValidator<UserNameValidator, CharSequence> { @Override public void initialize(UserNameValidator constraintAnnotation) { } @Override public boolean isValid(CharSequence aCharSequence, ConstraintValidatorContext aConstraintValidatorContext) { if (aCharSequence == null) { return false; } return aCharSequence.toString().trim().length() > 0 && aCharSequence.toString().trim().length() <= 16; } }
將約束用在 User 類上, 在 mName
字段上添加 @UserNameValidator
約束, 並指定國際化信息, User 類應該以下所示,
package com.seliote.restfuldemo.pojo; import com.seliote.restfuldemo.annotation.UserNameValidator; /** * @author seliote * @date 2019-03-24 * @description 用戶的 POJO 類 */ public class User { private static volatile long sNextId = 0; private long mId; @UserNameValidator(message = "{validation.user.name}") private String mName; private static synchronized long getNextId() { return ++sNextId; } public User(String aName) { mId = getNextId(); mName = aName; } public long getId() { return mId; } public void setId(long aId) { mId = aId; } public String getName() { return mName; } public void setName(String aName) { mName = aName; } @Override public boolean equals(Object aO) { if (!(aO instanceof User)) { return false; } return ((User) aO).getId() == mId; } }
建立約束國際化數據源文件 /WEB-INF/i18n/validation_zh_CN.properties
validation.user.name=用戶名長度需大於 1 且小於 16
接下來配置約束所用的 Bean 依賴, Root 上下文中添加 MessageSource
LocalResolver
LocalValidationFactoryBean
以及 MethodValidationPostProcessor
依賴, 以下
@Bean public MessageSource messageSource() { ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource = new ReloadableResourceBundleMessageSource(); reloadableResourceBundleMessageSource.setCacheSeconds(-1); reloadableResourceBundleMessageSource.setDefaultEncoding(StandardCharsets.UTF_8.name()); reloadableResourceBundleMessageSource.setBasenames("/WEB-INF/i18n/validation"); return reloadableResourceBundleMessageSource; } @Bean public LocalValidatorFactoryBean localValidatorFactoryBean() throws ClassNotFoundException { LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean(); // 若是類路徑上提供了多個實現這步則是必須, 可是對於 Tomcat 來講這步是多餘的 localValidatorFactoryBean.setProviderClass(Class.forName("org.hibernate.validator.HibernateValidator")); localValidatorFactoryBean.setValidationMessageSource(messageSource()); return localValidatorFactoryBean; } @Bean public MethodValidationPostProcessor methodValidationPostProcessor() throws ClassNotFoundException { MethodValidationPostProcessor methodValidationPostProcessor = new MethodValidationPostProcessor(); methodValidationPostProcessor.setValidator(localValidatorFactoryBean()); return methodValidationPostProcessor; } @Bean public LocaleResolver localResolver() { return new AcceptHeaderLocaleResolver(); }
接下來添加 App 上下文使用 Bean 驗證所需的依賴, 須要讓其實現 WebMvcConfigurer
接口並實現 getValidator
方法以更換 Spring MVC 爲 Controller 自動配置的驗證器
package com.seliote.restfuldemo.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.stereotype.Controller; import org.springframework.validation.Validator; import org.springframework.validation.beanvalidation.SpringValidatorAdapter; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * @author seliote * @date 2019-03-24 * @description App 模塊的 Spring 上下文配置 */ @Configuration @EnableWebMvc @ComponentScan( basePackages = "com.seliote.restfuldemo.app", useDefaultFilters = false, includeFilters = @ComponentScan.Filter( type = FilterType.ANNOTATION, classes = Controller.class ) ) public class AppContextConfig implements WebMvcConfigurer { private SpringValidatorAdapter mSpringValidatorAdapter; @Autowired public void setSpringValidatorAdapter(SpringValidatorAdapter aSpringValidatorAdapter) { mSpringValidatorAdapter = aSpringValidatorAdapter; } @Override public Validator getValidator() { return mSpringValidatorAdapter; } }
此時, 驗證器以能夠正常工做, 若是遇到約束不符將拋出異常
返回 UserService
引入 User
類, 並添加約束檢測, 最終的 UserService
類應該以下
package com.seliote.restfuldemo.service; import com.seliote.restfuldemo.annotation.UserNameValidator; import com.seliote.restfuldemo.pojo.User; import org.springframework.validation.annotation.Validated; import javax.validation.Valid; import java.util.Set; /** * @author seliote * @date 2019-03-24 * @description User 相關的服務 */ @Validated public interface UserService { void addUser(@UserNameValidator(message = "{validation.user.name}") String aName); @Valid User queryUser(long aId); @Valid Set<User> listAllUser(); }
返回 AppController, 此時發現 UserService
尚未實現, 添加一個類 com.seliote.restfuldemo.impl.service.UserServiceImpl
package com.seliote.restfuldemo.impl.service; import com.seliote.restfuldemo.pojo.User; import com.seliote.restfuldemo.repository.UserRepository; import com.seliote.restfuldemo.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.validation.Valid; import java.util.Set; /** * @author seliote * @date 2019-03-24 * @description UserService 的實現 */ @Service public class UserServiceImpl implements UserService { private UserRepository mUserRepository; @Autowired public void setUserRepo(UserRepository aUserRepository) { mUserRepository = aUserRepository; } @Override public void addUser(String aName) { mUserRepository.addUser(new User(aName)); } @Override public User queryUser(long aId) { return mUserRepository.queryUser(aId); } @Override public Set<User> listAllUser() { return mUserRepository.listAllUser(); } }
接着定義用戶倉庫 com.seliote.restfuldemo.repository.UserRepository
package com.seliote.restfuldemo.repository; import com.seliote.restfuldemo.pojo.User; import java.util.Set; /** * @author seliote * @date 2019-03-24 * @description 用戶的倉庫 */ // 參數校驗是服務的事情 public interface UserRepository { void addUser(User aUser); User queryUser(long aId); Set<User> listAllUser(); }
倉庫實現 com.seliote.restfuldemo.impl.repository.UserRepositoryImpl
package com.seliote.restfuldemo.impl.repository; import com.seliote.restfuldemo.pojo.User; import com.seliote.restfuldemo.repository.UserRepository; import org.springframework.lang.Nullable; import org.springframework.stereotype.Repository; import javax.validation.constraints.NotNull; import java.util.Collections; import java.util.HashSet; import java.util.Set; /** * @author seliote * @date 2019-03-24 * @description 用戶倉庫實現 */ @Repository public class UserRepositoryImpl implements UserRepository { private Set<User> mUserSet = new HashSet<>(); @Override public synchronized void addUser(User aUser) { mUserSet.add(aUser); } @Override @NotNull public synchronized User queryUser(long aId) { User user = null; for (User eachUser : mUserSet) { if (aId == eachUser.getId()) { user = eachUser; break; } } if (user == null) { throw new NoSuchUserExceprion("用戶 " + aId + " 不存在"); } return user; } @Override public synchronized Set<User> listAllUser() { return Collections.unmodifiableSet(mUserSet); } }
建立自定義異常,
接下來建立一個能夠響應請求的 Controller 方法
@RequestMapping(value = "add", method = RequestMethod.GET) @ResponseBody @ResponseStatus(HttpStatus.OK) public ApiResult addUser(@RequestParam("name") String aName) { mUserService.addUser(aName); return new ApiResult(200, "建立用戶成功"); }
建立 ApiResult
實體 com.seliote.restfuldemo.pojo.ApiResult
package com.seliote.restfuldemo.pojo; /** * @author seliote * @date 2019-03-25 * @description Api 返回實體 */ public class ApiResult { private int mCode; private String mMsg; public ApiResult(int aCode, String aMsg) { mCode = aCode; mMsg = aMsg; } public int getCode() { return mCode; } public void setCode(int aCode) { mCode = aCode; } public String getMsg() { return mMsg; } public void setMsg(String aMsg) { mMsg = aMsg; } }
接下來配置 App 模塊的內容類型協商以及消息轉換器, 須要注入一個 ObjectMapper
的 Bean, 其餘均爲實現接口 WebMvcConfigurer
的方法
private ObjectMapper mObjectMapper; @Autowired public void setObjectMapper(ObjectMapper aObjectMapper) { mObjectMapper = aObjectMapper; } @Override public void configureContentNegotiation(ContentNegotiationConfigurer aContentNegotiationConfigurer) { // 容許擴展名與 Accept 頭 aContentNegotiationConfigurer.favorPathExtension(true) .favorParameter(false) .ignoreAcceptHeader(false) .defaultContentType(MediaType.APPLICATION_JSON); } @Override public void configureMessageConverters(List<HttpMessageConverter<?>> aHttpMessageConverters) { // 只容許 JSON MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(); mappingJackson2HttpMessageConverter.setSupportedMediaTypes(Arrays.asList( new MediaType("application", "json"), new MediaType("text", "json") )); mappingJackson2HttpMessageConverter.setObjectMapper(mObjectMapper); aHttpMessageConverters.add(mappingJackson2HttpMessageConverter); }
下來添加 Lg4j 依賴, 由於排除了 JBOSS, 若是不添加如下依賴則會一直 404
<!-- log4j 接口 --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>${log4j.version}</version> <scope>compile</scope> </dependency> <!-- log4j 具體實現 --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>${log4j.version}</version> <scope>runtime</scope> </dependency> <!-- 使 slf4j 使用 log4j 的橋接口 --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>${log4j.version}</version> <scope>runtime</scope> </dependency> <!-- commons logging 使用 log4j 的橋接口 --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-jcl</artifactId> <version>${log4j.version}</version> <scope>runtime</scope> </dependency> <!-- Hibernate Validator 默認使用不支持 Log4j2 的 JBoss 版本用於記錄日誌 --> <!-- 咱們須要明確提供一個支持 Log4j 的版本並排除舊版依賴 --> <dependency> <groupId>org.jboss.logging</groupId> <artifactId>jboss-logging</artifactId> <version>3.3.2.Final</version> <scope>runtime</scope> </dependency>
建立 Log4j 2 的配置文件, /resources/log4j2.xml
<?xml version="1.0" encoding="UTF-8" ?> <configuration status="info"> <appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </Console> <RollingFile name="FileAppender" fileName="/home/seliote/Temp/application.log" filePattern="../logs/application-%d{MM-dd-yyyy}-%i.log"> <PatternLayout> <pattern>%d{HH:mm:ss.SSS} [%t] %X{%id} %X{username} %-5level %c{36} %l: %msg%n</pattern> </PatternLayout> <Policies> <SizeBasedTriggeringPolicy size="10 MB"/> </Policies> <DefaultRolloverStrategy min="1" max="4"/> </RollingFile> </appenders> <loggers> <root level="info"> <appender-ref ref="Console"/> </root> <logger name="com.seliote" level="info" additivity="false"> <appender-ref ref="Console" /> <appender-ref ref="FileAppender"> <MarkerFilter marker="file" onMatch="NEUTRAL" onMismatch="DENY"/> </appender-ref> </logger> <logger name="org.apache" level="info"/> <logger name="org.springframework" level="info"/> </loggers> </configuration>
再把依賴添加至生成的 WAR 文件的 lib 目錄裏, Project Structure -> Artifacts -> RESTfulDemo:Web Exploded -> 而後選中 Avaliable Elements 中的全部 JAR 後 put into /WEB-INF/lib.
打開瀏覽器訪問 http://localhost:8080/add?name=OK, 應該是正常的 json 返回數據, 接着訪問 http://localhost:8080/add?name=, 此時約束不經過, 代碼拋出異常 ConstraintViolationException 且未處理, 用戶將收到 500 返回碼, 下面建立一個處理程序中可能拋出異常的處理器 com.seliote.restfuldemo.config.ExceptionHandler
package com.seliote.restfuldemo.config; import com.seliote.restfuldemo.exception.NoSuchUserException; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import javax.validation.ConstraintViolationException; /** * @author seliote * @date 2019-03-25 * @description 異常處理 */ @ControllerAdvice public class ExceptionHandler { @org.springframework.web.bind.annotation.ExceptionHandler(ConstraintViolationException.class) @ResponseBody // 該狀態碼將覆蓋 Controller 方法返回的狀態碼 @ResponseStatus(HttpStatus.BAD_REQUEST) public Error ConstraintViolationExceptionHandler(ConstraintViolationException aExp) { return new Error(aExp.getMessage()); } @org.springframework.web.bind.annotation.ExceptionHandler(NoSuchUserException.class) @ResponseBody @ResponseStatus(HttpStatus.BAD_REQUEST) public Error NoSuchUserException(NoSuchUserException aExp) { return new Error(aExp.getMessage()); } } class Error { String errMsg; public Error(String aErrMsg) { errMsg = aErrMsg; } public String getErrMsg() { return errMsg; } public void setErrMsg(String aErrMsg) { errMsg = aErrMsg; } }
再在 AppController 添加一個查詢方法
@RequestMapping(value = "query/{userId:\\d+}", method = RequestMethod.GET) @ResponseBody @ResponseStatus(HttpStatus.OK) public User query(@PathVariable("userId") long aId) { return mUserService.queryUser(aId); }
App 模塊至此完成, 嘗試訪問
添加控制器 com.seliote.restfuldemo.admin.AdminController
package com.seliote.restfuldemo.admin; import com.seliote.restfuldemo.pojo.User; import com.seliote.restfuldemo.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import java.util.Set; /** * @author seliote * @date 2019-03-25 * @description Admin 模塊的控制器 */ @Controller public class AdminController { private UserService mUserService; @Autowired public void setUserService(UserService aUserService) { mUserService = aUserService; } @RequestMapping(value = "listAllUser", method = RequestMethod.GET) @ResponseBody @ResponseStatus(HttpStatus.OK) public Set<User> listAllUser() { return mUserService.listAllUser(); } }
Admin 模塊與 App 模塊配置相似 com.seliote.restfuldemo.config.AdminContextConfig
package com.seliote.restfuldemo.config; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.stereotype.Controller; import org.springframework.validation.Validator; import org.springframework.validation.beanvalidation.SpringValidatorAdapter; import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.Arrays; import java.util.List; /** * @author seliote * @date 2019-03-24 * @description Admin 上下文配置類 */ @Configuration @EnableWebMvc @ComponentScan( basePackages = "com.seliote.restfuldemo.admin", useDefaultFilters = false, includeFilters = @ComponentScan.Filter( type = FilterType.ANNOTATION, classes = Controller.class ) ) public class AdminContextConfig implements WebMvcConfigurer { private SpringValidatorAdapter mSpringValidatorAdapter; private ObjectMapper mObjectMapper; @Autowired public void setObjectMapper(ObjectMapper aObjectMapper) { mObjectMapper = aObjectMapper; } @Autowired public void setSpringValidatorAdapter(SpringValidatorAdapter aSpringValidatorAdapter) { mSpringValidatorAdapter = aSpringValidatorAdapter; } @Override public void configureContentNegotiation(ContentNegotiationConfigurer aContentNegotiationConfigurer) { // 容許擴展名與 Accept 頭 aContentNegotiationConfigurer.favorPathExtension(true) .favorParameter(false) .ignoreAcceptHeader(false) .defaultContentType(MediaType.APPLICATION_JSON) .mediaType("json", MediaType.APPLICATION_JSON); } @SuppressWarnings("Duplicates") @Override public void configureMessageConverters(List<HttpMessageConverter<?>> aHttpMessageConverters) { // 只容許 JSON MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(); mappingJackson2HttpMessageConverter.setSupportedMediaTypes(Arrays.asList( new MediaType("application", "json"), new MediaType("text", "json") )); mappingJackson2HttpMessageConverter.setObjectMapper(mObjectMapper); aHttpMessageConverters.add(mappingJackson2HttpMessageConverter); } @Override public Validator getValidator() { return mSpringValidatorAdapter; } }
訪問 http://localhost:8080/admin/listAllUser 測試