Saudação, amigos. Esta sexta-feira será a primeira lição no novo grupo de cursos
Java Developer . É para este curso que a publicação atual será dedicada.

Muitos desenvolvedores de java usam o
Spring para implementar dependências. Alguns podem ter experimentado o
Google Guice ou mesmo os
Serviços OSGi . Mas muitos não sabem que o Java já possui um DI interno. Você acha que apareceu no Java 11 ou 12? Não, está disponível no Java 6.
O ServiceLoader fornece a capacidade de pesquisar e criar instâncias registradas de interfaces ou classes abstratas. Se você estiver familiarizado com o Spring, isso será muito semelhante às
anotações Bean e
Autowired . Vejamos exemplos de uso do Spring e ServiceLoader. E discuta as semelhanças e diferenças.
Primavera
Primeiro, vamos ver como criar uma DI simples na primavera. Crie uma interface simples:
public interface SimpleService { String echo(String value); }
E a implementação da interface:
import org.springframework.stereotype.Component; @Component public class SimpleServiceImpl implements SimpleService { public String echo(final String value) { return value; } }
Confira
@Component
. Esta anotação registrará nossa classe como um bean em um contexto do Spring.
E a nossa classe principal.
@SpringBootApplication public class SpringExample implements CommandLineRunner { private static final Logger log = LoggerFactory.getLogger(SpringExample.class); @Autowired List<SimpleService> simpleServices; public static void main(String[] args) { SpringApplication.run(SpringExample.class, args); } public void run(final String... strings) throws Exception { for (SimpleService simpleService : simpleServices) { log.info("Echo: " + simpleService.echo(strings[0])); } } }
Observe a anotação
@Autowired
na
SimpleService
combinação
SimpleService
. Anotação
@SpringBootApplication
criada para procurar automaticamente beans em um pacote. Em seguida, na inicialização, eles são injetados automaticamente no
SpringExample
.
Carregador de serviço
Usaremos a mesma interface do exemplo do Spring, portanto não a repetiremos aqui. Em vez disso, observe imediatamente a implementação do serviço:
import com.google.auto.service.AutoService; @AutoService(SimpleService.class) public class SimpleServiceImpl implements SimpleService { public String echo(final String value) { return value; } }
Na implementação, “registramos” a instância do serviço usando a anotação
@AutoService
. Essa anotação é necessária apenas no momento da compilação, pois o javac a usa para gerar automaticamente um arquivo de registro de serviço (
Nota do tradutor: para dependência maven que contém @AutoService
, especifique o escopo - fornecido) :
META-INF/services/io.github.efenglu.serviceLoader.example.SimpleService
Este arquivo contém uma lista de classes que implementam o serviço:
io.github.efenglu.serviceLoader.example.SimpleServiceImpl
O nome do arquivo deve ser o nome completo do serviço (interface). Um arquivo pode ter qualquer número de implementações, cada uma em uma linha separada.
Nas implementações,
DEVE haver um construtor sem parâmetros. Você pode criar esse arquivo manualmente, mas o uso da anotação é muito mais fácil. E a classe principal:
public class ServiceLoaderExample { public static void main(String [] args) { final ServiceLoader<SimpleService> services = ServiceLoader.load(SimpleService.class); for (SimpleService service : services) { System.out.println("Echo: " + service.echo(args[0])); } } }
O método
ServiceLoader.load
é chamado para obter um
ServiceLoader
, que pode ser usado para obter instâncias de serviço. A instância ServiceLoader implementa a interface
Iterable
para o tipo de serviço, portanto, a variável
services
pode ser usada na
for each
loop.
E daí?
Ambos os métodos são relativamente pequenos. Ambos podem ser usados com anotações e, portanto, são bastante fáceis de usar. Então, por que usar o ServiceLoader em vez do Spring?
Dependências
Vejamos a árvore de dependência do nosso exemplo simples do Spring:
[INFO] -----------< io.github.efenglu.serviceLoader:spring-example >----------- [INFO] Building spring-example 1.0.X-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- maven-dependency-plugin:3.1.1:tree (default-cli) @ spring-example --- [INFO] io.github.efenglu.serviceLoader:spring-example:jar:1.0.X-SNAPSHOT [INFO] +- org.slf4j:slf4j-api:jar:1.7.25:compile [INFO] +- org.springframework:spring-context:jar:4.3.22.RELEASE:compile [INFO] | +- org.springframework:spring-aop:jar:4.3.22.RELEASE:compile [INFO] | +- org.springframework:spring-core:jar:4.3.22.RELEASE:compile [INFO] | | \- commons-logging:commons-logging:jar:1.2:compile [INFO] | \- org.springframework:spring-expression:jar:4.3.22.RELEASE:compile [INFO] +- org.springframework.boot:spring-boot-autoconfigure:jar:1.5.19.RELEASE:compile [INFO] +- org.springframework.boot:spring-boot:jar:1.5.19.RELEASE:compile [INFO] \- org.springframework:spring-beans:jar:4.3.22.RELEASE:compile
E compare com o ServiceLoader:
[INFO] io.github.efenglu.serviceLoader:serviceLoader-example:jar:1.0.X-SNAPSHOT ## Only provided dependencies for the auto service annotation [INFO] \- com.google.auto.service:auto-service:jar:1.0-rc4:provided [INFO] +- com.google.auto:auto-common:jar:0.8:provided [INFO] \- com.google.guava:guava:jar:23.5-jre:provided [INFO] +- com.google.code.findbugs:jsr305:jar:1.3.9:provided [INFO] +- org.checkerframework:checker-qual:jar:2.0.0:provided [INFO] +- com.google.errorprone:error_prone_annotations:jar:2.0.18:provided [INFO] +- com.google.j2objc:j2objc-annotations:jar:1.1:provided [INFO] \- org.codehaus.mojo:animal-sniffer-annotations:jar:1.14:provided
Se não prestarmos atenção às dependências fornecidas, o ServiceLoader
NÃO terá dependências. É isso mesmo, ele só precisa de Java.
Realmente não importa se você está desenvolvendo seu aplicativo baseado no Spring, mas se estiver escrevendo algo que será usado em muitas estruturas diferentes ou se você tiver um aplicativo de console pequeno, isso já poderá ser de grande importância.
Velocidade
Para aplicativos de console, o tempo de inicialização do ServiceLoader é
MUITO mais curto que o Spring Boot App. Isso se deve ao menor número de códigos para download, à ausência de varredura, à ausência de reflexão, à ausência de grandes estruturas.
A memória
A primavera não é famosa por economizar memória. Se o uso da memória for importante para você, considere usar o ServiceLoader for DI.
Módulos Java
Um dos aspectos principais dos módulos Java foi a capacidade de proteger totalmente as classes em um módulo do código fora do módulo. O ServiceLoader é um mecanismo que permite que o código externo “acesse” implementações internas. Os módulos Java permitem registrar serviços para implementações internas, preservando a borda.
De fato, este é o único mecanismo de suporte à injeção de dependência oficialmente aprovado para módulos Java. Spring e a maioria das outras estruturas de DI usam reflexão para encontrar e conectar seus componentes. Mas isso não é compatível com os módulos Java. Mesmo a reflexão não pode olhar para os módulos (a menos que você permita, mas por que você precisa).
Conclusão
A primavera é uma grande coisa. Tem muito mais funcionalidade do que nunca no ServiceLoader. Mas há momentos em que o ServiceLoader é a escolha certa. É simples, pequeno, rápido e sempre disponível.
Código fonte completo para exemplos no meu
Git Repo .
Só isso. Vejo você no
curso !