Spring Data Rest é vida

Fala aí galera, tudo bem? Meu nome é Fernando e sou instrutor e desenvolvedor na caelumalura.

Não é de hoje que o Spring vem facilitando nossa vida, e vamos combinar que os caras mandam muito bem!

Agora imagina só um mundo perfeito, onde declaramos nossas classes de modelo, repositórios e magicamente temos enpoints para fazermos todas as operações que declaramos nos nossos repositórios (por exemplo incluir, listar, alterar e remover).

Esse mundo perfeito existe e nele essa mágica se chama Spring data rest. Sim, ao utilizarmos ele só de declararmos nossos repositórios temos endpoints disponíveis.

Tá, legal saber que existe o Spring Data Rest.  Mas e como uso ele? (Show me the code!!!)

Bom galera eu montei um exemplo bem básico com Spring Boot Maven e está no meu Github. Nele você vai ver no arquivo pom.xml as seguintes declarações:

Spring Boot

<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>1.4.2.RELEASE</version>
</parent>

E os staters do Spring Data e do Spring data Rest

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

Além dos Starters tenho também o h2 como banco de dados em memória e o lombok.

<dependency>
	<groupId>com.h2database</groupId>
	<artifactId>h2</artifactId>
</dependency>

<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
</dependency>

Para quem não conheçe o lombok, ele nos ajuda a escrever menos código repetitivo por exemplo: getter, setter, construtor, toString, equals/hashcode e etc...

Com ele anotamos nossos atributos com @Getter e/ou @Setter, anotamos nossa classe com @NoArgsConstructor ou @RequiredArgsConstructor, @AllArgsConstructor e ao compilarmos nosso código ele gera os byte-codes equivalentes.Vale a pena dar uma conferida!

No meu caso estou usando a anotação @Data (que engloba um monte dessas anotações) e @NoArgsConstructor (para ter um construtor sem parâmetros, por conta dos frameworks).

(OBS: Para rodar o projeto por dentro do IDE você precisa instalar o agent lombok, do contrário seu IDE não vai encontrar o getters, setters e afins.)

Em java temos duas classes de modelo Book e Author.

@Entity
@Data
@NoArgsConstructor
public class Author {

	@Id
	@GeneratedValue
	private Long id;

	@NotBlank
	private String name;

}
@Entity
@Data
@NoArgsConstructor
public class Book {

	@Id
	@GeneratedValue
	private Long id;

	@NotBlank
	private String title;

	@NotBlank
	private String description;

	@Min(50)
	private Integer numOfPages;

	@Valid
	@ManyToMany(fetch=FetchType.EAGER)
	private Set<Author> authors = new HashSet<>();

	public void add(Author author) {
		authors.add(author);
	}

}

Além do modelo tenho também um repositórios para cada, BookRepository e AuthorRepository

public interface BookRepository extends CrudRepository<Book, Long> {
}
public interface AuthorRepository extends CrudRepository<Author, Long>{
}

Para subir a aplicação tenho a classe Application que inicia o Spring Boot e salva um livro e um autor no banco de dados;

@SpringBootApplication
public class Application {

	public static void main(String[] args) throws Exception {
		SpringApplication.run(Application.class, args);
	}

	@Autowired
	private BookRepository bookRepository;

	@Autowired
	private AuthorRepository authorRepository;

	@PostConstruct
	@Transactional
	public void onLoad(){

		Author alberto = new Author();
		alberto.setName("Alberto");
		authorRepository.save(alberto);

		Book book = new Book();
		book.setTitle("Spring MVC");
		book.setDescription("Domine o principal framework web Java");
		book.setNumOfPages(237);

		book.add(alberto);

		bookRepository.save(book);

	}

}

E pronto! Temos uma endpoints para efetuarmos o CRUD de livros e autores. (Spring Data Rest é ou não é vida?)

Ao acessarmos http://localhost:8080, temos o seguinte retorno

{
"_links" : {
"authors" : {
"href" : "http://localhost:8080/authors&quot;
},
"books" : {
"href" : "http://localhost:8080/books&quot;
},
"profile" : {
"href" : "http://localhost:8080/profile&quot;
}
}
}

O Spring Data Rest usa fortemente os conceitos de HATEOAS que a grosso modo diz que através de um recurso disponível deve ser possível navegar pelo estado desse recurso através dos links. Por padrão o spring usa um modelo de apresentação chamado HAL para representação da resposta.

Bom os endpoints /authors e /books me parecem bem ok, o que deve ter neles, mas e o endpoint /profile?

Esse enpoint tem as meta-informações do seus serviços, por exemplo ao acessarmos http://localhost:8080/profile/authors temos o seguinte resultado

{
"alps":{
"version":"1.0",
"descriptors":[
{
"id":"author-representation",
"href":"http://localhost:8080/profile/authors&quot;,
"descriptors":[
{
"name":"name",
"type":"SEMANTIC"
}
]
},
{
"id":"create-authors",
"name":"authors",
"type":"UNSAFE",
"rt":"#author-representation"
},
{
"id":"get-authors",
"name":"authors",
"type":"SAFE",
"rt":"#author-representation"
},
{
"id":"patch-author",
"name":"author",
"type":"UNSAFE",
"rt":"#author-representation"
},
{
"id":"update-author",
"name":"author",
"type":"IDEMPOTENT",
"rt":"#author-representation"
},
{
"id":"get-author",
"name":"author",
"type":"SAFE",
"rt":"#author-representation"
},
{
"id":"delete-author",
"name":"author",
"type":"IDEMPOTENT",
"rt":"#author-representation"
}
]
}
}

Essas meta-informações estão associadas a qual representação temos no enpoint (no nosso caso)  authors e toda a definição dessa representação (os atributos e métodos). O endpoint profile é definido na RFC 6906.

Dentro da representação do author temos um objeto com id author-representation e nele temos um descritor com nome name e o tipo dele é SEMANTIC.
Esse tipo SEMANTIC indica um elemento que guarda um estado (atributo).

Além desse objeto temos um objeto com id create-authors, get-authors ou update-author ambos com nome author porém com tipos diferentes (nesse caso temos UNSAFE, SAFE e IDEMPOTENT). Esse objetos são as operações que podemos fazer sobre essa representação (methods).

Cada tipo tem um comportamento diferente:

SAFE – operações desse tipo são idempotentes e não alteram estado no servidor (GET, HEAD).

UNSAFE – operações desse tipo não são idempotentes e alteram estado no servidor (POST).

IDEMPOTENT – operações desse tipo são idempotentes e alteram estado no servidor (PUT, DELETE).

Uma operação é considerada idempotente se o resultado dele for bem sucedida indiferente do numero de vezes que ela for executada.

Agora sim, vamos para os endpoints mais “óbvieis“. Irei utilizar o programa curl para efetuar as chamadas para nosso endpoints.

Para listar nosso autores podemos efetuar a seguinte requisição:

curl -X GET http://localhost:8080/authors

(Deixei explicito o verbo GET somente por questões didáticas).

Nesse caso temos o seguinte resultado:

{
"_embedded" : {
"authors" : [ {
"name" : "Alberto",
"_links" : {
"self" : {
"href" : "http://localhost:8080/authors/1&quot;
},
"author" : {
"href" : "http://localhost:8080/authors/1&quot;
}
}]
},
"_links" : {
"self" : {
"href" : "http://localhost:8080/authors&quot;
},
"profile" : {
"href" : "http://localhost:8080/profile/authors&quot;
}
}
}

Nesse caso temos um autor com o nome Alberto e os links do autor levam ao recurso dele mesmo. E os links da representação como um todo levam a lista de autores ou ao profile de autores.

Vamos seguirmos o link para listar somente o autor Alberto.

curl -X GET http://localhost:8080/authors/1

E nesse caso temos o seguinte resultado:

{
"name" : "Alberto",
"_links" : {
"self" : {
"href" : "http://localhost:8080/authors/1&quot;
},
"author" : {
"href" : "http://localhost:8080/authors/1&quot;
}
}
}

E para criar um novo autor? bom para isso temos que usar o verbo POST.

curl -X POST -H "Content-type: application/json" -d '{"name": "Fernando"}' http://localhost:8080/authors

Agora temos dois autores, e para alterar? Fácil né, só usar outro verbo. Nesse caso o PATCH

 curl -X PATCH -H "Content-type: application/json" -d '{"name": "Fernando Willian"}' http://localhost:8080/authors/2

E para excluir vamos usar o verbo DELETE

curl -X DELETE http://localhost:8080/authors/2

Podemos usar as mesmas operações para os livros.

Para deixar um pouco mais interessante, vamos adicionar um pouco de segurança ao nossos endpoints.
Para isso vamos adicionar o starter do Spring Security no nosso arquivo pom.xml:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>

E vamos configurar como será nossa autenticação e quais os endereços que queremos proteger:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.inMemoryAuthentication()
		.withUser("common").password("123").roles("USER").and()
		.withUser("admin").password("@dm1n").roles("ADMIN");
		
	}
	
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			.httpBasic()
			.and()
				.authorizeRequests()
					.antMatchers(HttpMethod.POST, "/**").hasRole("ADMIN")
					.antMatchers(HttpMethod.PATCH, "/**").hasRole("ADMIN")
					.antMatchers(HttpMethod.DELETE, "/**").hasRole("ADMIN")
				.and()
					.csrf()
						.disable();
	}
	
}

Nessa configuração temos que nos autenticar com uma conta com permissão de ADMIN para efetuar qualquer requisição com os verbos POST, PATCH e DELETE.

Agora para incluir um novo autor devemos nos autenticar:

curl -X POST -H "Content-type: application/json" -d '{"name": "Fernando"}' http://localhost:8080/authors -u admin:@dm1n

O mesmo se aplica para alteração ou exclusão.

E aí o que achou do Spring Data Rest? Já o conhecia?

Entrevista com Arjen Poutsma, commiter do Spring!

Eu estava esperando ansiosamente para escrever este post, ele contém uma rápida entrevista com um dos principais commiters do Spring, Arjen! Ele respondeu algumas perguntas que fiz sobre o mundo que nos espera com a nova versão do framework, com o suporte integral ao conceito de Reactive Programming. Já vou deixar aqui a versão em inglês, depois posto a tradução.

Alberto: Just to introduce to our readers, could you talk a little bit about you and your role in the Spring Framework?

Arjen:  My name is Arjen Poutsma. I have been working on Spring for over ten years. I started out with Spring Web Services, then worked on Spring MVC – specifically the REST features, then Spring Scala, and for the last two years I have been working on Spring Reactive, together with Brian Clozel, Sebastien Deleuze, Stephane Maldini, abd Rossen Stoyanchev.

In this project, our team has been creating a new reactive foundation for Spring. We have essentially looked at the Spring MVC module, evaluated the code therein, and rewrote those pieces we need over to a non-blocking, event-driven style. We used Reactor as a foundation for this. The resulting code lives in the new spring-web-reactive module, next to the existing spring-webmvc module. The two modules share many concepts, but do not share any code. This is because Spring Web Reactive runs on a Reactive Streams HTTP adapter layer that’s fully non-blocking and reactive all the way down to the HTTP runtime. So while Spring MVC is built for and runs on Servlet containers, Spring Web Reactive runs also on non-Servlet runtimes such as Netty.

On top of the Spring Web Reactive module, we have two programming models. One is the existing annotation-based model, with @Controller, @RequestMapping and so on. These are the exact same annotations you use in Spring Web MVC. The other model is a recently introduced functional model, with RouterFunctions and HandlerFunctions.

I’ve attached a picture which hopefully makes this clearer.

architecture

Alberto: So, we are all excited about the new version of Spring, with the Reactive support. Could you tell us how this update will affect our way of programming?

Arjen: Just to be clear: upgrading to Spring 5 alone will not require any changes any changes to your code. As with previous major version upgrades, we have made sure that Spring 5 is backward compatible with Spring 4. So if you have a Spring 4 MVC application running on a Servlet engine, you will not have to change anything.

However, if you choose to use the new Reactive features, you will have to reconsider your architecture, as there any many differences. For instance, the guarantee that a HTTP request will be handled in a single thread is no longer the case: request will be handled by many threads, each doing a piece of the work when there is need for it (i.e. backpressure). Essentially, you are switching from a model a big thread pool, where most threads are waiting (either for incoming HTTP request, or waiting on a blocking datastore) to a model with a smaller thread pool, where each thread switches between requests many times and is effectively . Fortunately, you will not have to deal with these threads yourself, as this is where Reactor comes in. Reactor allows you to define your request handling pipeline in a functional, high-level abstraction through Flux and Mono. So rather than returning a List<Person> from your controller, for instance, you would return a Flux<Person>. However, this does also mean that relying on ThreadLocals will no longer work, and there are quite a number of frameworks and libraries (including Spring) that rely on these for some of their functionality.

Alberto: A lot of frameworks and libraries do not support this style of programming, Hibernate is an example. How can we combine our most used frameworks and Reactive Programming? The stack will evolve to support? I really like Spring Data JPA and I was thinking about that.

Arjen: Indeed, another difference between a traditional architecture and a reactive one is data stores. Obviously, in Reactive environments, there is a strong preference for non-blocking, asynchronous data stores. For some data stores, this is a great fit; for others it is not. There are a couple of NoSQL data stores that do reactive support: Couchbase, and MongoDB come to mind. For other, more traditional data access APIs, such as JDBC and JPA, there is no reactive support yet, so if you rely on them, there is no other choice than to wrap these synchronous, blocking APIs, and give them access to their own thread pool. Oracle is actually working on a non-blocking version of JDBC, but details are very sparse at this point. For JPA, the question is whether a session-based model actually makes sense in a reactive world.

Alberto: Could you explain how the Reactive Programming will help our applications to scale? Is there this relation between Reactive Programming and scalibility?

Arjen: I like to compare the two programming styles with an analogy. Imagine you are eating in a restaurant, and want to order food. The moment you make your order with the waiter, he goes off to the kitchen and prepare it. When the food is done, he will come back from the kitchen to serve it to you. After that, he will look around to see if there are any other customers in need of his attention. Now imagine the owner of the restaurant wants to have more tables, i.e. he wants to scale up. If he wants to make sure every table does not have to wait for too long, he would have to hire as many waiters as he added tables, adding a lot of cost. This is the traditional, one-thread-per-request Servlet model, where the customers are HTTP clients, and the waiters represent threads.

A reactive model would operate more like a real-life restaurant, where the waiters just relay the orders to kitchen staff, and many people (i.e. threads) are working on your order before it is served to you. Looking at it this way, it becomes quite obvious why the reactive model scales better: you are using the resources available more efficiently.

That said, the reactive model does come with a certain cost. It is harder to write asynchronous code, and it is harder to debug. So Reactive Programming will definitely help *certain* application to scale; for others I would simply say that the effort is simply not worth it. There will always be many cases where imperative is just fine for the task at hand and others where reactive and non-blocking are a must. And with Spring 5, you can do both.

Espero que você tenha gostado da entrevista! Arjen foi muito solícito e acho que nos forneceu boas respostas.

Como vai ser seu código com o Spring 5?

Faz mais ou menos 1 mês que saiu o primeiro milestone do Spring 5 com o suporte a tão comentada Reactive Programming. Não vou me alongar explicando os conceitos de programação reativa, porque já o fiz em outro post, aqui no blog mesmo. A ideia desse post é mostrar as configurações necessárias para você ter o seu projeto usando o Spring 5 e também ter um exemplo funcionando e que esteja linkado com os código que fazemos no dia a dia.

A primeira coisa que você pode fazer é clonar o projeto que eu disponibilizei no github. Lá dentro você vai encontrar o pom.xml.

		<properties>
			...
			<reactor.version>3.0.0.RC1</reactor.version>
			<spring.version>5.0.0.M1</spring.version>		
		</properties>
		<dependencyManagement>
			<dependencies>
				<dependency>
					<groupId>org.springframework.boot.experimental</groupId>
					<artifactId>spring-boot-dependencies-web-reactive</artifactId>
					<version>0.1.0.M1</version>
					<type>pom</type>
					<scope>import</scope>
				</dependency>
			</dependencies>
		</dependencyManagement>	
		<dependencies>
			<dependency>
				<groupId>org.springframework.boot.experimental</groupId>
				<artifactId>spring-boot-starter-web-reactive</artifactId>
			</dependency>		

Perceba que usamos um novo starter do Spring Boot chamado spring-boot-starter-web-reactive. Ele contém justamente todas as dependências que precisamos, algumas delas são:

  1. Project Reactor
  2. Adapters para os servidores

Outro detalhe importante é definir que queremos a versão 5 do Spring. Isso foi feito através da propriedade spring.version.

Pronto, com o projeto configurado, podemos dar uma olhada no código em si. A nossa ideia aqui é ter um endpoint que retorna um JSON representando uma lista de autores. Um código normal para isso seria assim:


	@Repository
	public class AutorDao {
		
		@Autowired
		private EntityManager manager;

		public List<Autor> listaTodos(){		
			return manager.createQuery("select a from Autor a").getResultList())				
		}
	}



	@RestController
	public class AutoresController {
		
		@Autowired
		private AutorDao autoresDao;

		@RequestMapping("/autores")
		public List<Autor> index() {
			List<Autor> result = autoresDao.listaTodos();		
			return result;
		}
	}

O problema desse código é que quando o Spring delega a execução para seu método, ele perde completamente o controle da situação. Você escreve a lógica que quiser aí dentro e ela vai ser executada até o fim. É o código que chamamos de imperativo.

A grande sacada do Spring 5 é trazer um pouco de programação funcional para nossas soluções do dia a dia. Antes de detalhar o código, vamos apenas dar uma olhada nele.

	@Repository
	public class AutorDao {
		
		@Autowired
		private EntityManager manager;

		public Mono<List<Autor>> listaTodos(){		
			Mono<List<Autor>> query = Mono.fromSupplier(() -> manager.createQuery("select a from Autor a").getResultList());
			return query;
			
		}
	}

	@RestController
	public class AutoresController {
		
		@Autowired
		private AutorDao autoresDao;

		@RequestMapping("/autores")
		public Mono<List<Autor>> index() {
			Mono<List<Autor>> result = autoresDao.listaTodos();
			System.out.println("ainda não vai ter feito a query");
			return result;
		}
	}	

Agora que você já viu, espero que tenha respirado fundo também, podemos debater um pouco do que aconteceu. A primeira coisa é que em vez de retornar uma List<Autor>, você retornou uma Mono<List<Autor>>. Lembre que Mono é um evento que emite apenas um item, nesse caso uma lista de autores. Uma variação dessa solução é a que segue abaixo.

	@Repository
	public class AutorDao {
		
		@Autowired
		private EntityManager manager;

		public Flux<Autor> listaTodos(){		
			Mono<Query> query = Mono.fromSupplier(() -> manager.createQuery("select a from Autor a"));
			Flux<Autor> list = query.flatMap((q) -> Flux.fromIterable(q.getResultList()));
			return list;
			
		}
	}

	@RestController
	public class AutoresController {
		
		@Autowired
		private AutorDao autoresDao;

		@RequestMapping("/autores")
		public Flux<Autor> index() {
			Flux<Autor> result = autoresDao.listaTodos();
			System.out.println("ainda não vai ter feito a query");
			return result;
		}
	}



Em vez do nosso DAO ter um evento só, nós dividimos os passos do método em criar um evento que emite uma Query e aí aplicamos uma transformação para ele gerar um evento que emite vários autores, no caso Flux<Autor>Não estou explicando os tipos, já que isso já está explicado no outro post.

A grande mudança aqui é que você declara os comportamentos em vez de executá-los e, fazendo isso, você delega para o Spring a responsabilidade de executar os seus códigos. A ideia é que a sua aplicação vai ter a chance de usar melhor os recursos da máquina onde ela está executando. Já que o framework parece ser mais competente para isso do que nós, desenvolvedores do código de negócio.

No exemplo que eu usei, entretanto, o maior problema é que usamos o Hibernate e ele não tem nada de reativo dentro dele. O ideal era que o método que executa a query com Hibernate retornasse a Flux e por aí vai. Quando a stack de tecnologias inteira da nossa aplicação for para esse lado, aí sim, vamos realmente tirar proveito.

 

Facilidades com Mapping no Spring MVC 4.3

Olá pessoal, tudo bem? Me chamo Alex Felipe, sou editor de conteúdo na Alura com mais foco no blog da Alura, e também, sou desenvolvedor! A partir de agora e eventualmente, estarei escrevendo alguns posts sobre Spring, ou seja, novidades do framework, dicas que fazem toda diferença no nosso dia-a-dia como desenvolvedor entre diversos tópicos super bacanas! Espero que gostem 🙂

Uma das maiores rotinas para nós, desenvolvedores que usam o Spring, é justamente mapear um método para disponibilizarmos um recurso no sistema. Como fazemos isso no Spring MVC? Basicamente utilizamos a annotation @RequestMapping, como por exemplo, para exibir a home do nosso sistema:

@Controller
public class HomeController {

	@RequestMapping("/")
	public String home() {
	    return "home";
	}

}

E então, quando acessamos o endereço "/" do nosso sistema, ele exibe o conteúdo da nossa página home. Porém, existe casos em que estamos dentro de um controller e queremos acessar a mesma URL, porém, dependendo do verbo HTTP, queremos que o nosso sistema tenha um comportamento diferente.

Por exemplo, em um sistema que eu estava desenvolvendo, continha o controller ProdutoController e dentro tinha duas funcionalidades a gravar() e a listar():

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("produtos")
public class ProdutoController {

	@RequestMapping(method = RequestMethod.POST)
	public String gravar(Produto produto) {
		// recebe produto e salva
		return "redirect:/produtos";
	}

	@RequestMapping(method = RequestMethod.GET)
	public ModelAndView listar() {
		ModelAndView mav = new ModelAndView("/produtos/lista");
		// pega produtos do banco e lista
		return mav;
	}

    //métodos

}

Note que ambos os métodos estão mapeados para o mesmo endereço, "/produtos". Porém, para que isso seja possível no controller, diferenciamos cada método pelo request method do HTTP. Em outras palavras, quando acessamos o endereço "produtos" com GET entramos no método listar() e quando acessamos com POST entramos no método gravar().

Por enquanto não há muita novidade, mas e se ao invés de ficar acessando @RequestMapping(method=AlgumaCoisa), pudéssemos fazer algo como @GetMapping para GET, ou então, @PostMapping para POST? Seria super bacana, né? Pois é, a partir da nova release 4.3 do Spring que saiu agora em Julho de 2016, já podemos fazer isso!

Então aquele mesmo controller, com essas novas annotations, ficaria da seguinte forma:

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("produtos")
public class ProdutoController {

	@PostMapping
	public String gravar(Produto produto) {
		// recebe produto e salva
		return "produtos/salvo";
	}

	@GetMapping
	public ModelAndView listar() {
		ModelAndView mav = new ModelAndView("/produtos/lista");
		// pega produtos do banco e lista
		return mav;
	}

	//métodos

}

Veja que agora o nosso mapeamento é muito mais objetivo! Em outras palavras, não precisamos mais nos preocupar em ficar passando o parâmetro method para indicar a qual tipo de request method o método irá funcionar. Além do @GetMapping e @PostMapping, temos também os seguintes métodos do HTTP:

Lembrando que essas novas annotations não são implementações totalmente novas, pois, por de trás dos panos, elas ainda fazem uso da annotaton @RequestMapping. Por exemplo, a implementação do @GetMapping:

@Target(value=METHOD)
 @Retention(value=RUNTIME)
 @Documented
 @RequestMapping(method=GET)
public @interface GetMapping

Isso significa que ainda podemos utilizar os mesmos atributos que utilizamos no @RequestMapping com exceção do method que já não é mais necessário 😉

Guia das annotations do Spring

O leitor Vinicius Silva escreveu um email dizendo que seria legal ter um post, meio que de referência, sobre várias das annotations mais comuns utilizadas em projetos com Spring. A seguir fiz uma lista das que vieram na minha cabeça e que realmente eu já utilizei nos projetos. Caso tenha faltado alguma, ou exista alguma outra que você queira saber, fique a vontade para perguntar :). Então vamos lá!

@Configuration

É uma annotation que indica que determinada classe possui métodos que expõe novos beans.

@Controller

Associada com classes que possuem métodos que processam requests numa aplicação web.

@Repository

Associada com classes que isolam o acesso aos dados da sua aplicação. Comumente associada a DAO’s.

@Service

Associada com classes que representam a ideia do Service do Domain Driven Design. Para ficar menos teórico pense em classes que representam algum fluxo de negócio da sua aplicação. Por exemplo, um fluxo de finalização de compra envolve atualizar manipular o carrinho, enviar email, processar pagamento etc. Este é o típico código que temos dificuldade de saber onde vamos colocar, em geral ele pode ficar num Service :).

@Component

A annotation básica que indica que uma classe vai ser gerenciada pelo container do Spring. Todas as annotations descritas acima são, na verdade, derivadas de @Component. A ideia é justamente passar mais semântica.

@ComponentScan

Em geral você a usa em classes de configuração(@Configuration) indicando quais pacotes ou classes devem ser scaneadas pelo Spring para que essa configuração funcione.

@Bean

Anotação utilizada em cima dos métodos de uma classe, geralmente marcada com @Configuration, indicando que o Spring deve invocar aquele método e gerenciar o objeto retornado por ele. Quando digo gerenciar é que agora este objeto pode ser injetado em qualquer ponto da sua aplicação.

@Autowired

Anotação utilizada para marcar o ponto de injeção na sua classe. Você pode colocar ela sobre atributos ou sobre o seu construtor com argumentos.

@Scope

Annotation utilizada para marcar o tempo de vida de um objeto gerenciado pelo container. Pode ser utilizada em classes anotadas com @Component, ou alguma de suas derivações. Além disso também pode usada em métodos anotados com @Bean. Quando você não utiliza nenhuma, o escopo default do objeto é o de aplicação, o que significa que vai existir apenas uma instância dele durante a execução do programa. Você alterar isso, anotando o local e usando alguma das constantes que existem na classe ConfigurableBeanFactory ou WebApplicationContext.

@RequestMapping

Geralmente utilizada em cima dos métodos de uma classe anotada com @Controller. Serve para você colocar os endereços da sua aplicação que, quando acessados por algum cliente, deverão ser direcionados para o determinado método.

@ResponseBody

Utilizada em métodos anotados com @RequestMapping para indicar que o retorno do método deve ser automaticamente escrito na resposta para o cliente. Muito comum quando queremos retornar JSON ou XML em função de algum objeto da aplicação.

@Primary

Caso você tenha dois métodos anotados com @Bean e com ambos retornando o mesmo tipo de objeto, como o Spring vai saber qual dos dois injetar por default em algum ponto da sua aplicação? É para isso que serve a annotation @Primary. Indica qual é a opção padrão de injeção. Caso você não queira usar a padrão, pode recorrer a annotation @Qualifier.

@Profile

Indica em qual profile tal bean deve ser carregado. Muito comum quando você tem classes que só devem ser carregadas em ambiente de dev ou de produção. Essa eu utilizo bastante e acho uma ideia simples, mas super relevante dentro do Spring.

@SpringBootApplication

Para quem usa Spring Boot, essa é uma das primeiras que você. Ela engloba a @Component, @ComponentScan e mais uma chamada @EnableAutoConfiguration, que é utilizada pelo Spring Boot para tentar advinhar as configurações necessárias para rodar o seu projeto.

Bom, acho que é isso aí. Adoro escrever posts que foram solicitados diretamente pela galera que acompanha o meu trabalho, é uma motivação bem grande. Querendo de outros temas, é só avisar por aqui ou pelo meu twitter.

@EnableAsync

Essa aqui não é tão comum, mas muitas vezes você precisa ações no sistema em background(outra thread). Essa annotation deve ser colocada em alguma classe marcada com @Configuration, para que o Spring habilite o suporte a execução assíncrona.

@Async

Uma vez que seu projeto habilitou o uso de execução de métodos assíncronos com a @EnableAsync, você pode marcar qualquer método de um bean gerenciado do projeto com essa annotation. Quando tal método for invocado, o Spring vai garantir que a execução dele será em outra thread.

Como anda a qualidade do seu projeto com Spring?

Quando pensamos em software, principalmente os de médio e longo prazo, um dos fatores que, com certeza vai machucar o time de desenvolvimento, é o momento onde começamos a modificar ou implementar novas funcionalidades no projeto. É nesse momento que os parentes dos antigos programadores começam a ser xingados e, com o passar do tempo, até uma reescrita do sistema começa a ser cogitada.

Acompanhamento e medição da qualidade de código é um fator super importante para a saúde do sistema. Para projetos que envolvem times, é sempre importante lembrar que, provavelmente, as pessoas do seu time vão sair e outras vão chegar. Buscando justamente colaborar com o mundo de desenvolvimento nesse aspecto, meu amigo Maurício Aniche (autor do livro de TDD da Casa do Código) vem trabalhando em projetos que nos ajudam a tirar métricas de qualidade do nosso código.

E para a felicidade geral dos times que usam Spring como alicerce dos seus sistemas, ele publicou um projeto focado em fornecer métricas para softwares que usem o framework. O Springlint pode ser executado como um plugin do maven ou até mesmo diretamente através do jar.

   mvn com.github.mauricioaniche:springlint-maven-plugin:0.4:springlint

Ele fornece uma saída indicando justamente os pontos onde estamos mandando bem e também todos os pontos onde o springlint considera que estamos pecando.

relatorio-springlint

A saída dele é uma página HTML. Na figura acima vemos o que ele fala de cada classe anotada com @Controller. Cada quadradinho verde revela o nome da classe :).

Existem métricas que já são estabelecidas no mercado como:

  1. Métricas de Acoplamento (CBO e RFC), pois não queremos classes acopladas.
  2. Coesão (LCOM), pois uma classe não coesa também não é bom.
  3. Complexidade ciclomática (WMC), pois um código muito complicado é difícil de manter.

Além dessas, também existem métricas criadas por ele durante os estudos do mestrado e do seu atual doutorado.

  1. Promiscuous Controller: Quando um Controller oferece muitas ações diferentes para seus clientes.
  2. Smart Controller: Quando há muito fluxo dentro de um só Controller.
  3. Meddling Service: Quando um Service acessa o banco de dados diretamente.
  4. Smart repositories: Quando um Repositório tem lógicas muito complicadas.
  5. Laborious Repository Method: Quando um único método em um Repositório faz mais de uma coisa só no banco de dados.
  6. Fat Repository: Quando um Repositório lida com muitas entidades de uma só vez.

Este é o tipo de informação que você deve buscar o tempo todo nos seus projetos. Muito simples de executar e com um resultado muito interessante.

Fiquei um bom tempo sem postar, mas o motivo foi nobre! Meu filho nasceu e eu passei 1 mês num curso super prático de cuidados de bebês! Semana que vem, na segunda, tem post novo!

 

Abstraindo o download de arquivos no Spring MVC

Há um tempo atrás, escrevi um post sobre como realizar download de arquivos utilizando o Spring MVC. Rafael Ponte,  um amigo meu, empreendedor/programador que mora em Fortaleza, perguntou se não tinha um jeito mais fácil de realizar o mesmo processo. Basicamente ele queria que tivesse uma abstração melhor para indicar um download, algo como o que segue:

	@RequestMapping(value = "/downloads/{code}", 
		method = RequestMethod.GET)
	public FileDownload dowload(@PathVariable("code") String code,
			@AuthenticationPrincipal User user) {

		Optional<Course> foundCourse = courseDao.findByUserEmailAndCode(
				user.getEmail(), code);

		if (!foundCourse.isPresent()) {
			return ResponseEntity.notFound().build();
		}

		Book book = bookDao.find(foundCourse.get().getCode());
		return new FileDownload(...);
	}

Pronto, eu não encontrei nada. Confesse que não procurei muito, e, ainda acho, que existe a chance de ter algo mais elegante e que eu não conheça. De todo jeito eu caí no mesmo problema, trabalhando em um projeto interno aqui na Caelum e a pessoa que estava pareando comigo fez o mesmo questionamento :/. Nesse momento paramos para pensar um pouco mais e chegamos na seguinte solução de código:

@RequestMapping(value = "/downloads/{code}", 
	method = RequestMethod.GET)
public HttpEntity<?> dowload(@PathVariable("code") String code,
		@AuthenticationPrincipal User user) {

	Optional<Course> foundCourse = courseDao.findByUserEmailAndCode(
			user.getEmail(), code);
	if (!foundCourse.isPresent()) {
		return ResponseEntity.notFound().build();
	}

	Book book = bookDao.find(foundCourse.get().getCode());
	return new DownloadEntity(book.generateDrmVersion(user), 
		code + ".pdf");
}

Basicamente alcançamos a mesma abstração que Rafael tinha sugerido :). A sacada é que um dos possíveis retornos do seu método é um objeto cuja classe seja igual ou filha de HttpEntity. Herdamos dela e criamos nossa abstração para representar um download.

package br.com.caelum.alumni.infra.spring;

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;

public class DownloadEntity extends HttpEntity<byte[]> {

	private HttpEntity<byte[]> httpEntity;

	public DownloadEntity(byte[] bytes, String fileName) {
		HttpHeaders httpHeaders = new HttpHeaders();
		httpHeaders.add("Content-disposition", "attachment; filename=\""+fileName+"\"");
		httpEntity = new HttpEntity<byte[]>(bytes, httpHeaders);
	}

	public HttpHeaders getHeaders() {
		return httpEntity.getHeaders();
	}

	public byte[] getBody() {
		return httpEntity.getBody();
	}

	public boolean hasBody() {
		return httpEntity.hasBody();
	}

	public boolean equals(Object other) {
		return httpEntity.equals(other);
	}

	public int hashCode() {
		return httpEntity.hashCode();
	}

	public String toString() {
		return httpEntity.toString();
	}
}

Herdamos de HttpEntity e simplesmente delegamos as chamadas dos métodos para o HttpEntity que criamos internamente no nosso objeto. Uma solução simples e que você pode reaproveitar para vários projetos seus.

Acho que esse é um passo muito importante quando falamos de programação, no caso aqui estamos usando orientação a objetos. Precisamos ser capazes de aplicar os conceitos que aprendemos em qualquer situação, não importa se o código é nosso ou provido por algum framework. Tem uma interface, você pode implementar, ta vendo uma classe que não é marcada como final, você pode herdar! O foco é a solução e a facilidade de manutenção :).