'A Scala Spring Framework dependency injection example'에 해당되는 글 1건

  1. 2014.03.28 A Scala Spring Framework dependency injection example
00.scala2014. 3. 28. 12:58
반응형

In my Java days I used the Spring Framework(Spring) all the time for different things, primarily involving dependency injection (DI) and pretty much anything related to a database. I became curious how Spring would work with Scala, so I created a small Scala/Spring dependency injection example project, which I'm sharing here.

The short answer is that Scala worked just fine with Spring, but it also showed me that I still have plenty to learn about inheritance in Scala.

My Spring applicationContext.xml file

I copied a Spring applicationContext.xml file from another project, then whittled it down to these bare essentials:

<?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="dog" class="scalaspring.Dog">
    <constructor-arg value="Fido" />
  </bean>

  <bean id="cat" class="scalaspring.Cat">
    <constructor-arg value="Felix" />
  </bean>

</beans>

You can't tell it from there, but both the Dog and Cat classes extend an Animal class that I created. (More on that in a few moments.)

My Scala object with a main method

Next, I created a simple Scala object with a main method to test everything:

package scalaspring

import org.springframework.context.support.ClassPathXmlApplicationContext

object ScalaSpringExample
{
  def main(args: Array[String]) {
    
    // open/read the application context file
    val ctx = new ClassPathXmlApplicationContext("applicationContext.xml")

    // instantiate our dog and cat objects from the application context
    val dog = ctx.getBean("dog").asInstanceOf[Animal]
    val cat = ctx.getBean("cat").asInstanceOf[Animal]

    // let them speak
    dog.speak
    cat.speak

  }
}

As you can see from the code, I loaded the applicationContext.xml file, created my dog and catinstances by getting their bean definitions from the application context, then executed their speak methods.

One note here: Using the Scala asInstanceOf method is not considered "good practice", but I think it's very concise, particularly for a simple example like this.

My Spring-injected Scala classes

I defined my Dog and Cat classes, as well as a parent abstract class named Animal, in a file namedAnimals.scala:

package scalaspring

abstract class Animal(name: String) {
  def speak:Unit
}

class Dog(name: String) extends Animal(name) {  
  override def speak {
    println(name + " says Woof")
  }
}

class Cat(name: String) extends Animal(name) {
  override def speak {
    println(name + " says Meow")
  }
}

As you can see, all three classes have a speak method, which is implemented differently in the Dog andCat classes. I've also defined the classes using a one-argument constructor, and if you look back at the application context file, you'll see that I pass in the names "Fido" and "Felix" in my Spring bean definitions.

Note that I didn't have to make things this complicated, but I wanted to make sure that the Spring Framework was really working with Scala as expected.

My log4j.properties file

Spring wouldn't run without a logger configuration, so I copied this Log4j properties file from another project, then whittled it down to these lines:

log4j.rootLogger=INFO, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p | %d{HH:mm:ss} | %m | %F | %L%n

log4j.logger.org.springframework=INFO
log4j.logger.org.springframework.context.support.ClassPathXmlApplicationContext=INFO

I don't need all those lines, but I haven't used Log4j in a while, and didn't feel like fooling around with it, so I left it as is.

Jar files needed

I needed these jar files to get everything to run:

commons-logging-1.1.1.jar
log4j-1.2.14.jar
spring-2.5.5.jar

My project directory structure

I used the Scala sbt tool to compile and run my project, and therefore I created my directory structure like this:

./build.sbt
./lib
./lib/commons-logging-1.1.1.jar
./lib/log4j-1.2.14.jar
./lib/spring-2.5.5.jar
./project
./src
./src/main
./src/main/config
./src/main/java
./src/main/resources
./src/main/resources/applicationContext.xml
./src/main/resources/log4j.properties
./src/main/scala
./src/main/scala/scalaspring
./src/main/scala/scalaspring/Animals.scala
./src/main/scala/scalaspring/ScalaSpringTest.scala
./src/test
./src/test/java
./src/test/resources
./src/test/scala
./target

Running my Scala / Spring Framework example

Finally, when I ran my Scala / Spring Framework dependency injection example, I got this very exciting output:

Fido says Woof
Felix says Meow

I actually got more output than that from both sbt and Log4j, but that was the important part.

As you can see, Scala and the Spring Framework seem to play well together. These lines of code from my Scala main method:

val ctx = new ClassPathXmlApplicationContext("applicationContext.xml")
val dog = ctx.getBean("dog").asInstanceOf[Animal]
val cat = ctx.getBean("cat").asInstanceOf[Animal]

are very similar to the lines of code I used in a similar Java Spring Framework dependency injection example, and the Spring applicationContext.xml file and the Log4j configuration file were identical to what they'd look like in a regular Java application.

My Scala inheritance problems

One thing I learned in tackling this project is that I need to learn more about Scala inheritance, in particular, overriding constructor methods that accept fields/parameters. If you look back at my applicationContext.xml file, you'll see that I passed the names "Fido" and "Felix" in using the Spring bean "constructor-arg" parameter. I choose this route because I got tired of trying to get my class "setter" methods to work.

What I mean by that is that I wanted to define my Spring beans like this:

<bean id="dog" class="scalaspring.Dog">
  <property name="name" value="Fido"/>
</bean>

but because I haven't worked with Scala inheritance much yet, I didn't know the proper way to define the "name" field in my Animal, Dog, and Cat classes for this "setter" syntax to work. I'll update this article after I take the time to learn that.

Posted by 1010