Spring

Links

Spring Eclipse Plugin

Hier findet man ein Spring Eclipse Plugin.

Spring beziehen

Hier gibt es die Spring Libs. In älteren Spring Versionen benötigte man

  • dist/spring.jar<li>lib/jakarta-commons/commons-logging.jar</li>

In neueren Spring Versionen benötigt man den kompletten "dist" Ordner und das "commons-logging.jar".

Kleines Spring Tutorial

Eine Klasse für einen (Buch-) Leser. Erst ein Interface

package invalid.mypackage;

public interface BookReader
{
 public void setCreditStatus(String creditStatus);

 @Override
 public String toString();
}

dann die eigentliche Implementierung

package invalid.mypackage;

public class BookReaderImpl implements BookReader
{
 private BookStore buysBooksAt;

 private String creditStatus;

 public BookReaderImpl()
 {
  this.buysBooksAt = new BookStoreOnline();
 }

 public BookReaderImpl(BookStore pBookLibrary)
 {
  this.buysBooksAt = pBookLibrary;
 }

 public void setCreditStatus(String creditStatus)
 {
  this.creditStatus = creditStatus;
 }

 @Override
 public String toString()
 {
  return "BookReader [buysBooksAt=" + buysBooksAt + ", creditStatus=" creditStatus + "]";
 }

}

Und noch eine Buchverkäufer Klasse. Wieder erste eine Schnittstelle

package invalid.mypackage;

public interface BookStore
{
 public boolean buy(String pTitle);
}
 

dann die eigentliche Implementierung

package invalid.mypackage;

public class BookStoreOnline implements BookStore
{
 public BookStoreOnline()
 {
 }

 public boolean buy(String pTitle)
 {
  // ...
 }

 @Override
 public String toString()
 {
  return "Online Store";
 }
}

Jetzt eine XML Datei, kann man z.B. "beans.xml" nennen, oder (vor allem für Webapplikationen) "applicationContext.xml" Den Ordner unbedingt in den Klassenpfad übernehmen.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

 <bean id="myBookStore" scope="singleton" class="invalid.mypackage.BookStoreOnline" />

 <bean id="bookReader"  scope="singleton" class="invalid.mypackage.BookReaderImpl">
  <constructor-arg ref="myBookStore"/>
  <property name="creditStatus" value="BBB" />
 </bean>

</beans>

Das Ganze wird dann so benutzt

package invalid.mypackage;

import org.springframework.beans.factory.*;
import org.springframework.context.support.*;

public class MyMainClass
{
 public static void main(String[] args)
 {
  BeanFactory beanFactory =new ClassPathXmlApplicationContext("beans.xml");
  BookReader lBookReader = (BookReader) beanFactory.getBean("bookReader");
  System.out.println(lBookReader);
 }

}

Autowire

Normalerweise setzt man mit Spring so eine Referenz von buch auf buchLaden:

<bean id="buchLaden" class="BuchLaden" />

<bean id="buch" class="Buch">
   <property name="buchLaden" ref="buchLaden" />
</bean>
public class Buch
{

   private BuchLaden buchLaden;

   public BuchLaden getBuchLaden() {
      return buchLaden;
   }

   public void setBuchLaden(BuchLaden buchLaden)
   {
      this.buchLaden = buchLaden;
   }

   //...
}

Mit Autowire kann man diese Referenz automatisch setzen lassen. Und das ohne entsprechend Einträge in der XML Datei und der Code benötigt nicht einmal einen entsprechenden Setter:

<context:annotation-config />

<bean id="buch"      class="autowiredexample.Buch" />
<bean id="buchLaden" class="autowiredexample.BuchLaden" />
import org.springframework.beans.factory.annotation.Autowired;

public class Buch
{
   @Autowired
   private BuchLaden buchLaden;
}

Der Einsatz von Autowire ist aber wohl nicht unumstritten.

scope

Spring Einführung, die auch den Scope behandelt

Wenn man von Spring eine Bean geliefert bekommt, ist es noch wichtig, zu wissen, wie lange diese Bean gültig ist, und unter welchen Bedingungen man eine neue Bean erhält.

singleton(default) Man bekommt immer wieder genau die selbe Bean
prototypeMan bekommt eine neue Bean
requestweb
sessionweb
globalSessionweb

Spring für Webanwendungen

Einführung: Spring für Webanwendungen

Ein normale Webapplikation starten. Per Contextmenü dem Projekt (mithilfe des Eclipse Spring Plugins) Spring Unterstützung hinzufügen.

Adding Spring support

Eine kleine Bean erstellen:

package foo;

import java.io.Serializable;

public class MyHelloWorldBean implements Serializable
{
 private String message;

 public MyHelloWorldBean()
 {
 }

 public MyHelloWorldBean(String pMessage)
 {
  setMessage(pMessage);
 }

 public String getMessage()
 {
  return message;
 }

 public void setMessage(String message)
 {
  this.message = message;
 }

}

WEB-INF/applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans
 xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:aop="http://www.springframework.org/schema/aop"
 xsi:schemaLocation=
 "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"

>

 <bean id="sayHi" scope="singleton" class="foo.MyHelloWorldBean">
  <constructor-arg value="Say hello World" />
 </bean>

</beans>

WEB-INF/web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns="http://java.sun.com/xml/ns/javaee"
 xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
 version="2.5">

 <!-- Get spring reference -->
 <listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 </listener>
 <listener>
  <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
 </listener>

 <servlet>
  <servlet-name>MyServlet</servlet-name>
  <servlet-class>foo.MyServlet</servlet-class>
 </servlet>
 <servlet-mapping>
  <servlet-name>MyServlet</servlet-name>
  <url-pattern>/SayHiServlet</url-pattern>
 </servlet-mapping>

</web-app>

Und kann das Servlet dann über Spring auf den Wert in der Bean zugreifen:

package foo;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

public class MyServlet extends HttpServlet
{
 private static final long serialVersionUID = 1L;

 public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
 {
  response.setContentType("text/html");
  PrintWriter out = response.getWriter();
  out.println("<html>");
  out.println("<head>");
  out.println("<title>Hello World!</title>");
  out.println("</head>");
  out.println("<body>");

  // get bean via Spring
  ApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext((getServletContext()));
  MyHelloWorldBean b=(MyHelloWorldBean) context.getBean("sayHi");
  out.println("<h1>"+b.getMessage()+"</h1>");

  out.println("</body>");
  out.println("</html>");
 }

}

JavaServerFaces und Spring

In der faces-config.xml konfigurieren, dass Beans auch über Spring aufgelöst werden

<?xml version="1.0"?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
      http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"

   version="2.0">

 <application>
  <!-- For Spring -->
  <variable-resolver>
  org.springframework.web.jsf.DelegatingVariableResolver
  </variable-resolver>
  <!-- For Spring -->
 </application>

</faces-config>

In der web.xml muss, wie beim Servlet Beispiel, auch eine Referenz auf Spring vorhanden sein:

<!-- Get spring reference -->
 <listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 </listener>
 <listener>
  <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
 </listener>

In der JSF kann man dann so die Inhalte der Bean ausgeben:

<h:outputText value="#{sayHi.message}" />

Ist ist dann unerheblich, ob die Bean jetzt über Spring aufgelöst wird, ober durch eine mit

@ManagedBean

annotierten Klasse.

Aspektorientierte Programmierung (AOP) mit Spring

Aspektorientierte Programmierung

Man hat eine Menge von Methoden. Als erstes schränkt man ein, welche dieser Methoden man beinflussen möchte. Diese Filterbedingung nennt man Pointcut. Eine Methode, die man dabei auswählt hat, nennen man Join-Point. Jetzt definiert man eine neue Methode, die z.B. beim Verlassen der Join-Point Methode aufgerufen werden soll. Diese neue Methode nenne man dann Advice.

Kleines Beispiel für aspektorientierte Programmierung mit Spring

Folgende jars werden benötigt (Spring 3.x)

Aus dem "dist" Order:

  • org.springframework.aop-3*
  • org.springframework.asm-3*
  • org.springframework.beans-3*
  • org.springframework.context-3*
  • org.springframework.core-3*
  • org.springframework.expression-3*

Plus diese hier aus dem großen Spring Paket (mit Dependencies)

  • com.springsource.org.aopalliance-1*.jar
  • com.springsource.org.aspectj.weaver-1*.jar

beans.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans
 xmlns="http://www.springframework.org/schema/beans xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance xmlns:aop=http://www.springframework.org/schema/aop
 xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                     http://www.springframework.org/schema/aop   http://www.springframework.org/schema/aop>  http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"
 >

 <bean id="
myBookStore" scope="singleton" class="invalid.mypackage.BookStoreOnline" />
 <bean id="
bookReader" scope="singleton" class="invalid.mypackage.BookReaderImpl">
  <constructor-arg ref="
myBookStore"/>
  <property name="
creditStatus" value="BBB" />
 </bean>

 <bean id="
myBookStoreBuyAdvice" class="invalid.mypackage.BookStoreBuyAdvice" />
 <aop:config>
  <!-- <aop:pointcut id="
myQueryPointcut" expression="target(invalid.mypackage.BookStore)" /> -->
  <aop:pointcut id="
myQueryPointcut" expression="execution(Integer buy(java.lang.String))" />
  <aop:advisor advice-ref="
myBookStoreBuyAdvice" pointcut-ref="myQueryPointcut" />
 </aop:config>

</beans>

Neue Bean:

package invalid.mypackage;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class BookStoreBuyAdvice implements MethodInterceptor
{

 public Object invoke(MethodInvocation invocation) throws Throwable
 {
  System.out.println("Advice started.");
  return invocation.proceed();
 }

}

Das in eine Main:

public static void main(String[] args)
{
 BeanFactory beanFactory =new ClassPathXmlApplicationContext("beans.xml");
 BookReader lBookReader = (BookReader) beanFactory.getBean("bookReader");
 System.out.println(lBookReader);
}

Siehe auch:

Spring & JDBC

Spring & Hibernate

Man braucht schon mal folgende jars (Version kann natürlich abweichen)

  • com.springsource.antlr-2.7.7.jar
  • com.springsource.javax.ejb-3.0.0.jar
  • com.springsource.net.sf.cglib-2.2.0.jar
  • com.springsource.org.apache.commons.pool-1.3.0.jar
  • com.springsource.org.apache.openjpa-1.1.0.jar
  • com.springsource.org.hibernate.annotations-3.4.0.GA.jar
  • com.springsource.org.hibernate.annotations.common-3.3.0.ga.jar
  • com.springsource.org.hibernate.ejb-3.4.0.GA.jar
  • com.springsource.slf4j.api-1.5.3.jar
  • commons-collections-3.2.jar
  • commons-dbcp-1.2.1.jar
  • dom4j-1.4.jar
  • ejb3-persistence.jar
  • hibernate-annotations.jar
  • hibernate-commons-annotations.jar
  • hibernate3.jar
  • jta.jar
  • log4j-1.2.14.jar
  • ojdbc14.jar
  • slf4j-api.jar
  • slf4j-log4j12.jar

Das muss in die beans.xml

<!-- Hibernate -->
<bean id="myHibernateProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
 <props>
  <prop key="hibernate.dialect">org.hibernate.dialect.Oracle9Dialect</prop>
  <prop key="hibernate.cache.provider_class">org.hibernate.cache.NoCacheProvider</prop>
  <prop key="hibernate.jdbc.fetch_size">50</prop>
  <prop key="hibernate.show_sql">true</prop>
 </props>
</property>
</bean>

<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
 <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
 <property name="url" value="jdbc:oracle:thin:@MyDBServerName:1521:MyDBName" />
 <property name="username" value="yourDBAUserName" />
 <property name="password" value="yourSuperSecretPasswort" />
</bean>

<bean id="mySessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
 <property name="dataSource" ref="myDataSource" />
 <property name="annotatedClasses">
  <list>
   <value>invalid.mypackage.MyDataObject</value>
  </list>
 </property>

 <!--
  <property name="mappingResources">
   <list>
    <value>invalid/mypackage/MyDataObject.hbm.xml</value>
  </list>
 </property>
 -->

 <property name="hibernateProperties" ref="myHibernateProperties" />
</bean>

<bean id="myUserDAO" class="invalid.mypackage.UserDAOImpl">
 <property name="sessionFactory" ref="mySessionFactory"/>
</bean>
<!-- Hibernate -->

Eine kleine Klasse, die per Annotations gemappt wird:

package invalid.mypackage;

import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;

@Entity(name="MyDataObject")
@Table(name="schemaFoo.TableBar")
public class MyDataObject
{
 private Integer id;

 private String name;

 private Integer age;

 @Id
 public Integer getId()
 {
  return id;
 }

 public void setId(Integer id)
 {
  this.id = id;
 }

 public String getName()
 {
  return name;
 }

 public void setName(String name)
 {
  this.name = name;
 }

 public Integer getAge()
 {
  return age;
 }

 public void setAge(Integer age)
 {
  this.age = age;
 }

 @Transient
 @Override
 public String toString()
 {
  return "MyDataObject [id=" + id + ", name=" + name + ", age=" + age + "]";
 }

}

Eine Komponente, um auf die Datenbank zuzugreifen:

package invalid.mypackage;

import java.util.List;
import org.hibernate.SessionFactory;
import org.springframework.orm.hibernate3.HibernateTemplate;

public class UserDAOImpl
{
 private HibernateTemplate hibernateTemplate;

 public void setSessionFactory(SessionFactory sessionFactory)
 {
  this.hibernateTemplate = new HibernateTemplate(sessionFactory);
 }

 @SuppressWarnings("unchecked")
 public List<MyDataObject> listMyDataObjects()
 {
  return hibernateTemplate.find("from MyDataObject");
 }

}

Und so greift man dann z.B. zu:

UserDAOImpl lDB=(UserDAOImpl) beanFactory.getBean("myUserDAO");

for(MyDataObject d : lDB.listMyDataObjects())
{
 System.out.println(d.toString());
}