Spring Framework 3 100% Livre de XML

Comecei a usar o Spring Framework quando este estava ainda na versão 2.5. Naquela época, muitos programadores reclamavam e criticavam o Spring pelo excesso de configurações baseadas em arquivos XML. Realmente, tudo era configurado via XML, embora eu pessoalmente, não achasse essa configuração tão confusa a ponto de deixar de usar o framework.

Após o lançamento da versão 3, o Spring passou a fornecer a possibilidade do uso de anotações de forma oficial. Assim, o número excessivo de arquivos do tipo XML foi realmente reduzido, restando apenas arquivos de configurações essenciais. Mesmo assim o Spring ainda continuou a receber criticas em relação a necessidade do uso de XML. Porém, o que muitos desconhecem é que existe a chamada “configuração programática”, que evita a necessidade de qualquer arquivo do tipo de XML. Esta possibilidade também apareceu na versão 3 do framework e, neste tutorial, vou demonstrar como configurar uma simples aplicação com acesso a banco de dados sem o uso de qualquer arquivo de configuração do tipo XML.

1. Preparando o projeto

Para o exemplo deste tutorial será necessário usar as bibliotecas do Spring Framework 3 e mais algumas dependências, faça os seguintes downloads:

Figura 1 – Bibliotecas do projeto

2. Iniciando o projeto

Crie um projeto na sua IDE de preferência, adicione as bibliotecas citadas no projeto e em seguida crie a classe de entidade Person.

Listagem 1. Classe Person
package com.mballem.tutorial.entity;

/**
 * http://www.mballem.com/
 */
public class Person {
    private long id;
    private String firstName;
    private String surname;
    private String email;
    private int age;

	//gere os metodos get/set e toString().
}

Vamos agora criar a interface IDAO – Listagem 2 – e classe PersonDAO – Listagem 3 – usando como modo de persistencia a API JdbcTemplate do Spring Framework.

Listagem 2. Interface IDAO.
package com.mballem.tutorial.dao;

import java.util.List;

/**
 * http://www.mballem.com/
 */
public interface IDAO {

    void createTable();

    void save(Object... values);

    void update(long id, Object... values);

    void delete(long id);

    List find();

    T findOne(String value);
}
Listagem 3. Classe PersonDAO.
package com.mballem.tutorial.dao;

import com.mballem.tutorial.entity.Person;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

/**
 * http://www.mballem.com/
 */
public class PersonDAO implements IDAO {

    private JdbcTemplate jdbcTemplate;

    public PersonDAO(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
        createTable();
    }

    @Override
    public void createTable() {
        String sql = "CREATE TABLE IF NOT EXISTS Persons( " +
                "id bigint " +
                "GENERATED BY DEFAULT AS IDENTITY (START WITH 1) not null, " +
                "firstName varchar(20) not null, " +
                "surname varchar(50) not null, " +
                "email varchar(50) not null unique, " +
                "age integer not null, " +
                "CONSTRAINT PK_Person PRIMARY KEY(id)" +
                ")";

        jdbcTemplate.execute(sql);
    }

    @Override
    public void save(Object... values) {
        String sql =
                "insert into Persons (firstName, surname, email, age) " +
                "values (?,?,?,?)";

        jdbcTemplate.update(sql, values);
    }

    @Override
    public void update(long id, Object... values) {
        String sql =
                "update Persons " +
                "set firstName = ?, surname = ?, email = ?, age = ? " +
                "where id = ?";

        jdbcTemplate.update(sql, values[0], values[1], values[2], values[3], id);
    }

    @Override
    public void delete(long id) {
        String sql = "delete from Persons where id = ?";
        jdbcTemplate.update(sql, id);
    }

    @Override
    public List find() {
        return jdbcTemplate.query("select * from Persons", new PersonMapper());
    }

    @Override
    public Person findOne(String value) {
        String sql = "select * from Persons where email like ?";
        return jdbcTemplate.queryForObject(sql, new PersonMapper(), value);
    }

    private static final class PersonMapper implements RowMapper {
        public Person mapRow(ResultSet rs, int rowNum) throws SQLException {
            Person person = new Person();
            person.setId((Long) rs.getObject("id"));
            person.setFirstName(rs.getString("firstName"));
            person.setSurname(rs.getString("surname"));
            person.setEmail(rs.getString("email"));
            person.setAge(rs.getInt("age"));
            return person;
        }
    }
}

A classe PersonDAO implementa a interface IDAO, assim, devemos codificar os metodos da interface. Temos também uma variável de instancia do tipo JdbcTemplate, a qual é inicializada (injetada) através do método construtor da classe. Ainda no construtor, invocamos o método createTable(), o qual será responsável por criar a tabela Persons no banco de dados (HSQLD em modo Standalone). Através do objeto jdbcTemplate, temos acesso a uma variedade de metodos que simplificam as instruções de insert, update, delete e também as queries (consultas). No final da classe temos um classe interna chamada PersonMapper. Esta classe tem como responsabilidade transformar os dados recebidos do banco em um objeto do tipo Person. E note que nos metodos de consulta, find() e findOne(), passamos como par metro um instancia da classe interna. Saiba mais sobre a API JdbcTemplate através da documentação do Spring.

Vamos agora criar a classe PersonService (Listagem 4). O padrão service é normalmente usado como a parte responsável pelas regras de negocio da aplicação e também quem faz as chamadas ao DAO. Neste exemplo, não teremos nenhuma regra de negócio que fizesse ser necessário ter um service, mas resolvi adicioná-lo ao projeto para exemplificar como podemos fazer a injeção de dependência em diversos beans. Nesta classe teremos uma variável de instancia do tipo PersonDAO, a qual será injetada através do construtor da classe PersonService.

Listagem 4. Classe PersonService.
package com.mballem.tutorial.service;

import com.mballem.tutorial.dao.PersonDAO;
import com.mballem.tutorial.entity.Person;

import java.util.List;

/**
 * http://www.mballem.com/
 */
public class PersonService {

    private PersonDAO personDAO;

    public PersonService(PersonDAO personDAO) {
        this.personDAO = personDAO;
    }

    public void save(Object... values) {
        personDAO.save(values);
    }

    public void update(long id, Object... values) {
        personDAO.update(id, values);
    }

    public void delete(long id) {
        personDAO.delete(id);
    }

    public List find() {
        return personDAO.find();
    }

    public Person findOne(String value) {
        return personDAO.findOne(value);
    }
}

3. Configurando os Beans sem uso de arquivos XML

A partir de agora vamos construir três classes, onde teremos a classe BeanDataSource responsável pela conexão com o banco de dados e também por criar o bean jdbcTemplate. Teremos ainda a classe BeanDaoSource, responsável pelos beans do tipo DAO e a classe BeanServiceSource, responsável pelos beans do tipo Service.

Veja na Listagem 5, a classe de configuração BeanDataSource. Essa classe será reconhecida pelo Spring como uma classe de configuração através da anotação @Configuration. Temos também três variáveis de instancia, urluser e pass, que representam os dados de conexão que estão armazenados em um arquivo de propriedades:

jdbc.url=jdbc:hsqldb:file:./db/tutorial-spring
jdbc.username=sa
jdbc.password=

Para ler este arquivo, usamos o bean nomeado como propriedades – @Bean(name = “propriedades”) – e assim, através das anotações @Value as variáveis são inicializadas com os valores contidos no arquivo. O bean chamado de dataSource será o responsável por realizar a conexão com o banco de dados e devemos injetá-lo no bean jdbcTemplate para que o objeto jdbcTemplate seja inicializado com uma instancia desta conexão.

Listagem 5. Classe BeanDataSource.
package com.mballem.tutorial.configuration;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PropertiesLoaderSupport;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

import javax.sql.DataSource;

/**
 * http://www.mballem.com/
 */
@Configuration
public class BeanDataSource {
    @Value("#{propriedades['jdbc.url']}")
    private String url;
    @Value("#{propriedades['jdbc.user']}")
    private String user;
    @Value("#{propriedades['jdbc.pass']}")
    private String pass;

    @Bean(name = "dataSource")
    public DataSource dataSource() {
        return new DriverManagerDataSource(url,url, pass);
    }

    @Bean(name = "jdbcTemplate")
    public JdbcTemplate jdbcTemplate() {
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource());
        return jdbcTemplate;
    }

    @Bean(name = "propriedades")
    public PropertiesLoaderSupport jdbcProperties() {
        PropertiesFactoryBean props = new PropertiesFactoryBean();
        props.setLocation(new ClassPathResource("/jdbc.properties"));
        return props;
    }
}

Vamos agora criar o bean para a classe PersonDAO, para isso, usamos a classe de configuração BeanDaoSource, da Listagem 6. Observe que usamos uma nova anotação, a @Import, que representará uma classe que desejamos importar para nosso bean. Essa anotação é similar a instrução import usada em arquivos de configuração do Spring do tipo XML. Temos também um variável de instancia do tipo JdbcTemplate, anotada como @Autowired. E por fim, criamos o bean personDAO e injetamos o objeto jdbcTemaplte através da construtor da classe PersonDAO.

Listagem 6. Classe BeanDaoSource.
package com.mballem.tutorial.configuration;

import com.mballem.tutorial.dao.PersonDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.jdbc.core.JdbcTemplate;

/**
 * http://www.mballem.com/
 */
@Configuration
@Import(value = { BeanDataSource.class })
public class BeanDaoSource {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Bean(name = "personDAO")
    public PersonDAO personDAO() {
        return new PersonDAO(jdbcTemplate);
    }
}

Na Listagem 7 encerramos as configurações com a classe BeanServiceSource, a qual irá criar um bean para a classe PersonService. Aqui, fazemos a injeção do bean personDAO através do construtor da classe PersonService.

Listagem 7. Classe BeanServiceSource.
package com.mballem.tutorial.configuration;

import com.mballem.tutorial.dao.PersonDAO;
import com.mballem.tutorial.service.PersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/**
 * http://www.mballem.com//
 */
@Configuration
@Import(value = { BeanDaoSource.class })
public class BeanServiceSource {

    @Autowired
    private PersonDAO personDAO;

    @Bean(name = "personService")
    public PersonService personService() {
        return new PersonService(personDAO);
    }
}

Basta agora testar a aplicação, para isso, utilize a classe Teste da Listagem 8. Quando usamos a configuração programática, devemos usar a classe AnnotationConfigApplicationContext ou AnnotationConfigWebApplicationContext para aplicações web. Através do método scan(), informamos o pacote em que o Spring deverá procurar pelas classes anotadas. Segundo a documentação, devemos invocar o método refresh().

Listagem 8. Classe Teste.
package com.mballem.tutorial;

import com.mballem.tutorial.entity.Person;
import com.mballem.tutorial.service.PersonService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * http://www.mballem.com/
 */
public class Teste {

    private PersonService personService;

    public Teste(PersonService personService) {
        this.personService = personService;
    }

    public static void main(String[] args) {
        //inicializa o container do Spring
        AnnotationConfigApplicationContext ctx =
                new AnnotationConfigApplicationContext();

        //informa em que pacote se encontram as anotacoes do Spring
        ctx.scan("com.mballem.tutorial");
        ctx.refresh();

        //inicializa a variavel personService com o bean
        //PersonService por meio do construtor de Teste.
        Teste t = new Teste(ctx.getBean(PersonService.class));

        t.save();

        t.update();

        t.delete();
    }

    private void save() {

        personService.save("Diogo Luiz", "Da Silva", "diogo@email.com", 25);
        personService.save("Roberto Carlos", "Ramos", "rbo.carlos@email.com", 30);
        personService.save("Luiz Carlos","De Souza", "luiz22@email.com", 22);
        personService.save("Viviane","De Vargas", "vivi.ane@email.com", 27);

        for (Person person : personService.find()) {
            System.out.println(person.toString());
        }
    }

    private void update() {

        Person person = personService.findOne("vivi.ane@email.com");

        personService.update(
                person.getId(), person.getFirstName(),
                person.getSurname(), "viviane@email.com", 31
        );

        for (Person p : personService.find()) {
            System.out.println(p.toString());
        }
    }

    private void delete() {

        Person person = personService.findOne("luiz22@email.com");

        personService.delete(person.getId());

        for (Person p : personService.find()) {
            System.out.println(p.toString());
        }
    }
}

Veja na Figura 2 a estrutura do projeto:

Figura 2 – Estrutura do projeto

Saiba mais em:

Ballem

Marcio Ballem é bacharel em Sistemas de Informação pelo Centro Universitário Franciscano em Santa Maria/RS. Tem experiência com desenvolvimento Delphi e Java em projetos para gestão pública e acadêmica. Possui certificação em Java, OCJP 6.

Você pode gostar...