Primeiras aulas do curso Java Reflection parte 2: Anotações e Injeção de Dependências

Java Reflection parte 2: Anotações e Injeção de Dependências

Avançando com reflection - Introdução

Estamos de volta com o curso de Java Reflection da Alura, agora com a parte 2! Eu me chamo Gabriel Leite, e serei responsável por esse treinamento, aprofundando ainda mais os seus conhecimentos sobre a API de Reflection que começamos a aprender na parte 1. Para aproveitar bastante esse conteúdo, é necessário ter todos os cursos de Java da plataforma bem aproveitados. Ou seja, você deverá lidar bem e ter uma boa prática com a linguagem Java, além de saber quais recursos surgiram a partir do JDK8.

No caso, indicamos ter feito os seguintes cursos:

Tendo concluído esses cursos, você conseguirá o máximo possível de conhecimento deste treinamento. Além disso, você também deve ter feito a primeira parte do curso de Java Reflection, afinal o projeto que desenvolveremos nessa etapa terá como base o que foi desenvolvido anteriormente.

Como cenário de atuação, criaremos uma aplicação que simulará a interação entre o navegador e uma aplicação Java, na qual o usuário passará uma URL pedindo por um determinado recurso - por exemplo, listagem de recursos -, e a aplicação devolverá uma resposta em formato XML ou JSON. Embora esse cenário pareça simplista, existem várias bibliotecas e frameworks, coo o VRaptor, o XStream e o Spring, que existem somente para resolver esse tipo de situação.

O foco desse treinamento será pegar as informações acessadas na parte 1 e transformá-las em tipo de dado de transferência, como XML ou JSON. Nesse caso, trabalharemos com o XML. Portanto, veremos como a API de Reflection do Java (ou "API da Mãe Diná, para os íntimos) consegue nos ajudar nesse objetivo, além de conhecermos outros recursos mais avançados que deixarão nosso sistema bem mais flexível.

Ao final do projeto, se entrarmos com uma URL /produto/lista, teremos como resposta um XML da listagem de produtos cadastrados no sistema. Embora isso pareça simples, existem muitos conceitos interessantes por trás disso. Vamos começar?

Avançando com reflection - Manipulação de atributos

Começando deste ponto? Você pode fazer o download dos projetos alurator, alurator-playground e estoque-api já com as modificações feitas na primeira parte do curso e continuar seus estudos a partir deste capítulo.

Nessa aula, nosso objetivo será, com a ajuda da API de Reflection do Java, conseguirmos manipular atributos de objetos para gerar um XML referente aos dados deles. Com isso, queremos que o usuário consiga entrar com uma URL e, a partir dela, acionar um método que devolverá uma resposta na forma de um objeto. Pegaremos então esse objeto e transformaremos os dados que ele representa em um XML, que devolveremos ao usuário.

Mas como iremos manipular atributos de um objeto qualquer? A resposta para isso é bem simples. Novamente, utilizaremos a famosa classe Class<T>, que aprendemos na primeira parte do curso. A partir dela, é possível inferir diversos tipos de informações relativas à classe que ela parametriza, como os próprios atributos, conseguindo seus tipos, listando-os, entre outras ações.

Se tivermos um objeto da classe Class<T> parametrizado para a classe que queremos inferir, teremos quatro formas de recuperar so atributos dessa classe. A primeira é o métood getFields(), que retornará um array de objetos do tipo Field. Essa é uma classe da API de Reflection do Java que existe simplesmente para representar um atributo de uma classe. O array retornado será composto por todos os atributos públicos da classe que está sendo inferida pela classe Class<T>, ou por possíveis superclasses dela.

A segunda forma é por meio do método getField(), que recebe uma string indicando o nome do atributo que queremos recuperar. Ou seja, se a classe tem um atributo idade, precisaremos passar para o método getField() uma string com o valor idade. Novamente, a exemplo do método anterior, o getField() só retornará atributos públicos da classe que está sendo inferida pela classe Class<T> ou atributos públicos de possíveis superclasses que ela estenda.

Prosseguindo, temos o método getDeclaredFields(), que também retornará um array de objetos do tipo Field. A diferença é que esse array será composto por atributos públicos ou não - ou seja, os privados serão considerados nesse caso -, levando em conta somente atributos da classe que está sendo representada e excluindo os de possíveis superclasses.

Por últimos, temos o getDeclaredField(), que assim como o getField() recebe uma string representando o nome do atributo que queremos recuperar, nos retornando um objeto do tipo Field caso esse atributo seja encontrado. Do contrário, ele lançará uma exceção do tipo NoSuchFieldException. Esse método também retorna atributos públicos ou não, e apenas da classe inferida pela classe Class<T>.

Nos dois últimos métodos, podemos ter como retorno um atributo privado. Se tentarmos manipulá-lo, por exemplo tentando pegar o seu valor, obteremos uma exceção IlegalAccessException, que já vimos na parte 1. Ou seja, se quisermos manipular um atributo privado, teremos que setar a sua acessibilidade com o já conhecido setAccessible, passando como argumento a flag true. No entanto, pode ser que o Security Manager não permita essa mudança e lance uma exceção na nossa aplicação.

Agora que vimos esses conceitos, trabalhar. De volta ao Eclipse, dentro do pacote reflexao do projeto alurator-playground, criaremos uma nova classe TesteManipulaAtributos.java. No método main() dessa classe, criaremos um novo Produto e o atribuiremos a uma variável produto do tipo Object - lembrando que queremos transformar em XML qualquer objeto, por isso a escolha desse tipo.

De posse do produto, criaremos um objeto da classe Class<?> chamado classe utilizando o método getClass().

public class TesteManipulaAtributos {

    public static void main(String[] args) {
        Object produto = new Produto("Produto 1", 20.0, "Marca 1");
        Class<?> classe = produto.getClass();
    }

}

Nesse ponto encontraremos um problema, afinal ainda não temos uma classe Produto dentro do nosso projeto alurator-playground. Resolveremos isso copiando a classe Produto.java de estoque-api e colando-a no projeto, dentro de um novo pacote chamado br.com.alurator.playground.modelo que criaremos agora. Dentro desse mesmo pacote, criaremos também uma classe SuperProduto.java, que terá um atributo público do tipo int chamado id.

public class SuperProduto {
    public int id; 
}

Na classe Produto.java, adicionaremos um extends SuperProduto.

public class Produto extends SuperProduto {
    private String nome;
    private double valor;
    private String marca;
    public Produto(String nome, double valor, String marca) {
        this.nome = nome;
        this.valor = valor;
        this.marca = marca;
    }
//...

Assim, estaremos aptos a importar a classe Produto no nosso TesteManipulaAtributos, algo que pode ser feito com o atalho "Ctrl + Shift + O".

Nota: em sistemas operacionais macOS a tecla "Ctrl" é substituída por "Command".

package br.com.alura.alurator.playground.reflexao;

import br.com.alura.alurator.playground.modelo.Produto;

public class TesteManipulaAtributos {

    public static void main(String[] args) {
        Object produto = new Produto("Produto 1", 20.0, "Marca 1");
        Class<?> classe = produto.getClass();
    }

}

Queremos pegar o objeto produto e iterar por cada um dos seus atributos, mostrando na tela o nome do atributo e seu respectivo valor, mas sem considerar possíveis atributos de superclasses. Para isso, criaremos um laço for e iteraremos pelos atributos representando-os com a classe Field. Então, chamaremos o método classe.getDeclaredFields(). Por fim, mostraremos na tela o nome do atributo, que conseguiremos com o método getName() da classe Field, e o valor, que conseguiremos com get(). Esse método recebe como parâmetro o objeto ao qual o atributo pertence, nesse caso, o produto. Como ele lança algumas exceptions, adicionaremos um trows IllegalArgumentException para conseguirmos em frente.

public class TesteManipulaAtributos {

    public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
        Object produto = new Produto("Produto 1", 20.0, "Marca 1");
        Class<?> classe = produto.getClass();

        for(Field atributo : classe.getDeclaredFields()) {
            System.out.println(atributo.getName() + ": " + atributo.get(produto));
        }
    }

}

Se executarmos a classe dessa forma, receberemos uma IllegalAccessException informando que não conseguimos acessar os membros da classe Produto pois seus atributos possuem modificadores do tipo private. Isso porque o método getDeclaredFields() nos retorna atributos públicos ou não, ou seja, que sejam privados ou tenham outros tipos de modificadores de acesso. Quando tentamos manipular um atributo que não é público, recebemos esse tipo de exceção. Resolveremos o problema setando a acessibilidade do atributo como true.

public class TesteManipulaAtributos {

    public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
        Object produto = new Produto("Produto 1", 20.0, "Marca 1");
        Class<?> classe = produto.getClass();

        for(Field atributo : classe.getDeclaredFields()) {
            atributo.setAccessible(true);
            System.out.println(atributo.getName() + ": " + atributo.get(produto));
        }
    }

}

Executando novamente a classe, teremos:

nome: Produto 1

valor: 20.0

marca: Marca 1

Ou seja, recebemos exatamente os atributos que definimos na classe Produto. Para testarmos, tentaremos recuperar o atributo id da superclasse SuperProduto. Para isso, antes de iterarmos pelos atributos, faremos um System.out.printl() e chamaremos classe.getField(), que retorna um único atributo, seja ele da classe representada pelo objeto Class<?> ou de possíveis superclasses que ela estenda. Para isso, precisaremos passar como parâmetro o nome do atributo. Como esse método pode lançar várias exceções, adicionaremos um throws.

public class TesteManipulaAtributos {

    public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
        Object produto = new Produto("Produto 1", 20.0, "Marca 1");
        Class<?> classe = produto.getClass();

        System.out.println(classe.getDeclaredField("id"));

        for(Field atributo : classe.getDeclaredFields()) {
            atributo.setAccessible(true);
            System.out.println(atributo.getName() + ": " + atributo.get(produto));
        }
    }

}

Executando a classe dessa forma, teremos:

public int br.com.alura.alurator.playground.modelo.SuperProduto.id

nome: Produto 1

valor: 20.0

marca: Marca 1

Assim, logo antes da iteração que já havíamos recebido, conseguiremos o atributo id da classe SuperProduto, que é público e do tipo inteiro. Porém, se mudarmos o atributo id para private e tentarmos executar novamente essa classe, teremos como retorno um NoSuchFieldException informando que o atributo id não foi encontrado. Isso porque o getField() só retorna atributos públicos.

Agora que já sabemos iterar por atributos, pegando seus nomes e valores, nosso próximo desafio será colocar esses conhecimentos em prática gerando um XML com essas informações, como no exemplo:

<produto>
    <nome>Produto 1</nome>
    <valor>20.0</valor>
    <marca>Marca 1</marca>
</produto>

Avançando com reflection - Manipulação de atributos na prática

Nosso plano agora é colocarmos em prática os conhecimentos aprendidos no último vídeo, ou seja, a manipulação de atributos, para gerarmos o nosso XML. No método executa() da classe Alurator.java, do nosso projeto alurator, ao invés de retornarmos diretamente na declaração return o objeto recebido pelo método que o usuário solicitou, faremos com que o objeto retorno receba a devolução de uma classe chamada ConversorXML. Instanciaremos um objeto dessa classe e executaremos um método chamado converte(), que receberá o retorno como parâmetro.

public Object executa(String url) {
    // TODO - processa a requisicao executando o metodo
    // da classe em questao

    Request request = new Request(url);

    String nomeControle = request.getNomeControle();
    String nomeMetodo = request.getNomeMetodo();
    Map<String, Object> params = request.getQueryParams();



    Object retorno = new Reflexao()
                            .refleteClasse ( pacoteBase + nomeControle )
                            .criaInstancia()
                            .getMetodo(nomeMetodo, params)
                            .invoca();

        System.out.println(retorno);

        retorno = new ConversorXML().converte(retorno);

        return retorno;

}

Como a classe ConversorXML não existe, o Eclipse nos mostrará um erro. Criaremos então essa classe em um novo pacote br.com.alura.alurator.conversor, o que pode ser feito com a ajuda do Eclipse. Na classe, criaremos um método converte() recebendo um objeto do tipo Object.

public class ConversorXML {

    public String convert(Object objeto) {

    }

}

Nesse método, implementaremos toda a lógica de conversão de um objeto para um XML em string. Primeiro, conseguiremos a classe do objeto com o método getClass() e salvaremos o retorno em uma variável classeObjeto, afinal de contas precisaremos da classe para inferir as informações dela. Em seguida, criaremos um StringBuilder chamado xmlBuilder, recebendo a instância de um novo StringBuilder.

public class ConversorXML {

    public String converte(Object objeto) {
        Class<?> classeObjeto = objeto.getClass();
        StringBuilder xmlBuilder = new StringBuilder();
    }

}

Quem fez os cursos de Java mais básicos aqui na plataforma Alura já viu que quando queremos fazer várias concatenações/manipulações de strings, sempre é recomendado trabalhar com um StringBuilder, pois isso nos traz algumas vantagens. Aqui você pode encontrar uma breve explicação sobre essas vantagens.

Prosseguindo, verificaremos se o objeto que possuímos é uma instância de Collection e importaremos essa classe. Faremos isso pois podemos receber como parâmetro do conversor tanto um objeto simples representando um produto como também uma lista de produtos. No caso de uma lista, precisaríamos retornar um XML com várias tags <produto>, e um XML válido tem apenas uma tag raiz. Sendo assim, precisaríamos englobá-los com uma tag raiz, por exemplo <lista>. Já se não for uma lista, nosso objeto retornaria somente o XML com o <produto>.

Sendo assim, no caso da primeira situação ser verdadeira, faremos um casting do objeto para uma Collection<?> genérica, e salvaremos essa coleção dentro um novo objeto colecao.

public String converte(Object objeto) {
    Class<?> classeObjeto = objeto.getClass();
    StringBuilder xmlBuilder = new StringBuilder();

    if (objeto instanceof Collection) {
        Collection<?> colecao = (Collection<?>) objeto;
    }
}

Em seguida, a partir do xmlBuilder, chamaremos o método append() para adicionarmos uma nova string <lista>, que será a abertura dessa tag. Podemos, inclusive, copiar essa instrução e repetir o processo para o fechamento da tag.

public String converte(Object objeto) {
    Class<?> classeObjeto = objeto.getClass();
    StringBuilder xmlBuilder = new StringBuilder();

    if (objeto instanceof Collection) {
        Collection<?> colecao = (Collection<?>) objeto;

        xmlBuilder.append("<lista>");


        xmlBuilder.append("</lista>");
    }
}

Entre a abertura e o fechamento, geraremos o XML de cada um dos objetos da nossa coleção. Para isso, criaremos um laço for de Object, chamando-o de o, e iteraremos pela colecao. De posse desse objeto, chamaremos converte() passando o como parâmetro, e teremos como retorno um XML. Sendo assim, atribuiremos esse retorno a uma variável xml, que adicionaremos ao xmlBuilder com append().

public String converte(Object objeto) {
    Class<?> classeObjeto = objeto.getClass();
    StringBuilder xmlBuilder = new StringBuilder();

    if (objeto instanceof Collection) {
        Collection<?> colecao = (Collection<?>) objeto;

        xmlBuilder.append("<lista>");

        for (Object o : colecao) {
            String xml = converte(o);
            xmlBuilder.append(xml);
        }

        xmlBuilder.append("</lista>");
    }
}

O próximo passo será tratarmos a condição do objeto ser simples, e não uma coleção. Para isso, adicionaremos um else e desenvolveremos a nossa lógica. A ideia é, como vimos anteriormente, criar o XML de um produto. Para isso, precisaremos ter uma tag <produto>, que é o nome da classe, e depois várias tags com o nome de cada um dos atributos e seus valores.

Para conseguirmos o nome da classe, criaremos uma variável nomeClasse recebendo o retorno de classeObjeto.getName(), com o qual conseguiremos o nome da classe inferida pelo objeto da classe Class<?>. De posse desse nome, faremos xmlBuilder.append() para acrescentarmos a abertura da tag "<" concatenada com o nomeClasse e novamente concatenada com o fim da tag ">". Novamente, copiaremos essa instrução e a utilizaremos para criar o fechamento da tag.

public String converte(Object objeto) {
    Class<?> classeObjeto = objeto.getClass();
    StringBuilder xmlBuilder = new StringBuilder();

    if (objeto instanceof Collection) {
        Collection<?> colecao = (Collection<?>) objeto;

        xmlBuilder.append("<lista>");

        for (Object o : colecao) {
            String xml = converte(o);
            xmlBuilder.append(xml);
            } 
            xmlBuilder.append("</lista>");

    } else {
            String nomeClasse = classeObjeto.getName();
            xmlBuilder.append("<"+nomeClasse+">");

            xmlBuilder.append("</"+nomeClasse+">");
    }
}

Entre as chamadas de append(), precisaremos pegar cada um dos atributos do nosso objeto, adicionando suas tags e valores. Se precisaremos percorrer os atributos, adiocionaremos um novo laço for de Field, que chamaremos de atributo, e pegaremos da classeObjeto todos os atributos declarados, que conseguiremos com o getDeclaredFields(). Para nosso código funcionar, importaremos a classe Field.

for (Field atributo: classeObjeto.getDeclaredFields()) {

}

Sabemos que os atributos podem ser privados, e que, nesse caso, precisaremos permitir o acesso a eles. Sendo assim, faremos atributo.setAccessible() passando a flag true. Em seguida, pegaremos o nomeAtributo por meio de atributo.getName(). Por fim, conseguiremos o valorAtributo com atributo.get(), passando como parâmetro o objeto alvo do qual queremos conseguir a informação. 1

Como nesse ponto podemos ter várias exceções sendo lançadas, faremos um bloco try/catch para tratarmos essas exceções e, se acontecer um erro, printaremos esse erro e lançaremos uma nova RuntimeException() passando como mensagem "Erro na geração do XML!". Ao invés desse bloco englobar somente a declaração do get(), faremos com que ele englobe toda a declaração do nosso método converte().

public String converte(Object objeto) {
    try {
    Class<?> classeObjeto = objeto.getClass();
    StringBuilder xmlBuilder = new StringBuilder();

    if (objeto instanceof Collection) {
        Collection<?> colecao = (Collection<?>) objeto;

        xmlBuilder.append("<lista>");

        for (Object o : colecao) {
            String xml = converte(o);
            xmlBuilder.append(xml);
            } 
            xmlBuilder.append("</lista>");

    } else {
            String nomeClasse = classeObjeto.getName();
            xmlBuilder.append("<"+nomeClasse+">");

            for (Field atributo: classeObjeto.getDeclaredFields()) {
                atributo.setAccessible(true);

                String nomeAtributo = atributo.getName();

                Object valorAtributo = atributo.get(objeto);

            }


            xmlBuilder.append("</"+nomeClasse+">");
        }

    } catch (IllegalArgumentException | IllegalAccessException e) {
        e.printStackTrace();
        throw new RuntimeException("Erro na geração do XML!");

    }
}

Se já temos o nome e o valor dos atributos, basta montarmos as tags dos nossos conteúdos. Para isso, chamaremos xmlBuilder.append() e adicionaremos a abertura de uma tag "<" com o nomeAtributo e o fechamento ">". Em seguida, repetiremos essa linha, dessa vez fechando propriamente a tag. Entre elas, adicionaremos o valorAtributo também por meio do append().

for (Field atributo: classeObjeto.getDeclaredFields()) {
    atributo.setAccessible(true);

    String nomeAtributo = atributo.getName();

    Object valorAtributo = atributo.get(objeto);

    xmlBuilder.append("<" + nomeAtributo + ">");
    xmlBuilder.append(valorAtributo);
    xmlBuilder.append("</" + nomeAtributo + ">");

}

Com isso, bastará retornarmos o xmlBuilder.string().

public String converte(Object objeto) {
    try {
    Class<?> classeObjeto = objeto.getClass();
    StringBuilder xmlBuilder = new StringBuilder();

    if (objeto instanceof Collection) {
        Collection<?> colecao = (Collection<?>) objeto;

        xmlBuilder.append("<lista>");

        for (Object o : colecao) {
            String xml = converte(o);
            xmlBuilder.append(xml);
            } 
            xmlBuilder.append("</lista>");

    } else {
            String nomeClasse = classeObjeto.getName();
            xmlBuilder.append("<"+nomeClasse+">");

        for (Field atributo: classeObjeto.getDeclaredFields()) {
            atributo.setAccessible(true);

            String nomeAtributo = atributo.getName();

            Object valorAtributo = atributo.get(objeto);

            xmlBuilder.append("<" + nomeAtributo + ">");
            xmlBuilder.append(valorAtributo);
            xmlBuilder.append("</" + nomeAtributo + ">");

        }


            xmlBuilder.append("</"+nomeClasse+">");
        }

        return xmlBuilder.toString();

    } catch (IllegalArgumentException | IllegalAccessException e) {
        e.printStackTrace();
        throw new RuntimeException("Erro na geração do XML!");

    }
}

Após salvarmos todas as alterações, executaremos a classe Main.java do nosso estoque-api. No console, digitaremos /produto/lista e pressionaremos "Enter". Como resposta, teremos:

[Produto [nome=Produto 1, valor=20.0, marca=Marca 1], Produto [nome=Produto 2, valor=30.0, marca=Marca 1], Produto [nome=Produto 3, valor=40.0, marca=Marca 2]]
Response: <lista><br.com.alura.estoque.modelo.Produto><nome>Produto 1</nome><valor>20.0</valor><marca>Marca 1</marca></br.com.alura.estoque.modelo.Produto><br.com.alura.estoque.modelo.Produto><nome>Produto 2</nome><valor>30.0</valor><marca>Marca 1</marca></br.com.alura.estoque.modelo.Produto><br.com.alura.estoque.modelo.Produto><nome>Produto 3</nome><valor>40.0</valor><marca>Marca 2</marca></br.com.alura.estoque.modelo.Produto></lista>

Ou seja, conseguimos justamente a lista que queríamos, com as tags e valores devidamente colocadas. Porém, gostaríamos que cada produto fosse identificado pela tag <produto>, e na saída temos o fully qualified name da nossa classe. Esse é um detalhe que resolveremos na próxima aula lançando mão de uma funcionalidade muito importante do java...mas sem spoilers! Se você anseia por descobrir, vá correndo para a próxima aula!

Sobre o curso Java Reflection parte 2: Anotações e Injeção de Dependências

O curso Java Reflection parte 2: Anotações e Injeção de Dependências possui 114 minutos de vídeos, em um total de 43 atividades. Gostou? Conheça nossos outros cursos de Java em Programação, ou leia nossos artigos de Programação.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda Java acessando integralmente esse e outros cursos, comece hoje!

  • 1112 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Projeto avaliado pelos instrutores

    Projeto práticos para entrega e avaliação dos professores da Alura com certificado de aprovação diferenciado

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

Premium

  • 1112 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Projeto avaliado pelos instrutores

    Projeto práticos para entrega e avaliação dos professores da Alura com certificado de aprovação diferenciado

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

12X
R$75
à vista R$900
Matricule-se

Premium Plus

  • 1112 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Projeto avaliado pelos instrutores

    Projeto práticos para entrega e avaliação dos professores da Alura com certificado de aprovação diferenciado

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

12X
R$100
à vista R$1.200
Matricule-se

Max

  • 1112 cursos

    Cursos de programação, UX, agilidade, data science, transformação digital, mobile, front-end, marketing e infra.

  • Certificado de participação

    Certificado de que assistiu o curso e finalizou as atividades

  • App para Android e iPhone/iPad

    Estude até mesmo offline através das nossas apps Android e iOS em smartphones e tablets

  • Projeto avaliado pelos instrutores

    Projeto práticos para entrega e avaliação dos professores da Alura com certificado de aprovação diferenciado

  • Acesso à Alura Start

    Cursos de introdução a tecnologia através de games, apps e ciência

  • Acesso à Alura Língua

    Reforço online de inglês e espanhol para aprimorar seu conhecimento

12X
R$120
à vista R$1.440
Matricule-se
Procurando planos para empresas?
Acesso por 1 ano
Estude 24h/dia onde e quando quiser
Novos cursos toda semana