• Post Reply Bookmark Topic Watch Topic
  • New Topic

Trying to Understand What Spring in Action Is Telling Me  RSS feed

 
Kevin Simonson
Ranch Hand
Posts: 198
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Okay, I've read Craig Walls' book Spring in Action from page 1 to page 37. On page 35 are listings:

and:

I can't get the second one to compile, but let's forget that for right now. In order to get class {SgtPeppers} wired into the interface (am I saying that right?), I need another piece of code, namely the listing on page 36:

On the bottom of page 36 Walls says, "Believe it or not, with only two classes created, you already have something that you can try out. To test that component scanning works, let's write a simple JUnit test that creates a Spring application context and asserts that the CompactDisc bean is, in fact, created." Then on page 37 is the listing:

Now, if I were to get rid of all the Spring annotations and then run {CDPlayerTest}, variable {cd} would have value {null}, and therefore the test would fail. But, if I'm understanding the Walls book right, because I have the Spring annotations, and because of autowiring, Spring detects that in the {soundsystem} package there's a bean {SgtPeppers} that implements {CompactDisc}, so it wires that bean into {cd}, right? So the expression:

would give me {true}, right? Is that all I need to know at the bottom of page 37, that Spring takes a variable declared with an interface, and instantiates it as a variable of an autowired bean that implements that interface? And what do I have to do to get this to happen? Does compiling class {CDPlayerConfig} cause Spring to autowire, or do I have to do something else with class {CDPlayerConfig}? I'm just trying to figure out what pages 1-37 are telling me.
 
Kevin Simonson
Ranch Hand
Posts: 198
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Furthermore, at the bottom of Spring in Action, page 37, Walls says, "Any classes in or under the soundsystem package that are annotated with @Component will also be created as beans. One line with @ComponentScan in exchange for countless automatically created beans is a good trade-off." What does Walls mean here? What's he talking about when he speaks of "countless automatically created beans"?
 
Jesper de Jong
Java Cowboy
Sheriff
Posts: 15813
76
Android IntelliJ IDE Java Scala Spring
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I don't have the book, so I don't know what's on page 37 and where the book is at that point when explaining Spring. But I do have a lot of experience working with Spring.

Kevin Simonson wrote:Is that all I need to know at the bottom of page 37, that Spring takes a variable declared with an interface, and instantiates it as a variable of an autowired bean that implements that interface? And what do I have to do to get this to happen? Does compiling class {CDPlayerConfig} cause Spring to autowire, or do I have to do something else with class {CDPlayerConfig}? I'm just trying to figure out what pages 1-37 are telling me.

No, this has nothing to do with compiling, Spring autowiring happens at runtime and not at compile time.

To make it work, you have to create a Spring ApplicationContext of the right type, one that is initialized by looking at the annotations. Then you can lookup the Spring beans from the application context.

Something like this:

See Instantiating the Spring container using AnnotationConfigApplicationContext in Spring's reference documentation for more information.

In the example, you have a JUnit test class named CDPlayerTest which is run using the SpringJUnit4ClassRunner. With this, you don't have to create the application context yourself; the SpringJUnit4ClassRunner, that's called by JUnit when you run the test, takes care of creating and initializing the Spring application context. The CDPlayerTest class has a @ContextConfiguration that specifies to the Spring test runner how it should configure the application context, in this case by taking the CDPlayerConfig configuration class.

The SpringJUnit4ClassRunner also makes it possible to autowire beans into your test class, as you can see because there's an @Autowired field in the test class.

So, how it works when you run your CDPlayerTest class using JUnit, is this:

  • JUnit sees the @RunWith annotation and creates a SpringJUnit4ClassRunner object to run the test
  • The SpringJUnit4ClassRunner is a Spring-specific unit test runner, which creates the Spring application context, using the configuration that's specified with the @ContextConfiguration annotation on your test class
  • Autowiring is done when the Spring application context is created
  • It also does autowiring on your test class, so the member variable 'cd' of the test class is made to point to the bean that implements interface CompactDisc (which is a SgtPeppers object)
  • Then the test methods in the CDPlayerTest class are executed, and they will have the CompactDisc bean to work with that has been autowired in the previous step.


  • What is very important to understand is that you must always get Spring beans from the Spring application context somehow. Never instantiate a Spring bean class using new, because then autowiring doesn't work - you'll just get a regular instance of the class on which Spring hasn't done its autowiring logic.
     
    Kevin Simonson
    Ranch Hand
    Posts: 198
    1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Jesper de Jong wrote:So, how it works when you run your CDPlayerTest class using JUnit, is this:

  • JUnit sees the @RunWith annotation and creates a SpringJUnit4ClassRunner object to run the test
  • The SpringJUnit4ClassRunner is a Spring-specific unit test runner, which creates the Spring application context, using the configuration that's specified with the @ContextConfiguration annotation on your test class
  • Autowiring is done when the Spring application context is created
  • It also does autowiring on your test class, so the member variable 'cd' of the test class is made to point to the bean that implements interface CompactDisc (which is a SgtPeppers object)
  • Then the test methods in the CDPlayerTest class are executed, and they will have the CompactDisc bean to work with that has been autowired in the previous step.


  • What is very important to understand is that you must always get Spring beans from the Spring application context somehow. Never instantiate a Spring bean class using new, because then autowiring doesn't work - you'll just get a regular instance of the class on which Spring hasn't done its autowiring logic.

    Okay, now I've moved on to the listing on page 42 of Spring in Action, which is:

    Walls has added a few new things, but largely it's the same file as before, and has all the Spring annotations it had before.

    But, Jesper, you said, "Autowiring is done when the Spring application context is created," and then you said, "It also does autowiring on your test class, so the member variable 'cd' of the test class is made to point to the bean that implements interface CompactDisc (which is a SgtPeppers object)". Realize that I'm new to this, and autowiring is a new concept that I don't totally grasp. I think I understand what autowiring means when it comes to the {cd} variable. Correct me if I'm wrong, but I think autowiring keeps that {cd} variable from being {null}, and actually makes it become a {SgtPeppers} object. But you seem to be implying that more autowiring than that is being done. What other autowiring is there that is being done?
     
    Jesper de Jong
    Java Cowboy
    Sheriff
    Posts: 15813
    76
    Android IntelliJ IDE Java Scala Spring
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    The whole reason why you want to do autowiring is the following.

    Autowiring is Spring's name for dependency injection. When you design a Java class which is part of a larger program, then that class will most likely need to use other classes in the program. In other words, the class depends on those other classes - it needs other classes to do what it has to do.

    For example, you might have a class that needs to insert or get things from a database. That class will need to get a database connection object from somewhere. The database connection object is a dependency of the class you're creating.

    The simple thing to do is to just create the database connection object using, for example, new MySQLDatabaseConnection(...) at the point where it's needed in the class. But there are disadvantages to this approach.

    First of all, it tightly binds your class to one specific implementation of a database connection class. Suppose that later you'd want to use OracleDatabaseConnection instead of MySQLDatabaseConnection, then you would need to modify the source code of your class and all other classes where you were previously doing new MySQLDatabaseConnection(...).

    Also, for testing you might want to use some mock database connection object instead of a real database connection object, or you might want to make it configurable which kind of database you're using. That wouldn't be possible if you would have hard-coded the use of a specific database connection class everywhere in your code.

    Dependency injection is a solution to this problem. Instead of creating the database connection object in the class itself, you only declare that your class needs a database connection, and then you let a dependency injection framework such as Spring create the specific database connection object and initialize it in your class.

    Note that class Example now does not have a direct dependency on any specific implementation of DatabaseConnection. Which DatabaseConnection is used, is determined by the configuration that you pass to Spring. This makes it easy to use different kinds of connections for different circumstances. For example, for a unit test I can configure that a MockDatabaseConnection is used, and for production that an OracleDatabaseConnection is used.

    When you initialize the Spring application context, it will search for classes in the packages that you specified which have a @Component annotation (or a related annotation, such as @Service). It will create an instance of each of these classes, which is stored in the application context. It will also look for fields in those classes with an @Autowired annotation, and it will set these fields to other components that it has found.

    In your example, this means it will indeed set the 'cd' member variable of your test class to a SgtPeppers object.
     
    Kevin Simonson
    Ranch Hand
    Posts: 198
    1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Jesper de Jong wrote:When you initialize the Spring application context, it will search for classes in the packages that you specified which have a @Component annotation (or a related annotation, such as @Service). It will create an instance of each of these classes, which is stored in the application context. It will also look for fields in those classes with an @Autowired annotation, and it will set these fields to other components that it has found.

    In your example, this means it will indeed set the 'cd' member variable of your test class to a SgtPeppers object.

    Thanks for the feedback!
     
    • Post Reply Bookmark Topic Watch Topic
    • New Topic
    Boost this thread!