Java шпаргалка

Файл application.properties

Большой список всевозможных свойств и описаний к ним

https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#data-properties

Получение значения параметра свойства

Надо пометить переменную наподобие такого

@Value("${app.rest.employee.count-on-page}") /* работает только на не статическом поле*/
public int countOnPage;

RestTemplate send PATCH request

При попытке отправить PATCH запрос, возникает исключение ProtocolException: Invalid HTTP method: PATCH или ResourceAccessException: I/O error on PATCH request

https://github.com/spring-cloud/spring-cloud-netflix/issues/1777

Для решения проблемы следует добавить зависимость:

        <!-- for path requests via resttemplate -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.4.1</version>
        </dependency>

И создавать RestTemplate следующим образом:

HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
        RestTemplate restTemplate = new RestTemplate(requestFactory);

Spring инициализация

Запуск своего кода при загрузке Spring

@Component
private class Starter implements CommandLineRunner {
   @Override
   public void run(String... args) {
      //todo
   }
}

i18n Internalization

Поддержка языков при использовании Spring Boot.

Достаточно определить бин:

	@Bean
	public MessageSource messageSource() {
		ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
		messageSource.setCacheSeconds(3600); //refresh cache once per hour
		messageSource.setDefaultEncoding("UTF-8");
		messageSource.setFallbackToSystemLocale(false);
		messageSource.setBasenames("classpath:locale/messages/app");
		return messageSource;
	}

И разместить бандлы в указанном месте (locale/messages/app). Т.е.:

src/main/resources/locale/messages/app.properties

и
src/main/resources/locale/messages/app_ru.properties

В этом случае если браузер желает русскую локаль, то ему будет отдан app_ru.properties, в противном случае - app.properties

Также можно переопределить логику определения текущей локали с помощют объявления бина :

@Bean
	public LocaleResolver localeResolver() {
		SessionLocaleResolver slr = new SessionLocaleResolver();
		slr.setDefaultLocale(Locale.forLanguageTag("ru"));
		return slr;
	}

Если этого не сделать SpringBoot будет использовать

org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver.class
в нём можно ставить брейкпоинты.

Таким образом, если не переопределять LocaleResolver, то локаль будет определяться их хэдеров запроса.

Более подробно написано тут https://knasys.ru/spring-boot-i18n-thymeleaf/


Аннотации

Как переиспользлвать настроенную аннотацию @Pattern

Если Вы много где используете настроенную аннотацию @Pattern, например,

1
@Pattern (regexp = "^\\+[0-9]{11,16}$", message = "{constraints.phoneIncorrectFormat}")

, то Вы можете её сохранить как свою кастомную аннотацию так:

import javax.validation.Constraint;
import javax.validation.Payload;
import javax.validation.constraints.Pattern;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;


	
import static java.lang.annotation.ElementType.*; import static java.lang.annotation.ElementType.TYPE_USE; import static java.lang.annotation.RetentionPolicy.RUNTIME; @Pattern(regexp = "^\\+[0-9]{11,16}$", message = "{constraints.phoneIncorrectFormat}") @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) @Retention(RUNTIME) @Documented @Constraint(validatedBy = { }) public @interface PhonePattern { /** * @return the error message template */ String message() default "{constraints.phoneIncorrectFormat}"; /** * @return the groups the constraint belongs to */ Class<?>[] groups() default { }; /** * @return the payload associated to the constraint */ Class<? extends Payload>[] payload() default { }; }

Использование констант в @PreAuthorize и thymeleaf sec:authoriz

https://stackoverflow.com/questions/64795958/spring-security-rolesallowed-and-thymeleaf-secauthorize

@Controller
//@PreAuthorize("hasAnyRole(T(ru.knastnt.prj.web.admin.MenuEditorController).ALLOWED_ROLES)")
@PreAuthorize("hasAnyRole(@menuEditorController.ALLOWED_ROLES)")
public class MenuEditorController {
    public static final String[] ALLOWED_ROLES = {Role.ADMIN.toString(), Role.EDITOR.toString()};
    ...
}
<ul th:fragment = "nav-default">
    <li sec:authorize="hasAnyRole(@menuEditorController.ALLOWED_ROLES)"><a th:href="@{${@menuEditorController.MENU_EDITOR_URL}}">menu editor</a></li>
</ul>

Отличие @Valid от @Validated

@Valid это "стандартная" аннотация для валидации бина, не привязанная к Спрингу. Даже если вместо Спринга ты будешь использовать другой фреймворк, она будет работать. А вот @Validated относится уже конкретно к Спрингу, т.е. она не "переносимая". Цель та же, что и у @Valid, но со всякими плюшками, например - наличие возможности указать группы валидации.

@Valid еще нужна, если нужно валидировать поля сложного объекта, объектов коллекций и массивов, иначе валидатор пройдется только по верхам.

@NotEmpty List<@NotEmpty String> list; - сработает только на объект списка, что список непустой, внутрянку не проверит.
@Valid @NotEmpty List<@NotEmpty String> list; - проверит и строки внутри коллекции.

Аннотацию @Validated можно проверить если ее установить над классом контроллера, когда аргументом метода является коллекция и ее нужно отвалидировать, без нее валидатор не работает.

Спасибо за ответы Evgeny Nagorny, Sergey Zhukov.


Тестирование

Подмена бина при тестировании

Способ 1: автовайрим этот бин не через @Autowired, а через @MockBean. В этом случее бин подменится для всего контекста.

Способ2: объявляем этот бин в конфигурации, используя другое имя. А затем автовайрим с использованием аннотации Qualifier. Класс теста:

@SpringBootTest
@ContextConfiguration(classes = {SmsDeliveryServiceImplTestConfig.class})
class SmsDeliveryServiceTest {

    @Autowired
    @Qualifier("SmsDeliveryServiceForTesting")
    SmsDeliveryService smsDeliveryService;

    @Test
    void testmethod() {
    .........

Класс конфигурации:

@TestConfiguration
class SmsDeliveryServiceImplTestConfig {
    @Bean(name = "SmsDeliveryServiceForTesting")
    public SmsDeliveryService smsDeliveryService(SmsSender smsSender) {
        Duration[] smsPauseTime = new Duration[]{
                Duration.ofMillis(200),
                Duration.ofMillis(600),
                Duration.ofMillis(1400)
        };
        Duration timeForResetPauseTime = Duration.ofMillis(2500);
        TemporalUnit temporalUnit = ChronoUnit.MILLIS;
        return new SmsDeliveryService(smsSender, smsPauseTime, timeForResetPauseTime, temporalUnit);
    }
}

Изменение авторизованного пользователя в тесте

Вот тут я задавал вопрос и описывал свою проблему. Там же сам себе ответил. https://stackoverflow.com/questions/64812736/change-authorized-user-in-tests

@Test
    void allMenusAuthorizePermissions() throws Exception {

        for (User user : ALL_ROLES_USERS) {
            log.debug("User role: " + user.getAuthorities());

            if (user == ADMIN || user == EDITOR) {
//              perform(get(MenuEditorController.MENU_EDITOR_URL).with(SecurityMockMvcRequestPostProcessors.user(user.getUsername()).authorities(user.getAuthorities())))
                perform(get(MenuEditorController.MENU_EDITOR_URL).with(SecurityMockMvcRequestPostProcessors.user(user)))
                        .andExpect(status().isOk());
            }else{
                perform(get(MenuEditorController.MENU_EDITOR_URL).with(SecurityMockMvcRequestPostProcessors.user(user)))
                        .andExpect(status().isForbidden());
            }

        }
    }

Thymeleaf

Синтаксис

Учебник Thymeleaf: Глава 4. Standard Expression Syntax

th:href со ссылкой на контроллер

Во-первых, зачем писать th:href="@{/path}" когда можно написать просто href="/path"? Вот зачем.

Во-вторых чтобы сослаться на какой-то бин в контексте из thymeleaf, можно использовать конструкцию ${@menuEditorController.MENU_EDITOR_URL}, где menuEditorController - имя бина (по-умолчанию это имя класса с маленькой буквы), MENU_EDITOR_URL - какая-то статическая константа (можно также ссылаться и на методы).

Ну и к делу:

<ul th:fragment = "nav-default">
    ...
    <li sec:authorize="hasAnyRole(@menuEditorController.ALLOWED_ROLES)">
        <a th:href="@{${@menuEditorController.MENU_EDITOR_URL}}">Редактор меню</a>
    </li>
    ...
</ul>
@Controller
@RequestMapping(MENU_EDITOR_URL)
@PreAuthorize("hasAnyRole(@menuEditorController.ALLOWED_ROLES)")
public class MenuEditorController {
    public static final String MENU_EDITOR_URL = ADMIN_URL + "/menu";
    public static final String[] ALLOWED_ROLES = {Role.ADMIN.toString(), Role.EDITOR.toString()};
    ...
}

Аспекты (Aspects, AOP)

Вводная лекция (логгирование, замер времени выполнения метода)

Примеры Pointcut`s:

"execution(* ru.knastnt.myapp.*.*(..))" - любое возвращаемое значение, любой класс непосредственно в указанном пакете, любой метод класса, любые аргументы метода
"execution(* ru.knastnt...*.*(..))" - любое возвращаемое значение, любой класс в указанном пакете и всех дочерних, любой метод класса, любые аргументы метода
"execution(* ru.knastnt.repo.ClientDao.*GroupIn(..))" - любое возвращаемое значение, указанный класс в указанном пакете, любой метод класса заканчивающийся на GroupIn, любые аргументы метода
"@annotation(AspectAnnotation)" - любой метод помеченный аннотацией

Правила формирования: тут и тут

Пример аспекта

@Aspect
@Component
public class MyAspect {
    @Before("execution(* ru.knastnt.prj..*.*(..))")
    public void beforeMethodInvocation(JoinPoint jp){
        System.out.println("Вызов метода с сигнатурой: " + jp.getSignature());
    }
}

JSON

JSON ObjectMapper игнорирует аннотацию @JsonFormat(shape = STRING, pattern = "MM.dd.yyyy").

Там какие-то заморочки с JSR301 и чтобы починить надо немного настроить ObjectMapper:

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());


Binding JSON null field to empty collection/list in Object:

@Data
public class MyDTO {
    @JsonSetter(nulls = Nulls.AS_EMPTY)
    private List<InnerDTO> innerDtos;
}

Теперь если даже в JSON это поле будет как null, то в объект всё равно сбиндится пустой List.


Swagger

Подключение:

    <properties>
        <swagger.version>2.9.2</swagger.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${swagger.version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${swagger.version}</version>
        </dependency>
    </dependencies>

Эндпоинты:

localhost:8080/v2/api-docs
localhost:8080/v3/api-docs
localhost:8080/swagger-ui/index.html

Настройки:

@Configuration
@EnableSwagger2
@PropertySource("classpath:configuration.properties")
public class SwaggerConfiguration {

    @Bean
    public Docket configureDocs(@Value("${projectVersion}") String version) {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo(version))
                .useDefaultResponseMessages(false)
                .select()
                .paths(Predicates.not(PathSelectors.regex("/actuator/*")))
                .paths(Predicates.not(PathSelectors.regex("/error")))
                .apis(RequestHandlerSelectors.basePackage("ru.knastnt.myproject"))
                .build()
                .tags(new Tag("MainController", "Мой тестовый проект"));
    }

    private ApiInfo apiInfo(String version) {
        return new ApiInfoBuilder()
                .title("Мой проект")
                .description("REST API к моему проекту")
                .termsOfServiceUrl("")
                .version(version)
                .build();
    }
}

Документирование контроллера:

@Api("Главный контроллер")
@RestController
@RequestMapping("/api")
public class MainController {

    @ApiOperation("Поиск объекта по идентификатору")
    @GetMapping("/{objectId}")
    public ObjectDto getObjectById(@ApiParam(value = "Идентификатор объекта", example = "1") @PathVariable Long objectId) {
        return ...;
    }
}

Документирование DTO:

@ApiModel("Параметры запроса объекта")
public class ObjectDto {
    @ApiModelProperty("Номер объекта")
    private String objNum;
}

Camunda

Типы заданий

  • Human Task
    • Generated Forms (генерирование формы исходя из заданных полей)
    • Generic Form (указание переменных вручную)
    • Embedded form (HTML-код)
    • External Form (форма во внешнем приложении)
  • Service Task
    • Вызов Java кода (Java class / Delegate Expression) – синхронное исполнение
    • Expression (выражение)
    • Connector (http-connector, mail-connector)
    • External (топик) – асинхронное исполнение
  • Send Task – отправка сообщения (аналог Service Task)
  • Receive Task – прием сообщения (процесс ожидает прихода сообщения)
  • Business Rule Task – таблица бизнес-правила / DMN
  • Script Task (javaScript, Groovy)
  • Manual Task

JPA

Получение единственного результата из базы данных

Generic DAO:

Search search = new Search(MyEntity.class);
return (MyEntity) commonDao.searchUnique(search);

Если объектов не найдено: null;
Если объектов больше одного: IncorrectResultSizeDataAccessException(NonUniqueResultException);

Javax.persistence:

em()
.createQuery(queryStr, MyEntity.class)
.getResultList()

Если объектов не найдено: NoResultException;
Если объектов больше одного: NonUniqueResultException;

Spring DataAccessUtils:

DataAccessUtils.singleResult(em()
.createQuery(queryStr, MyEntity.class)
.getResultList())

Если объектов не найдено: null;
Если объектов больше одного: IncorrectResultSizeDataAccessException(NonUniqueResultException);


LDAP

Теория

Термины и определения: https://pro-ldap.ru/tr/zytrax/apd/

Объектные классы и их атрибуты: https://pro-ldap.ru/tr/zytrax/ape/

Общая информация о работе LDAP: https://pro-ldap.ru/tr/zytrax/ch2/

(Просмотрено 221 раз, 2 раз за сегодня)
Вы можете оставить комментарий, или Трекбэк с вашего сайта.

Оставить комментарий