Wielowarstwowe aplikacje internetowe Spring Autorzy wykładu: Mikołaj Morzy Marek Wojciechowski Spring
Plan wykładu Wprowadzenie Infrastruktura Spring Kontener Inversion of Control / Dependency Injection Fabryki komponentów Spring DAO Transakcje w Spring JMS w Spring Spring MVC Programowanie aspektowe
Czym jest Spring? Framework dla platformy Java Dla aplikacji różnego typu Ukierunkowany na aplikacje webowe na platformie Java EE Otwarty, darmowy, dojrzały Powstał w 2002 roku Wyrósł na krytyce platformy J2EE jako nadmiernie złożonej i niewydajnej Główne zarzuty dotyczyły technologii EJB Projekt "open source" rozwijany od 2003 Framework modularny, obejmujący wszystkie obszary aplikacji, ale integrujący się z innymi rozwiązaniami
Ideologia frameworka Spring Budowa aplikacji z komponentów POJO (beans) Dostarczenie komponentom POJO usług niezbędnych w aplikacjach klasy enterprise w sposób nieinwazyjny Ukrycie specyfiki i złożoności poszczególnych interfejsów programistycznych Javy / Javy EE przed kodem aplikacji Odwrócenie sterowania (Inversion of Control) poprzez wstrzykiwanie zależności (dependency injection) Wykorzystanie istniejących rozwiązań i integracja ich z frameworkiem ORM, programowanie aspektowe, MVC, technologie szablonów,
Modularna architektura Spring Źródło: Spring Framework Reference Documentation
Inversion of Control / Dependency Injection Sercem frameworka Spring jest kontener IoC Tradycyjne podejście do współpracy komponentów aplikacji ze sobą zakłada aktywne tworzenie lub wyszukiwanie komponentów przez komponenty z nich korzystające Spring odwraca sterowanie w porównaniu z tradycyjnym podejściem: Komponenty deklarują jakich innych komponentów potrzebują do swojego działania Kontener wstrzykuje zależności do komponentu w momencie jego tworzenia Uwaga: obecna wersja Java EE również stosuje wstrzykiwanie zależności (przez adnotacje), ale najpierw pojawiło się ono w Springu
Metody specyfikacji metadanych o komponentach dla kontenera Inversion of Control XML Tradycyjny format konfiguracji w Springu Konfiguracja odseparowana od kodu Java Adnotacje Mechanizm często preferowany przez programistów Konfiguracja blisko kodu Mniej elastyczne rozwiązanie niż XML Konfiguracja Java-based Najnowsze rozwiązanie z dostępnych w Springu Konfiguracja w Javie, ale zewnętrzna w stosunku do kodu aplikacji Adnotacje @Configuration, @Bean, @Import, @DependsOn
Konfiguracja beanów (XML) <beans xmlns="http://www.springframework.org/schema/beans"...> <!-- Instantiation with a constructor --> <bean id="..." class="..." /> <!-- Instantiation with a static factory method --> <bean id="..." class="..." factory-method="..." /> <!-- Instantiation with an instance factory method --> <bean id="..." factory-bean="..." factory-method="..."/> </beans>
Strategie Dependency Injection w Spring Wstrzykiwanie zależności przez konstruktor Generalnie zalecane Odpowiednie dla obowiązkowych zależności Pozwala na tworzenie obiektów niemodyfikowalnych Wstrzykiwanie zależności przez metody setter Odpowiednie dla zależności opcjonalnych Pozwala na rozwiązanie problemu cyklicznych zależności między beanami
Współpraca beanów podejście tradycyjne public interface MovieFinder { List findall(); } 1 class MovieLister { private MovieFinder finder; 2 trwałe powiązanie public MovieLister() { this.finder = new IMDBFinder(); } 3 public Movie[] moviesdirectedby(string director) { List allmovies = finder.findall(); 4 // przetwarzaj liste filmow }}
Wstrzykiwanie zależności przez konstruktor (1/2) public interface MovieFinder { List findall(); } 1 class IMDBFinder implements MovieFinder { public List findall() { } } 2 class MovieLister { private MovieFinder finder; 3 public MovieLister (MovieFinder finder) { this.finder = finder; } 4 } public Movie[] moviesdirectedby(string director) { }
Wstrzykiwanie zależności przez konstruktor (2/2) beans.xml <beans> <bean id="moviefinder" class="spring.imdbfinder"> </bean> <bean id="movielister" class="spring.movielister"> <constructor-arg ref="moviefinder"/> </bean> </beans> 1 2
Wstrzykiwanie zależności przez setter (1/2) public interface MovieFinder { List findall(); } 1 class IMDBFinder implements MovieFinder { private String url; public void seturl(string url) { this.url = url; } public List findall() { } } class MovieLister { private MovieFinder finder; metody setter 3 public void setfinder(moviefinder finder) { this.finder = finder; } 2 } public Movie[] moviesdirectedby(string director) { } 4
Wstrzykiwanie zależności przez setter (2/2) <beans> <bean id="movielister" class="spring.movielister"> <property name="finder"> <ref bean="moviefinder"/> </property> </bean> <bean id="moviefinder" class="spring.imdbfinder"> <property name="url"> <value>http://www.imdb.com</value> </property> </bean> </beans> 2 1 beans.xml
Fabryki komponentów (kontenery) Pakiet org.springframework.beans BeanFactory - fabryka komponentów JavaBean ApplicationContext - obsługa komunikatów, internacjonalizacji, obsługa zdarzeń, rejestrowanie nasłuchu zdarzeń import org.springframework.context.applicationcontext; import org.springframework.context.support. ClassPathXmlApplicationContext; class Foo { ApplicationContext ctx = new plik konfiguracyjny ClassPathXmlApplicationContext("beans.xml"); }
Spring beans natura, zasięgi, tworzenie Brak nadklas bazowych i interfejsów Podstawowe zasięgi komponentów singleton (zasięg domyślny) Jedna współdzielona instancja na kontener Zastosowanie: bezstanowa logika biznesowa prototype Wiele instancji w kontenerze Przy każdym odwołaniu tworzona nowa instancja Zastosowanie: gdy stan powiązany z klientem Dodatkowe zasięgi w kontenerach web-aware request, session, application, globalsession, websocket
Zachłanne i odroczone tworzenie singletonów Domyślnie instancje beanów o zasięgu singleton są tworzone podczas inicjalizacji kontenera Można zlecić tworzenie singletona dopiero w momencie odwołania do beana, tj. gdy faktycznie jest potrzebny: lazy-init="true
Autowiring beanów Domyślnie komponenty współpracujące są wskazywane jawnie w konfiguracji beanów Rozwiązanie zalecane w dużych projektach Istnieje możliwość zlecenia kontenerowi automatycznego dopasowania współpracujących beanów (autowiring) Atrybut autowire w elemencie <bean>: no byname bytype constructor
Korzystanie z beanów w kontenerze - Przykład (1/4) Department.java package my.spring; class Department { String name; String city; } public void setname(string name) { this.name = name; } public String getname() { return this.name; } public void setcity(string city) { this.city = city; } public String getcity() { return this.city; }
Korzystanie z beanów w kontenerze - Przykład (2/4) Employee.java package my.spring; class Employee { String name; Department dept; } public void setname(string name) { this.name = name; } public String getname() { return this.name; } public void setdept(department dept) {this.dept=dept;} public Department getdept() { return this.dept; }
Korzystanie z beanów w kontenerze - Przykład (3/4) <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="empbean" class="my.spring.employee"> <property name="name" value="jan Kowalski"/> <property name="dept"><ref bean="deptbean"/></property> </bean> <bean id="deptbean" class="my.spring.department" scope="singleton"> <property name="name" value="internet Applications"/> <property name="city" value="poznan"/> </bean> </beans> 2 4 1 beans.xml domyślnie 3
Korzystanie z beanów w kontenerze - Przykład (4/4) package ploug.spring; import org.springframework.context.applicationcontext; SpringTest.java 1 import org.springframework.context.support.classpathxmlapplicationcontext; public class SpringTest { public static void main(string[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); Employee employee = (Employee) ctx.getbean("empbean"); System.out.println("Name: " + employee.getname()); System.out.println("Department: " + employee.getdept().getname()); System.out.println("Location: " + employee.getdept().getlocation());... 3 2
Konfiguracja beanów Springa przez adnotacje Adnotacje stereotypowe @Component @Repository @Service @Controller Adnotacje dotyczące wstrzykiwania zależności @Required @Autowired @Resource
Spring DAO Wzorzec projektowy Data Access Object (DAO) rozdzielenie mechanizmu trwałości obiektów od reguł biznesowych Spring oferuje wsparcie dla interfejsu JDBC narzędzi ORM: Hibernate, JDO, ibatis SQL Maps Dlaczego kolejna warstwa nad JDBC? zarządzanie Connection, ResultSet, Statement uniwersalny wyjątek DataAccessException
Spring DAO JDBC - przykład (1/2)... import org.springframework.jdbc.core.jdbctemplate; import org.springframework.jdbc.datasource.drivermanagerdatasource;... DriverManagerDataSource dbbean = (DriverManagerDataSource)ctx.getBean("dbBean"); JdbcTemplate template = new JdbcTemplate(dbBean); List result = template.queryforlist("select * FROM emp"); for (Object o : result) { Map map = (Map)o; 4 System.out.println(map.get("ENAME") + " " + map.get("job")); } 1 SpringDAOTest.java 2 3
Spring DAO JDBC - przykład (2/2) <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> beans.xml <beans>... <bean id="dbbean" class="org.springframework.jdbc.datasource.drivermanagerdatasource"> <property name="driverclassname" value="oracle.jdbc.driver.oracledriver"/> <property name="url" value="jdbc:oracle:thin:@host:port:sid"/> <property name="username" value="student"/> <property name="password" value="student"/> </bean> </beans>
Transakcje w Spring Wsparcie transakcji w architekturze Spring Interfejs PlatformTransactionManager Wybór między JTA, Hibernate, JDO, JDBC Szablon TransactionTemplate Deklaratywne zarządzanie transakcjami za pomocą POJO i programowania aspektowego przy użyciu ProxyFactoryBean i TransactionInterceptor Demarkacja transakcji przez metadane i adnotacje @Transactional Jednolity obraz dla transakcji lokalnych i globalnych
Transakcje w Spring - przykład (1/2)... <bean id="datasource" class="org.springframework.jndi.jndiobjectfactorybean"> <property name="jndiname" value="java:comp/env/jdbc/sid"/> </bean> <bean id="transactionmanager" class="org.springframework.transaction.jta.jtatransactionmanager"/> <bean id="empmanagerjdbcbean" class="my.spring.empmanagerjdbc"> 2 <property name="datasource"><ref bean="datasource"/></property> </bean> 3 beans.xml 1
Transakcje w Spring - przykład (2/2)... <bean id="empmanagerbean" class="org.springframework.transaction.interceptor.transactionproxyfactorybean"> <property name="transactionmanager"> <ref bean="transactionmanager"/></property> <property name="target"><ref bean="empmanagerjdbcbean"/></property> <property name="transactionattributes"> <props> <prop key="load*">propagation_required,readonly</prop> <prop key="store*">propagation_required</prop> </props> </property> </bean> PROPAGATOIN_REQUIRED, PROPAGATION_SUPPORTS, PROPAGATION_MANDATORY, PROPAGATION_REQUIRES_NEW, PROPAGATION_NOT_SUPPORTED, PROPAGATION_NEVER, PROPAGATION_NESTED 3 4 2 1 beans.xml
Własności transakcji w Spring Propagation (MANDATORY, NESTED, NEVER, NOT_SUPPORTED, REQUIRED, REQUIRES_NEW, SUPPORTS) Isolation (DEFAULT, READ_COMMITTED, READ_UNCOMMITTED, REPEATABLE_READ, SERIALIZABLE) readonly: false / true rollbackfor norollbackfor
JMS w Spring Spring integruje się z JMS i upraszcza korzystanie z JMS API na podobnych zasadach jak z JDBC JmsTemplate Wysyłanie komunikatów Synchroniczny odbiór komunikatów Message-Driven POJOs do asynchronicznego odbioru komunikatów JmsTransactionManager do integracji JMS z mechanizmami transakcyjnymi Springa
JmsTemplate wysłanie komunikatu public class JmsQueueSender { private JmsTemplate jmstemplate; private Queue queue; public void setconnectionfactory(connectionfactory cf) { this.jmstemplate = new JmsTemplate(cf); } public void setqueue(queue queue) { this.queue = queue; } public void sendmessage() { this.jmstemplate.send(this.queue, new MessageCreator() { public Message createmessage(session session) throws JMSException { return session.createtextmessage("message text"); } }); } }
Message-Driven POJO (MBP) public class MyPojoListener implements MessageListener { public void onmessage(message message) { if (message instanceof TextMessage) { try { string txt = ((TextMessage) message).gettext();... } catch (JMSException ex) { throw new RuntimeException(ex); } } } }
Kontener dla MDP <!-- MDP --> <bean id="messagelistener" class="mypack.mypojolistener" /> <!-- Message listener container --> <bean id="jmscontainer" class="org.springframework.jms.listener.defaultmessagelistenercontainer"> <property name="connectionfactory" ref="connectionfactory"/> <property name="destination" ref="destination"/> <property name="messagelistener" ref="messagelistener" /> </bean>
Spring MVC Implementacja wzorca architektonicznego Model-View-Controler Kontrolery i interceptory wewnątrz kontenera IoC Zalety: Elastyczna architektura Serwlety DispatcherServlet współdzielą kontekst Łatwa konfiguracja środowiska Brak zależności aplikacji od szkieletu Warstwa widoku niezwiązana z technologią
Przykład Spring MVC (1/4) Zarządca DispatcherServlet web.xml <?xml version = '1.0' encoding = 'windows-1252'?> <web-app> <servlet> <servlet-name>empdept</servlet-name> <servlet-class>org.springframework.web.servlet.dispatcherservlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>empdept</servlet-name> <url-pattern>/empdept/*</url-pattern> </servlet-mapping> </web-app> 2 3 1
Przykład Spring MVC (2/4) <bean name="/index" class="my.spring.empdeptcontroller"/> <bean id="dbbean" </bean> <bean 2 class="org.springframework.web.servlet.view.internalresourceviewresolver"> <property name="prefix"><value>/web-inf/jsp/</value></property> <property name="suffix"><value>.jsp</value></property> <property name="viewclass"> <value>org.springframework.web.servlet.view.jstlview</value> </property> </bean> empdept-servlet.xml 1 3
Przykład Spring MVC (3/4) public class EmpDeptController implements Controller { public ModelAndView handlerequest( HttpServletRequest request,httpservletresponse response) throws ServletException, IOException { ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); DriverManagerDataSource dbbean = (DriverManagerDataSource)ctx.getBean("dbBean"); JdbcTemplate template = new JdbcTemplate(dbBean); List employeesraw = template.queryforlist("select ename from emp"); List employees = new ArrayList(); for (Object o : employeesraw) employees.add(((map)o).get("ename")); ModelAndView mv = new ModelAndView("emps"); mv.addobject("employees", employees); return mv; } } 4 1 EmpDeptController.java 3 2
Przykład Spring MVC (4/4) /WEB-INF/jsp/emps.jsp <%@ page contenttype="text/html;charset=windows-1252" %> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <html> <body> <c:foreach var="emp" items="${employees}"> <c:out value="${emp}"/> <br/> </c:foreach> </body> </html> 1
Język wyrażeń SpEL Spring Expression Language Język o składni podobnej do Unified EL #{ } pełna składnia wyrażeń ${ } tylko odwołania do właściwości Dodatkowe możliwości w porównaniu z Unified EL Wywołania metod Obsługa szablonów dla łańcuchów znaków
Biblioteka znaczników dla JSP Upraszcza budowę formularzy Znaczniki obsługują wiązanie danych do obiektu obsługującego stan formularza, umieszczonego przez kontroler w PageContext <%@ taglib prefix="form" <form:form> First Name: uri="http://www.springframework.org/tags/form" %> <form:input path="firstname"/> Last Name: <form:input path="lastname"/> <input type="submit" value="save Changes"/> </form:form>
Programowanie aspektowe Magazyn Zamówienie Obsługa klienta Trwałość obiektów Aspekty Dziennik aplikacji Bezpieczeństwo Moduły biznesowe
Programowanie aspektowe Metoda programowania mająca na celu modularyzację kodu i likwidację nakładających się problemów przekrojowych Podstawowe pojęcia AOP aspekt (aspect): nazwany problem przekrojowy rada (advice): dodatkowe zachowanie/czynność które zostaje zainicjowane w punkcie złączenia punkt złączenia (join point): miejsce w strukturze wykonywania się aplikacji w którym powinna zostać zastosowana rada
Spring AOP Rodzaje rad around advice: przed i po punkcie styku, możliwość przerwania przepływu kontroli przez zwrócenie własnej wartości lub wyjątku before advice: przed punktem styku, brak blokowania przepływu kontroli throws advice: w momencie zgłoszenia wyjątku after returning advice: po poprawnym wykonaniu
Przykład Spring AOP (1/3) public interface MyInterface { public void foo(); } 1 public class MyClass implements MyInterface { } public void foo() { /* tutaj ciało metody */ } 2 import java.lang.reflect.method; import org.springframework.aop.methodbeforeadvice; public class TraceBeforeAdvice implements MethodBeforeAdvice { public void before(method m, Object[] args, Object target) throws Throwable { /* tutaj kod porady */ } } 3
Przykład Spring AOP (2/3) <beans> <bean id="mybean" class="org.springframework.aop.framework.proxyfactorybean"> <property name="proxyinterfaces"> <value>myinterface</value> </property> <property name="target"><ref bean="beantarget"/></property> <property name="interceptornames"> <list><value>thetracingbeforeadvisor</value></list> </property> </bean> beans.xml 1 3 2
Przykład Spring AOP (3/3) <bean id="beantarget" class="myclass"/> <bean id="thetracingbeforeadvisor" class="org.springframework.aop.support.regexpmethodpointcutadvisor"> <property name="advice"> <ref bean="thetracingbeforeadvice"/> </property> <property name="pattern"> beans.xml 1 4 <value>./myinterface/.foo</value> </property> </bean> <bean id="thetracingbeforeadvice" class="tracingbeforeadvice"/> </beans> 2 3 5