314 Tworzenie aplikacji J2EE w oparciu o Spring Framework Jeśli to rozumiesz, to znaczy, że już jest przestarzałe. Kryterium nowoczesności wg Bittona
Plan prezentacji 315 Wprowadzenie Infrastruktura Spring kontener IoC programowanie aspektowe fabryki komponentów Spring DAO transakcje w Spring Spring MVC
Wprowadzenie 316
Wprowadzenie do Spring 317 Spring to szkielet aplikacyjny rozwijany na licencji open source od 2003 roku "Expert one-on-one J2EE Design and Development" R.Johnson Ideologia J2EE jest zbyt skomplikowane, encyjne EJB są przestarzałe, złożone, nadużywane i prawie zbędne, lepiej wykorzystywać interfejsy niż dziedziczenie, komponenty JavaBean doskonale się nadają do tworzenia aplikacji, paradygmat obiektowy jest ważniejszy niż implementacja, obsługiwane wyjątki są nadużywane w aplikacjach Java, kluczową rolę pełni możliwość niezależnego testowania poszczególnych modułów.
Czym jest Spring? 318 Wielowarstwowy szkielet aplikacji Java/J2EE zawierający: lekki kontener umożliwiający scentralizowane zarządzanie i łączenie komponentów JavaBean i POJO, warstwę zarządzania transakcjami, warstwę obsługi JDBC wraz z hierarchią wyjątków, moduły umożliwiające integrację z Toplink, Hibernate, JDO i ibatis SQL Maps za pomocą standardowych DAO, pełnowartościowe środowisko obsługujące paradygmat programowania aspektowego AOP, elastyczne środowisko do tworzenia aplikacji internetowych zgodnie z modelem MVC umożliwiające integrację ze Struts, WebWork, Tapestry; Spring MVC wspiera także wiele technologii implementowania warstwy widoku, np. JSP, Velocity, Tiles.
Główne cele 319 Uproszczenie aplikacji J2EE eliminacja "martwego kodu" sklejającego poszczególne komponenty, dostarczenie wydajnych mechanizmów IoC i AOP. Luźne wiązanie aplikacji ze szkieletem kod aplikacji praktycznie uniezależniony od Spring API, promowanie dobrych praktyk programowania obiektowego, przekazanie logiki do POJO. Ułatwienie procesu testowania aplikacji tworzenie aplikacji w oparciu o testy (TDD, test-driven development), umożliwienie testowania obiektów biznesowych poza kontenerem. Święta wojna z encyjnymi komponentami EJB
Architektura Spring Framework 320
Schemat architektury Spring 321
Cechy Spring 322 Spring jest uzupełnieniem serwerów aplikacji cały szkielet tworzony z perspektywy aplikacji a nie serwera Pełna modułowość szkieletu można wykorzystać dowolną część Spring niezależnie od pozostałych Nie wyważa drzwi otwartych na oścież brak implementacji gdy istnieją już dobre rozwiązania: Log4J, O/R Dwa zdania przewodnie "Framework that makes the right thing easy to do" "Trying to find the simplest thing that can possibly work"
Składniki Spring Kontener podstawowa część architektury, głównym składnikiem jest BeanFactory zapewniająca mechanizm IoC (Inversion of Control) Spring context plik konfiguracyjny dostarczający opis środowiska: JNDI, EJB, wielojęzyczność, walidacja, itp. Spring AOP moduł implementujący mechanizm AOP (Aspect-Oriented Programming) i zapewniający transakcyjność przetwarzania Spring DAO hierarchia wyjątków ujednolicająca kody błędów różnych baz danych Spring ORM szablony ułatwiające korzystanie z narzędzi ORM Spring Web module integracja z Jakarta Struts i dostarczanie kontekstu aplikacjom webowym Spring MVC framework implementacja MVC obsługująca szeroką gamę technologii 323
Inversion of Control 324 Zasada Hollywood: "to nie Państwo dzwonią do nas lecz my dzwonimy do Państwa"! Użytkownik nie tworzy i łączy obiektów w aplikacji, lecz opisuje zależności między poszczególnymi klasami. Kontener jest w pełni odpowiedzialny za tworzenie obiektów i ich łączenie (wiring) Metody implementacji wzorca IoC: typ 1: obiekty implementują specjalny interfejs do wyszukiwania obiektów zależnych typ 2: zależności są ustawiane w obiektach przez metody setter typ 3: zależności są ustawiane w obiektach przez konstruktor
IoC - przykład (1/2) 325 public interface MovieFinder { List findall(); class MovieLister... private MovieFinder finder; w jaki sposób przechowywane są filmy? klasa MovieLister jest na trwałe związana z klasą IMDBMovieFinder public MovieLister() { this.finder = new IMDBMovieFinder(); public Movie[] moviesdirectedby(string arg) { List allmovies = finder.findall(); // przetwarzaj listę tytułów
public interface MovieFinder { List findall(); class IMDBMovieFinder implements MovieFinder... private String url; public void seturl(string url) { this.url = url; class MovieLister... private MovieFinder finder; IoC - przykład (2/2) public void setfinder(moviefinder finder) { this.finder = finder; public Movie[] moviesdirectedby(string arg) { List allmovies = finder.findall(); // przetwarzaj listę tytułów beans.xml <beans> <bean id="movielister" class="spring.movielister"> <property name="finder"> <ref local="moviefinder"/> </property> </bean> <bean id="moviefinder" class="spring.imdbmoviefinder"> <property name="url"> <value>http://www.imdb.com</value> </property> </bean> </beans> 326
Programowanie aspektowe 327 Dziennik aplikacji Obsługa Klienta Zamówienie Magazyn Trwałość obiektów aspekty Bezpieczeństwo moduły biznesowe
Programowanie aspektowe 328 Metoda programowania mająca na celu modularyzację kodu i likwidację nakładających się problemów przekrojowych Typowe problemy przekrojowe: zapisywanie dziennika, obsługa błędów, autoryzacja i uwierzytelnianie, zapewnianie trwałości. Podstawowe pojęcia AOP aspekt (aspect): nazwany problem przekrojowy, rada (advice): dodatkowe zachowanie/czynność które zostaje zaninicjowane w punkcie złączenia, punkt złączenia (join point): miejsce w strukturze wykonywania się aplikacji w którym powinna zostać zastosowana rada.
Spring AOP framework 329 Rodzaje rad: around advice: wykonuje się przed i po punkcie styku, ma możliwość przerwania przepływu kontroli programu przez zwrócenie własnej wartości lub wyjątku, before advice: wykonuje się przed punktem styku, nie może zablokować przepływu kontroli programu, throws advice: wykonuje się w momencie zgłoszenia wyjątku, after returning advice: wykonuje się po poprawnym wykonaniu. Aktualnie Spring AOP umożliwia wprowadzanie rad tylko w odniesieniu do metod. Doradzanie odbywa się przez przechwytywanie wywołań metod (brak specjalnej kompilacji). Głównym celem jest ścisła integracja Spring AOC i kontenera Spring IoC.
Spring AOP (przykład 1/2) public interface MyInterface { public void foo(); public class MyClass implements MyInterface { public void foo() { // tutaj ciało metody beans.xml <beans> <bean id="mybean" class="org.springframework.aop. framework.proxyfactorybean"> <property name="proxyinterfaces"> <value>myinterface</value> </property> <property name="target"> <ref local="beantarget"/> </property> </bean> <bean id="beantarget" class="myclass"/> </beans> 330 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 która ma się wykonać przed punktem styku
Spring AOP (przykład 2/2) beans.xml <beans> <bean id="mybean" class="org.springframework.aop.framework.proxyfactorybean"> <property name="proxyinterfaces"><value>myinterface</value></property> <property name="target"><ref local="beantarget"/></property> <property name="interceptornames"> <list><value>thetracingbeforeadvisor</value></list> </property> </bean> definicja obiektu proxy <bean id="beantarget" class="myclass"/> <bean id="thetracingbeforeadvisor" class="org.springframework.aop.support.regexpmethodpointcutadvisor"> <property name="advice"> <ref local="thetracingbeforeadvice"/> </property> definicja punktu styku <property name="pattern"> <value>./myinterface/.foo</value> </property> klasa zarządzająca poradami </bean> <bean id="thetracingbeforeadvice" class="tracingbeforeadvice"/> </beans> 331
Spring i AspectJ 332 Spring umożliwia specyfikację aspektów, punktów złączeń i rad na podstawowym poziomie. Zaawansowana funkcjonalność AOP jest zapewniona przez integrację z zaawansowanymi szkieletami, np. AspectJ, AspectWerkz public aspect Tracing { public static boolean enabled = false; pointcut tobetraced() : call(*spring.*.*(int,string...)) execution(new(..)); before() : tobetraced() && if(enabled) { Object[] args = thisjoinpoint.getargs(); // sczytanie argumentów i zapisanie logu
Fabryki komponentów 333 Podstawowa funkcjonalność Spring jest udostępniana przez pakiet org.springframework.beans. Pakiet zawiera infrastrukturę umożliwiającą wyszukiwanie i wiązanie komponentów JavaBean. BeanFactory to główna fabryka dostarczająca komponentów JavaBean i zarządzająca tworzeniem i wiązaniem komponentów, ApplicationContext to interfejs rozszerzający BeanFactory o obsługę komunikatów i internacjonalizacji, obsługę zdarzeń i możliwość rejestrowania nasłuchu zdarzeń, poza środowiskiem o ograniczonej pamięci (np. applety Java) zaleca się stosowanie ApplicationContext import org.springframework.context.applicationcontext; import org.springframework.context.support.classpathxmlapplicationcontext; class Foo {... ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");...
Komponenty JavaBean 334 Spring nie nakłada na klasy POJO żadnych dodatkowych wymagań dotyczących rozszerzanych klas lub implementowanych interfejsów Dwa tryby tworzenia komponentów: singleton: fabryka tworzy jedną instancję komponentu JavaBean o podanej nazwie, wszystkie żądania współdzielą jeden komponent, prototype: w odpowiedzi na każde żądanie fabryka tworzy nową instancję komponentu JavaBean. Fabryki dostarczają mechanizmu zarządzania cyklem życia komponentu, interfejsy InitializingBean i DisposableBean umożliwiają podejmowanie określonych akcji podczas tworzenia i niszczenia komponentu
Przykład (1/3) 335 package ploug.spring; class Department { String name; String location; public void setname(string name) { this.name = name; public String getname() { return name; public void setlocation(string location) { this.location = location; public String getlocation() { return location; class Employee { String name; Department department; public void setname(string name) { this.name = name; public String getname() { return name; public void setdepartment(department department) {this.department=department; public Department getdepartment() { return department;
Przykład (2/3) 336 beans.xml <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="employeebean" class="ploug.spring.employee"> <property name="name" value="mikolaj Morzy"/> <property name="department"> <ref bean="departmentbean"/> </property> </bean> wskazanie komponentu zależnego <bean id="departmentbean" class="ploug.spring.department" singleton="true"> <property name="name" value="database Systems"/> <property name="location" value="piotrowo 2"/> </bean> </beans> zostaną wywołane metody setter obiektu: setname("database Systems") setlocation("piotrowo 2") tryb tworzenia komponentu
Przykład (3/3) 337 package ploug.spring; import org.springframework.context.applicationcontext; 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("employeeBean"); System.out.println("Name: " + employee.getname()); System.out.println("Department: " + employee.getdepartment().getname()); System.out.println("Location: " + employee.getdepartment().getlocation());... Name: Mikolaj Morzy Department: Database Systems Location: Piotrowo 2 utworzenie komponentu employeebean i powiązanie wszystkich obiektów zależnych
Spring DAO 338 Data Access Object (DAO) to powszechnie stosowany wzorzec projektowy dostępu do danych zewnętrznych zakładający rozdzielenie kodu odpowiedzialnego za trwałość obiektów od kodu implementującego reguły biznesowe. Spring oferuje wsparcie dla: interfejsu JDBC, narzędzi O/R: Hibernate, JDO, ibatis SQL Maps. Dlaczego kolejna warstwa nad JDBC? zarządzanie obiektami Connection, ResultSet, Statement uniwersalny wyjątek SQLException: różne kody błędów
Spring DAO JDBC 339 W odniesieniu do JDBC Spring oferuje następujące cechy: spójna hierarchia wyjątków DataAccessException opakowująca wyjątek SQLException, możliwość wykorzystywania mechanizmu callback (przez interfejs JdbcTemplate) lub modelowanie JDBC w postaci zbioru obiektów, całkowicie automatyczna obsługa bloków try/catch/finally automatyczne i gwarantowane zamykanie wszystkich połączeń, automatyczne tłumaczenie kodów błędów producenta bazy danych na ogólną hierarchię wyjątków, wsparcie dla wywoływania procedur składowanych.
Spring DAO JDBC - przykład (1/4) 340... import org.springframework.jdbc.core.jdbctemplate; import org.springframework.jdbc.datasource.drivermanagerdatasource; import org.springframework.jdbc.badsqlgrammarexception;... DriverManagerDataSource dbbean = (DriverManagerDataSource)ctx.getBean("dbBean"); JdbcTemplate template = new JdbcTemplate(dbBean); int count = template.queryforint("select count(*) from emp"); System.out.println("Number of employees: " + count); List result = template.queryforlist("select * from emp"); for (Object o : result) { Map map = (Map)o; System.out.println(map.get("ENAME") + " " + map.get("job")); try { template.execute("dropp table emp"); catch (BadSqlGrammarException e) { // obsłuż wyjątek
Spring DAO JDBC - przykład (2/4) 341 <?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> tylko dla testów, w rzeczywistej aplikacji... należy użyć JndiObjectFactoryBean <bean id="dbbean" class="org.springframework.jdbc.datasource.drivermanagerdatasource"> <property name="driverclassname" value="oracle.jdbc.driver.oracledriver"/> <property name="url" value="jdbc:oracle:thin:@miner.cs.put.poznan.pl:1521:miner10g"/> <property name="username" value="mikolaj"/> <property name="password" value="*****"/> </bean> </beans> Number of employees: 14 SMITH CLERK ALLEN SALESMAN WARD SALESMAN JONES MANAGER MARTIN SALESMAN... StatementCallback; bad SQL grammar [dropp table emp]; nested exception is java.sql.sqlexception: ORA-00900: invalid SQL statement
Spring DAO JDBC - przykład (3/4) 342... public class EmployeeJdbcDao extends JdbcDaoSupport implements EmployeeDao { public List getemployees() { JdbcTemplate template = getjdbctemplate(); return (List)template.query("select ename, job from emp", new RowMapperResultSetExtractor(new EmployeeRowMap())); class EmployeeRowMap implements RowMapper { public Object maprow(resultset resultset, int i) throws SQLException { Employee emp = new Employee(); emp.setname(resultset.getstring(1)); emp.setjob(resultset.getstring(2)); return emp; EmployeeDao.java package pl.org.ploug.spring; import java.util.list; public interface EmployeeDao { public List getemployees();
Spring DAO JDBC - przykład (4/4) 343... public class EmployeeJdbcDao extends JdbcDaoSupport implements EmployeeDao {... public static void main(string[] args) { EmployeeJdbcDao employeejdbcdao = new EmployeeJdbcDao(); ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); DriverManagerDataSource dbbean = (DriverManagerDataSource)ctx.getBean("dbBean"); employeejdbcdao.setdatasource(dbbean);... for (Object o : employeejdbcdao.getemployees()) { Employee e = (Employee)o; System.out.println("Employee: " + e.getname() + " " + e.getjob()); Employee: SMITH CLERK Employee: ALLEN SALESMAN Employee: WARD SALESMAN Employee: JONES MANAGER Employee: MARTIN SALESMAN Employee: BLAKE MANAGER...
Transakcje w Spring 344 Spring dostarcza infrastruktury do obsługi transakcji PlatformTransactionManager do programowego zarządzania transakcjami, szablon TransactionTemplate do wygodnego użytku, deklaratywne zarządzanie transakcjami (na wzór EJB) za pomocą POJO i programowania aspektowego przy użyciu ProxyFactoryBean i TransactionInterceptor, demarkacja transakcji przez metadane i anotacje, możliwość wyboru między JTA, Hibernate, JDO, JDBC, spójny obraz dla transakcji lokalnych i globalnych.
Transakcje w Spring - przykład (1/2) 345... <bean id="datasource" class="org.springframework.jndi.jndiobjectfactorybean"> <property name="jndiname" value="java:comp/env/jdbc/miner10g"/> </bean> <bean id="transactionmanager" class="org.springframework.transaction.jta.jtatransactionmanager"/> <bean id="empmanagerjdbcbean" class="ploug.spring.empmanagerjdbc"> <property name="datasource"><ref bean="datasource"/></property> </bean> <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>
Transakcje w Spring - przykład (2/2)... beans.xml <bean id="datasource" class="org.springframework.jndi.jndiobjectfactorybean"> <property name="jndiname" value="java:comp/env/jdbc/miner10g"/> </bean> <bean id="transactionmanager" class="org.springframework.transaction.jta.jtatransactionmanager"/> <bean id="empmanagerjdbcbean" class="ploug.spring.empmanagerjdbc"> <property name="datasource"><ref bean="datasource"/></property> </bean> <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 346
Spring MVC 347 Spring MVC przypomina architekturę Struts wszystkie żądania określonego typu są obsługiwane przez współdzielony kontroler Kontrolery i interceptory wykonują się wewnątrz kontenera IoC i w pełni korzystają z mechanizmu Dependence Injection Zalety w porównaniu ze Struts: bardziej elastyczna architektura, serwlety DispatcherServlet współdzielą kontekst aplikacji, bardzo łatwa konfiguracja środowiska, interfejs nie jest oparty na dziedziczeniu klas, niewielki stopień zależności aplikacji od szkieletu, niezwiązany ściśle z JSP w wartwie widoku (Velocity, Excel, PDF, możliwość definiowania własnego mechanizmu prezentacji).
Spring MVC 348 Zalety w porównaniu ze Struts (ciąg dalszy): wyraźna separacja warstw modelu, widoku i kontrolera, model nie jest ściśle uzależniony od Spring API lub Servlet API, zamiast ActionForm do obsługi formularzy można wykorzystywać obiekty modelu, (!) bardzo łatwe testowanie poszczególnych modułów aplikacji webowej przy pomocy JUnit, integracja z warstwą pośrednią (ang. middle tier) bez konieczności jakiegokolwiek kodowania (eliminacja obiektów ServiceLocator i Singleton), integracja z popularnymi technologiami warstwy prezentacji: WebWork, Tapestry, Struts, intensywne prace nad integracją Spring i JSF są aktualnie w toku.
Składniki Spring MVC (1/3) Zarządca ServletDispatcher podstawowy punkt wejścia do aplikacji, musi być zadeklarowany w web.xml, odpowiada za przyjmowanie żądań z zewnątrz i przekierowywanie żądań do właściwych kontrolerów. ServletDispatcher musi utworzyć kontekst aplikacji webowej (WebApplicationContext). Konfiguracja musi się mieścić w pliku o nazwie WEB-INF/servletname-servlet.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> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app> web.xml 349
Składniki Spring MVC (2/3) <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> empdept-servlet.xml 350 <beans> <bean name="/index" class="ploug.spring.empdeptcontroller"/> <bean id="dbbean" class="org.springframework.jdbc.datasource.drivermanagerdatasource"> <property name="driverclassname" value="oracle.jdbc.driver.oracledriver"/> <property name="url" value="jdbc:oracle:thin:@miner.cs.put.poznan.pl:1521:miner10g"/> <property name="username" value="scott"/> <property name="password" value="tiger"/> </bean> <bean 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> index.jsp </beans> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/tr/html4/loose.dtd"> <%@ page contenttype="text/html;charset=windows-1252"%> <jsp:forward page="example/index"/>
package ploug.spring; import... Składniki Spring MVC (3/3) EmpDeptController.java 351 public class ExampleController 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; komponent JavaBean <%@ 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> WEB-INF/jsp/emps.jsp