790 likes | 939 Views
Training - Day 4. The Spring Framework & Testing Web Applications. “Spring's main aim is to make J2EE easier to use and promote good programming practice. It does this by enabling a POJO-based programming model that is applicable in a wide range of environments.”
E N D
Training - Day 4 The Spring Framework & Testing Web Applications
“Spring's main aim is to make J2EE easier to use and promote good programming practice. It does this by enabling a POJO-based programming model that is applicable in a wide range of environments.” - Rod Johnson What does Spring do?
No logging API No connection pools No O/R mapping layer It does not reinvent the wheel. There are great projects that solve these problems and Spring’s goal is to make them easy to use. What it does not do
Inversion of Control Spring’s bean factory, which allows objects to be retrieved by name Singleton – one shared object in the factory (Default) Prototype – the factory will always create a new object Dependency Injection… a better name. “Don’t call me, I’ll call you!” Takes the responsibility of making things happen away from the application code and moves it into the framework Spring is an IoC Container
No Spring API intrusion in your code if you don’t want it Objects, or dependencies, are injected using ordinary Java method calls Configured in XML and injected at runtime Dependency Injection
Setter Injection Uses JavaBean setter methods Probably used more often Constructor Injection Uses constructor arguments Useful in special cases where objects need to do work in constructors Both types can be mixed Types of Dependency Injection
Developers don’t have to worry about looking dependencies up…such as JNDI in EJB projects. Easier to test. Simple JavaBean setters are easier to mock than a JNDI service. Promotes strong typing. What are the benefits?
Dependencies are explicit in the configuration… no need to read the code to determine application configuration. Easy to integrate legacy code because Spring Dependency Injection works with POJO (because of the two types of injection) – so it’s not intrusive. Benefits continued…
Different ways to use Spring Can be a full fledged application Can be used modularly Major Pieces Core Context DAO & ORM AOP Web & Web MVC Use as much of Spring as needed
Provides the Dependency Injection functionality which allows configuration and management of your bean container. This is the BeanFactory No more need for programmatic singletons Decouples configuration and the specification of dependencies in code Core
Provides access to beans in a framework-style manner ApplicationContext JNDI / EJB Support Remoting Etc. Context
DAO Programmatic and Declarative transaction management of any object (POJO) JDBC abstraction: vendor error codes, etc. ORM Provides integration for popular O/R mapping frameworks like OJB and Hibernate. Lots of hooks to take advantage of, for example, the declarative transaction management. DAO and ORM
Aspect Oriented Programming AOP Alliance Compliant implementation of AOP. Provides method interceptors and pointcuts to cleanly separate code. AOP
Web Web oriented application contexts. Initialization via servlet listeners. Multipart functionality. Web MVC Spring’s version of Struts. Web and Web MVC
Typical UIS Spring Usage Scenario Spring middle-tier using a third-party web framework Note: We will use OJB rather than Hibernate.
An ApplicationContext is a BeanFactory, and will be used most, if not all, of the time in UIS applications. ApplicationContext adds capabilities to the BeanFactory, most notibly J2EE specific functionality. You will need default empty constructors for setter injection. BeanFactory & ApplicationContext
Programmatic Instantiation of an Application Context BeanFactoryLocator bfLocator = SingletonBeanFactoryLocator.getInstance("Spring.xml"); BeanFactoryReference bfReference = bfLocator.useBeanFactory("appContext"); BeanFactory factory = bfReference.getFactory(); ApplicationContext appContext = (ApplicationContext) factory; Spring.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="appContext" class="org.springframework.context.support.ClassPathXmlApplicationContext"> <constructor-arg> <list> <value>SpringDataSource.xml</value> <value>SpringBeans.xml</value> </list> </constructor-arg> </bean> </beans> Example
Partial Contents of SpringBeans.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <!-- User Service --> <bean id="trnUserService" class="edu.iu.uis.train.service.UserServiceImpl"> <property name="userDAO" ref="trnUserDAO" /> </bean> <!-- User DAO --> <bean id="trnUserDAO" class="edu.iu.uis.train.dao.UserDAOOjbImpl"> <property name="jcdAlias" value="MYDB"/> </bean> </beans> Example continued…
A class name An id or name Singleton or prototype Bean properties Autowiring Lifecycle methods Defining a <bean/>
class=“package.classname” Most likely, the name of the actual implementing class of the object. The preceding example Can also be the Factory that creates the object (this is a more rare case). A few examples follow Class Name
<bean id="exampleBean" class="examples.ExampleBean2" factory-method="createInstance"/> or… <bean id="myFactoryBean" class="...“/> <bean id="exampleBean" factory-bean="myFactoryBean" factory-method="createInstance"/> Factory examples
id=“beanName” or name=“beanName” The id attribute is limited to the characters in a valid XML id If you need special characters, or want multiple ids, or aliases, you can use the name attribute (comma or semicolon separates multiple). Id and Name
singleton=“[true|false]” Singleton will use one shared instance of the bean for all requests (Default). Prototype will result in the creation of a new bean for each request. This is not used very often. Spring does not handle the lifecycle of prototype bean as it does singletons. Singleton or Prototype?
Setter-injection Recommended approach as constructor arguments can become unwieldy Decouples code and enforces true JavaBeans Constructor-injection Many ways to configure. Ensures beans are created in a valid state. Ensures beans have values if needed at object creation. Bean Properties
<bean id="exampleBean" class="examples.ExampleBean"> <property name="beanOne"> <ref bean="anotherExampleBean"/> </property> <property name="beanTwo"> <ref bean="yetAnotherBean"/> </property> <property name="integerProperty"> <value>1</value> </property> </bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/> Setter Properties Example
public class ExampleBean { private AnotherBean beanOne; private YetAnotherBean beanTwo; private int i; public void setBeanOne(AnotherBean beanOne) { this.beanOne = beanOne; } public void setBeanTwo(YetAnotherBean beanTwo) { this.beanTwo = beanTwo; } public void setIntegerProperty(int i) { this.i = i; } } Example continued…
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg> <ref bean="anotherExampleBean"/> </constructor-arg> <constructor-arg> <ref bean="yetAnotherBean"/> </constructor-arg> <constructor-arg type="int"> <value>1</value> </constructor-arg> </bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/> Constructor Example
public class ExampleBean { private AnotherBean beanOne; private YetAnotherBean beanTwo; private int i; public ExampleBean(AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) { this.beanOne = anotherBean; this.beanTwo = yetAnotherBean; this.i = i; } } Example continued…
<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance"> <constructor-arg> <ref bean="anotherExampleBean"/> </constructor-arg> <constructor-arg> <ref bean="yetAnotherBean"/> </constructor-arg> <constructor-arg> <value>1</value> </constructor-arg> </bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/> Factory/Constructor Example
public class ExampleBean { // a private constructor private ExampleBean(...) { ... } // a static factory method // the arguments to this method can be considered // the dependencies of the bean that is returned, // regardless of how those arguments are actually used. public static ExampleBean createInstance( AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) { ExampleBean example = new ExampleBean(...); // some other operations return example; } } Example continued…
Happens by argument type. If no ambiguity, Spring can resolve for you via type matching (as in previous example). Otherwise, you can use argument indexing. Constructor Argument Resolution
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg index="0"> <value>7500000</value> </constructor-arg> <constructor-arg index="1"> <value>42</value> </constructor-arg> </bean> Argument Indexing Example
<value>…</value> Used to set a textual value Can be used for int, boolean, and String <null/> Lists, sets, maps and props Allows for their equivalent data types to be used. More on property “values”
<bean id="moreComplexObject" class="example.ComplexObject"> <!-- results in a setPeople(java.util.Properties) call --> <property name="people"> <props> <prop key="HarryPotter">The magic property</prop> <prop key="JerrySeinfeld">The funny property</prop> </props> </property> <!-- results in a setSomeList(java.util.List) call --> <property name="someList"> <list> <value>a list element followed by a reference</value> <ref bean="myDataSource"/> </list> </property> <!-- results in a setSomeMap(java.util.Map) call --> <property name="someMap"> <map> <entry> <key><value>yup an entry</value></key> <value>just some string</value> </entry> </map> </property> <!-- results in a setSomeSet(java.util.Set) call --> <property name="someSet"> <set> <value>just some string</value> <ref bean="myDataSource"/> </set> </property> </bean> A few more examples…
<idref bean=“…”/> Same a id Benefit is that Spring can validate it. <idref local=“…”/> When bean is in the same XML config file. Similar to above, but the XML parser can validate at document parse time. <idref> Attribute
Allows a property to use another bean reference as its value <ref bean="someBean"/> Can also take advantage of local Shortcuts <property name="myProperty" value="hello"/> <constructor-arg value="hello"/> <entry key="myKey" value="hello"/> <ref> Attribute
Create a movie DAO bean in the SpringBeans.xml file and inject it into the movie service. Exercise: Bean wiring
Spring Magic! Spring will try to figure out what dependencies should be injected into your beans. Can not have ambiguities. Not recommended. Autowiring
< bean … init-method=“…”/> Specifies a method to call after a bean is created. <bean … destroy-method=“…”/> Specifies a method to call after a bean is about to be destroyed (a callback). Lifecycle Attributes
More framework oriented Recommended over a plain Bean Factory Provides: Access to I18N style messages Access to resources Event propagation Loading of multiple contexts The Application Context
Add a listener to web.xml to start Spring <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> The listener can be configured with a context parameter <context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/classes/SpringBeans.xml /WEB-INF/classes/SpringDataSource.xml </param-value> </context-param> Creating an Application Context
In your struts-config.xml <plug-in className="org.springframework.web.struts.ContextLoaderPlugIn"/> You also need to define your Action injection in /WEB-INF/action-servlet.xml <beans> <bean name="/BanUser" class="edu.iu.uis.train.web.BanUserAction"> <property name="userService" ref="trnUserService" /> </bean> <bean name="/Admin" class="edu.iu.uis.train.web.AdminAction"> <property name="userService" ref="trnUserService" /> </bean> </beans> Injecting your Struts Actions
- Finally, delegate Struts actions to Spring proxies <action path=”/Admin" type="org.springframework.web.struts.DelegatingActionProxy" name=”AdminForm" parameter="methodToCall"> <forward name="success" path="/WEB-INF/jsp/admin/Admin.jsp" /> </action> Injecting Actions Continued…
Modify struts-config.xml to use Spring Update action-servlet.xml with proper classes and injected services. Exercise: Injecting Struts
Another approach to obtaining spring services in Actions or other services Allows “singleton-like” access to services by a constant name Calls the BeanFactory to retrieve the bean behind the scenes Couples code a bit more than injection, but is sometimes easier to use Need to keep the constants up to date in the locator class file Need to recompile code when additions are needed A Spring Service Locator
public class SpringServiceLocator { private static ApplicationContext appContext; private static boolean initializationBegan = false; public static final String USER_SRV = "trnUserService"; public static final String PROJECT_SRV = "trnProjectService"; public static Object getService(String serviceName) { if (appContext == null && !initializationBegan) { initializationBegan = true; initializeAppContexts("Spring.xml"); } else if (appContext == null && initializationBegan) { throw new RuntimeException("Spring not initialized properly."); } return appContext.getBean(serviceName); } protected static void initializeAppContexts(String rootContextFile) { BeanFactoryLocator bfLocator = SingletonBeanFactoryLocator.getInstance(rootContextFile); BeanFactoryReference bfReference = bfLocator.useBeanFactory("appContext"); BeanFactory factory = bfReference.getFactory(); appContext = (ApplicationContext) factory; } Example
public static UserService getUserService() { return (UserService) getService(USER_SRV); } public static ProjectService getProjectService() { return (ProjectService) getService(PROJECT_SRV); } public static void setAppContext(ApplicationContext newAppContext) { appContext = newAppContext; } public static ApplicationContext getAppContext() { return appContext; } public static void close() { if (appContext instanceof ConfigurableApplicationContext) { ((ConfigurableApplicationContext) appContext).close(); } } } Example continued…
User user = SpringServiceLocator.getUserService(). findUserByUsername(role.getUser().getUsername()); As opposed to… User user = getUserService().findUserByUsername( role.getUser().getUsername()); Using the locator
Aspect Oriented Programming compliments OOP. Breaks programs down into aspects and concerns, often times called cross-cutting concerns. In Spring, AOP is used for: Declarative enterprise services Implementing custom aspects You choose how much AOP you want to use Spring AOP
AOP terminology is confusing and unintuitive. The Spring Framework did not want to change this terminology, which could have made it more confusing, but rather stuck with the generic AOP terms. AOP Concepts