Ответы пользователя по тегу Java
  • Как правильно внедрять зависимости в классы, зависящие от не-бинов?

    Vamp
    @Vamp
    Есть ли альтернативы, кроме как отпустить идею с определением FilePatcher как бина?

    Вы можете определить его как бин чуть позже - в момент, когда вам становится известен directory. FilePatcher надо будет инжектить с аннотацией @Lazy.

    Пример

    // Обычный класс. Не компонент.
    public class FilePatcher {
        private final Path directory;
    
        public FilePatcher(Path directory) {
            this.directory = directory;
        }
    
        public void hello() {
            System.out.println(directory);
        }
    }

    @Component
    public class CommitManager {
    
        private final FilePatcher filePatcher;
    
        // Spring сгенерирует объект-заглшку. Реальный бин будет запрошен из
        // спринг контекста при первом вызове любого метода объекта-заглушки.
        public CommitManager(@Lazy FilePatcher filePatcher) {
            this.filePatcher = filePatcher;
        }
    
        public void callFilePatcher() {
            filePatcher.hello();
        }
    }

    @Component
    public class RuntimeSetter {
    
        private final ApplicationContext ctx;
        private final CommitManager cm;
    
        public RuntimeSetter(ApplicationContext ctx, CommitManager cm) {
            this.ctx = ctx;
            this.cm = cm;
        }
    
        @Scheduled(initialDelay = 500, fixedDelay = Long.MAX_VALUE)
        public void set() {
            // Спустя 500 мс становится известен path.
            // В этот момент создаём бин FilePatcher вручную.
            var factory = (BeanDefinitionRegistry) ctx.getAutowireCapableBeanFactory();
            var gbd = new GenericBeanDefinition();
            gbd.setBeanClass(FilePatcher.class);
            var cav = new ConstructorArgumentValues();
            cav.addGenericArgumentValue(Path.of("hello_world"));
            gbd.setConstructorArgumentValues(cav);
            factory.registerBeanDefinition("filePatcher", gbd);
        }
    
    
        @Scheduled(initialDelay = 1000, fixedDelay = Long.MAX_VALUE)
        public void run() {
            // Спустя 1000 мс вызываем код, который триггерит резолв lazy бина.
            cm.callFilePatcher();
        }
    }



    Альтернативно можно не бин создавать, а property. Делается чутка сложнее:

    Пример с property

    public class CtxInit implements ApplicationContextInitializer<ConfigurableApplicationContext> {
        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {
            applicationContext.getEnvironment()
                .getPropertySources()
                .addLast(new PropertySource<String>("pathPropertyResolver") {
    
                    private volatile String path;
    
                    {
                        var t = new Thread(() -> {
                            try {
                                // Спустя 500 мс после старта становится
                                // известен path.
                                Thread.sleep(500);
                                path = "hello_world";
                            } catch (InterruptedException e) {
                                Thread.currentThread().interrupt();
                            }
                        });
                        t.start();
                    }
    
                    @Override
                    public Object getProperty(String name) {
                        // Наш проперти называется myLazyPath.
                        if ("myLazyPath".equals(name)) {
                            return path;
                        }
                        return null;
                    }
                });
        }
    }

    @SpringBootApplication
    @EnableScheduling
    public class ApplicationMain {
        public static void main(String[] args) {
            var sa = new SpringApplication(ApplicationMain.class);
            sa.addInitializers(new CtxInit());
            sa.run(args);
        }
    }

    @Component
    public class FilePatcher {
        private final Path directory;
    
        public FilePatcher(@Value("${myLazyPath}") Path directory) {
            this.directory = directory;
        }
    
        public void hello() {
            System.out.println(directory);
        }
    }

    @Component
    public class CommitManager {
    
        private final FilePatcher filePatcher;
    
        public CommitManager(@Lazy FilePatcher filePatcher) {
            this.filePatcher = filePatcher;
        }
    
        public void callFilePatcher() {
            filePatcher.hello();
        }
    }

    @Component
    public class RuntimeRunner {
    
        private final CommitManager cm;
    
        public RuntimeRunner(CommitManager cm) {
            this.cm = cm;
        }
    
        @Scheduled(initialDelay = 1000, fixedDelay=Long.MAX_VALUE)
        public void run() {
            // Спустя 1000 мс вызываем код, который триггерит резолв lazy бина.
            cm.callFilePatcher();
        }
    }

    Ответ написан
    Комментировать
  • Как избавиться от требования обработки исключения в Project Reactor?

    Vamp
    @Vamp
    неужели есть только один вариант это обрабатывать с поощью try/catch внутри каждого блока что бы избежать Unhandled exceptions?

    Вобщем-то да. Можно вынести try/catch в отдельный универсальный метод, который оборачивает исключение в RuntimeException:

    private @NotNull Mono<JsonObject> invoke(Channel channel, Class<?> clazz, Method method, Object... args) {
        return Mono.fromCallable(() -> clazz.getDeclaredConstructor(Server.class, Channel.class))
            .map(constructor -> ex(() -> constructor.newInstance(this.manager.getServer(), channel)))
            .flatMap(obj -> new GenericData<JsonObject>().invoke(method, obj, args))
            .onErrorResume(Mono::error);
    }
    
    private <T> T ex(Callable<T> code) {
        try {
            return code.call();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    Конкретно в вашем примере можно вызов конструктора переместить в callable, из которого делается Mono, так как из Callable разрешено стрелять проверяемыми исключениями, в отличии от Function, который принимает map:

    private @NotNull Mono<JsonObject> invoke(Channel channel, Class<?> clazz, Method method, Object... args) {
        return Mono.fromCallable(() -> {
                var constructor = clazz.getDeclaredConstructor(Server.class, Channel.class);
                return constructor.newInstance(this.manager.getServer(), channel);
            })
            .flatMap(obj -> new GenericData<JsonObject>().invoke(method, obj, args))
            .onErrorResume(Mono::error);
    }
    Ответ написан
    Комментировать
  • Годная книга по Java Core для профессионалов?

    Vamp
    @Vamp
    class loader, JMM, GC

    Это не core, а продвинутые штуки. Их необязательно знать, чтобы программировать на java.

    Если хотите углубиться, то маст хэв - это многопоточность. Лучшая книга на эту тему - Java Concurrency in Practice. Читайте на английском, так как русский перевод ужасен.

    Далее Effective Java, Джошуа Блоха. Классика. Нельзя считать себя java профессионалом, не прочитав эту кингу.

    Ну и JLS. Это уже высший пилотаж. Очень сложное чтиво.

    Ещё посоветую смотреть записи докладов с конференций jpoint, jug, joker. Довольно доходчиво объясняют сложные вещи.
    Ответ написан
    Комментировать
  • Почему не работает List-Unsubscribe?

    Vamp
    @Vamp
    Спамеры пользуются этой фичей как подтверждением доставки. Раз отписался, значит точно живой и начнут спамить в три раза сильнее.

    Раз кнопка не появилась, значит ваша репутация рассыльщика недостаточно хороша или вообще отсутствует. Соблюдайте требования почтовых сервисов и кнопка появится рано или поздно.
    Ответ написан
    Комментировать
  • Как организовать monorepo с привязкой к версии maven?

    Vamp
    @Vamp
    Вы можете собрать весь проект целиком, тогда в maven reactor будут добавлены сразу все модули и dependency resolver сможет разобраться в зависимостях от соседних модулей. Но если собирать один конкретный модуль, то мавен уже не разберётся и полезет во внешние репозитории. В таком случае необходимо передать дополнительный ключ -am (--also-make):

    mvn -am -pl модуль package
    Ответ написан
    Комментировать
  • Почему поток не останавливается?

    Vamp
    @Vamp
    Тут есть две проблемы.

    Первая очевидная проблема в том, что main поток на первой же итерации впадает в сон на методе wait(), а в программе больше нет потоков, которые могли бы вызвать notify() и разбудить main. Поэтому main бесконечно ждёт и не может остановить liche и sude.

    Вторая совсем неочевидная в том, что синхронизация некорректно используется. У вас синхронизируется только запись в переменную isRun. Чтение этой переменной так же должно быть синхронизировано. Иначе это приведет к странным неуловимым багам.
    Ответ написан
    2 комментария
  • Как запустить майнкрафт через коммандную строку?

    Vamp
    @Vamp
    Для 1.17.1 нужна java версии 17. Судя по ошибкам у вас 8 версия.
    Ответ написан
    Комментировать
  • Как происходит связывание с предыдущим узлом в LinkedStack?

    Vamp
    @Vamp
    1. Просто чтобы продемонстрировать использование паттерна sentinel value. В данном конкретном примере можно было обойтись просто null.

    2. В присваивании сначала выполняется правая часть - создаётся объект Node, которому в аргументы конструктора передается item и текущее значение, хранящееся в переменной top. После этого переменной top присваивается ссылка на этот новый объект.

    Это более короткий вариант следующего кода:
    Node<T> oldTop = top;
    top = new Node<T>(item, oldTop);
    Ответ написан
    4 комментария
  • Что за фигурные скобки в Java?

    Vamp
    @Vamp
    Эта конструкция называется блоком статической инициализации. Есть ещё точно такой же блок динамической инициализации, только без ключевого слова static.

    Блоки инициализации нужны для задания начальных значений полям класса.
    class Initable2 {
        static int staticNonFinal;
    
        public static void main(String[] args) {
            System.out.println(staticNonFinal);
        }
    }

    Данный пример выведет ноль, хотя переменной staticNonFinal не присваивалось никакое значение. Java гарантирует, что любые поля класса будут проинициализированы "нулевым" значением. То есть компилятор неявно вставляет в класс блок статической инициализации, в котором переменной staticNonFinal присваивается ноль.

    class Initable2 {
        static int staticNonFinal;
    
        // Вот этот блок будет добавлен к
        // вашему классу во время компиляции.
        static {
            staticNonFinal = 0;
        }
    
        public static void main(String[] args) {
            System.out.println(staticNonFinal);
        }
    }


    Разумеется, вы можете инициализировать переменные своими значениями:
    class Initable2 {
        static int staticNonFinal = 42;
    
        static String a = "hello";
    
        static Cache<String, Integer> b = CacheBuilder.newBuilder()
            .maximumSize(100)
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .build();
    }

    И тогда компилятор в блоке инициализации будет подставлять ваши значения вместо нулей:
    class Initable2 {
        static int staticNonFinal;
    
        static String a;
    
        static Cache<String, Integer> b;
    
        static {
            staticNonFinal = 42;
            a = "hello";
            b = CacheBuilder.newBuilder()
                .maximumSize(100)
                .expireAfterWrite(10, TimeUnit.MINUTES)
                .build();
        }
    }

    А если вашей переменной требуется какая-то сложная инициализация, которую одной строкой не представить (как в примере с Cache), то тогда вам придется написать блок инициализации явным образом вручную:
    class Initable2 {
        static int staticNonFinal = 42;
    
        static Map<Integer, String> statusCodes = new HashMap<>();
    
        static {
            statusCodes.put(200, "OK");
            statusCodes.put(404, "Not Found");
            statusCodes.put(418, "I'm a teapot");
        }
    }

    И тогда компилятор сгенерирует вам такой код:
    class Initable2 {
        static int staticNonFinal;
    
        static Map<Integer, String> statusCodes;
    
        static {
            staticNonFinal = 42;
            statusCodes = new HashMap<>()
            statusCodes.put(200, "OK");
            statusCodes.put(404, "Not Found");
            statusCodes.put(418, "I'm a teapot");
        }
    }


    Точно таким же способом инициализируются и нестатические поля при помощи блока динамической инициализации (перед таким блоком отсутствует ключевое слово static и выглядит как просто фигурные скобки в теле класса). Каждый раз при создании объекта класса сначала выполняется блок динамической инициализации, а затем конструктор. Именно в таком порядке. Блок статической инициализации выполняется один раз при загрузке класса в память.

    Блоки динамической инициализации редко используются программистами, так как проще всего сделать "инициализацию" в конструкторе (в кавычках, потому что формальная инициализация уже была выполнена перед запуском конструктора и конструктор просто переписывает уже инициализированное значение своим, поэтому более точным термином здесь будет переприсваивание). Но блоки статической инициализации используются довольно часто, так как статических конструкторов не существует.

    Вот эти все блоки инициализации были придуманы создателями языка java чтобы исключить целый класс лютых ошибок, связанных с доступом к неинициализированным переменным, характерный для языков си и си++.
    Ответ написан
    Комментировать
  • Как сделать свою mvn dependency?

    Vamp
    @Vamp
    Для этого нужно создать maven project. В pom.xml этого проекта прописать group, artifact и version. Затем собрать проект командой mvn install

    Maven скомпилирует код вашего проекта, упакует в jar и скопирует этот jar вместе с pom.xml в ваш локальный maven репозиторий.

    Далее в другом проекте подключаете свою зависимость как любую другую зависимость, указывая group, artifact и version, которые прописали ранее.
    Ответ написан
    Комментировать
  • Всегда ли выполняется выполняется блок finally?

    Vamp
    @Vamp
    Автор правильно пишет. Если исключение выбросится на строке lnputFile in = new InputFile("Cleanup.java")j, то оно попадёт сразу в catch(Exception e) { System.out.println("Ошибка при конструировании InputFile"); }. Здесь finally не выполняется, так как исполнение кода не заходит в try блок, к которому прикреплён тот finally.

    Но если исключение стрельнет где-то внутри цикла while((s = in.getLine()) != null), то сработает внутренний catch блок и finally блок. Внешний catch не будет выполнен.

    А если в блоке try либо catch выполнить System.exit() ?

    В таком случае finally блок не будет выполнен, так как javadoc System.exit() чётко пишет: "This method never returns normally." А раз метод не возвращает управление, то try блок не выполняется до конца, соответственно finally не запускается.
    Ответ написан
    Комментировать
  • Когда использовать Collection, а когда Iterator?

    Vamp
    @Vamp
    В каких случаях следует идти через Collection, а в каких через Iterator? (особенно если надо просто пробежаться всем элементам).

    Зависит от объекта, по которому вы хотите просто пробежаться. Если он имплементирует интерфейс Iterable (или унаследованный от него Collection), то for(Pet p:pets) - ваш вариант.

    Через Iterator следует идти, если вы хотите пробежаться по элементам и в процессе пробежки удалить какие-то элементы из итерируемой коллекции. Сделать это безопасно можно только через Iterator.

    Решение с Iterator выглядит привлекательно при написании класса, в котором реали­зация интерфейса Collection затруднена или непрактична.

    Здесь автор имеет ввиду, если вы сами пишете класс, по которому можно "пробежаться". В этом случае проще всего сделать это через итератор, так как в нем меньше методов, которые необходимо реализовать, по сравнению с интерфейсом Collection.
    Ответ написан
    Комментировать
  • Почему так работают битовые сдвиги в Java?

    Vamp
    @Vamp
    Битовый сдвиг для типа long не может превышать 63. Поэтому сдвиг 1L << 64 будет преобразован jvm в 1L << (64 & 0x3f), в результате чего получается 1L << 0 и поэтому у вас значение не изменяется.

    Вашу задачу решает сдвиг 1L << 63. Единица изначально находится на 1 позиции (позиции нумеруются справа налево) и сдвигается влево на 63 позиции. Вы получаете желаемую единицу на 64 позиции с 63 нулями позади.

    Что касается 128L << 56, то здесь всё корректно и логично. Единица находится на 8 позции и имеет 7 нулей сзади. Сдвигая её на 56 позиций, у вас получается желаемый результат - единица на 64 позиции (8 + 56) и 63 нуля позади (7 нулей + 56).
    Ответ написан
    Комментировать
  • Почему не работает dependency NametagEdit?

    Vamp
    @Vamp
    Вероятнее всего неправильно указан repository

    Попробуйте вместо
    <repository>
        <id>upstream</id>
        <url>https://ci.nametagedit.com/plugin/repository/everything/com/nametagedit/nametagedit/</url>
    </repository>


    прописать это
    <repository>
        <id>upstream</id>
        <url>https://ci.nametagedit.com/plugin/repository/everything/</url>
    </repository>
    Ответ написан
    4 комментария
  • Java. Сокеты. Как заставить сервер постоянно слушать до нажатия кнопки?

    Vamp
    @Vamp
    Нужно тело try блока, где открывается серверный сокет, обернуть в бесконечный цикл. Чтобы сервер не завершался после обработки первого клиента.
    Ответ написан
    5 комментариев
  • Можно ли сделать динамическое добавление WebAPI?

    Vamp
    @Vamp
    1. Blue-green deployment. Запускаете два инстанса своего приложения. Один из них условно называется green, другой blue. Ставите перед ними nginx/haproxy и настраиваете проксирование всех запросов на green инстанс. Когда приходит время изменений, делаете их на blue инстансе, рестартите его и перенастраиваете проксирование всего трафика с green инстанса на blue. В следующий раз делаете то же самое, но с green инстансом.

    Этот вариант не требует ни строчки изменений в коде.

    2. В бесконечном цикле с паузой в 1 сек между итерациями читаете файлы с конфигурацией из нужной папки. Затем останавливаете сервисы, для которых не нашлось определения в конфиге, запускаете сервисы, для которых определение есть и перезапускате сервисы, у которых конфиг изменился. Это уже реализовано в spring boot externalized configuration, но ничто не мешает вам реализовать это руками.
    Ответ написан
    1 комментарий
  • Static члены не копируют своих данных даже в наследниках?

    Vamp
    @Vamp
    Static члены не наследуются, верно. Так как это бессмысленно.

    Просто я думал, зачем же нужен в enum valueOf(). Разобрался. Предположил, что метод хранит в себе строковое представление констант, а затем сравнивает вводные данные с ними.

    Метод valueOf() ничего не хранит и компилятор подставляет одинаковую реализацию valueOf() во всех enum'ах:

    public enum Hello {
        FIZZ, BUZZ;
    
        // Данный метод автоматически генерируется компилятором.
        // Для любого enum'а.
        public static Hello valueOf(String name) {
            return Enum.valueOf(Hello.class, name);
        }
    }


    А внутри Enum.valueOf обычный HashMap<String, Hello>, где ключом является имя константы, а значением соответствующий инстанс класса Hello. Не могу согласиться, что этот вариант плох с точки зрения оптимизации.

    Но так как я знаю, что константы в enum это анонимные классы

    Это не так. Константы - это конкретные инстансы вашего enum класса, а не инстансы анонимных наследников от него.

    Получается мне действительно прийдется перекрывать эти переменные повторным объявлением для разрыва связи с предком?

    Не очень понял суть вопроса. Чтобы разорвать связь с предком нужно просто удалить наследование от него.
    Ответ написан
    2 комментария
  • Можно ли получать данные из ResultSet не удаляя их из ResultSet?

    Vamp
    @Vamp
    Можно "перемотать" ResultSet и повторно прочитать из него данные:
    try (ResultSet rs = statement.executeQuery(query)) {
        while (rs.next()) {
            System.out.println("User: " + rs.getString("login"));
        }
        rs.beforeFirst(); // <-- перематываем
        while (rs.next()) {
            System.out.println("Hello, " + rs.getString("login"));
        }
    }


    Но это не сработает, если драйвер создал ResultSet типа TYPE_FORWARD_ONLY или реализация ResultSet не поддерживает перемотку в принципе. В этом случае придётся прочитать весь результат полностью в промежуточное хранилище и дальше работать уже с ним:
    class User {
        private final String login;
        private final String name;
        private final String email;
    
        public User(ResultSet rs) throws SQLException {
            login = rs.getString("login");
            name = rs.getString("name");
            email = rs.getString("email");
        }
    
        public String getLogin() {
            return login;
        }
        public String getName() {
            return name;
        }
        public String getEmail() {
            return email;
        }
    }

    List<User> users = new ArrayList<>();
    try (ResultSet rs = statement.executeQuery(query)) {
        while (rs.next()) {
            users.add(new User(rs));
        }
    }
    for (User u : users) {
        System.out.println("Hello, " + u.getLogin());
    }

    Вариант с использованием отдельного класса для хранения результатов используется повсеместно и имеет своё собственное название - DTO (Data Transfer Object).
    Ответ написан
    Комментировать
  • Почему поток не отрабатывает задуманного в Java?

    Vamp
    @Vamp
    Такой вывод получается из-за того, что тест считается завершенным сразу после выхода из метода runWithSleep, после которого программа завершается принудительно, несмотря на наличие всё ещё выполняющихся не daemon потоков.

    Чтобы увидеть полный вывод запустите код не в контексте junit или добавьте ожидание в код теста:

    exec.shutdown();
    exec.awaitTermination(100, TimeUnit.SECONDS);
    Ответ написан
    2 комментария
  • Почему при отправке сообщений в Кафке, если не правильно написан топик которого не существует то сообщения все равно уходят и нет ошибки?

    Vamp
    @Vamp
    Нужно в настройках самого кластера кафки установить параметр auto.create.topics.enable в значение false
    Ответ написан
    Комментировать