Construção de componentes: JavaBeans
- Em Java, Bean significa Componente
- Tem dois modelos de componentes em Java:
- JavaBeans normais para componentes baseados em eventos
- Normalmente mas não necessariamente feitos para GUI
- Enterprise JavaBeans (EJB) para Server Components
- Não há relação entre os dois modelos
- Aqui, usaremos:
- "JavaBeans" (ou "Bean") para significar JavaBeans normais
- "EJB" para significar Enterprise JavaBeans
- Esta seção fala de JavaBeans
Introdução a JavaBeans
- O modelo de componentes JavaBeans foi bolado para permitir que componentes
reutilizáveis possam ser compostos em outros JavaBeans, applets e componentes usando
ferramentas visuais
- Embora seja possível fazer Beans não visuais, a composição é feita
visualmente
- JavaBeans apareceu no JDK1.1
- Conceitos básicos
- Uma ferramenta visual descobre as propriedades, métodos e eventos de um Bean usando
introspeção (introspection)
- Introspeção: olhar dentro do Bean
- Dizemos que as propriedades, métodos e eventos são expostos pelo Bean
- Há duas formas de usar introspeção para descobrir essas coisas:
- Usando a API Reflection (que permite descobrir todos os métodos, atributos,
etc. de uma classe) e usando nomes padronizados de métodos para carregar a semântica
desejada
- Detalhando a informação de propriedades, métodos e eventos numa classe especial
BeanInfo (Bean Information)
- Propriedades correspondem a atributos de aparência de comportamento que podem ser
mudados em tempo de Design
- Propriedades são expostas pelos Beans para que possam ser customizados em tempo de
design
- A customização pode usar:
- Editores de propriedades
- Customizadores de Beans (são wizards de customização mais sofisticados)
- Beans usam eventos para se comunicarem com outros Beans
- Um Bean que quer receber eventos (Listener Bean) cadastra seu interesse junto ao Bean
que dispara o evento (Source Bean)
- As ferramentas visuais podem examinar o Bean e verificar quais eventos podem ser
disparados (enviados) e quais eventos podem ser tratados (recebidos) pelo Bean
- A persistência permite que um Bean salve seu estado e o restaure adiante
- JavaBeans usa Object Serialization para implementar a persistência
- Os métodos de um Bean não são diferentes de outros métodos em Java
- Todos os métodos públicos são exportados e podem ser chamados por outros Beans
- Criar e manipular Beans é muito simples e pode ser feito por programadores humanos ou
por ferramentas de design
Conceitos básicos de JavaBeans
Propriedades
- Simple Properties representam tipos de dados simples
- Podem ser nativos (int, String, ...) ou não
- Métodos devem se chamar get<NomePropriedade> e set<NomePropriedade>
public Color getCor();
public void setCor(Color cor);

- Desta forma, a ferramenta visual infere que existe uma propriedade
chamada "cor" que pode ser lida (get) e gravada (set)
- Indexed Properties contêm um array de valores possíveis
- Métodos getter e setter são como para Simple Properties mas manipulam um array, ou
recebem um índice a mais
public String[] getEstados()
public String getEstados(int índice)
public void setEstados(String[] estados)
public void setEstados(int índice, String estados)

- Bound Properties (Propriedades amarradas) avisam outros
objetos (Beans, por exemplo) de mudanças nos seus valores
- Uma mudança de valor gera um evento PropertyChangeEvent
- Listeners deste evento serão notificados da mudança

- Constrained Properties (Propriedades restritas) são como
Bound Properties mas os Listeners podem vetar a mudança
- Uma mudança aqui gera um evento VetoableChangeEvent

Eventos
- JavaBeans usam o modelo de Eventos padrão do Java 1.1
- Resumindo:
- Quem dispara um evento é um Event Source
- Um objeto interessado neste evento de cadastra junto ao Source e passa a ser um Listener
- Todos os listeners são avisados do evento (através de um callback feito pelo Source)
- O evento pode encapsular ou não informação especial para os listeners
- A fonte do evento (o objeto Source) sempre está disponível dentro do evento
- O modelo de eventos no JavaBeans
- Os eventos possíveis são:
- PropertyChangeEvent (para mudanças em Bound Properties)
- VetoableChangeEvent (para mudanças em Constrained
Properties)
- Os Beans Source devem implementar alguns métodos dependendo dos eventos envolvidos
- Para eventos PropertyChangeEvent: implementar os seguintes
métodos:
public void addPropertyChangeListener(PropertyChangeListener pcl);
public void removePropertyChangeListener(PropertyChangeListener pcl);
- Para eventos VetoableChangeEvent: implementar os seguintes
métodos
public void addVetoableChangeListener(VetoableChangeListener pcl);
public void removeVetoableChangeListener(VetoableChangeListener pcl);
- Para eventos customizados: implementar os seguintes métodos
public void add<Custom>Listener(<Custom>Listener cl);
public void remove<Custom>Listener(<Custom>Listener cl);
- Os Beans Listener devem implementar a interface apropriada
- PropertyChangeListener
- Deve implementar o método PropertyChange(PropertyChangeEvent e);
- VetoableChangeListener
- Deve implementar o método VetoableChange(VetoableChangeEvent e);
- <Custom>Listener
- Deve implementar o método public Xpto(<CustomEvent> e);
Um Bean mínimo
- Tem um construtor default ("no-arg" constructor)
- Para poder instanciar um Bean na ferramenta visual
- Implementa a interface Serializable
- Para poder salvar o Bean customizado em tempo de design
Classes especiais para ajudar
- PropertyChangeSupport e VetoableChangeSupport
- Ajudam a mandar os eventos para todos os listeners e outras tarefas semelhantes
Packaging de JavaBeans
Manifest-Version: 1.0
Name: tv/BotaoOnOff.class
Java-Bean: True
Created-By: 1.2 (Sun Microsystems Inc.)
Name: tv/Limites.class
Java-Bean: True
Name: tv/TVDisplay.class
Java-Bean: True
Name: tv/KeyPad.class
Java-Bean: True
Name: tv/TVEstado.class
Java-Bean: True
Uma Demonstração
- Veremos os detalhes de JavaBeans através de um exemplo
- Controle Remoto de uma TV
- Demo
- Vá para o diretório tvDemoApplet
- Digite: appletviewer tvDemo.html
- Estou usando o appletviewer porque usei o JDK1.2 para criar o applet e os browsers
(ainda) não tem suporte generalizado a Java 2

O design e código do exemplo Demo
Elementos básicos
- Para a TV
- Um botão liga/desliga com luzinha mostrando o estado on/off
- Botões Up/Down de mudança de canal
- Não modelados aqui para simplificar
- Uma tela
- Um display do número de canal
- Para o controle remoto
- Um botão liga/desliga com luzinha mostrando o estado on/off
- Botões Up/Down de mudança de canal
- Uma ilha numérica (keypad)
- Poderíamos adicionar controle de volume, etc. mas foi deixado como exercício
Design de Beans úteis para resolver o problema
- Vamos bolar Beans úteis para muitas situações e não só para atender a este exemplo
- Este é o maior problema da solução original apresentada na referência "Java
Application Frameworks" de Govoni
O Bean BotaoOnOff
- Tem uma propriedade "on"
- Tipo boolean
- Ao set alterada com setOn(boolean estado), a luzinha muda de cor
- É uma propriedade simples
- Não avisa ninguém quando muda de valor
- Nossa implementação pode até deixá-la como propriedade Bound para ter mais
flexibilidade no reuso em outras situações (onde haja listeners)
- É listener de outras propriedades "on"
- Ao receber um PropertyChangeEvent, chama setOn()
- Observar que clicar no botão não chama setOn()
- Alguém tem que se amarrar ao ActionPerformedEvent do clique de botão (o dispositivo
sendo ligado/desligado), mudar uma propriedade "on" particular da qual este
botão seria listener
- Assim, temos certeza que o estado "on" do botão reflete o estado de algum
dispositivo sendo controlado
- Também permite que mais de um botão controle o dispositivo e que cada botão saiba o
estado real do dispositivo

- Código do Bean BotaoOnOff segue:
- Observe que deixamos a propriedade "on" bound mesmo não precisando neste
exemplo (generalidade)
package tv; // poderia estar num package mais genérico
import java.awt.*;
import java.beans.*;
import java.io.*;
/**
* Um botão que acende quando está "on"
*/
public class BotaoOnOff extends Button
implements Serializable, PropertyChangeListener {
protected boolean on; // estado do botão
protected PropertyChangeSupport pcs;
// Construtor
public BotaoOnOff() {
// Cada bean tem um construtor default ("no-arg")
super("Liga/Desliga");
pcs = new PropertyChangeSupport(this);
on = false;
}
// Método getter para propriedade "on"
public boolean isOn() {
return on;
}
// Método setter para propriedade "on"
public void setOn(boolean on) {
// Dispara a mudança para os listeners, se houver
pcs.firePropertyChange("on", new Boolean(this.on),
new Boolean(on));
this.on = on;
mudaVisual();
}
// Muda o visual da "lâmpada on/off"
private void mudaVisual() {
setBackground(isOn() ? Color.red : Color.gray);
}
// Se este bean for listener da propriedade "on"
// de outro bean, então precisamos deste método
public void propertyChange(PropertyChangeEvent pce) {
String propriedadeMudada = pce.getPropertyName();
Object novoValor = pce.getNewValue();
if(propriedadeMudada.equals("on")) {
this.setOn(((Boolean)novoValor).booleanValue());
}
}
// Método de cadastro de listeners
// usa delegação para implementar
public void addPropertyChangeListener(PropertyChangeListener l) {
pcs.addPropertyChangeListener(l);
}
// Método de remoção de listeners
// usa delegação para implementar
public void removePropertyChangeListener(PropertyChangeListener l) {
pcs.removePropertyChangeListener(l);
}
}
- Exercício: Para deixar o botão mais genérico ainda, poderíamos permitir que um
clique do botão mudasse diretamente a propriedade "on". Forneça um bom design
para isso. Lembre que este comportamento é apenas uma possibilidade e não deve ser
obrigatório. Em que situação seria útil usar essa funcionalidade?
O botão comum
- Gera o evento ActionPerfomedEvent quando clicado
- Não tem propriedade
- Usaremos o botão OurButton já fornecido no Beans Development Kit (BDK)

O bean KeyPad
- Tem 10 botões numéricos
- O próprio Bean atende aos cliques desses botões e não os repassa para fora
- Tem uma área para mostrar o valor temporário sendo digitado
- Tem um botão ENTER
- Para mudar a propriedade "valor" que recebe o valor temporário
- A propriedade "valor" é bound para avisar outros Beans da mudança
- Tem uma propriedade simples inteira informando o número máximo de dígitos que o
keypad manipula
- Setado em tempo de design
- Default: 3

- Código do Bean KeyPad segue:
package tv;
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import java.io.*;
/**
* Um keypad
*/
public class KeyPad extends Container implements ActionListener {
protected int valor = 0; // propriedade do bean
protected int maxDígitos = 3; // propriedade do bean:
// número max de dígitos
private String valorDisplay; // valor mostrado no display
// antes de dar ENTER
protected PropertyChangeSupport pcs;
private Button botãoEnter = new Button("ENTER");
private Label display = new Label("");
// Construtor
public KeyPad() {
pcs = new PropertyChangeSupport(this);
Panel p = new Panel(); // para os botões de 0 a 9
p.setLayout(new GridLayout(2,5));
setLayout(new BorderLayout()); // para o keypad inteiro
// cria os botões
for(int i = 0; i < 10; i++) {
Button b = new Button(Integer.toString(i));
p.add(b);
b.addActionListener(this);
}
botãoEnter.addActionListener(this);
add("South", botãoEnter);
add("Center", p);
add("North", display);
display.setAlignment(Label.CENTER);
valorDisplay = "";
}
// getter do valor
public int getValor() {
return valor;
}
// setter do valor
public void setValor(int valor) {
pcs.firePropertyChange("valor", new Integer(this.valor),
new Integer(valor));
this.valor = valor;
valorDisplay = "";
display.setText(valorDisplay);
}
// getter de maxDígitos
public int getMaxDígitos() {
return maxDígitos;
}
// setter de maxDígitos
public void setMaxDígitos(int maxDígitos) {
this.maxDígitos = maxDígitos;
}
// Mudança do display
public void mudaDisplay(String digito) {
valorDisplay += digito;
// só permite maxDígitos digitos de display
if(valorDisplay.length() > maxDígitos) {
valorDisplay = digito;
}
display.setText(valorDisplay);
}
// cadastro para mudança de propriedade
public void addPropertyChangeListener(PropertyChangeListener l) {
pcs.addPropertyChangeListener(l);
}
// remoção de cadastro para mudança de propriedade
public void removePropertyChangeListener(PropertyChangeListener l) {
pcs.removePropertyChangeListener(l);
}
// trata o evento de clique de qualquer botão
public void actionPerformed(ActionEvent ae) {
if(ae.getSource().equals(botãoEnter)) {
try {
setValor(Integer.parseInt(valorDisplay));
} catch(NumberFormatException nfe) {
// "não deveria" acontecer
nfe.printStackTrace();
return;
}
} else {
mudaDisplay(ae.getActionCommand());
}
}
}
- Separamos a TV (fora os botões) em 3 partes
- Tem design mais simples para resolver o problema mas fizemos assim para:
- Dividir a parte gráfica (TVDisplay) do miolo da TV (TVEstado)
- Para poder demonstrar Constrained Properties
- A mudança de canal pode ser vetada se estiver fora de faixa (digamos de 1 a 99)
- Usamos um Bean Limites para isso

- Exercício: Mudar o design acima de forma ao Bean Limites se comunicar apenas com
TVEstado e de forma a TVDisplay receber o evento de TVEstado
O Bean Limites
- Recebe um VetoableChangeEvent e veta se o valor não estiver nos limites apropriados
- Os limites inferior e superior são propriedades simples
- Ao aceitar um valor, a propriedade bound "valor" é alterada e os listeners
são avisados
- O código segue:
package tv;
import java.beans.*;
import java.io.*;
/**
* Verifica se um valor inteiro cai entre dois limites
* e lança exceção se não aceitável
*/
public class Limites
implements VetoableChangeListener, Serializable {
protected int inferior; // propriedade: limite inf. para "valor"
protected int superior; // propriedade: limite sup. para "valor"
protected int valor; // propriedade: o valor do Bean
protected PropertyChangeSupport pcs;
public Limites() {
inferior = 1; // valores default
superior = 99;
}
// getter de inferior
public int getInferior() {
return inferior;
}
// setter de inferior
public void setInferior(int inferior) {
this.inferior = inferior;
}
// getter de superior
public int getSuperior() {
return superior;
}
// setter de superior
public void setSuperior(int superior) {
this.superior = superior;
}
// verifica validade do valor
public boolean isValorValido(int valor) {
return inferior <= valor && valor <= superior;
}
public void setValor(int valor) {
pcs.firePropertyChange("valor", new Integer(this.valor),
new Integer(valor));
this.valor = valor;
}
// vetoableChange - é aqui que recebemos
// o evento de mudança de valor
// e verificamos se é aceitável
public void vetoableChange(PropertyChangeEvent pce)
throws PropertyVetoException {
String propriedadeMudada = pce.getPropertyName();
Object novoValor = pce.getNewValue();
if(propriedadeMudada.equals("valor")) {
int valorProposto = ((Integer)novoValor).intValue();
if(isValorValido(valorProposto)) {
setValor(valorProposto);
} else {
throw new PropertyVetoException(
"Valor fora dos limites", pce);
}
}
}
}
O Bean TVDisplay
- Trata da parte gráfica da TV
- Liga e desliga a tela
- Mostra o canal selecionado
- Propriedades simples "on" e "canal"
- Podem ser bound para ter generalidade (uso futuro)
- O código segue:
package tv;
import java.awt.*;
import java.beans.*;
import java.io.*;
/**
* Um display de TV simples
*/
public class TVDisplay extends Canvas
implements PropertyChangeListener, Serializable {
protected int canal; // propriedade
protected boolean on; // propriedade
protected PropertyChangeSupport pcs;
private Color corCorrente; // representa on/off
// Construtor
public TVDisplay() {
super();
pcs = new PropertyChangeSupport(this);
canal = 0;
on = false;
corCorrente = Color.gray;
}
public void paint(Graphics g) {
int altura = getSize().height;
int largura = getSize().width;
int x = 0;
int y = 0;
int offset = 1;
g.setColor(Color.black);
g.drawRect(x, y, largura-offset, altura-offset);
g.setColor(corCorrente);
g.fillRect(x+1, y+1, largura-offset-1, altura-offset-1);
g.setColor(Color.red);
g.drawString(String.valueOf(canal), largura/2, altura/2);
}
// getter para "on"
public boolean isOn() {
return on;
}
// setter para "on"
public void setOn(boolean on) {
if(on != this.on) {
pcs.firePropertyChange("on", new Boolean(this.on),
new Boolean(on));
this.on = on;
}
corCorrente = isOn() ? Color.white : Color.gray;
repaint();
}
// getter para o canal
public int getCanal() {
return canal;
}
// setter para canal
public void setCanal(int canal) {
pcs.firePropertyChange("canal", new Integer(this.canal),
new Integer(canal));
this.canal = canal;
repaint();
}
// trata da mudança da propriedade canal de outro bean
public void propertyChange(PropertyChangeEvent pce) {
String propriedadeMudada = pce.getPropertyName();
Object novoValor = pce.getNewValue();
// mudança de "valor" de outro Bean altera o canal
if(propriedadeMudada.equals("valor")) {
this.setCanal(((Integer)novoValor).intValue());
}
if(propriedadeMudada.equals("on")) {
this.setOn(((Boolean)novoValor).booleanValue());
}
}
// cadastro para mudança de propriedade
public void addPropertyChangeListener(PropertyChangeListener l) {
pcs.addPropertyChangeListener(l);
}
// remoção de cadastro para mudança de propriedade
public void removePropertyChangeListener(PropertyChangeListener l) {
pcs.removePropertyChangeListener(l);
}
}
O Bean TVEstado
- Propriedade bound "on" com métodos
- getOn()
- setOn()
- onOff() // toggle da propriedade
- Propriedade Constrained "canal" com métodos
- getCanal()
- setCanal()
- incrementaCanal()
- decrementaCanal()

package tv;
import java.beans.*;
import java.io.*;
/**
* Manutenção do estado da TV
*/
public class TVEstado implements Serializable {
protected int canal; // propriedade: canal da TV
protected boolean on; // propriedade: TV ligada ou não
protected PropertyChangeSupport pcs;
protected VetoableChangeSupport vcs;
// Construtor
public TVEstado() {
pcs = new PropertyChangeSupport(this);
vcs = new VetoableChangeSupport(this);
canal = 0;
on = false;
}
// getter para propriedade "on"
public boolean isOn() {
return on;
}
// setter para propriedade "on"
public void setOn(boolean on) {
if(on != this.on) {
pcs.firePropertyChange("on", new Boolean(this.on),
new Boolean(on));
this.on = on;
}
}
// getter para propriedade "canal"
public int getCanal() {
return canal;
}
// setter para propriedade "canal"
public void setCanal(int canal) throws PropertyVetoException {
vcs.fireVetoableChange("valor", new Integer(this.canal),
new Integer(canal));
// se for vetado, não chegaremos aqui
this.canal = canal;
}
// Incrementa canal
public void incrementaCanal() {
mudaCanal(1);
}
// Decrementa canal
public void decrementaCanal() {
mudaCanal(-1);
}
private void mudaCanal(int incremento) {
try {
setCanal(getCanal() + incremento);
} catch(PropertyVetoException e) {
// mensagem de visualização para testes
System.out.println("Vetado!");
}
}
// Método de mudança on/off
public void onOff() {
setOn(!isOn());
}
// cadastro para mudança de propriedade
public void addPropertyChangeListener(PropertyChangeListener l) {
pcs.addPropertyChangeListener(l);
}
// remoção de cadastro para mudança de propriedade
public void removePropertyChangeListener(PropertyChangeListener l) {
pcs.removePropertyChangeListener(l);
}
// cadastro para mudança "vetável" de propriedade
public void addVetoableChangeListener(VetoableChangeListener l) {
vcs.addVetoableChangeListener(l);
}
// remoção de cadastro para mudança "vetável" de propriedade
public void removeVetoableChangeListener(VetoableChangeListener l) {
vcs.removeVetoableChangeListener(l);
}
}
Como o BDK "cola" os Beans
- Vamos ver um pequeno exemplo com uso de BDK
- Crie um manifest file manifest.txt como mostrado aqui
- Empacote os Beans num arquivo jar com o comando:
jar cfm tv.jar manifest.txt tv/BotaoOnOff.class
tv/TVEstado.class tv/Limites.class
tv/KeyPad.class tv/TVDisplay.class
- Coloque os Beans (o arquivo tv.jar) no diretório C:\bdk1.1\beans\jars
- Se o BDK tiver sido instalado em C:\bdk1.1
- Assim o BDK toma conhecimento dos novos Beans
- Execute o BDK
- Arraste um BotaoOnOff e um TVEstado na área de design
- Ligue o evento ActionPerformedEvent do BotaoOnOff para o método onOff() do Bean
TVEstado
- No TVEstado, use BindProperty/on e ligue com o Botão/on
- Clique no botão
- O BDK vai ter criado a seguinte "Adapter class" para colar as coisas:
- Isto é, para que uma chamada a actionPerformed causada pelo clique chame, na realidade,
onOff() de TVEstado
// Automatically generated event hookup file.
package tmp.sunw.beanbox;
import tv.TVEstado;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class ___Hookup_15e5be4184
implements java.awt.event.ActionListener,
java.io.Serializable {
public void setTarget(tv.TVEstado t) {
target = t;
}
public void actionPerformed(java.awt.event.ActionEvent arg0) {
target.onOff();
}
private tv.TVEstado target;
}
- A classe é instanciada pelo BDK e o setTarget já chamado com parâmetro indicando o
Bean TVEstado
- O addActionListener do botão (na realidade de java.awt.Button) é chamado pelo BDK para
ter a classe Hookup como Listener do botão
- Ao salvar os Beans (serializados), tudo está pronto para funcionar:

Construindo a aplicação
- Execute os seguintes passos no BDK:
- Arraste um BotaoOnOff para o controle remoto e altere o label se desejar
- Arraste dois ourButton para o controle remoto e altere os labels para Up e Down
- Arraste um KeyPad para o controle remoto e altere o número de dígitos se desejar
- Arraste um BotaoOnOff para a TV e altere o label se desejar
- Arraste um TVEstado, TVDisplay e Limites para a TV
- Altere os limites inferior e superior de Limites se desejar
- Liga/Desliga Remoto/ActionPerformed para TVEstado/OnOff
- Liga/Desliga TV/ActionPerformed para TVEstado/OnOff
- TVEstado/BindProperty/on para Liga/desliga Remoto/on
- TVEstado/BindProperty/on para Liga/desliga TV/on
- TVEstado/BindProperty/on para TVDisplay/on
- Up/ActionPerformed para TVEstado/incrementaCanal
- Down/ActionPerformed para TVEstado/decrementaCanal
- Keypad/BindProperty/valor para TVEstado/canal
- TVEstado/Event/VetoableChange para Limites/vetoableChange
- Limites/PropertyChange para TVDisplay/PropertyChange
- agora, manda contruir um applet que faz tudo que pedimos
- Você pode ver o resultado aqui
- Se não tiver um browser que aceite Java 2, use o appletviewer:
appletviewer tvDemo.html
- Observe que fizemos o controle remoto e a TV na mesma aplicação
- Na prática, poderíamos separa os dois usando objetos distribuídos (usando RMI ou
CORBA)
Features Avançados de JavaBeans
- Coisas que não usamos para manter a simplicidade
Property Editors
- Para editar propriedades especiais quando o ambiente visual não oferece um editor
default ou você não gosta do editor default
Uso da classe BeanInfo
- Para expor apenas os features desejados em vez de depender de reflexão
- Para associar um ícone a um Bean
- Para especificar um Customizer
- Para segregar features em categorias "normal" e "expert"
- Para prover um nome de display mais descritivo ou mais informação acerca de um feature
do Bean
Customizadores
- Um Wizard para customizar um Bean complexo
- Um "Wizard" transforma uma GUI onde podemos clicar em qualquer lugar
(complexo) numa GUI baseada em perguntas e respostas com muito menos escolhas a cada passo
(menos complexo)
- Parece mais uma receita de bolo para quem não está acostumado com algo
Bridge ActiveX
- Permite converter um Bean num Controle ActiveX
comp-3 programa anterior
próxima