Friday, June 4, 2010

Spring recipes - Chapter 3 - continued.

It turns out there's a lot of ground covered in Chapter 3. While 2.0 covers XML spring injection and a little bit of autowiring, they went kind of crazy with it in 2.5. A lot of it involves annotations. So for example, if you type "@autowired" in your source code, you don't have to specify the injection in your XML - spring will do it automatically. If you use "@Component", you don't even have to specify the beans in your XML - it will look them up automatically. They even have subdivided the @component into @Dao, @Service and @Controller, so you can specify which ones are included. the @Required annotation just looks up and does the injection for you.

Well, there's a lot more in there, but I'm focused for now on getting the code working. While I got the first one going on, I'm have trouble importing the second chapter 3 project. Let's give it another go.

Well, it let me import something - but - ok, now I get it. it says "some project were hidden because they were already in the workspace directory.

Let me try renaming them - I don't wont' to throw out code I modified.

Ok, the problem was, it couldn't import the second one because the first had already been named "sequence".

So, in the previous blog, I already covered the SequenceGenerator project.

Now, let's see if we can make the SequenceService project work.

As always, set java compliance to 1.5. (For some reason, it doesn't like 1.6)

Jump right in and run "main" (right click and run as java application).


30100000A
30100001A


Ok, here's what main does:

public class Main {

public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("beans.xml");

SequenceService sequenceService =
(SequenceService) context.getBean("sequenceService");

System.out.println(sequenceService.generate("IT"));
System.out.println(sequenceService.generate("IT"));
}
}


Ah, ok. I saw this in the book. It stored the sequence service in a hash map, I think, so it could have multiple sequence services? Then it used its chosen service to track the the last sequence in another hash map.

Let's take a look at beans.xml



<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">

<context:component-scan base-package="com.apress.springrecipes.sequence">
<context:include-filter type="regex"
expression="com\.apress\.springrecipes\.sequence\..*Dao.*" />
<context:include-filter type="regex"
expression="com\.apress\.springrecipes\.sequence\..*Service.*" />
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller" />
<context:component-scan>


Ok, there is the old component scan. So, there are no beans, specified, no injection parameters passed - just basically telling spring to find anything name DAO or Service, and do its thing.


So, what in these sources?

ackage com.apress.springrecipes.sequence;

public class Sequence {

private String id;
private String prefix;
private String suffix;

public Sequence(String id, String prefix, String suffix) {
this.id = id;
this.prefix = prefix;
this.suffix = suffix;
}

public String getId() {
return id;
}

public String getPrefix() {
return prefix;
}

public String getSuffix() {
return suffix;
}

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

public void setPrefix(String prefix) {
this.prefix = prefix;
}

public void setSuffix(String suffix) {
this.suffix = suffix;
}
}


This is just a bean with a few properties.


package com.apress.springrecipes.sequence;

public interface SequenceDao {

public Sequence getSequence(String sequenceId);
public int getNextValue(String sequenceId);
}

An interface that gets the above named Sequence instance based on an id.

package com.apress.springrecipes.sequence;

import java.util.HashMap;
import java.util.Map;

import org.springframework.stereotype.Repository;

@Repository("sequenceDao")

public class SequenceDaoImpl implements SequenceDao {

private Map sequences;
private Map values;

public SequenceDaoImpl() {
sequences = new HashMap();
sequences.put("IT", new Sequence("IT", "30", "A"));
values = new HashMap();
values.put("IT", 100000);
}

public Sequence getSequence(String sequenceId) {
return sequences.get(sequenceId);
}

public synchronized int getNextValue(String sequenceId) {
int value = values.get(sequenceId);
values.put(sequenceId, value + 1);
return value;
}
}

This implements the DAO. The Sequence instance it stores provides its own id, a suffix and a prefix.

This annotation:

@Repository("sequenceDao")

Gives it a component name (remember there are three types of Components, including repository, and you can specify the name, "sequenceDao", which otherwise would have been given the name sequenceDaoImpl, I think).

Finally, we have SequenceService:

package com.apress.springrecipes.sequence;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service("sequenceService")
public class SequenceService {

private SequenceDao sequenceDao;

@Autowired
public void setSequenceDao(SequenceDao sequenceDao) {
this.sequenceDao = sequenceDao;
}

public String generate(String sequenceId) {
Sequence sequence = sequenceDao.getSequence(sequenceId);
int value = sequenceDao.getNextValue(sequenceId);
return sequence.getPrefix() + value + sequence.getSuffix();
}
}

The autowired sequenceDao property will inject the SequenceDaoImpl from above.

So, getting back to main,

SequenceService sequenceService =
(SequenceService) context.getBean("sequenceService");

System.out.println(sequenceService.generate("IT"));
System.out.println(sequenceService.generate("IT"));

So, it goes into sequenceService, and calls generate("IT")

Which does this:


Sequence sequence = sequenceDao.getSequence(sequenceId);
int value = sequenceDao.getNextValue(sequenceId);
return sequence.getPrefix() + value + sequence.getSuffix();


First, it gets the "Sequence" instance stored by the key "IT".

We know from the constructor that it consists of this:

sequences.put("IT", new Sequence("IT", "30", "A"));

The prefix is 30, the suffix is "A".

Then, it gets the value. We know the first value is values is 100000 from the constructor

values = new HashMap();
values.put("IT", 100000);

So the call to getNext value (stored under "IT" in the values map) is 1000000. It then increments and restores it.

public synchronized int getNextValue(String sequenceId) {
int value = values.get(sequenceId);
values.put(sequenceId, value + 1);
return value;
}


And of course add the prefix and suffix:

return sequence.getPrefix() + value + sequence.getSuffix();


So, these two calls

System.out.println(sequenceService.generate("IT"));
System.out.println(sequenceService.generate("IT"));

increment these two values:

30100000A
30100001A

The main point of this one was that by using componenent-scan and its annotations, you don't have to name the beans in beans.xml - you just use regex to identify which classes/packages you want included in the component scan, and use annotation do identify the components. The dependency injection is achieved by the @Autowired annotation.

And, voila! That's it for chapter 3.

No comments:

Post a Comment