ClassLoader de Driver JDBC

15 respostas
R

Bom, sei que para utilizar um driver JDBC preciso fazer o Class.forName afim de carregar a classe responsável por este driver. Vou fazer uma afirmação e gostaria que vocês me corrigissem se estiver errado, pois estou estudando o conceito de ClassLoaders:

1 - Quando um driver JDBC é carregado pela aplicação, ele possui um ClassLoader diferente do AppClassLoader, algo como: JDBCClassLoader. Então nós não poderíamos simplesmente usar as Classes do JDBC pois elas estão em outro ClassLoader. Sendo assim, ao fazer o Class.forName eu estou “puxando” as Classes JDBC para o ClassLoader AppClassLoader, assim ela estará no mesmo ClassLoader da minha aplicação e poderei utilizar normalmente.

Está certo ? Eu sei que é um pouco confusa a explicação, mas estou tentando entender o uso prático deste conceito.

15 Respostas

rodrigo.uchoa

Fala cara!

Acho que não nada diretamente a ver com classloaders não. Em drivers JDBC antigos (type 3 pra baixo), usar o Class.forName é uma maneira de carregar a classe do driver, que por sua vez deve ter um bloco de inicialização estático que precisa ser executado para o driver funcionar.

Lembrando que nos drivers JDBC novos isso não é mais necessário, pois ele usam o “Service Provider Mechanism”.

Acho que a maneira mais fácil de visualizar é a seguinte. Vamos supor que eu envio pra você uma classe, um arquivo .class, que contém uma lógica maluca qualquer para formatar seu computador (só um exemplo).

Como você faria para executar essa lógica maluca a partir de um código seu? Você instanciaria um objeto da classe que te mandei e invocaria o método que criei para fazer a formatação. Agora vamos complicar um pouco… Imagina que eu fosse criar a mesma classe, mas não colocasse essa lógica de formatação em método nenhum. Ou seja, se fosse do meu interesse que na verdade essa lógica de formatação fosse executada assim que a classe fosse carregada pela JVM, sem nem precisar invocar nenhum método dela? Eu usuaria um bloco de inicialização estática. E deve ser exatamente isso que os drivers JDBC fazem.

Na pratica, vc usar Class.forName(“com.mysql.jdbc.Driver”) ou simplesmente um “new com.mysql.jdbc.Driver()” daria o mesmo resultado. Só que claro, a primeira maneira, usando reflection, é a mais recomendada por uma série de motivos.

obs: Em java, indiretamente, tudo tem a ver com ClassLoaders. Mas não acho que era esse o foco da sua pergunta.

rodrigo.uchoa

Em termos de código para ficar ainda mais claro. Vamos supor que a classe do driver seja essa:

public class Driver {
	
	static {
		//bloco de inicialização estática
		System.out.println("Lógica caótica para carregar o driver!");
	}
}
Agora a sua classe que vai usar o driver:
public class MyDAO {	
	
	public void openConnection() {
		//nesse momento aquele System.out do driver vai ser executado
		Class.forName("org.foo.Driver"); 
	}
}

Pronto. O Driver para funcionar exigia que aquele bloco estático dele fosse executado. E usando o Class.forName() foi isso que nós fizemos acontecer.

R

Legal, entendi, mas você poderia resumir o porque usar Class.forName em vez de new MyClass

rodrigo.uchoa

Vou só lembrar mais uma vez que nos drivers JDBC mais novos, type 4, usar o Class.forName("") não é mais necessário. Eles utilizam o “Service Provider Mechanism”. SPI’s são inclusive um mecanismo poderoso que pouca gente conhece. Vale a pena pesquisar a respeito.

Então, acho que o principal motivo para usar o Class.forName("") seria garantir que o valor possa ser alterado em tempo de execução. Imagina que você vai fazer uma aplicação onde o usuário digita em uma tela o nome do driver que ele quer usar. Nesse caso, você poderia pegar o texto digitado por ele e passar pro Class.forName(""). Por outro lado usando o nome hard-coded com um “new”, você não iria conseguir. Ia perder essa flexibilidade. Digitar o nome do driver em uma tela é um caso extremo, mas é só um exemplo… O normal seria garantir a alteração de algum tipo de configuração ou propriedade de sistema (System.getProperty()).

Outro motivo é que você não precisa de fato que o driver seja instanciado, pois o código de inicialização está em bloco estático. Basta fazer com que a classe seja inicializada, e não uma instância específica. E o Class.forName("") atinge exatamente esse objetivo.

Ataxexe

Só pra complementar o Rodrigo. Os blocos estáticos dos drivers JDBC registram o Driver no DriverManager.

http://docs.oracle.com/javase/7/docs/api/java/sql/DriverManager.html#registerDriver(java.sql.Driver)

R

Chegamos a um ponto chave rodrigo.uchoa, quando você diz que o principal objetivo do Class.forName é inicializar a classe e não instanciar, entendo a diferença entre o “new” e o “Class.forName()”;

Pois com o “new” eu estaria criando um objeto em memória, e com o Class.forName eu estaria apenas inicializando a classe em memória, podemos dizer então: Com seu ClassLoader específico, que provavelmente é o ApplicationClassLoader.

Ataxexe

rlanhellas:
Chegamos a um ponto chave rodrigo.uchoa, quando você diz que o principal objetivo do Class.forName é inicializar a classe e não instanciar, entendo a diferença entre o “new” e o “Class.forName()”;

Pois com o “new” eu estaria criando um objeto em memória, e com o Class.forName eu estaria apenas inicializando a classe em memória, podemos dizer então: Com seu ClassLoader específico, que provavelmente é o ApplicationClassLoader.

O ClassLoader usado no Class.forName é o ClassLoader da classe que fez essa chamada. E o ClassLoader utilizado vai depender um pouco da hierarquia dos ClassLoaders utilizada e das configurações deles também. (Pode ser que o ClassLoader corrente delegue para o ClassLoader pai, por exemplo).

rodrigo.uchoa

Só pra complementar o Rodrigo. Os blocos estáticos dos drivers JDBC registram o Driver no DriverManager.

http://docs.oracle.com/javase/7/docs/api/java/sql/...terDriver(java.sql.Driver)


Você está certíssimo! Bom esclarecer sim! Valeu!

Chegamos a um ponto chave rodrigo.uchoa, quando você diz que o principal objetivo do Class.forName é inicializar a classe e não instanciar, entendo a diferença entre o “new” e o “Class.forName()”;

Pois com o “new” eu estaria criando um objeto em memória, e com o Class.forName eu estaria apenas inicializando a classe em memória, podemos dizer então: Com seu ClassLoader específico, que provavelmente é o ApplicationClassLoader.

O que você quer dizer por ApplicationClassLoader? O Class.forName("") faz com que a classe seja carregada pelo class loader sim. Qual deles vai depender do seu contexto, ele vai utilizar o class loader que foi usado para subir a classe atual nesse caso.

R

Obrigado rodrigo.uchoa, tenho ainda mais um dúvida a respeito do ClassLoader:

1 - O ClassLoader ApplicationClassLoader carrega por padrão as classes do meu CLASSPATH, ou seja, se todo meu projeto estiver lá, então está tudo certo.
PORÉM, eu posso criar uma pasta “/home/ronaldo/libs/” e colocar todas as minhas bibliotecas lá (hibernate, spring …) e depois só apontar no ECLIPSE o caminho para minhas bibliotecas. Neste caso como funciona o ClassLoader ? Como ele sabe que deve carregar minha biblioteca dentro de /home/ronaldo/libs/ ?

Ataxexe

rlanhellas:
Obrigado rodrigo.uchoa, tenho ainda mais um dúvida a respeito do ClassLoader:

1 - O ClassLoader ApplicationClassLoader carrega por padrão as classes do meu CLASSPATH, ou seja, se todo meu projeto estiver lá, então está tudo certo.
PORÉM, eu posso criar uma pasta “/home/ronaldo/libs/” e colocar todas as minhas bibliotecas lá (hibernate, spring …) e depois só apontar no ECLIPSE o caminho para minhas bibliotecas. Neste caso como funciona o ClassLoader ? Como ele sabe que deve carregar minha biblioteca dentro de /home/ronaldo/libs/ ?

Isso tudo será traduzido pelo Eclipse para o classpath da sua aplicação (o parâmetro -cp) ou então colocado em alguma pasta cujo ClassLoader utilizado procura os jars (a pasta WEB-INF/lib, por exemplo). O Eclipse não inbute nenhum ClassLoader em nada do que você faz.

R

Ah, então na verdade o Eclipse joga as minhas libs dentro do ClassPah atual mesmo que elas estejam em um diretório maluco qualquer.

Mas se estivéssemos trabalhando com o JAVAC puro, precisaremos dizer explicitamente onde estão nossas libs (que não estão no CLASSPATH), posso fazer isso pelo URLClassLoader, correto ?

Ataxexe

rlanhellas:
Ah, então na verdade o Eclipse joga as minhas libs dentro do ClassPah atual mesmo que elas estejam em um diretório maluco qualquer.

Mas se estivéssemos trabalhando com o JAVAC puro, precisaremos dizer explicitamente onde estão nossas libs (que não estão no CLASSPATH), posso fazer isso pelo URLClassLoader, correto ?

Calma lá. O Eclipse não joga as libs dentro do classpath, ele apenas mapeia o classpath do projeto para usar aquelas libs (exatamente o que você deveria fazer na mão). Para a compilação, a coisa é um pouco diferente porque o Eclipse não usa o javac (por isso ele não requer um JDK para ser utilizado, um JRE já basta), ele usa um compilador escrito em Java (muito poderoso, por sinal) que você pode encontrar no plugin JDT.

Quanto ao URLClassLoader, você não consegue pedir para ele carregar uma classe de fora do classpath sem alguns truques de reflexão.

Agora uma pergunta: por quê tanta insistência em manipular um classloader? E o funcionamento do classloader vai depender muito da plataforma onde você executar o código (o Eclipse tem um modelo de classloader, o JBoss tem outro, o Weblogic tem outro… existem semelhanças entre eles, mas você precisa ir à fundo se quiser saber exatamente como cada um vai se comportar).

R

Bom, ótima explicação, acho que ficamos por aqui neste tópico, o resto é só mais estudo e mais estudo.

A insistência no ClassLoader é porque estou estudando seu conceito, e tentando entender o seu real objetivo.

Ataxexe

rlanhellas:
Bom, ótima explicação, acho que ficamos por aqui neste tópico, o resto é só mais estudo e mais estudo.

A insistência no ClassLoader é porque estou estudando seu conceito, e tentando entender o seu real objetivo.

Ah tá. Vou dar uma explicada rápida então.

O Classloader é o mecanismo de carregamento de classes. A grande sacada é que você pode ter vários desses mecanismos para carregá-las e não é necessário que os bytecodes estejam fisicamente em um arquivo (você pode carregar uma classe de um outro servidor, por exemplo).

Além disso, Classloaders podem ter hierarquias, delegando o carregamento das classes para o Classloader pai, caso ele saiba como carregá-la. É com base nessa hierarquia de Classloaders que são feitos os servidores de aplicação. O JBoss 5, por exemplo, cria um Classloader para cada aplicação que está embaixo de um Classloader global e delega a este o carregamento das classes - por isso ele acaba carregando o hibernate dele em vez do hibernate no WEB-INF/lib. Você pode mudar esse comportamento, fazendo ele delegar ao pai somente se o classloader filho não puder carregar a classe (geralmente se faz isso quando aplicações do tomcat são migradas pro JBoss, já que o tomcat utiliza esse comportamento por padrão).

O Classloader é também uma das variáveis para comparações de classes. Para duas classes serem iguais, elas devem, também, terem sido carregadas pelo mesmo Classloader (por isso acontecem alguns erros malucos do tipo “A não é compatível com B, sendo que A implementa B”).

O fato de ser possível carregar classes usando outros classloaders é que faz o Eclipse poder manter versões diferentes dos mesmos plugins, isolar plugins de acessarem classes de outros plugins e mais algumas outras coisas. O OSGi, que é usado no Eclipse e agora também está implementado no novo JBoss 7, se utiliza muito desse comportamento para os relacionamentos entre módulos.

Não sei em que pé você está nos estudos de classloader, de repente já tem noção disso que escrevi, mas pode servir pra outros no fórum.

R

Obrigado, ajudou bastante. Já estou estudando a uns dias classLoaders e já me sinto confortável para falar a respeito :smiley:

Criado 12 de setembro de 2013
Ultima resposta 13 de set. de 2013
Respostas 15
Participantes 3