Sunday, October 31, 2010

Spring framework - beans

Introduction

As we all know Spring framework represent standard in Java application development. It can be used in desktop application or on web apps. It can help us with database access, security, RMI, web layer (MVC) and it can also integrate with other technologies like cutting edge GWT, or little bit older Flex stuff. There is also great Roo stuff (GWT or flex front-end)...but I don't know is this technology mature enough.

Spring framework secret lies in its simplicity, small footprint, ability to test outside of container.

In this article we will become more familiar with framework essential stuff -- wiring and instantiating Java POJO classes (Beans).

Source for following examples can be downloaded from my google code repository.

Spring wiring

In Spring, components (mainly Java classes...nothing fancy like CORBA or DCOM don't worry) are not responsible for managing their associations with other components, instead, references are passed through container. Controlling associations between components (passing reference from one place to another) is know an wiring. And this is at heard of Spring philosophy. This components are called beans and they share similarities with Java beans. And all this philosophy started with one of the greatest book in Java ecosystem: Expert One-on-One J2EE Design and Development.

Spring also act as bean factory (see my blog post about factory pattern that is here). But unlike many implementations of factory pattern, which often can create only single type of object, spring bean factory can create many different types of beans (list, data sources,...as you will see).

Off course, container is at the core of the Spring framework. This container uses inversion of o control (please see my post about template method design pattern that follows this principle, also known as Hollywood principle - "Don't call us, we will call you!") to manage component associations. There are several Spring containers and we will use Applicationcontext container that is most widely used and provide most basic Spring features we will explore in this post.

I choose to use simple Java application and not web app, because there are already many different examples of using Spring in web application and in this way we can avoid additional configurations and complexities that comes with web apps. For testing we will not use JUnit, but simple "public static void main".

In our simple Java application we will use Spring 2.5.6 (because it is lightweight -- 1.2MB with only essential libraries, and because I want to integrate it to Google App Engine at some point), we will also use XML file for bean wiring and defining. I don't like annotations because they mix with my code and break apart non-intrusive concept. I don't shy away from XML, because I think XML is not evil if you use it in right situations (like this one).

I will not go into details with Spring framework (like life cycle of bean), but I will just show main Spring futures on example (this will be enough to learn basic concepts). So let's start!

Example introduction

We will implement simple Employee (n)<->(1) Department system. This system can (beside classic CRUD operations) find largest department (department with largest number of employees), assign employee to that department and remove employee from department...and that's that!

Example service layer

First we will implement service layer of our example application. This is because service layer is on the top of abstraction hierarchy. In service layer we define operations we want our application to support.

There are two service components in service layer defined by interfaces (it is important to program to interface not implementation!): employee and department service. Employee service handles following employee matters (it's basically simple CRUD):

public interface EmployeeService {

    Employee findEmployee(int employeeId);

    void removeEmployee(int employeeId);

    List getAllEmployees();
    
    void createNewEmployee(Employee employee);

}

Department service can provide following operations (services):

public interface DepartmentService {
   
    Department findDepartment(int departmentId);
    
    void createNewDepartment(Department department);

    void removeDepartment(int departmentId);
    
    List getAllDepartments();
   
    /**
     * Find department with largest number of employees.
     * @return
     */
    Department findLargestDepartment();

    /**
     * Assign employee into department.
     * @param employee
     * @param department
     */
    void assign(Employee employee, Department department);
    
    /**
     * Remove employee from department.
     * @param employeeId
     * @param departmentId
     */
    void remove(int employeeId, int departmentId);

}

As you can see Department service provide couple of more complex operations than simple CRUD.

We have two classes that implement these two service interfaces:

Employee service concrete implementation:

public class EmployeeServiceImpl implements EmployeeService {

    private EmployeeDao employeeDao;

    public Employee findEmployee(int employeeId) {
        return getEmployeeDao().findById(employeeId);
    }

    public void createNewEmployee(Employee employee) {
        getEmployeeDao().create(employee);
    }

    public void removeEmployee(int employeeId) {
        Employee deleteEmployee = getEmployeeDao().findById(employeeId);
        getEmployeeDao().delete(deleteEmployee);
    }

    public List getAllEmployees() {
        return getEmployeeDao().retrieveAll();
    }

    public EmployeeDao getEmployeeDao() {
        return employeeDao;
    }
    
    public void setEmployeeDao(EmployeeDao employeeDao) {
        this.employeeDao = employeeDao;
    }   

Department service concrete implementation:

public class DepartmentServiceImpl implements DepartmentService {

    private DepartmentDao departmentDao;
    private EmployeeService employeeService;

    public Department findDepartment(int departmentId) {
        return getDepartmentDao().findById(departmentId);
    }

    public void createNewDepartment(Department department) {
        getDepartmentDao().create(department);
    }

    public List getAllDepartments() {
        return getDepartmentDao().retrieveAll();
    }

    public Department findLargestDepartment() {
        Department largestDepartment = getAllDepartments().size() > 0 ? getAllDepartments().get(0) : null;

        for (Department dep : getAllDepartments()) {

            if (dep.getEmployees().size() > largestDepartment.getEmployees().size()) {
                largestDepartment = dep;
            }
        }

        return largestDepartment;
    }

    public void removeDepartment(int departmentId) {
        Department deleteDepartment = findDepartment(departmentId);
        getDepartmentDao().delete(deleteDepartment);
    }

    public void assign(Employee employee, Department department) {
        if (department.getEmployees().size() < department.getMax()) {                                    
            getEmployeeService().createNewEmployee(employee);
            getDepartmentDao().addEmployee(department, employee);
        }
    }
    
    public void remove(int employeeId, int departmentId) {
       
        Department department = findDepartment(departmentId);
        Employee deleteEmployee = getEmployeeService().findEmployee(employeeId);

        getDepartmentDao().removeEmployee(department, deleteEmployee);
        
        if (department.getEmployees().isEmpty())
            removeDepartment(departmentId);
    }

    public EmployeeService getEmployeeService() {
        return employeeService;
    }

    public void setEmployeeService(EmployeeService employeeService) {
        this.employeeService = employeeService;
    }

    public DepartmentDao getDepartmentDao() {
        return departmentDao;
    }
    
    public void setDepartmentDao(DepartmentDao departmentDao) {
        this.departmentDao = departmentDao;
    }
    
}
There is no much stuff going on here (regarding both services - Employee and Departments) , as you can see much of the responsibility is delegated to underlaying DAO (Data Access Object) bean. Data Access Object beans handle interactions with database (or some other type of data storage - flat files for example, vintage databases, ...) to read and write employees and departments informations.

Implementation of these beans are not important for understanding Spring basic, so I will omit them (you can see implementation from downloadable example if you are curious). Let just say that implementation of these DAO beans in this example is very simple and it uses only java.util.List to hold data. You will see how we initially fill this list using Spring. So no persistence logic is implemented.

As you can see we both EmployeeServiceImpl and DepartmentServiceImpl can be given a reference to its DAO bean through "set" (setEmployeeDao and setDepartmentDao) method. You can also set them through constructor.

Example wiring

We choose XML as strategy for bean wiring. Wiring beans is quite simple. We use "" tags to define one Java Bean (or any Java object, actually -- it not need to be bean) to be configured with the Spring container.

Spring beans are by default singletons, but you can make beans to act like prototypes by setting singleton="false" attribute on bean element.
So we have something like this to define DepartmentService bean (EmployeeService bean is defined in same way):

        
            
        
        
            
        
    
In this simple bean XML wiring, we define bean with id departmentService and point to the full qualify class name (this class is concrete implemented class -- not interface). At this point we added a bean into Spring container (registered it) and now we can do whatever we want with this bean.

Beans can have properties. These properties are nothing more than Java properties. So we map these bean properties into Java properties (in this case) using XML. That is that.

This technique for populating a bean property based on standard naming convention is called dependencies injection. Off course this Java properties need to have matching "get" and "set" method (like in Java beans, or like in JSP/JSF backing beans). And this process is in the core of Spring framework (OK, there is also some other things like Aspect Oriented Programming - AOP, but I will talk about that later).

We can set properties with simple types (like int, long, float, String...) or we can set references to other beans (other Java object of any kind) like in this example (using ref tag). In this way and in our example we give DepartmentServiceImpl a reference to class that implement DepartmentDao interface (ref bean="departmentDao"/>).

Referencing other beans enable us to inject other beans into existing beans and in this way we create references between classes not as part of class logic but in Spring container this help us in leveraging this job to Spring and help us create cleaner and meaner code with ability of changing references only in XML.

We can also wire collections in Spring. In our example we did't use persistence logic or database, we just use simple java.util.List collections to hold data. So we use Spring to inject this data into DAO beans. We set list of departments into Java property named departments that have following type definition: java.util.List. So, we fill up one bean property with list of departments, this list of departments is defined in Spring XML file like so (I will show only one):

        
            1
        
        
            QA
        
        
            2
        
        
            
        
        
            
                
            
        
    
In this way we initially fill up collection that list of Departments.

Spring test

In oder to test Spring you need to do following:
ApplicationContext context = new ClassPathXmlApplicationContext("simpleappcontext.xml");

EmployeeService employeeService = (EmployeeService)context.getBean("employeeService");

DepartmentService departmentService = (DepartmentService)context.getBean("departmentService");

First get instance of context and read bean definition from xml file named "simpleappcontext.xml".
Then just get beans by their names and use them. :)

See my example for detail insight.

No comments:

Post a Comment