spring framework ioc - inversão de controle jobson ronan {[email protected]}
TRANSCRIPT
Spring FrameworkIoC - Inversão de Controle
Jobson Ronan {[email protected]}
Motivação
Desenvolver uma aplicação sempre impõe desafios
Muitos deles já foram solucionados por outros esforçados “guerreiros” Criaram padrões... Padrões de projeto... Design
patterns
Alguns padrões muito comuns
Facade Singleton Factory Abstract Factory Command Adapter Decorator Service Locator Prototype ...
Design e Manutenibilidade
Sistemas manuteníveis Evoluem bem com mudanças e iterações Chave do segredo: Gerenciar dependências
Outros aspectos importantes Design por contrato
Definição limpa do contrato das classes/interfaces Contrato: O que faz, como se comporta Não como está implementado!
Testes unitários Escrever testes que verificam o contrato da classe/interface
Separação de interesses Escrever uma classe/interface para cada interesse
Dependência
É quando se prende um componente a outro por: Herança Composição Instanciação Instanciação por factory Parâmetro de método Uso de métodos ou atributos estáticos
Princípios de desenvolvimento ágil
Quando uma Componente possui uma dependência, sua implementação pode precisar mudar quando Os requisitos mudarem, ou... Quando sua dependência mudar
Abstrações tendem a ser estáveis Interfaces estão normalmente pouco sujeitas a serem alteradas
Componentes concretos tendem a ser instáveis Classes concretas normalmente estão mais sujeitas a terem
sua implementação alterada
Dependência por Composição
Composição cria um fraco acoplamento Quando o comportamento ou a interface da dependência
(UserDao) muda, apenas o dependente precisa se adaptar Não Possui os efeitos colaterais de herança
public class UserManager { private UserDao userDao;}
UserManager depende de UserDao
UserManager
UserDao
<<use>>
Dependência por Instanciação
WebServerProbe depende de Socket
public class WebServerProbe { public boolean isRunning() { Socket socket = new Socket();...
WebServerProbe
Socket
<<use>>
Dependência por factory
UserManager depende de UserDaoFactory(UserManager também depende de UserDao)
public class UserManager { public Collection getUsers() { UserDAO userDAO = UserDAOFactory.newInstance();...
UserManager
UserDao
<<use>> UserDaoFactory
<<use>>
<<create>>
Dependência por parâmetro de metodo
UserManager depende de Usuário
Mesmo acoplamento fraco obtido por composicao
public class UserManager { public void saveUser(User user) {...
UserManager
User
<<use>>
Dependência por uso de método estático
WebServerProbe depende de Arrays
public class WebServerProbe { private Collection ports; public void setPorts(Integer[] ports) { this.ports = java.util.Arrays.asList(ports);...
WebServerProbe
Arrays
<<use>>
Dependências
Algumas formas de dependência são piores que outras Herança: Quando novos métodos são adicionados e usados,
os componentes não recebem nenhum aviso para sobrescreve-los Prefira Composição
Classes, métodos e atributos estáticos (ex: Singletons): dependências ficam escondidas nos componentes dependentes, tornando difícil a alteração dos dependentes quando componentes estáticos são alterados Usar inversão de controle
Dependências
Dependências não são um mal Claro que sempre existirão dependências.
O objetivo é Minimizar o número de dependências no seu modelo Depender apenas de interfaces
Deve-se primar por Componentes fracamente acoplados Depender de interfaces e não de classes, o máximo possível
Quanto menos sua aplicação estiver sujeita a mudanças, mais estável e manutenível estará
“Fracamente acoplados”
Acoplamento forte Instanciação de classes concretas dentro da lógica de negócio
Uso de métodos estáticos
Herança Acoplamento fraco
Usando Factories e ServiceLocator
Usando inversão de controle
InputStream is = new FileInputStream(...);
InputStream is = StreamUtils.getConfigFileInputStream();
InputStream is = new ConfigFactory().createInputStream();
Inversão de controle
...Mas primeiro, sem IoC
Implementação sem IoC
O próprio componente precisa obter suas dependências Ex: JNDI, EJB Stub, JDBC Connection, arquivos de
propriedades, etc. Desacoplável por:
Codificar por interfaces Obter esses objetos usando um Factory ou um ServiceLocator
Efetuar-se chamadas a esses objetos, potencialmente para obter outras dependências Ex: JNDI, JDBC Connection Pool, etc. Encadeia dependências
Conseqüências
Código cheio de código específico de obtenção a dependências
Instanciação cria acoplamento Código ligado a um determinado ambiente (container)
(EJB Container, Servlet Container, Rich client, ...) Dificulta a implementação de testes unitários
Código específico de obtenção a dependências
As vezes, mais código para isso que para a implementação da lógica de negócio
Solução clássica: encapsular > Service locator, Factory Problema: Escrever os Factories Problema: Singletons are evil
Dependências não muito claras, atravessam todo o código Dificultam unit testing Dificultam Refactories
Codificação por interface desacopla, mas...
A instanciação ainda usa uma implementação concreta (=> acoplamento)
Solução clássica: usar uma Factory Problema:
forte acoplamento com a Factory Torna difícil a manutenção Torna difícil a implementação de Testes Unitários
Código ligado a um determinado ambiente
Código de recuperação de dependências dentro da lógica de negócio
Ambiente/container variados Standalone Java Application Swing/SWT Java Application Dentro de um container EJB Dentro de um servlet container
Exemplo: Obtenção de um objeto Connection (JDBC) standalone: usando java.sql.DriverManager standalone com pool: usando Apache Commons DBCP Tomcat: usando InitialContext e DataSource ...
Dificulta a implementação de testes unitários
Porque o “como” de obter uma dependência está codificado dentro da classe que se quer testar
Ex: Usando um Factory, como você pode mudar seu comportamento de acordo com um teste unitário?
Agora... Como testar MyLogic sem EJB e precisando de um EJB container?
class MyLogic { public void doSomething() { // use the InitialContext (JNDI) Service Locator // to retrieve the “BusinessLogic” object InitialContext ctx = new InitialContext(); BusinessLogic partner = (BusinessLogic) ctx.lookup(“my/business/logic/impl”); // now perform the actual logic: partner.doYourOwnBusiness(); }}
Inversão de controle
...E agora com IoC
Inversão de controle
Um meio de por os componentes juntos Define-se
Interfaces e implementações Dependências entre classes e interfaces (transformando-as em
“colaboradores”) O Container de Inversão de controle
Cria o dependente e a dependência, e injeta esta ultima no dependente
Possibilita selecionar que implementação de dependência injetar em cada dependente (por configuração, código, automática)
Inversão de controle
Inversão de controle
Inversão de controle
Arquitetura do container leve Usa POJOs Sem necessidade de deploy em um container pesado Aumenta testabilidade
Não é intrusiva Não depende de nenhuma API especifica do container Sem interfaces para implementar, sem classes para herdar,
exceto as suas
IoC - Princípios
“Hollywood principle” “Don´t call me, i´ll call you”
Sem IoC, componentes lógicos tem o controle sobre suas dependências e,por conseguinte,devem obtê-las
Com IoC, a lógica dos componentes não tem controle sobre suas dependências e não as obtém
Um container de IoC irá injetar as dependências nos objetos (Dependency Injection)
Principais vantagens
Efetivamente desacopla componentes lógico de suas dependências
Remove o código de obtenção de dependências dos dependentes (Agora é trabalho do container).
Melhora o design do modelo Aumenta a flexibilidade e o reuso de componentes
Testes unitários ficam mais fáceis Não depende de ambientes específicos
Exemplos
Sem inversão de controleclass MyLogic { private BusinessServiceInterface businessService = null; protected final BusinessServiceInterface getBusinessService() { if (businessService == null) { // retrieve JNDI InitialContext Context ctx = new InitialContext(); Context env = (Context) ctx.lookup(“java:comp/env”); Object obj = env.lookup(“ejb/BusinessServiceHome”); // retrieve EJB stub BusinessServiceHome businessServiceHome = (BusinessServiceHome) PortableRemoteObject.narrow( env, BusinessServiceHome.class); businessService = businessServiceHome.create(); } return businessService; } public void doYourThing() { getBusinessService().doYourBusiness(); }}
Exemplos
Sem inversão de controle Tenta testar isso! Terá que re-implementar quando BusinessService não for mais
um EJB Terá que re-implementar quando não estiver em um EJB
container (Sem JNDI) Terá que re-implementar quando não quiser mais um cache do
Stub
Exemplos
Sem IoC, com ServiceLocator:
Estratégia de ciclo de vida centralizado para BusinessServiceInterface Dependência centralizada com o container EJB Testes unitários ainda difíceis (precisa alterar o comportamento de MyService) Se diferentes classes precisarem de objetos BusinessServiceInterface
de diferentes fontes? Como cuidar disso? Você terá um não manutenível número de Services Locators, ou catastróficos
efeitos colaterais ao alterar a implementação de MyService
protected final BusinessServiceInterface getBusinessService(){ if (businessService == null) { businessService = MyServices.getBusinessService(); } return businessService;}
Exemplos
Com IoC
Sem dependência em como o BusinessService é obtido Sem código de obtenção dedependêcia, apenas um atributo e um setter Depende apenas da interface BusinessServiceInterface
class MyLogic { // businessService will be set by the IoC container: private BusinessServiceInterface businessService; // we'll use setter-based injection (explained later): public void setBusinessService(BusinessServiceInterface businessService) { this.businessService = businessService; } public void doYourThing() { // perform call on businessService businessService.doYourBusiness(); }}
Exemplos
Sem inversão de controle
Exemplos
Sem IoC, com ServiceLocator
Exemplos
Com IoC
Mas como funciona??
class MyMain { public static void main(String[] args) { // initialize the IoC container (here it's Spring): XmlBeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource(“beans.xml”)); // retrieve MyLogic: MyLogic myLogic = (MyLogic) xmlBeanFactory.getBean(“myLogic”); // call the method: myLogic.doYourThing(); }}
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE beans ...><!-- this is beans.xml --><beans> <bean id=”theBusinessSvc” class=”foo.BusinessServiceMock”/> <bean id=”myLogic” class=”foo.MyLogic”> <property name=”businessService”> <bean ref=”theBusinessSvc”/> </property> </bean></beans>
Conclusões
O princípio da inversão de controle pode ser aplicado elegantemente com o Spring
Isto irá reduzir em grande quantidade a quantidade de padrões de projeto aplicados, simplificando o design do modelo
Isto está mudando a forma de como desenvolver aplicações
“Precisamos de novos Design Patterns,os que conheciamos não são mais necessários.”
Jobson Ronan
Tipos de injeção de dependência
Tipo 1: Interface-based injection Tipo 2: Setter-based injection Tipo 2: Contructor-based injection
Exercício
Criar aplicação completa de reservas de videos DAOs + Fachada Não é necessário classes de cadastro
Extrair dependências possuídas pela fachada a DAOs concretos. A fachada deve apenas conhecer a interface dos DAOs
Use o springframework para injetar as dependências
Spring FrameworkIoC - Inversão de Controle
Jobson Ronan {[email protected]}