Spring Inversão de Controle, Desenvolvimento leve, Orientação a aspectos, ... Ricardo Cavalcanti [email protected] Jobson Ronan [email protected] O que é? Spring é um framework “leve” de inversão de controle(IOC) e orientação a aspectos(AOP) Torna o desenvolvimento de aplicações J2EE mais fácil e interessante! Como assim? Como Spring ajuda os desenvolvedores J2EE: Oferece um container leve de JavaBeans que elimina a necessidade de escrever códigos repetitivos “de encanador” como lookups Oferece um framework de inversão de controle que permite as dependências dos beans serem descobertas durante a instanciação Permitir que cross cutting concers como gerenciamento de transações ser aplicado aos beans como Aspectos ao invés ser escrito no objeto de negócio Oferecer camadas de abstração sobre tecnologias populares como JDBC e Hibernate que facilite seu uso e organize o gerenciamento de configuração Como é? Tudo isso seguindo alguns principios: O Código de sua aplicação não deve depender da API do Spring Spring não deve competir com boas soluções existentes mais sim promover integração Escrever código testável é crítico e o container deve ajudar(não interferir) nesse objetivo Usar o Spring deve ser empolgante Visão geral Sobre a arquitetura Spring pode ser usado em uma variedade de situações Aplicações web Aplicações que envolvem EJBs ou outros serviços Aplicações que acessem uma ou mais recursos(BDs) Sobre a arquitetura Apesar disso, Spring lidera os chamados frameworks para o Desenvolvimento “leve” de aplicações J2EE Realmente precisamos de serviços pesados, como EJB, providos pelos tradicionais Servidores de aplicação J2EE? Mas o que é IOC? Um modo de remover as dependências entre os objetos “injetando” as referências para os colaboradores sobre demanda (Dependency Injection) O framework de IOC determina como os objetos serão injetados, normalmente através de um arquivo de configuração XML Benefícios Remove a responsabilidade de encontrar ou criar objetos dependentes, movendo-a para a configuração Reduz o acoplamento entre implementações de objetos e encoraja o design baseado em interfaces Permite a aplicação ser reconfigurada fora do código Pode encorajar a escrever código “testável” Modo tradicional ... private AccountService accountService = null; public void execute(HttpServletRequest req, ...) throws Exception { Account account = createAccount(req); AccountService service = getAccountService(); service.updateAccount(account); } private AccountService getAccountService() throws ... { if (accountService == null){ Context ctx = new InitialContext(); Context env = (Context)ctx.look p("java:comp/env"); Object obj = env.lookup("ejb/AccountServiceHome"); AccountServiceHome home = (AccountServiceHome) PortableRemoteObject.narrow(env,Acco ntService.class); accountService = home.create(); } return accountService; } ... Modo tradicional Ainda podemos usar padrões de projeto como “Service Locators” ou “Factories” Modo Spring Com Spring, o container de IOC ira cuidar da injeção de uma apropriada implementação .. private AccountService accountService = null; public void setAccountService(AccountService accountService){ this.accountService = accountService; } public void execte(HttpServletRequest req, ...) throws Exception { Account account = createAccount(req); accountService.updateAccount(account); } ... Conceitos de AOP Aplicações devem estar possuir interesses como: Gerenciamento de transações Logging Seurança Essas responsabilidades devem pertencer a implementação das classes? Nossa classe AccountService deve ser ser responsavel pelo gerenciamento de transação, logging e segurança? Conceitos de AOP Implementação tradicional com todos estes interesses //... public class AccountService { public void updateAccount(updateAccount acount) throws Exception { SecurityManager.requireUserRole(“admin”); TransactionManager.beginTransaction(); try { //...código de negócio TransactionManager.commit(); log.info(“acount updated”); } catch (Exception e) { TransactionManager.rollback(); log.warning(“exception throw”, e); throw e; } } ///... Segurança Transações Logging Conceitos de AOP Esses interesses são chamados de interesses atravessadores (Cross cutting Concerns) Sua implementação atravessa a implementação de toda uma classe ou até mesmo várias classes (como logging) Conceitos de AOP Com AOP separamos esses interesses e definimo-os como um advice Before Advice After Advice Around Advice Throws Advice E baseado em certos critérios (Pointcuts), estes são inseridos na aplicação (Weaver) Tipos de AOP AOP Estático Aspectos são tipicamente introduzidos ao byte code em tempo de compilação ou através de classloaders customizados em tempo de execução • AspectJ (byte code) • JBoss AOP, AspectWerkz (classloader) AOP Dinâmico Cria proxies para todos os objetos interessados Leve perda de performance Fácil de configurar • Spring Em detalhes... O Container de Beans do Spring O Container de Beans É o núcleo do Spring famework Usa IOC para gerenciar os componentes que compõem uma aplicação Componentes são expressos como simples Java Beans O Container gerencia as relações entre os Beans e é responsável por sua configuração O Container controla o ciclo de vida dos Beans Tipos de Containers Bean Factory Dá suporte básico a injeção de dependência Configuração e gerenciamento do ciclo de vida Application Context Construido sobre o Bean Factory adicionando serviços de: • Resolução “mensagens” através de arquivos de propriedades para internacionalização • Carregamento de recursos genéricos • Publicação de eventos Bean Factory public interface Greeting { String greeting(); } public class WelcomeGreeting implements Greeting { private String message; public void setMessage(String message) { this.message = message; } public String greeting() { return message; } } Bean Factory <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id=“greeting” class=“WelcomeGreeting”> <property name=“message”> <value>Bem vindos</value> </property> </bean> </beans> ...salvo em um arquivo chamado, por exemplo, myapp-config.xml Bean Factory ... public static void main(String[] args) { BeanFactory factory = new XmlBeanFactory( new FileSystemResource(“myapp-config.xml”)); Greeting greeting = (Greeting) factory.getBean(“greeting”); System.out.println(greeting.greeting()); } Saída no console: Bem vindos Bean Factory Características dos managed beans Singletons por default Propriedades setadas por Dependency Injection • • • • • Referência para outros managed beans Strings Tipos primitivos Coleções (lists, sets, map, props) Inner Beans Parâmetros podem ser extraídos para arquivos de propriedades Spring como suporte a camada de negócio Spring DAO + Spring ORM O que temos? Uma camada de abstração para persistência de dados utilizando um ou mais das seguintes tecnologias JDBC Hibernate, OJB JDO iBatis Uma robusta infra-estrutura para o gerenciamento de transações de forma declarativa que suporta tanto transações locais como transações distribuídas através da JTA Uma camada simplificadora para tecnologias de distribuição incluindo EJBs, RMI, e Web Services Útil suporte a JNDI, JMS, email, e agendamento de tarefas(task scheduling) Spring DAO Possui uma API de acesso a dados que ajuda a isolar e melhorar o modo como o dado é servido pela camada de negócio Mais uma consistente e rica hierarquia de exceções suficiente para mapear exceções específicas dependentes de tecnologia para outras exceções genéricas Além de uma série de templates e wrapper classes para se trabalhar com JDBC, Hibernate, JDO, etc. JDBC Tradicional public void updateCustomer(Customer customer) { Connection conn = null; PreparedStatement ps = null; try { conn = getConnection(); ps = conn.prepareStatement(“update customer set ” + “firstName = ?, lastName = ?, ...”); ps.setString(1, customer.getFirstName()); ps.setString(2, customer.getLastName()); ps.executeUpdate(); } catch (SQLException e) { log.error(e); } finally { try { if (ps != null) ps.close(); } catch (SQLException e) {log.error(e);} try {if (conn != null) conn.close();} catch (SQLException e) {log.error(e);} } } private Connection getConnection() { //... Mais código de encanador } Spring DAO Usando um template jdbc do spring public void updateCustomer(Customer customer) { String sql = “update customer set ” + “firstName = ?, lastName = ?, ...”); Object[] params = new Object[] { customer.getFirstName(), customer.getLastName(), ...}; int[] types = new int[] { Types.VARCHAR, Types.VARCHAR, ...}; jdbcTemplate.update(sql, params, types); } O jdbcTemplate pode ser injetado pelo container... Spring DAO Operações também podem ser modeladas como objetos public class UpdateCustomer extends SqlUpdate { public UpdateCustomer(DataSource ds) { setDataSource(ds); setSql(“update customer set… values (?,?..)”); declareParameter(new SqlParameter(Types.VARCHAR)); declareParameter(new SqlParameter(Types.VARCHAR)); //... compile(); } public int update(Customer customer) { Object[] params = new Object[] { customer.getFirstName(), customer.getLastName() }; return update(params); } } Spring DAO Usando o objeto UpdateCustomer public class JdbcCustomerDao extends JdbcDaoSupport implements CustomerDao { private UpdateCustomer updateCustomer; protected void initDao() throws Exception { super.initDao(); updateCustomer = new UpdateCustomer(getDataSource()); } public void updateCustomer(Customer customer) { updateCustomer.update(customer); } } A classe UpdateCustomer pode ser uma inner class Integração ao Hibernate Spring prove um bean SessionFactory que simplifica a configuração e o gerênciamento de sessões em objetos de negócio Uma classe HibernateTemplate Uma classe HibernateDaoSupport que pode ser herdada para maior abstração Gerenciamento e mapeamento das HibernateException´s Facilmente plugável ao framework de Transações do Spring Integração ao Hibernate Exemplo de configuração ... <beans> <bean id=“sessionFactory” class=“org.springframework.orm. hibernate.LocalSessionFactoryBean”> <property name=“dataSource”> <ref bean=“dataSource”/> </property> <property name=“hibernateProperties”> <props> <prop key=“hibernate.dialect”> net.sf.hibernate.dialect.MySQLDialect </prop> </props> </property> <property name=“mappingResources”> <list><value>Customer.hbm.xml</value></list> </property> </bean> Continua... Integração ao Hibernate Exemplo de configuração (cont) <bean id=“customerDao” class=“HibernateCustomerDao”> <property name=“sessionFactory”> <ref bean=“sessionFactory”/> </property> </bean> <bean id=“dataSource” class=“org.springframework.jndi. JndiObjectFactoryBean”> <property name=“jndiName”> <value>java:comp/env/jdbc/myDataSource</value> </property> </bean> Integração ao Hibernate Customer DAO... public class HibernateCustomerDao extends HibernateDaoSupport implements CustomerDao { public void updateCustomer(Customer customer) { getHibernateTemplate().update(customer); } } Simples e limpo! Existe suporte semelhante para outras tecnologias ORM como iBatis, JDO e OJB Transações com Spring Suporte para gerenciamento programático e declarativo de transações Transações locais são delegadas pelo Spring para o gerente de transações do data-source Quando múltiplos recursos estão involvidos (transações distribuídas), Spring delega para o gerente de transações JTA obtido através do JNDI Apenas algumas pequenas mudanças são necessárias para trocar entre local e JTA Transações com Spring Gerenciamento Declarativo (+) Usa AOP para encapsular chamadas a objetos transacionais com código de begin e commit de transações Comportamento de propagação • Mandatory, Never, Not Supported, Required, Requires New, Support, Nested • Similar a EJBs Também suporta níveis de isolação • Default, Read Uncommitted, Read Committed, Repeatable Read, Serializable Transações com Spring Declarando atributos Comportamento de propagação Transação somente de leitura? [opcional] PROPAGATION, ISOLATION, readOnly, -Exceptions, +Exception Grau de isolamento [opcional] Os atributos são declarados no arquivo de definição dos beans Regras de Rollback [opcional] Transações com Spring Exemplo ... <beans> <bean id=“customerService” class=“org.springframework.transaction. interceptor.TransactionProxyFactoryBean”> <property name=“transactionManager”> <ref bean=“transactionManager”/> </property> <property name=“target”> <ref bean=“customerServiceTarget”/> </property> <property name=“transactionAttributes”> <props> <prop key=“get*”>PROPAGATION_REQUIRED, readOnly</prop> <prop key=“store*”>PROPAGATION_REQUIRED</prop> </props> </property> <bean> ... Continua... Transações com Spring Exemplo (cont) ... <bean id=“customerServiceTarget” class=“CustomerServiceImpl”> <property name=“customerDao”> <ref bean=“customerDao”/> </property> </bean> <bean id=“transactionManager” class=“org.springframework.orm. hibernate.HibernateTransactionManager”> <property name=“sessionFactory”> <ref bean=“sessionFactory”/> </property> </bean> <bean id=“customerDao” class=“HibernateCustomerDao”> <property name=“sessionFactory”> <ref bean=“sessionFactory”/> </property> </bean> ... AOP com Spring Em prática public interface IBusinessLogic { public void foo(); } public class BusinessLogic implements IBusinessLogic { public void foo() { System.out.println( “Inside BusinessLogic.foo()” ); } } AOP com Spring Em prática import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; public class MainApplication { public static void main(String [] args) { // Read the configuration file ApplicationContext ctx = new FileSystemXmlApplicationContext("springconfig.xml"); //Instantiate an object IBusinessLogic testObject = (IBusinessLogic) ctx.getBean("businesslogicbean"); // Execute the public // method of the bean testObject.foo(); } } AOP com Spring Uma vez que nossa MainApplication chama o método ctx.getBean() ela abre mão da instanciação e gerênciamento do Bean para o Spring Controlando a inicialização do nosso objeto, o Spring implementa todas as tarefas de gerenciamentos requeridas por aplicações J2EE Isso, claro, antes que nosso objeto seja utilizado pela aplicação AOP com Spring springconfig.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <!-- Bean configuration --> <bean id="businesslogicbean" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>IBusinessLogic</value> </property> <property name="target"> <ref local="beanTarget"/> </property> </bean> <!-- Bean Classes --> <bean id="beanTarget" class="BusinessLogic"/> </beans> AOP com Spring Diagrama de seqüência AOP com Spring Implementando um Advice import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class TracingBeforeAdvice implements MethodBeforeAdvice { public void before(Method m, Object[] args, Object target) throws Throwable { System.out.println("Hello world! (by " + this.getClass().getName() + ")"); } } AOP com Spring Para acoplar o Advice a determinados joinpoints de nossa aplicação prescisamos acrescentar algumas declarações no springconfig.xml ... <!-- Advisor pointcut definition for before advice --> <bean id="theTracingBeforeAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"> <ref local="theTracingBeforeAdvice"/> </property> <property name="pattern"> Aplicado por <value>.*</value> </property> expressões regulares! </bean> <!-- Advice classes --> <bean id="theTracingBeforeAdvice" class="TracingBeforeAdvice"/> ... AOP com Spring springconfig.xml (cont.) <!-- Bean configuration --> <bean id="businesslogicbean" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>IBusinessLogic</value> </property> <property name="target"> <ref local="beanTarget"/> </property> <property name="interceptorNames"> <list> <value>theTracingBeforeAdvisor</value> </list> </property> </bean> AOP com Spring Diagrama de seqüência Chamada transparente ao Advice do aspecto AOP com Spring Existem ainda outras formas de aplicar aspectos com o Spring AutoProxy: • BeanNameAutoProxyCreator • DefaultAdvisorAutoProxyCreator Exercício Implemente o exemplo “greeting” Implemente um cadastro de usuários usando hibernate + Spring Cadastrar Remover Atualizar procurar Exercícios (++) Implemente DAOs para os objetos do modelo do projeto Java Avançado Implemente uma fachada para o sistema(SessionFacade) tratando as transações de forma declarativa Implemente, utilizando AOP, o logging de todas as exceções lançadas pelos DAOs