⬆️ ⬇️

Spring IoC Annotation-based configuration on the example of JSF

At the request of the workers I am writing an article about Spring IoC. I am not so guru in this matter, however I can tell something.



One of the most interesting innovations of Spring 2.5 was the ability to use annotations to manage bins and their properties. Of course, it won't do without xml, but still the main part spreads along the code. So let's get started.

In web.xml, as usual, we write where our beans will be described and, accordingly, the ContextLoader class. There is no magic, everything is standard for Spring IoC:

< context-param >

< param-name > contextConfigLocation </ param-name >

< param-value > /WEB-INF/beans.xml </ param-value >

</ context-param >

< listener >

< listener-class > org.springframework.web.context.ContextLoaderListener </ listener-class >

</ listener >




* This source code was highlighted with Source Code Highlighter .


And then the most interesting part begins - we declare that we will use annotations to configure the context. In the file /WEB-INF/beans.xml we write these things:

<? 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:context ="http://www.springframework.org/schema/context"

xsi:schemaLocation ="http://www.springframework.org/schema/beans

www.springframework.org/schema/beans/spring-beans-2.5.xsd

www.springframework.org/schema/context

www.springframework.org/schema/context/spring-context-2.5.xsd"
>



< context:annotation-config />

< context:component-scan base-package ="ru.mypackage" />

</ beans >




* This source code was highlighted with Source Code Highlighter .


<context: annotation-config /> - actually declares the configuration style annotations.

<context: component-scan base-package = “ru.mypackage” /> is a parameter that tells Spring which package (and subpackages, respectively) to search for its annotations.

At this setting is over, go to the most delicious!



Stereotypes



There are 4 annotations that declare to Spring that this class is a bin — @Component, Service , Repository, and Controller . In fact, all these annotations are practically the same, but @Component is the most common for all. Why then so many options? There are several answers to this question: a clearer division into layers ( Repository - the database operation layer; Controller - the presentation operation layer; Service - the service layer, business logic, etc.); the possibility of using aspects for different layers; maybe some functionality will be added in the future. It is better to always mark more specific annotations, i.e. when choosing between Component and Service it is better to choose Service.

A certain analogue of a property for dependency injection is the @Autowired annotation, but it has more features.

Now a few words about how it all fits in with JSF. There are no problems here either - Spring has its own EL-Resolver, which allows you to search for beans in context. In faces-config.xml we write the following line:

< el-resolver > org.springframework.web.jsf.el.SpringBeanFacesELResolver </ el-resolver >



* This source code was highlighted with Source Code Highlighter .


Now consider a small, completely abstract example. Suppose we have a page that displays flight data and their model class looks like this:

package ru.mypackage.domain;



import java.util.Date;



public class Flight {

private int number;

private String departureCity;

private String arrivalCity;

private Date departureDate;

private Date arrivalDate;



public int getNumber() {

return number;

}

public void setNumber( int number) {

this .number = number;

}

public String getDepartureCity() {

return departureCity;

}

public void setDepartureCity( String departureCity) {

this .departureCity = departureCity;

}

public String getArrivalCity() {

return arrivalCity;

}

public void setArrivalCity( String arrivalCity) {

this .arrivalCity = arrivalCity;

}

public Date getDepartureDate() {

return departureDate;

}

public void setDepartureDate(Date departureDate) {

this .departureDate = departureDate;

}

public Date getArrivalDate() {

return arrivalDate;

}

public void setArrivalDate(Date arrivalDate) {

this .arrivalDate = arrivalDate;

}

}




* This source code was highlighted with Source Code Highlighter .


And now the most interesting thing is to start writing the service layer and the database work layer.

Let's start with the database:

package ru.mypackage.dao;



import org.springframework.stereotype.Repository;



@Repository

public class FlightsDAO {

public List <Flight> getFlights() {

//

}

}




* This source code was highlighted with Source Code Highlighter .




We have marked our class with the Repository annotation and now Spring is creating in the context of a Bond-Siglton FlightDAD type.

Our service layer will somewhat duplicate DAO, but, obviously, this is due to the simplicity of the example model:

package ru.mypackage.beans;



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

import org.springframework.context.annotation.Scope;

import org.springframework.stereotype.Controller;



@Controller( "flightsBean" )

@Scope( "session" )

public class FlightsBean {



@Autowired

private FlightsDAO dao;



public List <Flights> getAllFlights() {

return dao.getFlights();

}

}




* This source code was highlighted with Source Code Highlighter .


Now let's see what we got here. FlightsBean is flagged with two Controller (“flightsBean”) and Scope (“session”) annotations. The Controller (“flightsBean”) says that the bean is the page controller and you can access it by the name of flightsBean. If you do not specify anything in the name, then by default Spring will create a variable according to the Java Naming Conventions - it will convert the first letter to lower case, leave the rest as is. Scope (“session”) - defines the scope, for web applications, the basic values ​​— session, request, and singleton — the rest are used less frequently. In the absence of the Scope parameter, a singleton object is created.

@Autowired tells Spring to find in the context an object of the FlightsDAO class and put it into the dao variable, respectively.

It remains to display the data on the JSF-page:

< h:dataTable value ="#{flightsBean.allFlights}" var ="item" >

< h:column >< h:outputText value ="#{item.number}" /></ h:column >

< h:column >< h:outputText value ="#{item.departureCity}" /></ h:column >

< h:column >< h:outputText value ="#{item.arrivalCity}" /></ h:column >

< h:column >< h:outputText value ="#{item.departureDate}" /></ h:column >

< h:column >< h:outputText value ="#{item.arrivalDate}" /></ h:column >

</ h:dataTable >




* This source code was highlighted with Source Code Highlighter .


I would like to mention several features of @Autowired: a variable that is injected should be the same for a given bin, naturally, you cannot inject a variable with a smaller region into a variable with a larger one (it is obvious that during a session there can be several requests, singleton, several sessions); when creating a bin, the fields are not yet injected, that is, when accessing dao, in our case, a NullPointerException will crash from the constructor.

The latter is managed through the annotation method @PostConstruct. Let us, for example, need to create a combo box on the page that displays flight numbers. The most obvious solution would look something like this:

package ru.mypackage.beans;



import java.util.LinkedList;

import java.util. List ;



import javax.faces.model.SelectItem;



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

import org.springframework.context.annotation.Scope;

import org.springframework.stereotype.Controller;



@Controller( "flightsBean" )

@Scope( "session" )

public class FlightsBean {



@Autowired

private FlightsDAO dao;



private List <SelectItem> flightNumberList;

private int number;



public FlightsBean() {

setFlightNumberList( new LinkedList<SelectItem>());

for (Flight flight : dao.getFlights()) {

getFlightNumberList().add( new SelectItem(flight.getNumber()));

}

}



public List <Flights> getAllFlights() {

return dao.getFlights();

}



public void setFlightNumberList( List <SelectItem> flightNumberList) {

this .flightNumberList = flightNumberList;

}



public List <SelectItem> getFlightNumberList() {

return flightNumberList;

}



public void setNumber( int number) {

this .number = number;

}



public int getNumber() {

return number;

}

}




* This source code was highlighted with Source Code Highlighter .


And accordingly JSF-code:

< h:selectOneMenu value ="#{flightsBean.number}" >

< f:selectItems value ="#{flightsBean.flightNumberList}" />

</ h:selectOneMenu >




* This source code was highlighted with Source Code Highlighter .


However, for the reasons described above, this will not work. It is necessary to replace the constructor with the following method:

@PostConstruct

public void init() {

setFlightNumberList( new LinkedList<SelectItem>());

for (Flight flight : dao.getFlights()) {

getFlightNumberList().add( new SelectItem(flight.getNumber()));

}

}




* This source code was highlighted with Source Code Highlighter .


The method with @PostConstruct is called immediately after creating a bean and injecting all dependencies into it. Similarly, @PreDestroy allows you to call a method before destroying.

To create most applications of this set is enough. I will try to write about other annotations in the next article, if of course it will be interesting to someone.

')

UPD. Learn more about the Spring Framework here, but about Inversion of Control here.

Source: https://habr.com/ru/post/48439/



All Articles