560 likes | 581 Views
Высшая школа ИТИС. Лекция 5 - Spring Framework Inversion of Control 16 октября 2013. Веб-разработка на Java. Алина Витальевна Васильева доцент факультета Компьютерных Наук Латвийский Университет инженер-разработчик, Одноклассники, Mail.ru Group alina.vasiljeva@gmail.com.
E N D
Высшая школа ИТИС Лекция 5 - Spring Framework Inversion of Control 16 октября 2013 Веб-разработка на Java Алина Витальевна Васильева доцент факультета Компьютерных Наук Латвийский Университет инженер-разработчик, Одноклассники, Mail.ru Group alina.vasiljeva@gmail.com
Spring Overview • Spring is an open source layered Java/JavaEE application framework • Created by Rod Johnson • Based on book “Expert one-on-one J2EE Design and Development” (October, 2002) • Current version 3.2.4 (released on August 6, 2013)
Initial Goal – Lightweight Framework • Non-intrusive • No container requirements • Simplify application development • Remove re-occurring pattern code • Productivity friendly • Unit test friendly • Very pluggable • Open source
SpringMission Statement [1/3] • Java EE should be easier to use • It's best to program to interfaces, rather than classes. Spring reduces the complexity cost of using interfaces to zero. • JavaBeans (POJO) offer a great way of configuring applications • OO design is more important than any implementation technology, such as Java EE
SpringMission Statement [2/3] • Checked exceptions are overused in Java. A framework shouldn't force you to catch exceptions you're unlikely to be able to recover from. • Testability is essential, and a framework such as Spring should help make your code easier to test • Spring should be a pleasure to use • Your application code should not depend on Spring APIs
SpringMission Statement [3/3] • Spring should not compete with good existing solutions, but should foster integration. For example, Hibernate is a great object- relational mapping solution. Don't need to develop another one.
Success Story SpringSource has been purchased for ~$400million by VMWare in August 2009
Spring Products • Spring platform today provides A LOT of products for many different tasks • “from configuration to security, web apps to big data” • The central and core product is Spring Framework
Some Advantages • Build applications from POJOs • Execute a database transaction without dealing with Transaction APIs • Make a local Java method: • a remote procedure without dealing with Remote APIs • a message handler without dealing with JMS APIs • a management operation without dealing with JMX APIs
Modules of the Spring Framework The Spring Framework consists of features organized into about 20 modules http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/overview.html#overview-modules
Modules of the Spring Framework • Core Container: • Core and Beans – Inversion of Control, Dependency Injection • Context • Expression Language • Data Access/Integration • JDBC, ORM, OXM, JMS and Transaction • Web MVC • Model-View-Controller implementation for web applications
Modules of the Spring Framework • AOP • Remote Access • Authentication and Authorization • Remote Management • Messaging Framework • Web Services • Email • Testing • many more . . . Spring is very loosely coupled, components widely reusable and separately packaged
How to start • Add Maven dependency to application’s pom.xml • Use the following command to download sources • spring-context provides core functionality <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>3.2.4.RELEASE</version> </dependency> </dependencies> mvn eclipse:eclipse -DdownloadSources=true
Let’s Start ! Simple demo example showing the basic concept of dependency injection: • Add Spring dependency to your Maven project http://projects.spring.io/spring-framework/#quick-start • <dependency> • <groupId>org.springframework</groupId> • <artifactId>spring-context</artifactId> • <version>3.2.4.RELEASE</version> • </dependency>
Dependency Injection Demo • Create an interface for a message service • Create a component printing out a message received from a message service public interface MessageService { String getMessage(); } @Component public class MessagePrinter { @Autowired private MessageService service; public void printMessage() { System.out.println(service.getMessage()); } }
Dependency Injection Demo • Create main application @Configuration @ComponentScan public class Application { @Bean MessageService mockMessageService() { return new MessageService() { public String getMessage() { return "Hello World!"; } }; } public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(Application.class); MessagePrinter printer =context.getBean(MessagePrinter.class); printer.printMessage(); } }
Dependency Injection Demo Result: • Spring Framework wired everything together • MessagePrinter is decoupled from the MessageService implementation Hello World!
Inversion of Control (IoC) • Central in the Spring is its Inversion of Control container • Based on “Inversion of Control Containers and the Dependency Injection pattern” (by Martin Fowler) • Inversion of Control and Dependency Injection, in fact, are not the same thing • Inversion of Control is a much more general concept • Dependency Injection is merely one concrete example of IoC
Inversion of Control • IoC covers a broad range of techniques that allow an object to become a passive participant in the system • IoC provides centralized, automated configuration, managing and wiring of application Java objects • Container responsibilities: • creating objects • configuring objects • calling initialization methods • etc Java objects that are managed by the Spring IoC containerare referred to as beans Object’s lifecycle
Dependency Injection • Dependency Injection is a technique that frameworks use to wire together an application • In nearly all applications there are at least two participants, and these participants are required to somehow collaborate • The objects in an application have dependencieson each other • The trick is to connect objects without locking in the connection
Dependency Injection • Java platform lacksthe means to organize the basic application building blocks into a coherent whole • Design patterns can help, such as Factory, Abstract Factory, Builder, Decorator, Service Locator … • But patterns are just formalized best practices that you must implement yourself in your application • Spring Framework IoCcomponent addresses this concern
Example Let’s consider a simple Internet Shop example
Example public interface CashRegister { public double calculateTotalPrice(ShoppingCart cart); } public interface PriceMatrix { public double lookupPrice(Item item); } public class CashRegisterImpl implements CashRegister { private PriceMatrix priceMatrix = new PriceMatrixImpl(); public double calculateTotalPrice(ShoppingCart cart) { double total = 0; for (Item item : cart.getItems()) { total += priceMatrix.lookupPrice(item)); } return total; } }
Major issues • Every instance of CashRegisterImpl has a separate instance of PriceMatrixImpl • With heavy services it is preferable to share a single instance across multiple clients • CashRegisterImpl now has concrete knowledge of the implementation of PriceMatrix • Tight coupling • By explicitly creating its own dependent objects, the CashRegisterImpl creates a difficult test situation • Unit tests have to be divorced from any environment requirements
Major issues • PriceMatrixImplmight need some specific and complex configuration after creation • initialization with business parameters • dependencies on other components PriceMatrix priceMatrix = new PriceMatrixImpl(); priceMatrix.setCoefficient(5); priceMatrix.setMaxValue(10000); priceMatrix.setRemoteProvider(new RemoteProviderImpl()); . . . Need to repeat it in any place where price matrix is needed . . .
First attempt – Service Locator • The Service Locatorpattern encapsulates the actions taken to obtain a reference to the object required • Thisshields the client from knowing how, or even where, to obtain a reference to the object
Updated code public class CashRegisterImpl implements CashRegister { private PriceMatrix priceMatrix; public CashRegisterImpl() { priceMatrix = ServiceLocator.getPriceMatrix(); } public double calculateTotalPrice(ShoppingCart cart) { double total = 0; for (Item item : cart.getItems()) { total += priceMatrix.lookupPrice(item)); } return total; } }
Result • Using Service Locator the class no longer has to manage object creation • Next benefit - client has no knowledge of theconcrete implementation of PriceMatrix • The third problem, the lack of testability, is unfortunately still with us • Creating a unit test is extremely difficult, becausethe implementation relies on a functioning PriceMatrix object
Testing with mock objects • The test should be written with amock PriceMatrix, but there is no way to insert the mock object during testing • The static ServiceLocator.getPriceMatrix is difficult to change during a test run • There is a need to swap different implementations of PriceMatrix without affecting the client
How to achieve it ? • To effectively do this, the client must not actively participate in the construction or retrieval of the resource • The resource must be given to the client • Solution Dependency Injection
Dependency Injection • Dependency Injection is a technique to wire an application together without any participationby the code that requires the dependency • The object provides a property that can hold a reference to a specific service • When the object is created a reference to an implementation of that type of service will automatically be injected into that property - by an external mechanism
Non-IoC versus IoC Non Inversion of Control approach Inversion of Control approach
Dependency Injection Basics • Basic JavaBean pattern: • include a “getter” and “setter” method for each private field: • Application components provide fields & setters through which resources are passed in during initialization • Spring extensively uses this pattern • Initialization is usually done through configuration file rather than application code class MyBean { private int counter; public int getCounter() { return counter; } public void setCounter(int counter) { this.counter = counter; } }
Final Code public class CashRegisterImpl implements CashRegister { private PriceMatrix priceMatrix; public void setPriceMatrix(PriceMatrix priceMatrix) { this.priceMatrix = priceMatrix; } public double calculateTotalPrice(ShoppingCartcart) { double total = 0; for (Item item : cart.getItems()) { total += priceMatrix.lookupPrice(item)); } return total; } }
Example (non-IoC) public class UsersDataImportProcessor implements DataImportProcessor { private XmlParser xmlParser; private CommonJpaDAO jpaDAO; . . . publicvoidimportData() { xmlParser = new DomXmlParser(. . .); jpaDAO = new CommonJpaDAOImpl (. . .); . . . List<User> users = xmlParser.parseUsers(usersXml); . . . jpaDAO.save(user); . . . } }
Example (IoC) @Component public class UsersDataImportProcessor implements DataImportProcessor { @Autowired private XmlParser xmlParser; @Autowired private CommonJpaDAO jpaDAO; . . . publicvoidimportData() { . . . List<User> users = xmlParser.parseUsers(usersXml); . . . jpaDAO.save(user); . . . } }
Spring’s Dependency Injection • Beans define their dependencies through constructor arguments or properties • Container resolves (injects) dependencies of components by setting implementation objectduring runtime • BeanFactoryinterface – the core that loads bean definitions and manages beans • Superset ApplicationContext - represents the Spring IoC container and is responsible for instantiating, configuring and assembling beans
Creating ApplicationContext The ApplicationContext interface builds on top of the BeanFactory (it is a sub-interface) and adds other functionality In standalone applications it is common to create an instance ClassPathXmlApplicationContext ApplicationContext context = new ClassPathXmlApplicationContext( "applicationContext.xml"); File in a Maven project: \spring-app\src\main\resources\applicationContext.xml
Configuration metadata Application classes are combined with configuration metadata so that after the ApplicationContextis created and initialized, you have a fully configured and executable system
Configuration metadata • Configuration metadata defines how you as an application developer tell the Spring container to instantiate, configure, and assemble the objects in your application • There exists several forms of defining configuration metadata: • XML-based (traditional form) • Annotation-based configuration • Java-based configuration
XML configuration metadata Configuration consists of at least one and typically more than one bean definition that the container must manage
Internet Shop configuration <?xml version="1.0" encoding="UTF-8"?> <beans ...> <bean id="priceMatrix" class="beans.PriceMatrixImpl"/> <bean id="cashRegister" class="beans.CashRegisterImpl"> <property name="priceMatrix" ref="priceMatrix"/> </bean> </beans>
XML configuration: example public class VenueDataImportProcessor implements DataImportProcessor { private boolean cleanDatabase; private Map<String, List<String>> request; private String count; . . . public void setCleanDatabase(boolean cleanDatabase) { this.cleanDatabase = cleanDatabase; } // setters for all properties } <bean id="venuesProcessor" class="lv.lu.meetings.impl.venues.VenueDataImportProcessor"> <property name="cleanDatabase" value="true"/> <property name="count" value="10"/> <property name="request"> <map> <entry> <key><value>Riga, LV</value></key> <list> <value>food</value> <value>coffee</value> </list> </entry> </map> </property> </bean>
Using theContainer The BeanFactory interface has the following methods for client code to call: • boolean containsBean(String): returns true if the BeanFactory contains a bean definition or bean instance that matches the given name. • Object getBean(String): returns an instance of the bean registered under the given name. • Object getBean(String, Class<T>): returns a bean, registered under the given name. The bean returned will be cast to the given Class. • Class getType(String name): returns the Class of the bean with the given name. • boolean isSingleton(String): determines if the bean under the given name is a singleton. • String[] getAliases(String): Return the aliases for the given bean name, if any were defined in the bean definition.
Main Application public class InternetShopApp { public static void main(String[] args){ ApplicationContext appCtxt = new ClassPathXmlApplicationContext( "/applicationContext.xml"); CashRegister cashRegister = (CashRegister)appCtxt.getBean("cashRegister"); System.out.println("Total price: " + cashRegister.calculateTotalPrice( getShorringCart())); System.exit(0); } }
Annotation-based configuration • Instead of using XML, it is possible to move the configuration into the component class by using annotations on the relevant class, method, or field declaration • Enable annotation support by adding the following tag to XML configuration <beans ... > ... <context:annotation-config/> ... </beans>
Annotations • Some of the most common annotations: • @Component • @Autowired • @PostConstruct • @PreDestroy • @Required • @Resource • @Repository • @Service • @Bean • @Configuration
Classpath Scanning • There is a way to avoid registering each bean in XML file - using classpath scanning • Enable by adding the following tag to XML configuration: • Then, add @Component annotation to Java class definitions <context:component-scan base-package="<your_package>"/> http://docs.spring.io/spring/docs/3.2.4.RELEASE/spring-framework-reference/html/beans.html#beans-classpath-scanning
@Component • To register a bean simply add annotation @Component to the class definition • Such classes are considered as candidates for auto-detection when using annotation-based configuration and classpath scanning @Component("cashRegister") public class CashRegisterImpl implements CashRegister { http://docs.spring.io/spring/docs/3.2.4.RELEASE/spring-framework-reference/html/beans.html#beans-classpath-scanning
@Autowired • Marks a constructor, field, setter method or configuration method as to be autowired by Spring's dependency injection facilities • If there are multiple candidates for autowiring one option is to use • @Qualifier http://docs.spring.io/spring/docs/3.2.4.RELEASE/spring-framework-reference/html/beans.html#beans-autowired-annotation