JavaRush /Blogue Java /Random-PT /Primavera. Lição 2. IoC/DI na prática
Umaralikhon
Nível 3
Красноярск

Primavera. Lição 2. IoC/DI na prática

Publicado no grupo Random-PT
E então... Na lição anterior revisamos brevemente a parte teórica de IoC e DI. Também configuramos o arquivo de configuração pom.xml para nosso projeto. Hoje começamos a criar a parte principal do programa. Primeiro, mostrarei como criar um programa sem IoC/DI. E então criaremos diretamente um programa que introduz dependências de forma independente. Ou seja, o controle do código passa para as mãos do framework (parece assustador). Enquanto gerenciamos o programa, imagine que existe uma determinada empresa. E a empresa (por enquanto) possui dois departamentos: Desenvolvimento Java e Departamento de Contratação. Deixe a classe que descreve o “Departamento de Desenvolvimento Java” ter dois métodos: String getName() - retornando o nome do funcionário, String getJob() - retornando a posição do funcionário. (Listagem 1)
package org.example;

public class JavaDevelopment {

    public String getName(){
        return "Alexa";
    }

    public String getJob(){
        return "Middle Java developer";
    }
}
Deixe a classe que descreve o departamento de contratação ter um construtor de entrada que aceita um funcionário e um método void displayInfo() que exibe informações sobre os funcionários. (Listagem 2)
package org.example;

public class HiringDepartment {
    private JavaDevelopment javaDevelopment;

    public HiringDepartment(JavaDevelopment javaDevelopment) {
        this.javaDevelopment = javaDevelopment;
    }

    public void displayInfo() {
        System.out.println("Name: " + javaDevelopment.getName());
        System.out.println("Job: " + javaDevelopment.getJob());
    }
}
Há também Main - uma classe que gerencia todos os departamentos. (Listagem 3)
package org.example;

public class Main {
    public static void main(String ... args){
        JavaDevelopment javaDevelopment = new JavaDevelopment();
        HiringDepartment hiringDepartment = new HiringDepartment(javaDevelopment);

        hiringDepartment.displayInfo();
    }
}
Estabilidade por enquanto. Quando executamos a classe Main obtemos o seguinte resultado:
Name: Alexa
Job: Middle Java developer
Agora vamos imaginar que a empresa está indo muito bem. Por isso, decidiram ampliar o escopo de suas atividades e abriram um departamento de desenvolvimento Python. E aqui surge a pergunta: como descrever este departamento ao nível do programa? Resposta: você precisa “copiar e colar” onde precisar descrever este departamento (o bom e velho método🙃). Primeiro, vamos criar a própria classe, que descreveria o departamento "Pythonists". (Listagem 4)
package org.example;

public class PythonDevelopment {
    public String getName(){
        return "Mike";
    }

    public String getJob(){
        return "Middle Python developer";
    }
}
E então iremos transferi-lo para o HiringDepartment. E HiringDepartment não diz nada sobre este departamento. Portanto, você terá que criar um novo objeto da classe PythonDevelopment e um construtor que aceite desenvolvedores Python. Você também terá que alterar o método displayInfo() para que ele exiba as informações corretamente. (Listagem 5)
package org.example;

public class HiringDepartment {
    private JavaDevelopment javaDevelopment;

    public HiringDepartment(JavaDevelopment javaDevelopment) {
        this.javaDevelopment = javaDevelopment;
    }


    //Тут создается отдел найма для Python - разработчиков
    private PythonDevelopment pythonDevelopment;

    public HiringDepartment(PythonDevelopment pythonDevelopment) {
        this.pythonDevelopment = pythonDevelopment;
    }

    //Тогда придется изменить метод displayInfo()
    public void displayInfo() {
        if(javaDevelopment != null) {
            System.out.println("Name: " + javaDevelopment.getName());
            System.out.println("Job: " + javaDevelopment.getJob());
        } else if (pythonDevelopment != null){
            System.out.println("Name: " + pythonDevelopment.getName());
            System.out.println("Job: " + pythonDevelopment.getJob());
        }
    }
}
Como podemos ver, o volume de código dobrou ou até mais. Com uma grande quantidade de código, sua legibilidade diminui. E o pior é que criamos todos os objetos manualmente e fazemos classes altamente dependentes umas das outras. Ok, concordamos com isso. Eles apenas descreveram um departamento. Não perderemos nada com isso. Bem, e se adicionarmos outro departamento? E se houver dois? Três? Mas ninguém proibiu “mineração e pastoreio”. Primavera.  Lição 2. IoC/DI na prática - 1 Sim, ninguém proibiu “Mina e Pasto”, mas não é profissional. Tyzh é um programador. E aqui você pode usar DI. Ou seja, trabalharemos não no nível da classe, mas no nível da interface. Agora os estados dos nossos objetos serão armazenados em interfaces. Dessa forma, as dependências entre as classes serão mínimas. Para fazer isso, primeiro criamos a interface Desenvolvimento, que possui dois métodos para descrever um funcionário. (Listagem 6)
package org.example;

public interface Development {
    String getName();
    String getJob();
}
Deixe então duas classes JavaDevelopment e PythonDevelopment implementarem (herdar) desta interface e substituir os métodos String getName() e String getJob(). (Listagem 7, 8)
package org.example;

public class JavaDevelopment implements Development {
    @Override
    public String getName(){
        return "Alexa";
    }

    @Override
    public String getJob(){
        return "Middle Java developer";
    }
}
package org.example;

public class PythonDevelopment implements Development {
    @Override
    public String getName(){
        return "Mike";
    }

    @Override
    public String getJob(){
        return "Middle Python developer";
    }
}
Então, na classe HiringDepartment você pode simplesmente definir um objeto de interface do tipo Development e também pode passar tal objeto para o construtor. (Listagem 9)
package org.example;

public class HiringDepartment {
    private Development development; //Определяем интерфейс

    //Конструктор принимает an object интерфейса
    public HiringDepartment(Development development){
        this.development = development;
    }

    public void displayInfo(){
        System.out.println("Name: " + development.getName());
        System.out.println("Job: " + development.getJob());
    }
}
Como podemos ver, a quantidade de código diminuiu. E o mais importante, as dependências foram minimizadas. Como os valores e dependências são realmente implementados para esses objetos? Existem três maneiras de fazer injeção de dependência:
  • Usando o construtor
  • Usando configuradores
  • Fiação automática (ligação automática)
Implementação usando um construtor Agora vamos falar sobre implementação usando um construtor. Veja a Listagem 9. O construtor da classe HiringDepartment espera um objeto do tipo Development como entrada. Tentaremos injetar dependências através deste construtor. É importante notar também que a injeção de dependência é realizada usando os chamados contêineres Spring. Existem três maneiras de configurar contêineres Spring:
  • Usando arquivos XML (método desatualizado)
  • Usando anotações + arquivos XML (modo moderno)
  • Usando código Java (modo moderno)
Agora estamos usando a configuração usando arquivos XML. Apesar de este método ser considerado desatualizado, muitos projetos ainda são escritos desta forma. Portanto você precisa saber. Primeiro, você deve criar um arquivo xml na pasta de recursos. Você pode dar qualquer nome, mas de preferência um nome significativo. Eu o chamei de "applicationContext.xml". Primavera.  Lição 2. IoC/DI na prática - 2 Neste arquivo escreveremos o seguinte trecho de código (Listagem 10):
<?xml version="1.0" encoding="UTF-8"?>
<beans  xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="javaDeveloper" class="org.example.JavaDevelopment"/>
    <bean id="pythonDeveloper" class="org.example.PythonDevelopment"/>

    <bean id="hiringDepartment" class="org.example.HiringDepartment">
        <constructor-arg ref="javaDeveloper"/>
    </bean>

</beans>
Agora, em ordem. As primeiras oito linhas de código não são interessantes para nós, são padrão. Você pode simplesmente copiá-los. A tag <bean> </bean> define um bean Spring. Um bean é um objeto criado e gerenciado por um contêiner Spring. Em palavras simples, o próprio contêiner Spring cria um novo objeto de classe para nós (por exemplo: JavaDevelopment javaDevelopment = new JavaDevelopment();). Dentro desta tag existem atributos de id e classe. id especifica o nome do bean. Este id será usado para acessar o objeto. É equivalente ao nome de um objeto em uma classe Java. class - define o nome da classe à qual nosso bean (objeto) está vinculado. Você deve especificar o caminho completo para a classe. Preste atenção ao bean contrataçãoDepartamento. Dentro deste bean existe outra tag <constructor-arg ref="javaDeveloper"/>. É aqui que ocorre a injeção de dependência (no nosso caso, injeção usando um construtor). <constructor-arg> - informa ao Spring que o contêiner Spring deve procurar dependências no construtor da classe definido no atributo bean. E qual objeto precisa ser associado é determinado pelo atributo ref , dentro da tag <constructor-arg>. ref - indica o id do bean a ser contatado. Se em ref em vez de javaDeveloper especificarmos o id pythonDeveloper, então a conexão ocorre com a classe PythonDevelopmen. Agora precisamos descrever a classe Main. Ficará assim: (Listagem11)
package org.example;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String ... args){
        //Определяем контекст файл в котором содержатся прописанные нами бины
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        //Получем бины, которые были определены в файле applicationContext.xml
        HiringDepartment hiringDepartment = context.getBean("hiringDepartment", HiringDepartment.class);

        hiringDepartment.displayInfo();

        context.close(); //Контекст всегда должен закрываться
    }
}
O que há aqui?
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Esta linha vincula a classe Main ao arquivo .xml que descreve nossos beans. O valor passado ao construtor deve corresponder ao nome do arquivo .xml. (No nosso caso applicationContext.xml).
HiringDepartment hiringDepartment = context.getBean("hiringDepartment", HiringDepartment.class);
Indica que queremos obter um bean (objeto) da classe HiringDepartment. O primeiro argumento aponta para o bean id que escrevemos no arquivo xml. O segundo argumento aponta para a classe que queremos contatar. Este processo é chamado de reflexão .
hiringDepartment.displayInfo();
 context.close(); //Контекст всегда должен закрываться
Aqui obtemos facilmente um método da classe HiringDepartment. Observe que não usamos a palavra-chave new para obter os objetos e não definimos objetos dependentes do tipo JavaDevelopment ou PythonDevelopment em nenhum lugar. Eles foram simplesmente descritos no arquivo applicationContext.xml. Preste atenção também na última linha. Você deve sempre fechar o contexto antes de encerrar. Caso contrário, os recursos não serão liberados e poderá ocorrer vazamento de memória ou operação incorreta do programa. Se você tiver dúvidas ou sugestões, escreva nos comentários, com certeza responderei. Obrigado pela atenção. Código fonte no link Meu carrinho do GitHub Conteúdo do curso Continua...
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION