Upgrade to Pro — share decks privately, control downloads, hide ads and more …

JHipsterで学ぶ!Springによるサーバサイド開発手法

 JHipsterで学ぶ!Springによるサーバサイド開発手法

JHipsterで学ぶ!Springによるサーバサイド開発手法
JJUG CCC 2017 Spring
#ccc_f1

Shinichi Kozake

May 19, 2017
Tweet

More Decks by Shinichi Kozake

Other Decks in Technology

Transcript

  1.  +)JQTUFSͰ͸.POPMJUIJDͳΞϓϦέʔγϣϯ ΋͘͠͸ɺϚΠΫϩαʔϏεΞϓϦέʔγϣϯΛબ୒Ͱ͖·͢ɻ ██╗ ██╗ ██╗ ████████╗ ███████╗ ██████╗ ████████╗

    ████████╗ ███████╗ ██║ ██║ ██║ ╚══██╔══╝ ██╔═══██╗ ██╔════╝ ╚══██╔══╝ ██╔═════╝ ██╔═══██╗ ██║ ████████║ ██║ ███████╔╝ ╚█████╗ ██║ ██████╗ ███████╔╝ ██╗ ██║ ██╔═══██║ ██║ ██╔════╝ ╚═══██╗ ██║ ██╔═══╝ ██╔══██║ ╚██████╔╝ ██║ ██║ ████████╗ ██║ ██████╔╝ ██║ ████████╗ ██║ ╚██╗ ╚═════╝ ╚═╝ ╚═╝ ╚═══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══════╝ ╚═╝ ╚═╝ https://jhipster.github.io Welcome to the JHipster Generator v4.4.1 Documentation for creating an application: https://jhipster.github.io/creating-an-app/ Application files will be generated in folder: /Users/s_kozake/develop/jhipsterApp/hogeHipster ? (1/16) Which *type* of application would you like to create? (Use arrow keys) ❯ Monolithic application (recommended for simple projects) Microservice application Microservice gateway [BETA] JHipster UAA server (for microservice OAuth2 authentication)
  2.  ΞϓϦέʔγϣϯߏ੒ 9GD 鱙鯮鱷鱝 鱭鲅鱈鲗 鲎鲁鱜鱬鲎 *QIG 4GUQWTEG *QIG 5GTXKEG

    *QIG&61 *QIG 4GRQUKVQT[ *QIG 鱟鱑鲉鲎鱪鱇崎⹻#12 .QIIKPI #URGEV 5GEWTKV[ %QPHKIWTCVKQP &QOCKP7UGT &GVCKN5GTXKEG
  3.  [packageName] │ ├── aop - AOP関連 ├── config -

    JavaConfig関連 ├── domain - ドメイン層のBean定義 ├── repository - リポジトリ層のBean定義 ├── security - セキュリティ関連 ├── service - サービス層のBean定義 └── web - プレゼンテーション層 └── rest - Spring MVC RESTコントローラの定義 ύοέʔδߏ੒ ύοέʔδߏ੒΋ΞϓϦέʔγϣϯߏ੒ʹԊͬͨ΋ͷͱͳ͍ͬͯΔͨΊɺ Ͳ͜ʹԿ͕͋Δͷ͔͕େมΘ͔Γ΍͍͢Ͱ͢ɻ
  4.  dependencies {
 :
 compile "org.springframework.boot:spring-boot-starter-logging" :
 } CVJMEHSBEMF 4QSJOH#PPUʹ͸$PNNPOT-PHHJOH"1*Λআ͍ͯඞਢͷϩΪϯάґଘؔ܎͸͋Γ·ͤΜɻ

    ྫ͑͹-PHCBDLΛ࢖༻͢Δ৔߹ɺຊདྷ͸༷ʑͳઃఆΛ௥Ճ͢Δඞཁ͕͋Γ·͢ɻ ʮTQSJOHCPPUTUBSUFSMPHHJOHʯΛґଘʹ૊ΈࠐΉ͜ͱͰɺΫϥεύεʹج͍ͮͯ ϩΪϯάΛઃఆΛͯ͘͠Ε·͢ɻ -PHCBDL͕༗ޮͳΒ͹ͦΕ͕࠷ॳʹ࠾༻͞Ε·͢ɻ +)JQTUFSͰ΋ɺʮTQSJOHCPPUTUBSUFSMPHHJOHʯΛґଘʹ૊ΈࠐΜͰ͍·͢ɻ ϩάग़ྗ
  5.  ϩάग़ྗ ΫϥεύεͷϧʔτʹMPHCBDLTQSJOHYNM͕͋Ε͹ɺͦͷઃఆ͕࠾༻͞Ε·͢ɻ 4QSJOH#PPU͸JODMVEFՄೳͳσϑΥϧτઃఆΛఏڙ͍ͯ͠·͢ɻ ͜ͷઃఆΛมߋ͢Δ͜ͱͰɺϩάϨϕϧͳͲͷมߋ΍৽ͨͳ௥Ճ͕ՄೳͰ͢ɻ <?xml version="1.0" encoding="UTF-8"?>
 
 <configuration

    scan="true">
 <include resource="org/springframework/boot/logging/logback/base.xml"/>
 :
 <logger name="com.mycompany.myapp" level="#logback.loglevel#"/>
 <logger name="io.github.jhipster" level="DEBUG"/>
 <logger name="javax.activation" level="WARN"/>
 :
 <root level="#logback.loglevel#">
 <appender-ref ref="CONSOLE"/>
 </root>
 </configuration>
 SFTPVSDFTMPHCBDLTQSJOHYNM σϑΥϧτઃఆ
  6.  ϩάग़ྗ @Pointcut("within(jjug_ccc.repository..*) "
 + "|| within(jjug_ccc.service..*) "
 + "||

    within(jjug_ccc.web.rest..*)")
 public void loggingPointcut() { }
 @AfterThrowing(pointcut = "loggingPointcut()", throwing = "e")
 public void logAfterThrowing( JoinPoint joinPoint, Throwable e) {
 :
 }
 @Around("loggingPointcut()")
 public Object logAround(
 ProceedingJoinPoint joinPoint) throws Throwable {
 :
 }
 BPQMPHHJOH-PHHJOH"TQFDU +)JQTUFSͰ͸ϩάग़ྗʹ"01Λ׆༻͍ͯ͠·͢ɻ MPHHJOH1PJOUDVUͰϩάग़ྗ͢ΔൣғΛࢦఆͯ͠ɺϝιουͷ։࢝ɾऴྃ΍ྫ֎ൃੜ࣌ ͷϩάग़ྗ͕Ͱ͖ΔΑ͏ʹͳ͍ͬͯ·͢ɻ ϩάग़ྗ ൣғͷࢦఆ ϝιουͷ ։࢝ɾऴྃ ྫ֎ൃੜ࣌
  7.  ϩάग़ྗ @Configuration
 @EnableAspectJAutoProxy
 public class LoggingAspectConfiguration {
 
 @Bean


    @Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)
 public LoggingAspect loggingAspect(Environment env) {
 return new LoggingAspect(env);
 }
 }
 DPOpH-PHHJOH"TQFDU$POpHVSBUJPO "01Λར༻ͯ͠ग़ྗ͢Δϩά͸ɺ։ൃϞʔυ͚ͩͰ༗ޮͱͳΔઃఆʹͳ͍ͬͯ·͢ɻ 1SPpMFΞϊʔςʔγϣϯΛ༻͍Δ͜ͱͰɺಛఆͷ؀ڥԼͰͷΈ#FBOఆٛΛ༗ޮʹ͢Δ͜ͱ͕ ՄೳͱͳΓ·͢ɻ ։ൃ࣌ͷΈ༗ޮ
  8.  ϩάग़ྗ @GetMapping("/logs")
 @Timed
 public List<LoggerVM> getList() {
 LoggerContext context

    = (LoggerContext) LoggerFactory.getILoggerFactory();
 return context.getLoggerList()
 .stream()
 .map(LoggerVM::new)
 .collect(Collectors.toList());
 }
 XFCSFTU-PHT3FTPVSDF ·ͨɺϩάઃఆΛฦ͢8FC"1*͕+)JQTUFSͰ࠷ॳ͔Β༻ҙ͞Ε͍ͯ·͢ɻ ্ͷίʔυ͸ɺϩάઃఆΛฦ͢8FCϋϯυϥʔͰ͢ɻ
  9.  ϩάग़ྗ @PutMapping("/logs")
 @ResponseStatus(HttpStatus.NO_CONTENT)
 @Timed
 public void changeLevel(@RequestBody LoggerVM jsonLogger)

    {
 LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
 context.getLogger(jsonLogger.getName()) .setLevel(Level.valueOf(jsonLogger.getLevel()));
 }
 XFCSFTU-PHT3FTPVSDF ͞Βʹ͸ɺϩάϨϕϧΛมߋ͢Δ8FCϋϯυϥʔ΋༻ҙ͍ͯ͠·͢ʂ
  10.  ϩάग़ྗ public LoggingConfiguration(…) {
 :
 if (jHipsterProperties.getLogging() .getLogstash().isEnabled()) {


    addLogstashAppender(context);
 :
 }
 }
 DPOpH-PHHJOH$POpHVSBUJPO +)JQTUFSͰ͸ɺMPHTUBTIͱ࿈ܞ͢Δίʔυ΋༻ҙ͞Ε͍ͯ·͢ɻ ઃఆΛ௥Ճ͢Δ͜ͱͰɺϩά಺༰ΛඇಉظʹMPHTUBTI΁࿈ܞͰ͖·͢ɻ
  11.  ೝূ )5514FTTJPO"VUIFOUJDBUJPO TUBUFGVM EFGBVMU4QSJOH4FDVSJUZNFDIBOJTN 0"VUI"VUIFOUJDBUJPO TUBUFMFTT XJUIBO0"VUITFSWFSJNQMFNFOUBUJPO +85BVUIFOUJDBUJPO TUBUFMFTT

    XJUIBUPLFO +)JQTUFSͰ͸ɺΞϓϦέʔγϣϯ࡞੒࣌ʹ ैདྷͷ)551ηογϣϯΛ༻͍ͨೝূํࣜͷ΄͔ɺ 0"VUI΍+85Λ༻͍ͨೝূํࣜΛબ୒͢Δ͜ͱ͕ग़དྷ·͢ɻ
  12.  ೝূ 0"VUI"VUIFOUJDBUJPO $MJFOU +)JQTUFS"QQ ೝূαʔό Ϧιʔεαʔό 3FTPVSDF0XOFS 1BTTXPSE$SFEFOUJBMT "DDFTT5PLFO

    8FCΞϓϦέʔγϣϯ͕ɺೝূαʔό݉ϦιʔεαʔόʹͳΔΠϝʔδɻ ೝূ͸ʮ3FTPVSDF0XOFS1BTTXPSE$SFEFOUJBMTάϥϯτछผʯͰ࣮ࢪɻ
  13.  @PostMapping("/authenticate")
 public ResponseEntity authorize(...) {
 : boolean rememberMe =

    (loginVM.isRememberMe() == null) ? false : loginVM.isRememberMe();
 String jwt = tokenProvider.createToken( authentication, rememberMe);
 response.addHeader(JWTConfigurer.AUTHORIZATION_HEADER, "Bearer " + jwt);
 return ResponseEntity.ok(new JWTToken(jwt));
 :
 TFDVSJUZKXU+85'JMUFS ೝূ ্ͷίʔυ͸+85Λ༻͍ͨೝূ෦෼Ͱ͢ɻ ೝূ0,ͷ৔߹ɺ+85τʔΫϯΛ࡞੒ͯ͠ɺ)551Ϩεϙϯεͱͯ͠ฦ٫͍ͯ͠·͢ɻ +85ੜ੒ )551Ϩεϙϯεͱͯ͠ฦ٫
  14.  @Override
 public void doFilter(…) throws … {
 : String

    jwt = resolveToken(httpServletRequest);
 if (StringUtils.hasText(jwt) && this.tokenProvider.validateToken(jwt)) {
 Authentication authentication = this.tokenProvider.getAuthentication(jwt);
 SecurityContextHolder.getContext() .setAuthentication(authentication);
 }
 filterChain.doFilter(servletRequest, servletResponse);
 : TFDVSJUZKXU+85'JMUFS ೝূ ্ͷίʔυ͸+85'JMUFSͷ࣮૷Ͱ͢ɻ )551ϦΫΤετϔομͷτʔΫϯΛݕূͯ͠ɺೝূ͢ΔγϯϓϧͳίʔυͰ͢ɻ ϦΫΤετ͔Β KXUऔಘ KXU͕༗ޮͳΒ KXU͔Β ೝূ࡞੒
  15.  @Override
 protected void configure(HttpSecurity http) throws Exception {
 http


    .csrf()
 .csrfTokenRepository( CookieCsrfTokenRepository.withHttpOnlyFalse()) :
 } DPOpH4FDVSJUZ$POpHVSBUJPO ηΩϡϦςΟ $43'ରࡦͷઃఆͰ͢ɻ +)JQTUFSͰ͸ɺϑϩϯτΤϯυͷ"OHVMBSʹదͨ͠$43'ઃఆ͕͞Ε͍ͯ·͢ɻ 0"VIU΍+85Λೝূํࣜʹબ୒ͨ͠৔߹ɺ$43'ରࡦ͸ແޮͱͳΓ·͢ɻ
  16.  @Override
 protected void configure(HttpSecurity http) throws Exception {
 http


    :
 .and()
 .addFilterBefore( corsFilter, UsernamePasswordAuthenticationFilter.class)
 :
 } DPOpH4FDVSJUZ$POpHVSBUJPO ηΩϡϦςΟ $034 $SPTT0SJHJO3FTPVSDF4IBSJOH ͷઃఆ΋ͯ͘͠Ε͍ͯ·͢ʂ 6TFSOBNF1BTTXPSE"VUIFOUJDBUJPO'JMUFSͷલʹDPST'JMUFSΛ௥Ճ͍ͯ͠·͢ɻ
  17.  @Bean
 public CorsFilter corsFilter() {
 UrlBasedCorsConfigurationSource source = new

    UrlBasedCorsConfigurationSource();
 CorsConfiguration config = jHipsterProperties.getCors();
 if (config.getAllowedOrigins() != null && !config.getAllowedOrigins().isEmpty()) {
 source.registerCorsConfiguration("/api/**", config);
 source.registerCorsConfiguration("/v2/api-docs", config);
 }
 return new CorsFilter(source);
 }
 DPOpH8FC$POpHVSBUJPO ηΩϡϦςΟ ϓϩύςΟʹ$034ͷઃఆ͕͋Δ৔߹ɺ$034ઃఆ͕༗ޮʹͳΓ·͢ɻ ͳ͓ɺσϑΥϧτͰ͸։ൃϞʔυͷΈɻ͢΂ͯͷ0SJHJO͕ڐՄͰઃఆ͞Ε͍ͯ·͢ɻ $034ઃఆ͕ ༗ޮͳ৔߹
  18.  @Override
 protected void configure(HttpSecurity http) throws Exception {
 http


    :
 .and()
 :
 .exceptionHandling()
 .authenticationEntryPoint( http401UnauthorizedEntryPoint() )
 :
 } DPOpH4FDVSJUZ$POpHVSBUJPO ηΩϡϦςΟ ೝূΤϥʔ͕ൃੜͨ͠৔߹ʹͷεςʔλείʔυΛฦ͢ઃఆΛ͍ͯ͠·͢ɻ ͜ͷઃఆΛ͠ͳ͍ͱɺ-PHJO6SM"VUIFOUJDBUJPO&OUSZ1PJOU͕༗ޮʹͳͬͯ ೝূΤϥʔ࣌ʹϩάΠϯϖʔδ͕දࣔ͞Ε·͢ɻ Λฦ͢ઃఆ
  19.  @Override
 public void configure(WebSecurity web) throws Exception {
 web.ignoring()


    .antMatchers(HttpMethod.OPTIONS, "/**")
 .antMatchers("/app/**/*.{js,html}")
 .antMatchers("/bower_components/**")
 .antMatchers("/i18n/**")
 .antMatchers("/content/**")
 .antMatchers("/swagger-ui/index.html")
 .antMatchers("/test/**")
 .antMatchers("/h2-console/**");
 }
 DPOpH4FDVSJUZ$POpHVSBUJPO ηΩϡϦςΟ 4QSJOH4FDVSJUZͷର৅֎ͱ͢Δ63-΋ઃఆ͞Ε͍ͯΔͷͰศརͰ͢ɻ ৽͘͠ର৅֎ͱ͢Δ63-Λ௥Ճ͢Δ৔߹ɺ͜͜ͷઃఆʹ௥Ճ͢Ε͹େৎ෉Ͱ͢ɻ
  20.  public interface AuditEventRepository {
 void add(AuditEvent event);
 List<AuditEvent> find(Date

    after);
 List<AuditEvent> find(String principal, Date after);
 List<AuditEvent> find(String principal, Date after, String type);
 
 }
 PSHTQSJOHGSBNFXPSLCPPUBDUVBUFBVEJU"VEJU&WFOU3FQPTJUPSZ ؂ࠪূ੻ 4QSJOH#PPU"DUVBUPSʹ༻ҙ͞Εͨ"VEJU&WFOU3FQPTJUPSZΠϯλϑΣʔεΛ࣮૷͢Δ͜ͱͰɺ ؂ࠪূ੻ͷΠϕϯτΛऔಘͰ͖·͢ɻ
  21.  /**
 * An implementation of Spring Boot's AuditEventRepository.
 */


    @Repository
 public class CustomAuditEventRepository implements AuditEventRepository {
 
 private final PersistenceAuditEventRepository persistenceAuditEventRepository;
 
 private final AuditEventConverter auditEventConverter;
 :
 } PSHTQSJOHGSBNFXPSLCPPUBDUVBUFBVEJU"VEJU&WFOU3FQPTJUPSZ ؂ࠪূ੻ +)JQTUFSͰ͸ɺ"VEJU&WFOU3FQPTJUPSZΠϯλϑΣʔεΛ࣮૷ͨ͠ɺ $VTUPN"VEJU&WFOU3FQPTJUPSZ͕༻ҙ͞Ε͍ͯ·͢ɻ
  22.  @RestController
 @RequestMapping("/management/audits")
 public class AuditResource {
 
 @GetMapping
 public

    ResponseEntity<List<AuditEvent>> getAll( @ApiParam Pageable pageable) {
 Page<AuditEvent> page = auditEventService.findAll(pageable);
 HttpHeaders headers = PaginationUtil .generatePaginationHttpHeaders( page, "/management/audits");
 return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
 }
 }
 XFCSFTU"VEJU3FTPVSDF 3&45ϖʔδϯά +)JQTUFSͰ͸ɺϖʔδϯάͷ৘ใ͸)551ϔομʹઃఆͯ͠ฦ͍ͯ͠·͢ɻ
  23.  @ControllerAdvice
 public class ExceptionTranslator {
 
 @ExceptionHandler(ConcurrencyFailureException.class)
 @ResponseStatus(HttpStatus.CONFLICT)
 @ResponseBody


    public ErrorVM processConcurrencyError( ConcurrencyFailureException ex) {
 return new ErrorVM( ErrorConstants.ERR_CONCURRENCY_FAILURE);
 }
 :
 } SFTUFSSPST&YDFQUJPO5SBOTMBUPS 3&45Τϥʔॲཧ &YDFQUJPO5SBOTMBUPSΫϥεͷͳ͔ͰҰ௨ΓͷΤϥʔॲཧ͕ఆٛ͞Ε͍ͯ·͢ͷͰɺ ৽ͨʹΞϓϦέʔγϣϯಠࣗͷΤϥʔϋϯυϦϯάΛ௥Ճ͍ͨ͠৔߹ɺ ͜͜ʹ௥Ճ͢Ε͹͍͍͚ͩͰ͢ɻͱͯ΋ศརͰ͢ɻ ഉଞϩοΫʹ ࣦഊͨ͠৔߹  $POqJDU  Λฦ͢ɻ
  24.  :
 dependencies {
 compile "org.springframework.boot:spring-boot-devtools"
 }
 :
 QSPpMF@EFWHSBEMF ޮ཰ྑ͍։ൃ

    4QSJOH#PPU%FWUPPMTΛ༻͍Δ͜ͱʹΑΓɺΞϓϦέʔγϣϯΛlIPUSFTUBSUzͰ͖·͢ɻ ϓϩδΣΫτͷΫϥε͕ίϯύΠϧ͞ΕΔͱɺมߋΛݕ஌ͯ͠ΞϓϦΛ࠶ىಈ͠·͢ɻ +)JQTUFSͰ͸ɺ։ൃϞʔυͰىಈ͚ͨ࣌ͩ͜͠ͷػೳ͕༗ޮʹͳΔΑ͏ʹઃఆ͞Ε͍ͯ·͢ɻ :
 if (project.hasProperty('prod')) {
 apply from: 'gradle/profile_prod.gradle'
 } else {
 apply from: 'gradle/profile_dev.gradle'
 }
 : CVJMEHSBEMF
  25.  ӡ༻ dependencies {
 :
 compile "org.springframework.boot:spring-boot-actuator"
 :
 } CVJMEHSBEMF

    4QSJOH#PPU"DUVBUPS͸ɺΞϓϦέʔγϣϯӡ༻Λָʹͯ͘͠ΕΔػೳΛఏڙͯ͘͠Ε·͢ɻ ɾ)551ΤϯυϙΠϯτͷ௥Ճ ɾϔϧενΣοΫ ɾϝτϦΫε ɾ؂ࠪূ੻
  26.  ӡ༻ ΤϯυϙΠϯτ આ໌ (&5BVUPDPOpH "VUP$POpHVSFͰ༗ޮʹͳ͍ͬͯΔ΋ͷɺແޮʹͳ͍ͬͯΔ΋ͷΛදࣔ͢Δ (&5CFBOT %*ίϯςφʹ؅ཧ͞Ε͍ͯΔ#FBOͷҰཡΛදࣔ͢Δ (&5FWO ؀ڥม਺ɺγεςϜϓϩύςΟͷҰཡΛදࣔ͢Δ

    (&5DPOpHQSPQT !$POpHVSBUJPO1SPQFSUJFTͷ෇͍ͨϓϩύςΟ஋ͷҰཡΛදࣔ͢Δ (&5EVNQ εϨουμϯϓΛදࣔ͢Δ (&5IFBMUI ϔϧενΣοΫͷ݁ՌΛදࣔ͢Δ (&5JOGP JOGP͔Β࢝·ΔϓϩύςΟ஋ͷҰཡͳͲͷΞϓϦέʔγϣϯ৘ใΛදࣔ͢Δ (&5MPHpMF ϩάϑΝΠϧΛදࣔ͢Δ (&5NFUSJDT ϝτϦΫεΛදࣔ͢Δ (&5TIVUEPXO ΞϓϦέʔγϣϯΛఀࢭ͢Δ (&5USBDF )551ϦΫΤετͷϩάΛදࣔ͢Δ (&5qZXBZɺ (&5MJRVJCBTF 'MBXBZ·ͨ͸-JRVJCBTFͷσʔλϕʔεϚΠάϨʔγϣϯ৘ใΛදࣔ͢Δ (Spring徹底入門 p.624より) 4QSJOH#PPU"DUVBUPSʹΑΓ্هͷΑ͏ͳ)551ΤϯυϙΠϯτ͕௥Ճ͞Ε·͢ɻ +)JQTUFSͰ͸ɺ͜ΕΒͷػೳΛ׆༻ͨ͠؅ཧػೳ͕ॆ࣮͍ͯ͠·͢ɻ