Комментарии 68
А как правильно? Если не сложно напишите или дайте ссылку.
Последнее предложение перед заключением. Поменял на то, чтобы раз в день была отправка. Проект не нужно перезапускать, т к по идее нужно было изначально задавать это значение. 10 секунд я сделал для примера, чтобы видно было логи
Можно добавить cron-pattern в application.properties, и инжектить в класс.
А обновлять собственно в рантайме можно при помощи spring-cloud-starter + actuator. Смотрите сюда
stackoverflow.com/questions/27919270/set-override-spring-spring-boot-properties-at-runtime
У Спринга есть возможности регистрировать таск и определять нужно ли ему выполнится в конкретный момент времени ориентируясь хоть на фазу луны.
Не могли бы вы чуть описать о какой возможности Spring идёт речь?
Да и @Scheduled воспринимает задание значений через переменные среды, и он вроде поддерживает SpEL.
У Спринга есть возможности регистрировать таск и определять нужно ли ему выполнится в конкретный момент времени ориентируясь хоть на фазу луны.
Простите, это вы о чём?
Вообще Spring boot все больше и больше превращается в нишевый продукт. Сейчас большое количество компаний поворачиваются в сторону serverless и FaaS подхода. И это гораздо выгоднее. Ниша это в основном компании кто не хочет и не может двигаться в сторону FaaS да и облака в целом.
Судя по всему ты у себя в нефтескваженске так и не понял как общаться с людьми. О чем твоя карма ярко свидетельствует. Ни одной публикации у тебя нет, и тебя постоянно плюсует один и тот же человек, что странно. Используешь два аккаунта на хабре?
… сервлетами и большими аппликейшн контейнерами… Теперь это лямбда Амазон
Обкурился что ли? Прежде чем чушь нести про IBM и большие сервлеты, почитал бы сначала как устроена архитектура у облачных ламбда функций.
Хотя что-то мне говорит что индивидууму вроде тебя сложно понять как устроен docker-container и в чем его преимущество перед спринг 2 приложением запущенным гденить на одном из ваших газпромвнешторгмаш серверов.
cloud.spring.io/spring-cloud-function
О интересно, это что то новое.
Support a uniform programming model across serverless providers
Но все равно это не то, в случае с lambda непозволительная роскошь когда у вас инициализация контекста занимает хоть какое-то время. Мы например используем dagger2. Попытка конечно отчаянная, но вернуть ламбда-писцев обратно на спринги вряд ли удастся.
StringBuffer message = new StringBuffer();
message.append("Happy Birthday dear ")
.append(user.getName())
.append("!");
emailService.send(user.getEmail(), "Happy Birthday!", message.toString());
Вот это бессмысленно. Во первых StringBuffer тут не нужен. Никакого мультипоточного доступа там нет. Во вторых простая конкатенация с java 7 если я правильно помню на уровне компиляции преобразуется в StringBuilder(). append().append().append(). Нет нужды городить.
SchedulerService. Вы проверьте будет ли у вас меняться DATE. Подозреваю что нет т.к. это по сути singleton сервис. Его только один instance существует.
} catch (Exception e) {
вот так тоже нехорошо делать. Если у вас будут какие-то неожиданные эксепшены у вас будет только строка в логе. И потом поди найди их. Stacktrace надо. Да и OutOfmemory у вас скроется.if (DATE.getMonth() == user.getBirthday().getMonth()
тут вы словите NPE если даты рождения нету (NULL) и судя по отсутствию NotNull на поле в Entity вы его таки получите.
Правильно будет выбирать эту дату на кажом цикле шедулера.
Выбирать всех пользователей и потом фильтровать их это дурной тон. Представьте, что у вас миллионы пользователей. Вы их начнете втаскивать в память из базы.
e.printStackTrace(); бахнет только в консоль, которой у вас может и не быть.
log.error(«Message», e); вот так логгер выведет во все источники.
Разберите Exception e на несколько тех что реально бросаются, чтобы не подавлять RuntimeException
Выбирать всех пользователей и потом фильтровать их это дурной тон. Представьте, что у вас миллионы пользователей. Вы их начнете втаскивать в память из базы.
А что вы предлагаете?
Разберите Exception e на несколько тех что реально бросаются, чтобы не подавлять RuntimeException
Как именно я могу узнать все возможные исключения?
C Exception тоже просто. Уберите try/catch и посмотрите на что ругается компилятор. Он вам скажет какие не обратотаны. Ловите только их через или если у вас обработка одинакова.
catch (IOException | SomeMoreExcemption | OneMoreException e)
findAllByBirthdayIsNotNullAndEmailIsNotNull()
// ...
user.getBirthday().getMonth()
по идее не должен получить НПЕ т.к. выбирает нот нулл.
открываем его в среде разработки, у меня это Intellij IDEA.
На windows это можно сделать с помощью pgAdmin или его альтернатив.
Database tool
if (userService.getAll() != null && userService.getAll().size() != 0) {
List<User> list = userService.getAll();
1. а зачем в данном контексте потокозащищенность StringBuffer-а?
2. А сколько пользователь получит писем? Там же правило крона на раз в 10 минут настроено. Получается, в свой ДР он получит 144 письма.
Ну и еще были вопросы, пока читал, но когда дочитал — не все вспомнил.
А с quartz для планирования вы не работали?
А не проще ли вместо @Getter
+@Setter
+@ToString
написать лишь @Data
? Или это было для наглядности?
Несколько замечаний:
Зачем в модели protected id, а не private?
Вы используете ломбок, это прекрасно, но тогда можно использовать его и для генерации логгера (https://projectlombok.org/features/log)
Опять же, для генерации Autowired конструктора можно использовать код типа
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
При условии что у вас stateless bean
Про StringBuffer уже писали выше
Неконсистентное именование переменных (верхний регистр обычно применяют для static final переменных, а у вас DATE)
- Методы репозитория, возвращающие список, не могут вернуть null, поэтому можно просто ограничиться проверкой .isEmpty()
Repository methods returning collections, collection alternatives, wrappers, and streams are guaranteed never to return null but rather the corresponding empty representation
А главное — вы вызываете этот метод три раза и не кэшируете его результат
Многие пишут, что StringBuffer тут не нужен, хотя @Scheduled использует многопоточность. Я не являюсь экспертом в области потоков, поэтому может ли кто-то из высказавшихся привести аргументы, и я исправлю его StringBuilder?
Менять на StringBuilder смысла нет, так как можно написать гораздо проще:
String message = "Happy Birthday dear " + user.getName() + "!"
А дальше компилятор сам заменит этот код на StringBuilder.
Решается helper.setFrom(«user@exchange.ru „) в методе send класса EmailServiceImpl.
теперь я хочу этот “user@exchange.ru » вписать в application.properties и оттуда пробросить в send
some.mail.var=someValue
Ваш класс:
@Component
public class EmailServiceImpl {
@Value("${some.mail.var}") String someMailVar;
@PostConstruct
public void init(){
System.out.println(someMailVar); // на консоли будет someValue
}
}
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
Component
public class EmailServiceImpl implements EmailService {
private final JavaMailSender emailSender;
Value("${spring.mail.from}") String someMailVar;
...}
Error:(20, 12) java: cannot find symbol
symbol: method value()
location: interface lombok.Value
А оно похоже Ломбок тянет.
Рекомендую вместо Lombok попробовать Koltin, тем более что Spring поддерживает его куда прозрачнее.
EmailService
:public interface EmailService {
void send(String to, String title, String body);
}
Реализуя его, создаёте сервис
EmailServiceImpl
:@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class EmailServiceImpl implements EmailService {
private final JavaMailSender emailSender;
Аналогично с
UserRepositoryServiceImpl
и UserRepositoryService
:@Service
public class UserRepositoryServiceImpl implements UserRepositoryService {
private final UserRepository repository;
Мне не понятно, почему в
SchedulerService
вы инжектитеEmailService
, а неEmailServiceImpl
UserRepositoryService
, а неUserRepositoryServiceImpl
:
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class SchedulerService {
private final UserRepositoryService userService;
private final EmailService emailService;
}
Rusya_2_0 (или кто-нибудь ещё), объясните, почему так.
Спасибо.
Если кратко , то
https://habr.com/ru/amp/post/30444/
Инжектя интерфейс мы завязываемся на абстракции. Таким образом, мы можем в любой момент заменить реализацию без изменения вызывающего класса. Также это один из принципов SOLID(который D).
Может быть полезно для подстановки мок реализаций в тестах например.
Вообще это довольно распространённый вопрос, стоит ли создавать интерфейсы если есть только одна реализация. С точки зрения правильности дизайна и чистоты кода наверное да, но часто можно обойтись и без них( в том числе и в этой статье), тут уже на вкус и цвет.
Spring Boot. Фоновые задачи и не только