рдирдорд╕реНрддреЗ! рдореЗрд░рд╛ рдирд╛рдо рдПрдВрдбреНрд░реА рдиреЗрд╡реЗрджреЛрдореНрд╕реНрдХреА рд╣реИ рдФрд░ рдореИрдВ SberTekh рдореЗрдВ рдореБрдЦреНрдп рдЕрднрд┐рдпрдВрддрд╛ рд╣реВрдВред рдореИрдВ рдПрдХ рдЯреАрдо рдореЗрдВ рдХрд╛рдо рдХрд░рддрд╛ рд╣реВрдВ рдЬреЛ рдИрдПрд╕рдПрдл (рдпреВрдирд┐рдлрд╛рдЗрдб рдлреНрд░рдВрдЯрд▓ рд╕рд┐рд╕реНрдЯрдо) рдХреА рд╕рд┐рд╕реНрдЯрдо рд╕реЗрд╡рд╛рдУрдВ рдореЗрдВ рд╕реЗ рдПрдХ рд╡рд┐рдХрд╕рд┐рдд рдХрд░рддрд╛ рд╣реИред рдЕрдкрдиреЗ рдХрд╛рдо рдореЗрдВ, рд╣рдо рд╕рдХреНрд░рд┐рдп рд░реВрдк рд╕реЗ рд╕реНрдкреНрд░рд┐рдВрдЧ рдлреНрд░реЗрдорд╡рд░реНрдХ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ, рд╡рд┐рд╢реЗрд╖ рд░реВрдк рд╕реЗ рдЗрд╕рдХреЗ DI рдореЗрдВ, рдФрд░ рд╕рдордп-рд╕рдордп рдкрд░ рд╣рдореЗрдВ рдЗрд╕ рддрдереНрдп рд╕реЗ рд╕рд╛рдордирд╛ рдХрд░рдирд╛ рдкрдбрд╝рддрд╛ рд╣реИ рдХрд┐ рд╡рд╕рдВрдд рдореЗрдВ рдирд┐рд░реНрднрд░рддрд╛ рдХрд╛ рд╕рдорд╛рдзрд╛рди рдХрд░рдирд╛ рд╣рдорд╛рд░реЗ рд▓рд┐рдП рдкрд░реНрдпрд╛рдкреНрдд рд╕реНрдорд╛рд░реНрдЯ рдирд╣реАрдВ рд╣реИред рдпрд╣ рд▓реЗрдЦ рдЗрд╕реЗ рдЕрдзрд┐рдХ рд╕реНрдорд╛рд░реНрдЯ рдмрдирд╛рдиреЗ рдХреЗ рдореЗрд░реЗ рдкреНрд░рдпрд╛рд╕реЛрдВ рдХрд╛ рдкрд░рд┐рдгрд╛рдо рд╣реИ рдФрд░ рдЖрдо рддреМрд░ рдкрд░ рдпрд╣ рд╕рдордЭ рдореЗрдВ рдЖрддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИред рдореБрдЭреЗ рдЙрдореНрдореАрдж рд╣реИ рдХрд┐ рдЖрдк рд╡рд╕рдВрдд рдХреЗ рдЙрдкрдХрд░рдг рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЗрд╕рд╕реЗ рдХреБрдЫ рдирдпрд╛ рд╕реАрдЦ рд╕рдХрддреЗ рд╣реИрдВред

рд▓реЗрдЦ рдкрдврд╝рдиреЗ рд╕реЗ рдкрд╣рд▓реЗ, рдореИрдВ рджреГрдврд╝рддрд╛ рд╕реЗ рдЕрдиреБрд╢рдВрд╕рд╛ рдХрд░рддрд╛ рд╣реВрдВ рдХрд┐ рдЖрдк
рдмреЛрд░рд┐рд╕ рдпреЗрд╡рдЧреЗрдиреА
рдПрд╡рдЧреЗрд░реАрдмреЛрд░рд┐рд╕реЛрд╡ рдХреА рд░рд┐рдкреЛрд░реНрдЯ рдкрдврд╝реЗрдВ:
рд╕реНрдкреНрд░рд┐рдВрдЧ рд░рд┐рдкрд░, рднрд╛рдЧ 1 ;
рд╕реНрдкреНрд░рд┐рдВрдЧ рд░рд┐рдкрд░, рднрд╛рдЧ 2 ред рдЕрднреА рднреА
рдЙрдирдХреА рдПрдХ
рдкреНрд▓реЗрд▓рд┐рд╕реНрдЯ рд╣реИ ред
рдкрд░рд┐рдЪрдп
рдЖрдЗрдП рдХрд▓реНрдкрдирд╛ рдХрд░реЗрдВ рдХрд┐ рд╣рдореЗрдВ рднрд╛рдЧреНрдп рдФрд░ рдХреБрдВрдбрд▓реА рдХреА рднрд╡рд┐рд╖реНрдпрд╡рд╛рдгреА рдХреЗ рд▓рд┐рдП рдПрдХ рд╕реЗрд╡рд╛ рд╡рд┐рдХрд╕рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд╣рд╛ рдЧрдпрд╛ рдерд╛ред рд╣рдорд╛рд░реА рд╕реЗрд╡рд╛ рдореЗрдВ рдХрдИ рдШрдЯрдХ рд╣реИрдВ, рд▓реЗрдХрд┐рди рд╣рдорд╛рд░реЗ рд▓рд┐рдП рдореБрдЦреНрдп рджреЛ рд╣реЛрдВрдЧреЗ:
- рдЧреНрд▓реЛрдмрд╛, рдЬреЛ рдлреЙрд░реНрдЪреНрдпреВрдирдЯреЗрд▓рд░ рдЗрдВрдЯрд░рдлреЗрд╕ рдХреЛ рд▓рд╛рдЧреВ рдХрд░реЗрдЧрд╛ рдФрд░ рднрд╛рдЧреНрдп рдХреА рднрд╡рд┐рд╖реНрдпрд╡рд╛рдгреА рдХрд░реЗрдЧрд╛;

- рдЬрд┐рдкреНрд╕реА, рдЬреЛ рд╣реЙрд░реЛрд╕реНрдХреЛрдкреЗрд▓рд░ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХреЛ рд▓рд╛рдЧреВ рдХрд░реЗрдЧреА рдФрд░ рдХреБрдВрдбрд▓реА рдмрдирд╛рдПрдЧреАред

рд╣рдорд╛рд░реА рд╕реЗрд╡рд╛ рдореЗрдВ рднреА, рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ, рднрд╛рдЧреНрдп-рдмрддрд╛рдиреЗ рдФрд░ рдХреБрдВрдбрд▓реА рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрдИ рд╕рдорд╛рдкрди рдмрд┐рдВрджреБ (рдирд┐рдпрдВрддреНрд░рдХ) рд╣реЛрдВрдЧреЗред рдФрд░ рд╣рдо рдПрдХ рдкрд╣рд▓реВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЖрдИрдкреА рджреНрд╡рд╛рд░рд╛ рд╣рдорд╛рд░реЗ рдЖрд╡реЗрджрди рддрдХ рдкрд╣реБрдВрдЪ рдХреЛ рдирд┐рдпрдВрддреНрд░рд┐рдд рдХрд░реЗрдВрдЧреЗ рдЬреЛ рдирд┐рдпрдВрддреНрд░рдХ рд╡рд┐рдзрд┐рдпреЛрдВ рдкрд░ рд▓рд╛рдЧреВ рд╣реЛрдЧрд╛ рдФрд░ рдХреБрдЫ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрд╛рдИ рджреЗрдЧрд╛:
RestrictionAspect.java@Aspect @Component @Slf4j public class RestrictionAspect { private final Predicate<String> ipIsAllowed; public RestrictionAspect(@NonNull final Predicate<String> ipIsAllowed) { this.ipIsAllowed = ipIsAllowed; } @Before("execution(public * com.github.monosoul.fortuneteller.web.*.*(..))") public void checkAccess() { val ip = getRequestSourceIp(); log.debug("Source IP: {}", ip); if (!ipIsAllowed.test(ip)) { throw new AccessDeniedException(format("Access for IP [%s] is denied", ip)); } } private String getRequestSourceIp() { val requestAttributes = currentRequestAttributes(); Assert.state(requestAttributes instanceof ServletRequestAttributes, "RequestAttributes needs to be a ServletRequestAttributes"); val request = ((ServletRequestAttributes) requestAttributes).getRequest(); return request.getRemoteAddr(); } }
рдпрд╣ рд╕рддреНрдпрд╛рдкрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐ рдРрд╕реЗ IP рд╕реЗ рдПрдХреНрд╕реЗрд╕ рдХреА рдЕрдиреБрдорддрд┐ рд╣реИ, рд╣рдо
ipIsAllowed
рд╡рд┐рдзреЗрдпрдХреЛрдВ рдХреЗ рдХреБрдЫ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВрдЧреЗред рд╕рд╛рдорд╛рдиреНрдп рддреМрд░ рдкрд░, рдЗрд╕ рдкрд╣рд▓реВ рдХреА рд╕рд╛рдЗрдЯ рдкрд░, рдХреБрдЫ рдЕрдиреНрдп рд╣реЛ рд╕рдХрддреЗ рд╣реИрдВ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдЕрдзрд┐рдХреГрддред
рдЗрд╕рд▓рд┐рдП, рд╣рдордиреЗ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рд╡рд┐рдХрд╕рд┐рдд рдХрд┐рдпрд╛ рдФрд░ рд╕рдм рдХреБрдЫ рд╣рдорд╛рд░реЗ рд▓рд┐рдП рдмрд╣реБрдд рдЕрдЪреНрдЫрд╛ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИред рд▓реЗрдХрд┐рди рдЕрдм рдмрд╛рдд рдХрд░рддреЗ рд╣реИрдВ рдЯреЗрд╕реНрдЯрд┐рдВрдЧ рдХреАред
рдЗрд╕рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХреИрд╕реЗ рдХрд░реЗрдВ?
рдЖрдЗрдП рдЗрд╕ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╛рдд рдХрд░реЗрдВ рдХрд┐ рд╣рдо рдкрд╣рд▓реБрдУрдВ рдХреЗ рдЕрдиреБрдкреНрд░рдпреЛрдЧ рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХреИрд╕реЗ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рдХрдИ рддрд░реАрдХреЗ рд╣реИрдВред
рдЖрдк рд╕реНрдкреНрд░рд┐рдВрдЧ рд╕рдВрджрд░реНрдн рдХреЛ рдмрдврд╝рд╛рдП рдмрд┐рдирд╛ рдПрдХ рдкрд╣рд▓реВ рдХреЗ рд▓рд┐рдП рдФрд░ рдирд┐рдпрдВрддреНрд░рдХреЛрдВ рдХреЗ рд▓рд┐рдП рдЕрд▓рдЧ-рдЕрд▓рдЧ рдкрд░реАрдХреНрд╖рдг рд▓рд┐рдЦ рд╕рдХрддреЗ рд╣реИрдВ (рдЬреЛ рдХрд┐ рдирд┐рдпрдВрддреНрд░рдХ рдХреЗ рд▓рд┐рдП рдПрдХ рдкрд╣рд▓реВ рдХреЗ рд╕рд╛рде рдПрдХ рдкреНрд░реЙрдХреНрд╕реА рдмрдирд╛рдПрдЧрд╛, рдЖрдк рдЖрдзрд┐рдХрд╛рд░рд┐рдХ
рджрд╕реНрддрд╛рд╡реЗрдЬ рдореЗрдВ рдЗрд╕рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЕрдзрд┐рдХ рдкрдврд╝ рд╕рдХрддреЗ рд╣реИрдВ), рд▓реЗрдХрд┐рди рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ рд╣рдо
рдмрд┐рд▓реНрдХреБрд▓ рдкрд░реАрдХреНрд╖рдг рдирд╣реАрдВ рдХрд░реЗрдВрдЧреЗ
рдХрд┐ рдХрд┐рди рдкрд╣рд▓реБрдУрдВ рдХреЛ рд╕рд╣реА рддрд░реАрдХреЗ рд╕реЗ рд▓рд╛рдЧреВ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ рдирд┐рдпрдВрддреНрд░рдХ рдФрд░ рдареАрдХ рд╡реИрд╕реЗ рд╣реА рдЬреИрд╕реЗ рд╣рдо рдЙрдореНрдореАрдж рдХрд░рддреЗ рд╣реИрдВ ;
рдЖрдк рдкрд░реАрдХреНрд╖рдг рд▓рд┐рдЦ рд╕рдХрддреЗ рд╣реИрдВ рдЬрд┐рд╕рдореЗрдВ рд╣рдо рдЕрдкрдиреЗ рдЖрд╡реЗрджрди рдХрд╛ рдкреВрд░рд╛ рд╕рдВрджрд░реНрдн рджреЗрдВрдЧреЗ, рд▓реЗрдХрд┐рди рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ:
- рдЪрд▓ рд░рд╣реЗ рдкрд░реАрдХреНрд╖рдгреЛрдВ рдореЗрдВ рд▓рдВрдмрд╛ рд╕рдордп рд▓рдЧреЗрдЧрд╛, рдХреНрдпреЛрдВрдХрд┐ рд╕рднреА рдбрд┐рдмреНрдмреЗ рдЙрдареЗрдВрдЧреЗ;
- рд╣рдореЗрдВ рд╡реИрдз рдкрд░реАрдХреНрд╖рдг рдбреЗрдЯрд╛ рддреИрдпрд╛рд░ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрдЧреА рдЬреЛ рдПрдХ рд╣реА рд╕рдордп рдореЗрдВ рдПрдирдкреАрдИ рдХреЛ рдлреЗрдВрдХрдиреЗ рдХреЗ рдмрд┐рдирд╛ рдбрд┐рдмреНрдмреЗ рдХреЗ рдмреАрдЪ рдХреЙрд▓ рдХреА рдкреВрд░реА рд╢реНрд░реГрдВрдЦрд▓рд╛ рд╕реЗ рдЧреБрдЬрд░ рд╕рдХрддреЗ рд╣реИрдВред
рд▓реЗрдХрд┐рди рд╣рдо рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдкрд░реАрдХреНрд╖рдг рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ рдХрд┐ рдкрд╣рд▓реВ рдиреЗ рдХреНрдпрд╛ рд▓рд╛рдЧреВ рдХрд┐рдпрд╛ рд╣реИ рдФрд░ рдЕрдкрдирд╛ рдХрд╛рдо рдХрд░ рд░рд╣рд╛ рд╣реИред рд╣рдо рдирд┐рдпрдВрддреНрд░рдХ рджреНрд╡рд╛рд░рд╛ рдХреЙрд▓ рдХреА рдЬрд╛рдиреЗ рд╡рд╛рд▓реА рд╕реЗрд╡рд╛рдУрдВ рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдирд╣реАрдВ рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рдФрд░ рдЗрд╕рд▓рд┐рдП рдкрд░реАрдХреНрд╖рдг рдбреЗрдЯрд╛ рдФрд░ рд╕реНрдЯрд╛рд░реНрдЯрдЕрдк рд╕рдордп рдХрд╛ рддреНрдпрд╛рдЧ рдХрд░рдХреЗ рд╣реИрд░рд╛рди рдирд╣реАрдВ рд╣реЛрдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВред рдЗрд╕рд▓рд┐рдП, рд╣рдо рдкрд░реАрдХреНрд╖рдг рд▓рд┐рдЦреЗрдВрдЧреЗ рдЬрд┐рд╕рдореЗрдВ рд╣рдо рдХреЗрд╡рд▓ рд╕рдВрджрд░реНрдн рдХрд╛ рд╣рд┐рд╕реНрд╕рд╛ рдЙрдард╛рдПрдВрдЧреЗред рдпрд╛рдиреА рд╣рдорд╛рд░реЗ рд╕рдВрджрд░реНрдн рдореЗрдВ рдПрдХ рд╡рд╛рд╕реНрддрд╡рд┐рдХ рдкрд╣рд▓реВ рдмреАрди рдФрд░ рдПрдХ рд╡рд╛рд╕реНрддрд╡рд┐рдХ рдирд┐рдпрдВрддреНрд░рдХ рдмреАрди рд╣реЛрдЧрд╛, рдФрд░ рдмрд╛рдХреА рд╕рдм рдореЛрдХрд╛рдореА рд╣реЛрдЧрд╛ред
рдореЛрдХрд╛ рдмреАрдиреНрд╕ рдХреИрд╕реЗ рдмрдирд╛рдпреЗ?
рд╡рд╕рдВрдд рдореЗрдВ рдореЛрдХрд╛ рдмреАрдиреНрд╕ рдмрдирд╛рдиреЗ рдХреЗ рдХрдИ рддрд░реАрдХреЗ рд╣реИрдВред рд╕реНрдкрд╖реНрдЯрддрд╛ рдХреЗ рд▓рд┐рдП, рдПрдХ рдЙрджрд╛рд╣рд░рдг рдХреЗ рд░реВрдк рдореЗрдВ, рд╣рдо рдЕрдкрдиреА рд╕реЗрд╡рд╛ рдХреЗ рдирд┐рдпрдВрддреНрд░рдХреЛрдВ рдореЗрдВ рд╕реЗ рдПрдХ рд▓реЗрддреЗ рд╣реИрдВ -
PersonalizedHoroscopeTellController
, рдЗрд╕рдХрд╛ рдХреЛрдб рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрддрд╛ рд╣реИ:
PersonalizedHoroscopeTellController.java @Slf4j @RestController @RequestMapping( value = "/horoscope", produces = APPLICATION_JSON_UTF8_VALUE ) public class PersonalizedHoroscopeTellController { private final HoroscopeTeller horoscopeTeller; private final Function<String, ZodiacSign> zodiacSignConverter; private final Function<String, String> nameNormalizer; public PersonalizedHoroscopeTellController( final HoroscopeTeller horoscopeTeller, final Function<String, ZodiacSign> zodiacSignConverter, final Function<String, String> nameNormalizer ) { this.horoscopeTeller = horoscopeTeller; this.zodiacSignConverter = zodiacSignConverter; this.nameNormalizer = nameNormalizer; } @GetMapping(value = "/tell/personal/{name}/{sign}") public PersonalizedHoroscope tell(@PathVariable final String name, @PathVariable final String sign) { log.info("Received name: {}; sign: {}", name, sign); return PersonalizedHoroscope.builder() .name( nameNormalizer.apply(name) ) .horoscope( horoscopeTeller.tell( zodiacSignConverter.apply(sign) ) ) .build(); } }
рдкреНрд░рддреНрдпреЗрдХ рдкрд░реАрдХреНрд╖рдг рдореЗрдВ рдирд┐рд░реНрднрд░рддрд╛ рдХреЗ рд╕рд╛рде рдЬрд╛рд╡рд╛ рд╡рд┐рдиреНрдпрд╛рд╕
рдкреНрд░рддреНрдпреЗрдХ рдкрд░реАрдХреНрд╖рдг рдХреЗ рд▓рд┐рдП, рд╣рдо рдЬрд╛рд╡рд╛ рдХреЙрдиреНрдлрд┐рдЧрд░ рд▓рд┐рдЦ рд╕рдХрддреЗ рд╣реИрдВ рдЬрд┐рд╕рдореЗрдВ рд╣рдо рдирд┐рдпрдВрддреНрд░рдХ рдФрд░ рдкрд╣рд▓реВ рдмреАрдиреНрд╕ рдФрд░ рдмреАрдиреНрд╕ рджреЛрдиреЛрдВ рдХреЛ рдирд┐рдпрдВрддреНрд░рдХ рдирд┐рд░реНрднрд░рддрд╛ рдореЛрдХреНрд╕ рдХреЗ рд╕рд╛рде рд╡рд░реНрдгрд┐рдд рдХрд░рддреЗ рд╣реИрдВред рдмреАрдиреНрд╕ рдХрд╛ рд╡рд░реНрдгрди рдХрд░рдиреЗ рдХрд╛ рдпрд╣ рддрд░реАрдХрд╛ рдЕрдирд┐рд╡рд╛рд░реНрдп рд╣реЛрдЧрд╛, рдХреНрдпреЛрдВрдХрд┐ рд╣рдо рд╡рд╕рдВрдд рдХреЛ рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рдмрддрд╛рдПрдВрдЧреЗ рдХрд┐ рд╣рдореЗрдВ рдмреАрдиреНрд╕ рдмрдирд╛рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдХреИрд╕реЗ рд╣реИред
рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ, рд╣рдорд╛рд░реЗ рдирд┐рдпрдВрддреНрд░рдХ рдХреЗ рд▓рд┐рдП рдкрд░реАрдХреНрд╖рдг рдЗрд╕ рддрд░рд╣ рджрд┐рдЦреЗрдЧрд╛:
javaconfig / PersonalizedHoroscopeTellControllerTest.java @SpringJUnitConfig public class PersonalizedHoroscopeTellControllerTest { private static final int LIMIT = 10; @Autowired private PersonalizedHoroscopeTellController controller; @Autowired private Predicate<String> ipIsAllowed; @Test void doNothingWhenAllowed() { when(ipIsAllowed.test(anyString())).thenReturn(true); controller.tell(randomAlphabetic(LIMIT), randomAlphabetic(LIMIT)); } @Test void throwExceptionWhenNotAllowed() { when(ipIsAllowed.test(anyString())).thenReturn(false); assertThatThrownBy(() -> controller.tell(randomAlphabetic(LIMIT), randomAlphabetic(LIMIT))) .isInstanceOf(AccessDeniedException.class); } @Configuration @Import(AspectConfiguration.class) @EnableAspectJAutoProxy public static class Config { @Bean public PersonalizedHoroscopeTellController personalizedHoroscopeTellController( final HoroscopeTeller horoscopeTeller, final Function<String, ZodiacSign> zodiacSignConverter, final Function<String, String> nameNormalizer ) { return new PersonalizedHoroscopeTellController(horoscopeTeller, zodiacSignConverter, nameNormalizer); } @Bean public HoroscopeTeller horoscopeTeller() { return mock(HoroscopeTeller.class); } @Bean public Function<String, ZodiacSign> zodiacSignConverter() { return mock(Function.class); } @Bean public Function<String, String> nameNormalizer() { return mock(Function.class); } } }
рдРрд╕рд╛ рдкрд░реАрдХреНрд╖рдг рдмрд▓реНрдХрд┐ рдмреЛрдЭрд┐рд▓ рд▓рдЧрддрд╛ рд╣реИред рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ, рд╣рдореЗрдВ рдкреНрд░рддреНрдпреЗрдХ рдирд┐рдпрдВрддреНрд░рдХ рдХреЗ рд▓рд┐рдП рдЬрд╛рд╡рд╛ рдХреЙрдиреНрдлрд┐рдЧрд░ рд▓рд┐рдЦрдирд╛ рд╣реЛрдЧрд╛ред рдпрджреНрдпрдкрд┐ рдпрд╣ рд╕рд╛рдордЧреНрд░реА рдореЗрдВ рднрд┐рдиреНрди рд╣реЛрдЧрд╛, рдЗрд╕рдХрд╛ рдПрдХ рд╣реА рдЕрд░реНрде рд╣реЛрдЧрд╛: рдЗрд╕рдХреА рдирд┐рд░реНрднрд░рддрд╛ рдХреЗ рд▓рд┐рдП рдПрдХ рдирд┐рдпрдВрддреНрд░рдХ рдмреАрди рдФрд░ рдореЛрдХреА рдмрдирд╛рдПрдВред рддреЛ рд╕рдВрдХреНрд╖реЗрдк рдореЗрдВ рдпрд╣ рд╕рднреА рдирд┐рдпрдВрддреНрд░рдХреЛрдВ рдХреЗ рд▓рд┐рдП рд╕рдорд╛рди рд╣реЛрдЧрд╛ред рдореИрдВ, рдХрд┐рд╕реА рднреА рдкреНрд░реЛрдЧреНрд░рд╛рдорд░ рдХреА рддрд░рд╣, рдПрдХ рдЖрд▓рд╕реА рд╡реНрдпрдХреНрддрд┐ рд╣реВрдВ, рдЗрд╕рд▓рд┐рдП рдореИрдВрдиреЗ рддреБрд░рдВрдд рдЗрд╕ рд╡рд┐рдХрд▓реНрдк рд╕реЗ рдЗрдирдХрд╛рд░ рдХрд░ рджрд┐рдпрд╛ред
рдирд┐рд░реНрднрд░рддрд╛ рдХреЗ рд╕рд╛рде рдкреНрд░рддреНрдпреЗрдХ рдХреНрд╖реЗрддреНрд░ рдкрд░ @MockBean рдПрдиреЛрдЯреЗрд╢рди
@MockBean рдПрдиреЛрдЯреЗрд╢рди рд╕реНрдкреНрд░рд┐рдВрдЧ рдмреВрдЯ рдЯреЗрд╕реНрдЯ рд╕рдВрд╕реНрдХрд░рдг 1.4.0 рдореЗрдВ рджрд┐рдЦрд╛рдИ рджрд┐рдпрд╛ред рдпрд╣
рдореЙрдХрд┐рдЯреЛ рд╕реЗ
@Mock рдХреЗ рд╕рдорд╛рди рд╣реИ (рдФрд░ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдпрд╣ рдЖрдВрддрд░рд┐рдХ рд░реВрдк рд╕реЗ рднреА рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИ), рдХреЗрд╡рд▓ рдЕрдВрддрд░ рдпрд╣ рд╣реИ рдХрд┐
@MockBean
рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ
@MockBean
, рдмрдирд╛рдпрд╛ рдЧрдпрд╛ рдореЙрдХ рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рд░реВрдк рд╕реЗ рд╡рд╕рдВрдд рд╕рдВрджрд░реНрдн рдореЗрдВ рд░рдЦрд╛ рдЬрд╛рдПрдЧрд╛ред рдореЛрдХ рдШреЛрд╖рд┐рдд рдХрд░рдиреЗ рдХрд╛ рдпрд╣ рддрд░реАрдХрд╛ рдШреЛрд╖рдгрд╛рддреНрдордХ рд╣реЛрдЧрд╛, рдХреНрдпреЛрдВрдХрд┐ рд╣рдореЗрдВ рд╡рд╕рдВрдд рдХреЛ рдпрд╣ рдмрддрд╛рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИ рдХрд┐ рдЗрди рдореЙрдХ рдХреЛ рдХреИрд╕реЗ рдмрдирд╛рдпрд╛ рдЬрд╛рдПред
рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ, рдкрд░реАрдХреНрд╖рдг рдЗрд╕ рддрд░рд╣ рджрд┐рдЦреЗрдЧрд╛:
mockbean / PersonalizedHoroscopeTellControllerTest.java @SpringJUnitConfig public class PersonalizedHoroscopeTellControllerTest { private static final int LIMIT = 10; @MockBean private HoroscopeTeller horoscopeTeller; @MockBean private Function<String, ZodiacSign> zodiacSignConverter; @MockBean private Function<String, String> nameNormalizer; @MockBean private Predicate<String> ipIsAllowed; @Autowired private PersonalizedHoroscopeTellController controller; @Test void doNothingWhenAllowed() { when(ipIsAllowed.test(anyString())).thenReturn(true); controller.tell(randomAlphabetic(LIMIT), randomAlphabetic(LIMIT)); } @Test void throwExceptionWhenNotAllowed() { when(ipIsAllowed.test(anyString())).thenReturn(false); assertThatThrownBy(() -> controller.tell(randomAlphabetic(LIMIT), randomAlphabetic(LIMIT))) .isInstanceOf(AccessDeniedException.class); } @Configuration @Import({PersonalizedHoroscopeTellController.class, RestrictionAspect.class, RequestContextHolderConfigurer.class}) @EnableAspectJAutoProxy public static class Config { } }
рдЗрд╕ рд╡рд┐рдХрд▓реНрдк рдореЗрдВ рдЕрднреА рднреА рдЬрд╛рд╡рд╛ рдХреЙрдиреНрдлрд┐рдЧрд░ рд╣реИ, рд▓реЗрдХрд┐рди рдпрд╣ рдмрд╣реБрдд рдЕрдзрд┐рдХ рдХреЙрдореНрдкреИрдХреНрдЯ рд╣реИред рдХрдорд┐рдпреЛрдВ рдХреЗ рдмреАрдЪ - рдореБрдЭреЗ рдирд┐рдпрдВрддреНрд░рдХ рдирд┐рд░реНрднрд░рддрд╛ рд╡рд╛рд▓реЗ рдХреНрд╖реЗрддреНрд░реЛрдВ (
@MockBean
рдПрдиреЛрдЯреЗрд╢рди рд╡рд╛рд▓реЗ рдХреНрд╖реЗрддреНрд░) рдХреА рдШреЛрд╖рдгрд╛ рдХрд░рдиреА рдереА, рднрд▓реЗ рд╣реА рд╡реЗ рдкрд░реАрдХреНрд╖рдг рдореЗрдВ рдЖрдЧреЗ рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд┐рдП рдЧрдП рд╣реЛрдВред рдареАрдХ рд╣реИ, рдпрджрд┐ рдЖрдк рдХрд┐рд╕реА рдХрд╛рд░рдг рд╕реЗ рд╕реНрдкреНрд░рд┐рдВрдЧ рдмреВрдЯ рд╕рдВрд╕реНрдХрд░рдг 1.4.0 рд╕реЗ рдХрдо рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рдЖрдк рдЗрд╕ рдПрдиреЛрдЯреЗрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд░ рдкрд╛рдПрдВрдЧреЗред
рдЗрд╕рд▓рд┐рдП, рдореИрдВ рдордЬрд╝рд╛рдХ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдФрд░ рд╡рд┐рдХрд▓реНрдк рдХреЗ рд▓рд┐рдП рдПрдХ рд╡рд┐рдЪрд╛рд░ рдХреЗ рд╕рд╛рде рдЖрдпрд╛ рдерд╛ред рдореИрдВ рдЗрд╕реЗ рдЗрд╕ рддрд░рд╣ рд╕реЗ рдХрд╛рдо рдХрд░рдирд╛ рдЪрд╛рд╣реВрдВрдЧрд╛ ...
рдЖрд╢реНрд░рд┐рдд рдШрдЯрдХ рдкрд░ @Automocked рдПрдиреЛрдЯреЗрд╢рди
рдореИрдВ рдЪрд╛рд╣реВрдВрдЧрд╛ рдХрд┐ рд╣рдорд╛рд░реЗ рдкрд╛рд╕
@Automocked
рдПрдиреЛрдЯреЗрд╢рди рд╣реЛ, рдЬрд┐рд╕реЗ рдореИрдВ рдирд┐рдпрдВрддреНрд░рдХ рдХреЗ рд╕рд╛рде рдХреЗрд╡рд▓ рдлрд╝реАрд▓реНрдб рдХреЗ рдКрдкрд░ рд░рдЦ рд╕рдХрддрд╛ рд╣реВрдВ, рдФрд░ рдлрд┐рд░ рдЗрд╕ рдирд┐рдпрдВрддреНрд░рдХ рдХреЗ рд▓рд┐рдП рдореЛрдХреА рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рд░реВрдк рд╕реЗ рдмрдирд╛рдИ рдЬрд╛рдПрдЧреА рдФрд░ рд╕рдВрджрд░реНрдн рдореЗрдВ рд░рдЦреА рдЬрд╛рдПрдЧреАред
рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ рдкрд░реАрдХреНрд╖рдг рдЗрд╕ рддрд░рд╣ рджрд┐рдЦ рд╕рдХрддрд╛ рд╣реИ:
рд╕реНрд╡рдЪрд╛рд▓рд┐рдд / рд╡реИрдпрдХреНрддрд┐рдХреГрдд @SpringJUnitConfig @ContextConfiguration(classes = AspectConfiguration.class) @TestExecutionListeners(listeners = AutomockTestExecutionListener.class, mergeMode = MERGE_WITH_DEFAULTS) public class PersonalizedHoroscopeTellControllerTest { private static final int LIMIT = 10; @Automocked private PersonalizedHoroscopeTellController controller; @Autowired private Predicate<String> ipIsAllowed; @Test void doNothingWhenAllowed() { when(ipIsAllowed.test(anyString())).thenReturn(true); controller.tell(randomAlphabetic(LIMIT), randomAlphabetic(LIMIT)); } @Test void throwExceptionWhenNotAllowed() { when(ipIsAllowed.test(anyString())).thenReturn(false); assertThatThrownBy(() -> controller.tell(randomAlphabetic(LIMIT), randomAlphabetic(LIMIT))) .isInstanceOf(AccessDeniedException.class); } }
рдЬреИрд╕рд╛ рдХрд┐ рдЖрдк рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ, рдпрд╣ рд╡рд┐рдХрд▓реНрдк рдкреНрд░рд╕реНрддреБрдд рд▓реЛрдЧреЛрдВ рдХреА рд╕рдмрд╕реЗ рдЕрдзрд┐рдХ рдХреЙрдореНрдкреИрдХреНрдЯ рд╣реИ, рдХреЗрд╡рд▓ рдПрдХ рдирд┐рдпрдВрддреНрд░рдХ рдмреАрди рд╣реИ (рдкреНрд▓рд╕ рдПрдХ рдкрд╣рд▓реВ рдХреЗ рд▓рд┐рдП рдПрдХ рд╡рд┐рдзреЗрдп),
@Automocked
рдПрдиреЛрдЯреЗрд╢рди
@Automocked
, рдФрд░
рд╕реЗрдо рдмрдирд╛рдиреЗ рдФрд░ рдЙрдиреНрд╣реЗрдВ рд╕рдВрджрд░реНрдн рдореЗрдВ рд░рдЦрдиреЗ рдХрд╛ рд╕рд╛рд░рд╛
рдЬрд╛рджреВ рдПрдХ рдмрд╛рд░ рд▓рд┐рдЦрд╛ рдЧрдпрд╛ рд╣реИ рдФрд░ рд╕рднреА рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ рдкрд░реАрдХреНрд╖рдгред
рдпрд╣ рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ?
рдЖрдЗрдП рджреЗрдЦреЗрдВ рдХрд┐ рдпрд╣ рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ рдФрд░ рдЗрд╕рдХреЗ рд▓рд┐рдП рд╣рдореЗрдВ рдХреНрдпрд╛ рдЪрд╛рд╣рд┐рдПред
TestExecutionListener
рд╡рд╕рдВрдд рдореЗрдВ рдРрд╕рд╛ рдПрдХ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рд╣реИ -
TestExecutionListener ред рдпрд╣ рдЕрдкрдиреЗ рд╡рд┐рднрд┐рдиреНрди рдЪрд░рдгреЛрдВ рдореЗрдВ рдкрд░реАрдХреНрд╖рдг рдирд┐рд╖реНрдкрд╛рджрди рдХреА рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдореЗрдВ рдПрдореНрдмреЗрдб рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдПрдкреАрдЖрдИ рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдкрд░реАрдХреНрд╖рдг рдХрдХреНрд╖рд╛ рдХрд╛ рдЙрджрд╛рд╣рд░рдг рдмрдирд╛рддреЗ рд╕рдордп, рдкрд░реАрдХреНрд╖рдг рд╡рд┐рдзрд┐ рдХреЛ рдХреЙрд▓ рдХрд░рдиреЗ рд╕реЗ рдкрд╣рд▓реЗ рдпрд╛ рдмрд╛рдж рдореЗрдВред рдЙрдирдХреЗ рдкрд╛рд╕ рдХрдИ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдмреЙрдХреНрд╕ рд╕реЗ рдмрд╛рд╣рд░ рд╣реИрдВред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП,
рдбрд░реНрдЯрд┐рд╕рдХреЙрдирдЯреЗрдХреНрд╕реНрдЯрдЯреЗрд╕реНрдЯ рдПрдХреНрд╕рдХреНрд▓реВрд╕рд┐рд╡ рд▓рд┐рд╕реНрдЯрдирд░ , рдЬреЛ рд╕рдВрджрд░реНрдн рдХреЛ рд╕рд╛рдл рдХрд░рддрд╛ рд╣реИ рдпрджрд┐ рдЖрдк рдЙрдкрдпреБрдХреНрдд рдПрдиреЛрдЯреЗрд╢рди рдбрд╛рд▓рддреЗ рд╣реИрдВ;
рдирд┐рд░реНрднрд░рддрд╛ InjectionTestExecutionListener - рдкрд░реАрдХреНрд╖рдгреЛрдВ, рдЖрджрд┐ рдореЗрдВ рдирд┐рд░реНрднрд░рддрд╛ рдЗрдВрдЬреЗрдХреНрд╢рди рдХрд░рддрд╛ рд╣реИред рдкрд░реАрдХреНрд╖рдг рдХреЗ рд▓рд┐рдП рдЕрдкрдиреЗ рдХрд╕реНрдЯрдо рд╢реНрд░реЛрддрд╛ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЖрдкрдХреЛ рдЗрд╕рдХреЗ рдКрдкрд░
@TestExecutionListeners
рдПрдиреЛрдЯреЗрд╢рди
@TestExecutionListeners
рд╣реЛрдЧрд╛ рдФрд░ рдЗрд╕рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЛ рдЗрдВрдЧрд┐рдд рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред
рдЖрджреЗрд╢ рджрд┐рдпрд╛
рд╡рд╕рдВрдд рдореЗрдВ рдПрдХ
рдСрд░реНрдбрд░ рдХрд┐рдпрд╛ рдЧрдпрд╛ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рднреА рд╣реИред рдпрд╣ рдЗрдВрдЧрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рдХрд┐ рд╡рд╕реНрддреБрдУрдВ рдХреЛ рдХрд┐рд╕реА рддрд░рд╣ рд╕реЗ рд╕реЙрд░реНрдЯ рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдЬрдм рдЖрдкрдХреЗ рдкрд╛рд╕ рдПрдХ рд╣реА рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХреЗ рдХрдИ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рд╣реИрдВ рдФрд░ рдЖрдк рдЙрдиреНрд╣реЗрдВ рдПрдХ рд╕рдВрдЧреНрд░рд╣ рдореЗрдВ рдЗрдВрдЬреЗрдХреНрдЯ рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рддреЛ рдЗрд╕ рд╕рдВрдЧреНрд░рд╣ рдореЗрдВ рдЙрдиреНрд╣реЗрдВ рдСрд░реНрдбрд░ рдХреЗ рдЕрдиреБрд╕рд╛рд░ рдСрд░реНрдбрд░ рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред TestExecutionListener рдХреЗ рдорд╛рдорд▓реЗ рдореЗрдВ, рдпрд╣ рдПрдиреЛрдЯреЗрд╢рди рдЗрдВрдЧрд┐рдд рдХрд░рддрд╛ рд╣реИ рдХрд┐ рдЙрдиреНрд╣реЗрдВ рдХрд┐рд╕ рдХреНрд░рдо рдореЗрдВ рд▓рд╛рдЧреВ рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред
рддреЛ, рд╣рдорд╛рд░реЗ рд╢реНрд░реЛрддрд╛ 2 рдЗрдВрдЯрд░рдлреЗрд╕ рдХреЛ рд▓рд╛рдЧреВ рдХрд░реЗрдВрдЧреЗ:
TestExecutionListener рдФрд░
рдСрд░реНрдбрд░ рдХрд┐рдП рдЧрдП ред рд╣рдо рдЗрд╕реЗ
AutomockTestExecutionListener рдХрд╣рддреЗ рд╣реИрдВ рдФрд░ рдпрд╣ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦреЗрдЧрд╛:
AutomockTestExecutionListener.java @Slf4j public class AutomockTestExecutionListener implements TestExecutionListener, Ordered { @Override public int getOrder() { return 1900; } @Override public void prepareTestInstance(final TestContext testContext) { val beanFactory = ((DefaultListableBeanFactory) testContext.getApplicationContext().getAutowireCapableBeanFactory()); setByNameCandidateResolver(beanFactory); for (val field : testContext.getTestClass().getDeclaredFields()) { if (field.getAnnotation(Automocked.class) == null) { continue; } log.debug("Performing automocking for the field: {}", field.getName()); makeAccessible(field); setField( field, testContext.getTestInstance(), createBeanWithMocks(findConstructorToAutomock(field.getType()), beanFactory) ); } } private void setByNameCandidateResolver(final DefaultListableBeanFactory beanFactory) { if ((beanFactory.getAutowireCandidateResolver() instanceof AutomockedBeanByNameAutowireCandidateResolver)) { return; } beanFactory.setAutowireCandidateResolver( new AutomockedBeanByNameAutowireCandidateResolver(beanFactory.getAutowireCandidateResolver()) ); } private Constructor<?> findConstructorToAutomock(final Class<?> clazz) { log.debug("Looking for suitable constructor of {}", clazz.getCanonicalName()); Constructor<?> fallBackConstructor = clazz.getDeclaredConstructors()[0]; for (val constructor : clazz.getDeclaredConstructors()) { if (constructor.getParameterTypes().length > fallBackConstructor.getParameterTypes().length) { fallBackConstructor = constructor; } val autowired = getAnnotation(constructor, Autowired.class); if (autowired != null) { return constructor; } } return fallBackConstructor; } private <T> T createBeanWithMocks(final Constructor<T> constructor, final DefaultListableBeanFactory beanFactory) { createMocksForParameters(constructor, beanFactory); val clazz = constructor.getDeclaringClass(); val beanName = forClass(clazz).toString(); log.debug("Creating bean {}", beanName); if (!beanFactory.containsBean(beanName)) { val bean = beanFactory.createBean(clazz); beanFactory.registerSingleton(beanName, bean); } return beanFactory.getBean(beanName, clazz); } private <T> void createMocksForParameters(final Constructor<T> constructor, final DefaultListableBeanFactory beanFactory) { log.debug("{} is going to be used for auto mocking", constructor); val constructorArgsAmount = constructor.getParameterTypes().length; for (int i = 0; i < constructorArgsAmount; i++) { val parameterType = forConstructorParameter(constructor, i); val beanName = parameterType.toString(); if (!beanFactory.containsBean(beanName)) { beanFactory.registerSingleton( beanName, mock(parameterType.resolve(), withSettings().stubOnly()) ); } log.debug("Mocked {}", beanName); } } }
рдпрд╣рд╛рдБ рдХреНрдпрд╛ рд╣реЛ рд░рд╣рд╛ рд╣реИ? рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ,
prepareTestInstance()
рд╡рд┐рдзрд┐ рдореЗрдВ, рдпрд╣
@Automocked
рдПрдиреЛрдЯреЗрд╢рди рдХреЗ рд╕рд╛рде рд╕рднреА рдлрд╝реАрд▓реНрдбреНрд╕ рдвреВрдБрдврддрд╛ рд╣реИ:
for (val field : testContext.getTestClass().getDeclaredFields()) { if (field.getAnnotation(Automocked.class) == null) { continue; }
рдлрд┐рд░ рдЗрди рдХреНрд╖реЗрддреНрд░реЛрдВ рдХреЛ рдпреЛрдЧреНрдп рдмрдирд╛рддрд╛ рд╣реИ:
makeAccessible(field);
рдлрд┐рд░,
findConstructorToAutomock()
рд╡рд┐рдзрд┐ рдореЗрдВ,
findConstructorToAutomock()
рдЙрдкрдпреБрдХреНрдд рдХрдВрд╕реНрдЯреНрд░рдХреНрдЯрд░ рдХреЛ рдЦреЛрдЬрддрд╛ рд╣реИ:
Constructor<?> fallBackConstructor = clazz.getDeclaredConstructors()[0]; for (val constructor : clazz.getDeclaredConstructors()) { if (constructor.getParameterTypes().length > fallBackConstructor.getParameterTypes().length) { fallBackConstructor = constructor; } val autowired = getAnnotation(constructor, Autowired.class); if (autowired != null) { return constructor; } } return fallBackConstructor;
рд╣рдорд╛рд░реЗ рдорд╛рдорд▓реЗ рдореЗрдВ рдПрдХ рдЙрдкрдпреБрдХреНрдд рдХрдВрд╕реНрдЯреНрд░рдХреНрдЯрд░ рдпрд╛ рддреЛ
@Autowired рдПрдиреЛрдЯреЗрд╢рди рдХреЗ рд╕рд╛рде рдХрдВрд╕реНрдЯреНрд░рдХреНрдЯрд░ рд╣реИ рдпрд╛ рд╕рдмрд╕реЗ рдЕрдзрд┐рдХ рддрд░реНрдХреЛрдВ рдХреЗ рд╕рд╛рде рдХрдВрд╕реНрдЯреНрд░рдХреНрдЯрд░ рд╣реИред
рдлрд┐рд░, рдкрд╛рдпрд╛ рдЧрдпрд╛ рдирд┐рд░реНрдорд╛рддрд╛
createBeanWithMocks()
рд╡рд┐рдзрд┐ рдХреЗ рддрд░реНрдХ рдХреЗ рд░реВрдк рдореЗрдВ рдкрд╛рд░рд┐рдд рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рдЬреЛ рдмрджрд▓реЗ рдореЗрдВ
createMocksForParameters()
рд╡рд┐рдзрд┐ рдХреЛ рдХреЙрд▓ рдХрд░рддрд╛ рд╣реИ, рдЬрд╣рд╛рдВ рдирд┐рд░реНрдорд╛рддрд╛ рддрд░реНрдХ рдХреЗ рд▓рд┐рдП
createMocksForParameters()
рдмрдирд╛рдП рдЬрд╛рддреЗ рд╣реИрдВ рдФрд░ рд╕рдВрджрд░реНрдн рдореЗрдВ рдкрдВрдЬреАрдХреГрдд рд╣реЛрддреЗ рд╣реИрдВ:
val constructorArgsAmount = constructor.getParameterTypes().length; for (int i = 0; i < constructorArgsAmount; i++) { val parameterType = forConstructorParameter(constructor, i); val beanName = parameterType.toString(); if (!beanFactory.containsBean(beanName)) { beanFactory.registerSingleton( beanName, mock(parameterType.resolve(), withSettings().stubOnly()) ); } }
рдпрд╣ рдзреНрдпрд╛рди рд░рдЦрдирд╛ рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИ рдХрд┐ рдмрд┐рди рдХреЗ рдирд╛рдо рдХреЗ рд░реВрдк рдореЗрдВ рддрд░реНрдХ рдХреЗ рдкреНрд░рдХрд╛рд░ (рдЬреЗрдирд░рд┐рдХ рдХреЗ рд╕рд╛рде) рдХрд╛ рдПрдХ рд╕реНрдЯреНрд░рд┐рдВрдЧ рдкреНрд░рддрд┐рдирд┐рдзрд┐рддреНрд╡ рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред рдпрд╣реА рд╣реИ, рдЯрд╛рдЗрдк
packages.Function<String, String>
рдПрдХ рддрд░реНрдХ рдХреЗ рд▓рд┐рдПред
packages.Function<String, String>
рд╕реНрдЯреНрд░рд┐рдВрдЧ рдкреНрд░рддрд┐рдирд┐рдзрд┐рддреНрд╡ рд╕реНрдЯреНрд░рд┐рдВрдЧ рд╣реЛрдЧрд╛ "packages.Function<java.lang.String, java.lang.String>"
ред
"packages.Function<java.lang.String, java.lang.String>"
ред
"packages.Function<java.lang.String, java.lang.String>"
ред рдпрд╣ рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИ, рд╣рдо рдЗрд╕ рдкрд░ рд╡рд╛рдкрд╕ рдЖрдПрдВрдЧреЗред
рд╕рднреА рддрд░реНрдХреЛрдВ рдХреЗ рд▓рд┐рдП рдореЙрдХ рдмрдирд╛рдиреЗ рдФрд░ рдЙрдиреНрд╣реЗрдВ рд╕рдВрджрд░реНрдн рдореЗрдВ рдкрдВрдЬреАрдХреГрдд рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж, рд╣рдо рдЖрд╢реНрд░рд┐рдд рд╡рд░реНрдЧ (рдпрд╛рдиреА, рд╣рдорд╛рд░реЗ рдорд╛рдорд▓реЗ рдореЗрдВ рдирд┐рдпрдВрддреНрд░рдХ) рдХреА рдмреАрди рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рд▓реМрдЯрддреЗ рд╣реИрдВ:
if (!beanFactory.containsBean(beanName)) { val bean = beanFactory.createBean(clazz); beanFactory.registerSingleton(beanName, bean); }
рдЖрдкрдХреЛ рдЗрд╕ рддрдереНрдп рдкрд░ рднреА рдзреНрдпрд╛рди рджреЗрдирд╛ рдЪрд╛рд╣рд┐рдП рдХрд┐ рд╣рдордиреЗ
рдСрд░реНрдбрд░ 1900 рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рд╣реИред рдпрд╣ рдЖрд╡рд╢реНрдпрдХ рд╣реИ рдХреНрдпреЛрдВрдХрд┐ рд╣рдорд╛рд░реЗ рд╢реНрд░реЛрддрд╛ рдХреЛ
рдбрд░реНрдЯрд┐рд╕рдХреЙрдирдЯреЗрдХреНрд╕реНрдЯрдмреЗрдмрд░реНрд╕рд╣рд╛рдЗрдбрдбреЗрд╕реНрдЯрдЯреЗрдХреНрд╕реНрдЯреЗрд▓рд┐рдпрди рд▓рд┐рд╕реНрдЯрдирд░ рдХреЗ рдУрдо рд╕рдВрджрд░реНрдн (рдЖрджреЗрд╢ = 1500) рдФрд░
рдбрд┐рдкреЗрдВрдбреЗрдВрд╕реАрдЗрдиреНрдЬреЗрдХреНрд╢рдирдЯреЗрд╕реНрдЯ рдПрдХреНрд╕рдХреНрд▓реВрд╕рд┐рд╡рд▓реАрд╕реНрдЯреЗрдирд░ - рдереНрд░ рдирд┐рд░реНрднрд░рддрд╛ рдЗрдВрдЬреЗрдХреНрд╢рди (рдСрд░реНрдбрд░ = 2000) рдХреЛ рдХреНрд▓рд┐рдпрд░ рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж рдмреБрд▓рд╛рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдП, рдХреНрдпреЛрдВрдХрд┐ рд╣рдорд╛рд░реА
рд▓рд┐рд╕реНрдЯреЗрдирд░ рдирдП рдбрд┐рдмреНрдмреЗ рдмрдирд╛рддрд╛ рд╣реИред
AutowireCandidateResolver
AutowireCandidateResolver рдХрд╛ рдЙрдкрдпреЛрдЧ рдпрд╣ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рдХрд┐ рдХреНрдпрд╛
рдмреАрдирдбрд┐рдлрд╛рдЗрдирдореЗрдВрдЯ рдирд┐рд░реНрднрд░рддрд╛ рд╡рд┐рд╡рд░рдг рд╕реЗ
рдореЗрд▓ рдЦрд╛рддрд╛ рд╣реИ ред рдЙрдирдХреЗ рдкрд╛рд╕ рдХрдИ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди "рдмреЙрдХреНрд╕ рд╕реЗ рдмрд╛рд╣рд░" рд╣реИрдВ, рдЙрдирдореЗрдВ рд╕реЗ:
рдЙрд╕реА рд╕рдордп, рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди "рдмреЙрдХреНрд╕ рд╕реЗ рдмрд╛рд╣рд░" рдПрдХ рд░реВрд╕реА рдЧреБрдбрд╝рд┐рдпрд╛ рд╣реИ рдЬреЛ рд╡рд┐рд░рд╛рд╕рдд рд╕реЗ рд╣реИ, рдЕрд░реНрдерд╛рдд рд╡реЗ рдПрдХ рджреВрд╕рд░реЗ рдХрд╛ рд╡рд┐рд╕реНрддрд╛рд░ рдХрд░рддреЗ рд╣реИрдВред рд╣рдо рдПрдХ рдбреЗрдХреЛрд░реЗрдЯрд░ рд▓рд┐рдЦреЗрдВрдЧреЗ, рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рдЕрдзрд┐рдХ рд▓рдЪреАрд▓рд╛ рд╣реИред
рд░рд┐рдЬрд╝реЙрд▓реНрд╡рд░ рдирд┐рдореНрдирд╛рдиреБрд╕рд╛рд░ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ:
- рд╕реНрдкреНрд░рд┐рдВрдЧ рдПрдХ рдирд┐рд░реНрднрд░рддрд╛ рд╡рд┐рд╡рд░рдгрдХ рд▓реЗрддрд╛ рд╣реИ - рдбрд┐рдкреЗрдВрдбреЗрдВрд╕реАрдбрд┐рд╕реАрд╕реНрдХреНрд░рд┐рдкреНрдЯ ;
- рддрдм рдпрд╣ рдЙрдкрдпреБрдХреНрдд рдХрдХреНрд╖рд╛ рдХреЗ рд╕рднреА рдмреАрдирдбрд┐рдлрд┐рдирд┐рд╢рди рд▓реЗрддрд╛ рд╣реИ;
- рдкреНрд░рд╛рдкреНрдд рдХрд┐рдП рдЧрдП рдмреАрдирдбрд┐рдлрд┐рдирд┐рд╢рди рдкрд░ Iterates, рд░рд┐рд╕реЙрд▓реНрд╡рд░ рдХреА
isAutowireCandidate()
рд╡рд┐рдзрд┐ рдХреЛ isAutowireCandidate()
;
- рдЗрд╕ рдмрд╛рдд рдкрд░ рдирд┐рд░реНрднрд░ рдХрд░рддрд╛ рд╣реИ рдХрд┐ рдмрд┐рди рдХрд╛ рд╡рд░реНрдгрди рдирд┐рд░реНрднрд░рддрд╛ рдХреЗ рд╡рд░реНрдгрди рд╕реЗ рдореЗрд▓ рдЦрд╛рддрд╛ рд╣реИ рдпрд╛ рдирд╣реАрдВ, рдпрд╣ рд╡рд┐рдзрд┐ рд╕рд╣реА рдпрд╛ рдЧрд▓рдд рд╣реИред
рдЖрдкрдХреЛ рдЕрдкрдиреА рд░рд┐рдЬрд╝реЙрд▓реНрд╡рд░ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдХреНрдпреЛрдВ рдереА?
рдЕрдм рджреЗрдЦрддреЗ рд╣реИрдВ рдХрд┐ рд╣рдореЗрдВ рдЕрдкрдиреЗ рдирд┐рдпрдВрддреНрд░рдХ рдХреЗ рдЙрджрд╛рд╣рд░рдг рдкрд░ рд╣рдорд╛рд░реА рд░рд┐рдЬрд╝реЙрд▓реНрд╡рд░ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдХреНрдпреЛрдВ рд╣реИред
public class PersonalizedHoroscopeTellController { private final HoroscopeTeller horoscopeTeller; private final Function<String, ZodiacSign> zodiacSignConverter; private final Function<String, String> nameNormalizer; public PersonalizedHoroscopeTellController( final HoroscopeTeller horoscopeTeller, final Function<String, ZodiacSign> zodiacSignConverter, final Function<String, String> nameNormalizer ) { this.horoscopeTeller = horoscopeTeller; this.zodiacSignConverter = zodiacSignConverter; this.nameNormalizer = nameNormalizer; }
рдЬреИрд╕рд╛ рдХрд┐ рдЖрдк рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ, рдЗрд╕рдореЗрдВ рдПрдХ рд╣реА рдкреНрд░рдХрд╛рд░ рдХреА рджреЛ рдирд┐рд░реНрднрд░рддрд╛рдПрдВ рд╣реИрдВ -
рдлрд╝рдВрдХреНрд╢рди , рд▓реЗрдХрд┐рди рд╡рд┐рднрд┐рдиреНрди рдЬреЗрдирд░рд┐рдХ рдХреЗ рд╕рд╛рдеред рдПрдХ рдорд╛рдорд▓реЗ рдореЗрдВ,
рд╕реНрдЯреНрд░рд┐рдВрдЧ рдФрд░
рд░рд╛рд╢рд┐ рдЪрдХреНрд░ , рджреВрд╕рд░реЗ рдореЗрдВ,
рд╕реНрдЯреНрд░рд┐рдВрдЧ рдФрд░
рд╕реНрдЯреНрд░рд┐рдВрдЧ ред рдФрд░ рдЗрд╕рдХреЗ рд╕рд╛рде рд╕рдорд╕реНрдпрд╛ рдпрд╣ рд╣реИ рдХрд┐
рдореЙрдХрд┐рдЯреЛ, рдЬреЗрдирд░рд┐рдХ рдХреЛ рдзреНрдпрд╛рди рдореЗрдВ рд░рдЦрддреЗ рд╣реБрдП рдореЛрдХреНрд╕ рдмрдирд╛рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рдирд╣реАрдВ рд╣реИ ред рдпрд╛рдиреА рдЕрдЧрд░ рд╣рдо рдЗрди рдЖрд╢реНрд░рд┐рддреЛрдВ рдХреЗ рд▓рд┐рдП рдореЛрдХрд╛ рдмрдирд╛рддреЗ рд╣реИрдВ рдФрд░ рдЙрдиреНрд╣реЗрдВ рд╕рдВрджрд░реНрдн рдореЗрдВ рд░рдЦрддреЗ рд╣реИрдВ, рддреЛ рд╕реНрдкреНрд░рд┐рдВрдЧ рдЙрдиреНрд╣реЗрдВ рдЗрд╕ рд╡рд░реНрдЧ рдореЗрдВ рдЗрдВрдЬреЗрдХреНрдЯ рдирд╣реАрдВ рдХрд░ рдкрд╛рдПрдВрдЧреЗ, рдХреНрдпреЛрдВрдХрд┐ рдЙрдирдХреЗ рдкрд╛рд╕ рдЬреЗрдирд░рд┐рдХ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЬрд╛рдирдХрд╛рд░реА рдирд╣реАрдВ рд╣реЛрдЧреАред рдФрд░ рд╣рдо рдЕрдкрд╡рд╛рдж рдХреЛ рджреЗрдЦреЗрдВрдЧреЗ рдХрд┐ рдЗрд╕ рд╕рдВрджрд░реНрдн рдореЗрдВ
рдлрд╝рдВрдХреНрд╢рди рд╡рд░реНрдЧ рдХрд╛ рдПрдХ рд╕реЗ рдЕрдзрд┐рдХ рд╕реЗрдо рд╣реИред рдпрд╣ рдареАрдХ рдпрд╣реА рд╕рдорд╕реНрдпрд╛ рд╣реИ рдЬрд┐рд╕реЗ рд╣рдо рдЕрдкрдиреЗ рд░рд┐рдЬрд╝реЙрд▓реНрд╡рд░ рдХреА рдорджрдж рд╕реЗ рд╣рд▓ рдХрд░реЗрдВрдЧреЗред рдЖрдЦрд┐рд░рдХрд╛рд░, рдЬреИрд╕рд╛ рдХрд┐ рдЖрдк рдпрд╛рдж рдХрд░рддреЗ рд╣реИрдВ, рд╢реНрд░реЛрддрд╛ рдХреЗ рд╣рдорд╛рд░реЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдореЗрдВ рд╣рдордиреЗ рдмрд┐рди рдХреЗ рдирд╛рдо рдХреЗ рд░реВрдк рдореЗрдВ рдЬреЗрдиреЗрд░рд┐рдХ рдХреЗ рд╕рд╛рде рдПрдХ рдкреНрд░рдХрд╛рд░ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛, рдЬрд┐рд╕рдХрд╛ рдЕрд░реНрде рд╣реИ рдХрд┐ рд╣рдо рд╕рднреА рдХреЛ рдмрд┐рди рдХреЗ рдирд╛рдо рдХреЗ рд╕рд╛рде рдирд┐рд░реНрднрд░рддрд╛ рдХреЗ рдкреНрд░рдХрд╛рд░ рдХреА рддреБрд▓рдирд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╡рд╕рдВрдд рд╕рд┐рдЦрд╛рдирд╛ рд╣реЛрдЧрд╛ред
AutomockedBeanByNameAutowireCandidateResolver
рдЗрд╕рд▓рд┐рдП, рд╣рдорд╛рд░реА рд░рд┐рдЬрд╝реЙрд▓реНрд╡рд░ рд╡рд╣реА рдХрд░реЗрдЧреА рдЬреЛ рдореИрдВрдиреЗ рдКрдкрд░ рд▓рд┐рдЦрд╛ рдерд╛, рдФрд░
isAutowireCandidate()
рдкрджреНрдзрддрд┐ рдХрд╛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдЗрд╕ рддрд░рд╣ рджрд┐рдЦреЗрдЧрд╛:
AutowireCandidateResolver.isAutowireCandidate () @Override public boolean isAutowireCandidate(BeanDefinitionHolder beanDefinitionHolder, DependencyDescriptor descriptor) { val dependencyType = descriptor.getResolvableType().resolve(); val dependencyTypeName = descriptor.getResolvableType().toString(); val candidateBeanDefinition = (AbstractBeanDefinition) beanDefinitionHolder.getBeanDefinition(); val candidateTypeName = beanDefinitionHolder.getBeanName(); if (candidateTypeName.equals(dependencyTypeName) && candidateBeanDefinition.getBeanClass() != null) { return true; } return candidateResolver.isAutowireCandidate(beanDefinitionHolder, descriptor); }
рдпрд╣рд╛рдВ рдЙрдиреНрд╣реЗрдВ рдирд┐рд░реНрднрд░рддрд╛ рд╡рд┐рд╡рд░рдг рд╕реЗ рдирд┐рд░реНрднрд░рддрд╛ рдкреНрд░рдХрд╛рд░ рдХрд╛ рд╕реНрдЯреНрд░рд┐рдВрдЧ рдкреНрд░рддрд┐рдирд┐рдзрд┐рддреНрд╡ рдорд┐рд▓рддрд╛ рд╣реИ, рдмреАрдирдбрд┐рдлрд┐рдирд┐рд╢рди рд╕реЗ рдмреАрди рдХрд╛ рдирд╛рдо рдорд┐рд▓рддрд╛ рд╣реИ (рдЬрд┐рд╕рдореЗрдВ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рд╕реЗрдо рдкреНрд░рдХрд╛рд░ рдХрд╛ рд╕реНрдЯреНрд░рд┐рдВрдЧ рдкреНрд░рддрд┐рдирд┐рдзрд┐рддреНрд╡ рд╣реЛрддрд╛ рд╣реИ), рдлрд┐рд░ рдЙрдирдХреА рддреБрд▓рдирд╛ рдХрд░рддрд╛ рд╣реИ рдФрд░, рдпрджрд┐ рд╡реЗ рдореЗрд▓ рдЦрд╛рддреЗ рд╣реИрдВ, рддреЛ рд╕рдЪ рд╣реИред рдпрджрд┐ рд╡реЗ рдореЗрд▓ рдирд╣реАрдВ рдЦрд╛рддреЗ рд╣реИрдВ, рддреЛ рдпрд╣ рдЖрдВрддрд░рд┐рдХ рд░рд┐рдЬрд╝реЙрд▓реНрд╡рд░ рдХреЛ рджрд░реНрд╢рд╛рддрд╛ рд╣реИред
рдЯреЗрд╕реНрдЯ рдмрд┐рди рдЧреАрд▓рд╛ рд╡рд┐рдХрд▓реНрдк
рдХреБрд▓ рдорд┐рд▓рд╛рдХрд░, рдкрд░реАрдХреНрд╖рдгреЛрдВ рдореЗрдВ рд╣рдо рдмрд┐рди рдЧреАрд▓рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рд╡рд┐рдХрд▓реНрдкреЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:
- рдЬрд╛рд╡рд╛ рд╡рд┐рдиреНрдпрд╛рд╕ - рдпрд╣ рдмреЙрдпрд▓рд░рдкреНрд▓реЗрдЯ рдХреЗ рд╕рд╛рде рдЕрдирд┐рд╡рд╛рд░реНрдп, рдмреЛрдЭрд┐рд▓ рд╣реЛ рдЬрд╛рдПрдЧрд╛, рд▓реЗрдХрд┐рди, рд╕рдВрднрд╡рдд: рдЬрд╛рдирдХрд╛рд░реАрдкреВрд░реНрдг рдХреЗ рд░реВрдк рдореЗрдВ;
@MockBean
- рдЬрд╛рд╡рд╛ @MockBean
, рдХрдо рднрд╛рд░реА рд╣реЛрдЧрд╛, рд▓реЗрдХрд┐рди рдЕрднреА рднреА рдирд┐рд░реНрднрд░рддрд╛ рд╡рд╛рд▓реЗ рдХреНрд╖реЗрддреНрд░реЛрдВ рдХреЗ рд░реВрдк рдореЗрдВ рдПрдХ рдЫреЛрдЯрд╛ рдмреЙрдпрд▓рд░рдкреНрд▓реЗрдЯ рд╣реЛрдЧрд╛ рдЬреЛ рдЯреЗрд╕реНрдЯ рдореЗрдВ рд╣реА рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ;
@Automocked
+ рдХрд╕реНрдЯрдо рд░рд┐рдЬрд╝реЙрд▓реНрд╡рд░ - рдкрд░реАрдХреНрд╖рдг рдФрд░ рдмреЙрдпрд▓рд░рдкреНрд▓реЗрдЯ рдореЗрдВ рдиреНрдпреВрдирддрдо рдХреЛрдб, рд▓реЗрдХрд┐рди рд╕рдВрднрд╛рд╡рд┐рдд рд░реВрдк рд╕реЗ рдХрд╛рдлреА рд╕рдВрдХреАрд░реНрдг рдЧреБрдВрдЬрд╛рдЗрд╢ рд╣реИ рдФрд░ рдЗрд╕реЗ рдЕрднреА рднреА @Automocked
рд╣реЛрдЧрд╛ред рд▓реЗрдХрд┐рди рдпрд╣ рдмрд╣реБрдд рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ рдЬрд╣рд╛рдВ рдЖрдк рдпрд╣ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ рдХрд┐ рд╡рд╕рдВрдд рд╕рд╣реА рдврдВрдЧ рд╕реЗ рдкреНрд░реЙрдХреНрд╕реА рдмрдирд╛рддрд╛ рд╣реИред
рд╕рдЬреНрдЬрд╛рдХрд╛рд░ рдЬреЛрдбрд╝реЗрдВ
рд╣рдорд╛рд░реА рдЯреАрдо рдЕрдкрдиреЗ рд▓рдЪреАрд▓реЗрдкрди рдХреЗ рд▓рд┐рдП
рдбреЗрдХреЛрд░реЗрдЯрд░ рдбрд┐рдЬрд╝рд╛рдЗрди рдЯреЗрдореНрдкрд▓реЗрдЯ рд╕реЗ
рдкреНрдпрд╛рд░ рдХрд░рддреА рд╣реИред рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ, рдкрд╣рд▓реВ рдЗрд╕ рд╡рд┐рд╢реЗрд╖ рдкреИрдЯрд░реНрди рдХреЛ рд▓рд╛рдЧреВ рдХрд░рддреЗ рд╣реИрдВред рд▓реЗрдХрд┐рди рдЕрдЧрд░ рдЖрдк рдПрдиреЛрдЯреЗрд╢рди рдХреЗ рд╕рд╛рде рд╕реНрдкреНрд░рд┐рдВрдЧ рд╕рдВрджрд░реНрдн рдХреЛ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рдкреИрдХреЗрдЬ рд╕реНрдХреИрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рдЖрдк рдПрдХ рд╕рдорд╕реНрдпрд╛ рдореЗрдВ рдЪрд▓реЗ рдЬрд╛рдПрдВрдЧреЗред рдпрджрд┐ рдЖрдкрдХреЗ рдкрд╛рд╕ рд╕рдВрджрд░реНрдн рдореЗрдВ рдПрдХ рд╣реА рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХреЗ рдХрдИ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рд╣реИрдВ, рддреЛ рдЬрдм рдЕрдиреБрдкреНрд░рдпреЛрдЧ
рд╢реБрд░реВ рд╣реЛрддрд╛ рд╣реИ , рддреЛ рдПрдХ
NoUniqueBeanDefinitionException рдЧрд┐рд░ рдЬрд╛рдПрдЧреА , рдЕрд░реНрдерд╛рддред рд╡рд╕рдВрдд рдпрд╣ рдкрддрд╛ рд▓рдЧрд╛рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рдирд╣реАрдВ рд╣реЛрдЧрд╛ рдХрд┐ рдХрд┐рд╕ рд╕реЗрдо рдХреЛ рдЗрдВрдЬреЗрдХреНрдЯ рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред рдЗрд╕ рд╕рдорд╕реНрдпрд╛ рдХреЗ рдХрдИ рд╕рдорд╛рдзрд╛рди рд╣реИрдВ, рдФрд░ рдлрд┐рд░ рд╣рдо рдЙрдиреНрд╣реЗрдВ рджреЗрдЦреЗрдВрдЧреЗ, рд▓реЗрдХрд┐рди рдкрд╣рд▓реЗ, рдЖрдЗрдП рдЬрд╛рдиреЗрдВ рдХрд┐ рд╣рдорд╛рд░рд╛ рдЖрд╡реЗрджрди рдХреИрд╕реЗ рдмрджрд▓ рдЬрд╛рдПрдЧрд╛ред
рдЕрдм
рдлреЙрд░реНрдЪреНрдпреВрдирдЯреЗрд▓рд░ рдФрд░
рд╣реЙрд░реЛрд╕реНрдХреЛрдкреЗрд▓рд░ рдЗрдВрдЯрд░рдлреЗрд╕ рдХрд╛ рдПрдХ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рд╣реИ, рд╣рдо рдкреНрд░рддреНрдпреЗрдХ рдЗрдВрдЯрд░рдлреЗрд╕ рдХреЗ рд▓рд┐рдП 2 рдФрд░ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдЬреЛрдбрд╝реЗрдВрдЧреЗ:

- рдХреИрд╢рд┐рдВрдЧ ... - рдХреИрд╢рд┐рдВрдЧ рдбреЗрдХреЛрд░реЗрдЯрд░;
- рд▓реЙрдЧрд┐рдВрдЧ ... рд▓реЙрдЧрд┐рдВрдЧ рдбреЗрдХреЛрд░реЗрдЯрд░ рд╣реИред
рддреЛ рдЖрдк рд╕реЗрдо рдХреЗ рдХреНрд░рдо рдХреЛ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░рдиреЗ рдХреА рд╕рдорд╕реНрдпрд╛ рдХреЛ рдХреИрд╕реЗ рд╣рд▓ рдХрд░рддреЗ рд╣реИрдВ?
рд╢реАрд░реНрд╖ рд╕реНрддрд░ рд╕рдЬреНрдЬрд╛рдХрд╛рд░ рдХреЗ рд╕рд╛рде рдЬрд╛рд╡рд╛ рд╡рд┐рдиреНрдпрд╛рд╕
рдЖрдк рдЬрд╛рд╡рд╛ рдХреЙрдиреНрдлрд┐рдЧрд░ рдХреЛ рдлрд┐рд░ рд╕реЗ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ, рд╣рдо рд╕реЗрдо рдХреЛ рд╡рд┐рдиреНрдпрд╛рд╕ рд╡рд░реНрдЧ рдХреЗ рддрд░реАрдХреЛрдВ рдХреЗ рд░реВрдк рдореЗрдВ рд╡рд░реНрдгрд┐рдд рдХрд░реЗрдВрдЧреЗ, рдФрд░ рд╣рдореЗрдВ рдмреАрди рдХреЗ рдирд┐рд░реНрдорд╛рддрд╛ рдХреЛ рддрд░реНрдХ рдХреЗ рд░реВрдк рдореЗрдВ рдХреЙрд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЖрд╡рд╢реНрдпрдХ рддрд░реНрдХреЛрдВ рдХреЛ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рдЬрд┐рд╕рд╕реЗ рдпрд╣ рдирд┐рдореНрдирд╛рдиреБрд╕рд╛рд░ рд╣реИ рдХрд┐ рдмрд┐рди рдХреЗ рдирд┐рд░реНрдорд╛рдгрдХрд░реНрддрд╛ рдореЗрдВ рдкрд░рд┐рд╡рд░реНрддрди рдХреЗ рдорд╛рдорд▓реЗ рдореЗрдВ, рд╣рдореЗрдВ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдХреЛ рдмрджрд▓рдирд╛ рд╣реЛрдЧрд╛, рдЬреЛ рдмрд╣реБрдд рдЕрдЪреНрдЫрд╛ рдирд╣реАрдВ рд╣реИред рдЗрд╕ рд╡рд┐рдХрд▓реНрдк рдХреЗ рдлрд╛рдпрджреЛрдВ рдореЗрдВ:
- рдбреЗрдХреЛрд░реЗрдЯрд░ рдХреЗ рдмреАрдЪ рдХрдо рдХрдиреЗрдХреНрдЯрд┐рд╡рд┐рдЯреА рд╣реЛрдЧреА, рдЬреИрд╕рд╛ рдХрд┐ рдЙрдирдХреЗ рдмреАрдЪ рдХрд╛ рдХрдиреЗрдХреНрд╢рди рд╡рд┐рдиреНрдпрд╛рд╕ рдореЗрдВ рд╡рд░реНрдгрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛, рдЕрд░реНрдерд╛рдд рд╡реЗ рдПрдХ рджреВрд╕рд░реЗ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдХреБрдЫ рднреА рдирд╣реАрдВ рдЬрд╛рдирддреЗ рд╣реЛрдВрдЧреЗ;
- рд╕рдЬреНрдЬрд╛рдХрд╛рд░реЛрдВ рдХреЗ рдХреНрд░рдо рдореЗрдВ рд╕рднреА рдкрд░рд┐рд╡рд░реНрддрди рдПрдХ рд╣реА рд╕реНрдерд╛рди рдкрд░ рд╕реНрдерд╛рдиреАрдп рд╣реЛрдВрдЧреЗ - рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рдиред
рд╣рдорд╛рд░реЗ рдорд╛рдорд▓реЗ рдореЗрдВ, рдЬрд╛рд╡рд╛ рд╡рд┐рдиреНрдпрд╛рд╕ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦреЗрдЧрд╛:
DomainConfig.java @Configuration public class DomainConfig { @Bean public FortuneTeller fortuneTeller( final Map<FortuneRequest, FortuneResponse> cache, final FortuneResponseRepository fortuneResponseRepository, final Function<FortuneRequest, PersonalData> personalDataExtractor, final PersonalDataRepository personalDataRepository ) { return new LoggingFortuneTeller( new CachingFortuneTeller( new Globa(fortuneResponseRepository, personalDataExtractor, personalDataRepository), cache ) ); } @Bean public HoroscopeTeller horoscopeTeller( final Map<ZodiacSign, Horoscope> cache, final HoroscopeRepository horoscopeRepository ) { return new LoggingHoroscopeTeller( new CachingHoroscopeTeller( new Gypsy(horoscopeRepository), cache ) ); } }
рдЬреИрд╕рд╛ рдХрд┐ рдЖрдк рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ, рдкреНрд░рддреНрдпреЗрдХ рдЗрдВрдЯрд░рдлреЗрд╕ рдХреЗ рд▓рд┐рдП рдпрд╣рд╛рдВ рдХреЗрд╡рд▓ рдПрдХ рдмреАрди рдШреЛрд╖рд┐рдд рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рдФрд░ рд╡рд┐рдзрд┐рдпрд╛рдВ рддрд░реНрдХ рдореЗрдВ рдирд┐рд╣рд┐рдд рд╣реИрдВ рдЬреЛ рдЕрдВрджрд░ рдмрдирд╛рдИ рдЧрдИ рд╕рднреА рд╡рд╕реНрддреБрдУрдВ рдХреА рдирд┐рд░реНрднрд░рддрд╛ рд╣реИрдВред рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ, рд╕реЗрдо рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рддрд░реНрдХ рдХрд╛рдлреА рд╕реНрдкрд╖реНрдЯ рд╣реИред
рдХреНрд╡рд╛рд▓реАрдлрд╛рдпрд░
рдЖрдк
@Qualifier рдПрдиреЛрдЯреЗрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдпрд╣ рдЬрд╛рд╡рд╛ рдХреЙрдиреНрдлрд┐рдЧ рдХреА рддреБрд▓рдирд╛ рдореЗрдВ рдЕрдзрд┐рдХ рдШреЛрд╖рдгрд╛рддреНрдордХ рд╣реЛрдЧрд╛, рд▓реЗрдХрд┐рди рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ рдЖрдкрдХреЛ рдмреАрди рдХреЗ рдирд╛рдо рдХреЛ рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рдирд╛ рд╣реЛрдЧрд╛, рдЬрд┐рд╕ рдкрд░ рд╡рд░реНрддрдорд╛рди рдмреАрди рдирд┐рд░реНрднрд░ рдХрд░рддрд╛ рд╣реИред рдиреБрдХрд╕рд╛рди рдХрд╛ рдХреНрдпрд╛ рдорддрд▓рдм рд╣реИ: рдбрд┐рдмреНрдмреЗ рдХреЗ рдмреАрдЪ рдХрдиреЗрдХреНрдЯрд┐рд╡рд┐рдЯреА рдореЗрдВ рд╡реГрджреНрдзрд┐ред рдФрд░ рдЬрдм рд╕реЗ рдХрдиреЗрдХреНрдЯрд┐рд╡рд┐рдЯреА рдмрдврд╝рддреА рд╣реИ, рдпрд╣рд╛рдВ рддрдХ тАЛтАЛрдХрд┐ рд╕рдЬреНрдЬрд╛рдХрд╛рд░реЛрдВ рдХреЗ рдХреНрд░рдо рдореЗрдВ рдмрджрд▓рд╛рд╡ рдХреЗ рдорд╛рдорд▓реЗ рдореЗрдВ, рдХреЛрдб рдкрд░ рд╕рдорд╛рди рд░реВрдк рд╕реЗ рдмрджрд▓рд╛рд╡ рдХрд┐рдП рдЬрд╛рдПрдВрдЧреЗред рдпрд╣реА рд╣реИ, рдПрдХ рдирдП рдбреЗрдХреЛрд░реЗрдЯрд░ рдХреЛ рдЬреЛрдбрд╝рдиреЗ рдХреЗ рдорд╛рдорд▓реЗ рдореЗрдВ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рд╢реНрд░реГрдВрдЦрд▓рд╛ рдХреЗ рдмреАрдЪ рдореЗрдВ, рдкрд░рд┐рд╡рд░реНрддрди рдХрдо рд╕реЗ рдХрдо 2 рд╡рд░реНрдЧреЛрдВ рдХреЛ рдкреНрд░рднрд╛рд╡рд┐рдд рдХрд░реЗрдЧрд╛ред
LoggingFortuneTeller.java @Primary @Component public final class LoggingFortuneTeller implements FortuneTeller { private final FortuneTeller internal; private final Logger logger; public LoggingFortuneTeller( @Qualifier("cachingFortuneTeller") @NonNull final FortuneTeller internal ) { this.internal = internal; this.logger = getLogger(internal.getClass()); }
, , ( ,
FortuneTeller , ),
@Primary .
internal @Qualifier
, тАФ
cachingFortuneTeller . .
Custom qualifier
2.5 Qualifier', . .
enum :
public enum DecoratorType { LOGGING, CACHING, NOT_DECORATOR }
, qualifier':
@Qualifier @Retention(RUNTIME) public @interface Decorator { DecoratorType value() default NOT_DECORATOR; }
: ,
@Qualifier
,
CustomAutowireConfigurer , .
Qualifier' :
CachingFortuneTeller.java @Decorator(CACHING) @Component public final class CachingFortuneTeller implements FortuneTeller { private final FortuneTeller internal; private final Map<FortuneRequest, FortuneResponse> cache; public CachingFortuneTeller( @Decorator(NOT_DECORATOR) final FortuneTeller internal, final Map<FortuneRequest, FortuneResponse> cache ) { this.internal = internal; this.cache = cache; }
тАУ ,
@Decorator
, , тАУ ,
,
FortuneTeller ', тАУ
Globa .
Qualifier' - , - . , , . , - тАУ , , .
DecoratorAutowireCandidateResolver
тАУ ! ! :) , - , Java Config', . , - , . :
DomainConfig.java @Configuration public class DomainConfig { @Bean public OrderConfig<FortuneTeller> fortuneTellerOrderConfig() { return () -> asList( LoggingFortuneTeller.class, CachingFortuneTeller.class, Globa.class ); } @Bean public OrderConfig<HoroscopeTeller> horoscopeTellerOrderConfig() { return () -> asList( LoggingHoroscopeTeller.class, CachingHoroscopeTeller.class, Gypsy.class ); } }
тАУ Java Config' , тАУ . , !
- . , , , . :
@FunctionalInterface public interface OrderConfig<T> { List<Class<? extends T>> getClasses(); }
BeanDefinitionRegistryPostProcessor
BeanDefinitionRegistryPostProcessor , BeanFactoryPostProcessor, , , , BeanDefinition'. , BeanFactoryPostProcessor, .
:
- BeanDefinition';
- BeanDefinition' , OrderConfig '. , .. BeanDefinition' ;
- , OrderConfig ', BeanDefinition', , () .
BeanFactoryPostProcessor
BeanFactoryPostProcessor , BeanDefinition' , . , ┬л Spring-┬╗.

, , тАУ AutowireCandidateResolver':
DecoratorAutowireCandidateResolverConfigurer.java @Component class DecoratorAutowireCandidateResolverConfigurer implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(final ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { Assert.state(configurableListableBeanFactory instanceof DefaultListableBeanFactory, "BeanFactory needs to be a DefaultListableBeanFactory"); val beanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory; beanFactory.setAutowireCandidateResolver( new DecoratorAutowireCandidateResolver(beanFactory.getAutowireCandidateResolver()) ); } }
DecoratorAutowireCandidateResolver
:
DecoratorAutowireCandidateResolver.java @RequiredArgsConstructor public final class DecoratorAutowireCandidateResolver implements AutowireCandidateResolver { private final AutowireCandidateResolver resolver; @Override public boolean isAutowireCandidate(final BeanDefinitionHolder bdHolder, final DependencyDescriptor descriptor) { val dependentType = descriptor.getMember().getDeclaringClass(); val dependencyType = descriptor.getDependencyType(); val candidateBeanDefinition = (AbstractBeanDefinition) bdHolder.getBeanDefinition(); if (dependencyType.isAssignableFrom(dependentType)) { val candidateQualifier = candidateBeanDefinition.getQualifier(OrderQualifier.class.getTypeName()); if (candidateQualifier != null) { return dependentType.getTypeName().equals(candidateQualifier.getAttribute("value")); } } return resolver.isAutowireCandidate(bdHolder, descriptor); }
descriptor' (dependencyType) (dependentType):
val dependentType = descriptor.getMember().getDeclaringClass(); val dependencyType = descriptor.getDependencyType();
bdHolder' BeanDefinition:
val candidateBeanDefinition = (AbstractBeanDefinition) bdHolder.getBeanDefinition();
. , :
dependencyType.isAssignableFrom(dependentType)
, , .. .
BeanDefinition' :
val candidateQualifier = candidateBeanDefinition.getQualifier(OrderQualifier.class.getTypeName());
, :
if (candidateQualifier != null) { return dependentType.getTypeName().equals(candidateQualifier.getAttribute("value")); }
тАУ (), тАУ false.
, :
- Java Config тАУ , , , ;
@Qualifier
тАУ , - ;
- Custom qualifier тАУ , Qualifier', ;
- - тАУ , , , .
, , . тАУ : . , , , . тАУ , JRE. , , .
, тАУ , , - . рдкрдврд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рдж!
:
https://github.com/monosoul/spring-di-customization .