330 likes | 479 Views
Intermediate Spring. Matt Wheeler. Notes. This is a training NOT a presentation Please ask questions Prerequisites Introduction to Java Stack Basic Java and XML skills Introduction to Spring Introduction to Spring Part 2 Installed LDSTech IDE (or other equivalent). Review.
E N D
Intermediate Spring Matt Wheeler
Notes • This is a training NOT a presentation • Please ask questions • Prerequisites • Introduction to Java Stack • Basic Java and XML skills • Introduction to Spring • Introduction to Spring Part 2 • Installed LDSTech IDE (or other equivalent)
Review • Bean lifecycle • XML Schema-based Configuration (namespace handlers) • Lifecycle hooks • Bean Initialization (JSR 250, @PostConstruct, …) • Bean post processors • Component scanning • Spring Component Annotations • DI Annotations (JSR 330, @Inject, @Named)
Overview • Advanced Injection • Providers • Spring EL • Additional Injection Annotations • Application Context web integration • Testing framework
Providers • Providers allow us to defer instantiation of a resource until it is needed • Providers facilitate (from the JavaDoc): • Retrieving multiple instances • Lazy or optimal retrieval of an instance • Breaking circular dependencies • Abstracting scope so you can look up an instance in a smaller scope from an instance in a containing scope
Previous Training Lab • We used the @Named annotation to select the prototypeRabbit to inject into the farm as the prize rabbit • The result was something like the following • Does anyone see any problem with this? @Component public class Farm { @Inject @Named("prototypeRabbit") private Rabbit prizeRabbit; … }
Provider Demo DEMO
Spring EL (SpEL) • Allows access to Spring beans and properties • Supports querying and manipulating object graph at runtime • Similar syntax to Unified EL (but more extensive) • Method invocation, string templating • Namespace handlers use it to inject references into attributes • For more specifics, please see the Spring docs: • http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/expressions.html
Spring EL Examples • We recommend only using this when necessary • For example • When extracting a property from a map • Or injecting a reference into a namespace handler <property name="someProperty" value="#{systemProperties['someValue']}" /> <jee:jndi-lookup id="databasePassword" jndi-name="dbPassword" /> <jee:jndi-lookup id="databaseUsername" jndi-name="dbUsername"/> <data-source driver-class="org.h2.Driver" url="jdbc:h2:mem:stack-starter;MODE=Oracle" user="#{databaseUsername}" password="#{databasePassword}" override="true" db-env="EMBEDDED"/>
Additional Injection Annotations • Many additional injection annotations • Please refer to the Spring documentation here: • http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-annotation-config
Lab 1: Providers https://tech.lds.org/wiki/Intermediate_Spring#Lab_1_Advanced_Injection
Web Context Listener • Loading application contexts in a web environment
Traditionally • Previously we have loaded application contexts with something like: • In a web environment however • You will want the context to automatically be loaded on startup • And be shared across the entire application ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); SomeBeansomeBean = context.getBean(SomeBean.class);
Servlet Listeners • The Java Servlet spec provides a listener (startup hook) • Listeners are triggered to run on startup • Spring utilizes this functionality and has created a listener that will load the application context when the application starts up
Context Loader Listener • Here is the web.xml configuration: • Utilizes the following context parameter: <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:META-INF/spring/applicationContext.xml,classpath:*beans.xml</param-value> <param-value>classpath:anotherContext.xml</param-value> </context-param>
Application Contexts and Servlets • Servlets not instantiated by Spring • Instantiated by the servlet container • Spring unable to inject dependencies • However Spring provides a way to access the application context
Application Context and Servlet • For the given servlet configuration (web.xml) • Application Context accessed as follows: <servlet> <servlet-name>servlet</servlet-name> <servlet-class>org.lds.training.TrainingServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>servlet</servlet-name> <url-pattern>/servlet</url-pattern> </servlet-mapping> public class TrainingServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //Context loader listener stores context in the servlet context - which is why it is required ApplicationContextapplicationContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); SomeBeansomeBean = (SomeBean) applicationContext.getBean(SomeBean.class); someBean.printSomething(); } }
A Better Way • Spring provides a servlet that delegates to a bean that is Spring managed • Called an HttpRequestHandler • Allows annotations and injection • Create a Spring bean that matches the name of the servlet name • This provides the mapping between the two
Utilizing a Spring Request Handler • The configuration: <!– web.xml --> <servlet> <servlet-name>trainingHandler</servlet-name> <servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>trainingHandler</servlet-name> <url-pattern>/trainingHandler</url-pattern> </servlet-mapping> @Component("trainingHandler") public class TrainingRequestHandler implements HttpRequestHandler { @Inject private SomeBeansomeBean; public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { someBean.printSomething(); } }
Spring MVC and Application Contexts • Spring MVC provides and even better way to integrate with the web container • Look forward to further discussion of this in a future training
Lab 2: Web Context Listener https://tech.lds.org/wiki/Intermediate_Spring#Lab_2_Web_Context_Listener
Spring Testing • Spring promotes testing in two prominent ways • Making the code more testable • Proving a testing framework
Testable Code • Dependency injection decouples code from the container • POJOs are normally easier to test • Dependencies are clearly defined • Allows you to swap implementations
Testing Help • Additionally we have an application context that can manage dependencies • Reloading the context for each test could be time consuming and unnecessary • Spring provides support for loading the context once per JVM (unless you explicitly tell it to reload)
Testing Framework • Many good testing frameworks available • JUnit • TestNG • Spring provides a context loader for tests • Currently Spring supports JUnit 3, JUnit 4, and TestNG • We will be using TestNG
Spring Test Framework • @ContextConfiguration • Allows you to specify application contexts to load • Important: Loaded in the order specified • Later bean definitions of the same name will override earlier ones • Allows us to utilize everything in the project context • But selectively override bean definitions in the test contexts • AbstractTestNGSpringContextTests @ContextConfiguration(locations={"classpath*:*beans.xml","classpath*:*beans-test.xml"}) public class FarmIT extends AbstractTestNGSpringContextTests { //… }
For example • The application bean definition file • The test bean definition file <jee:jndi-lookup id="securityConnectionUrl"jndi-name="ldap/URL"/> <jee:jndi-lookup id="securityConnectionDN"jndi-name="ldap/DN"/> <jee:jndi-lookup id="securityConnectionPassword"jndi-name="ldap/Password"/> <bean id="securityConnectionUrl" class="java.lang.String"> <constructor-arg value="ldaps://gdirstage/" /> </bean> <bean id="securityConnectionDN" class="java.lang.String"> <constructor-arg value="cn=LDAP-STACK,ou=apps,o=lds" /> </bean> <bean id="securityConnectionPassword" class="java.lang.String"> <constructor-arg value="password1" /> </bean>
Stack Utils namespace handler (stack-utils:null) • Additionally, sometimes you would like the value to be null in a test • This can be accomplished with a Stack provided Xml-schema based configuration • For more information: • http://code.lds.org/maven-sites/stack/module.html?module=spring-utils • http://code.lds.org/maven-sites/stack/module.html?module=spring-utils/xsddoc/ <stack-util:null id="securityConnectionUrl" /> <stack-util:null id="securityConnectionDN" /> <stack-util:null id="securityConnectionPassword" />
Another example • The application bean definition file • The test bean definition file <stack-db:data-source jndi-name="jdbc/default/DataSource"/> <stack-db:data-source driver-class="org.h2.Driver" url="jdbc:h2:mem:stack-starter;MODE=Oracle" user="" password="" override="true" db-env="EMBEDDED"/>
Testing with a Database • Suppose you wanted to use a database to test • Normally • You would add data, run the test, and then clean it up • Spring provides transactional support • Data from the test automatically rolls back after the test • Must extend AbstractTransactionalTestNGSpringContextTests • Must specify a transaction manager in your bean definition • Additional documentation: • http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/testing.html#testcontext-tx
Spring Provides Useful Testing Stubs • Lastly Spring provides many useful stubs (mock objects) • JNDI • Servlet API • These can save a lot of code • Often more usable that dynamic mock objects • For further information: • http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/testing.html#mock-objects
Lab 3: Spring Testing Integration https://tech.lds.org/wiki/Intermediate_Spring#Lab_3_Spring_Testing_Integration
Credit where credit is due • http://springsource.org • Spring Recipies 2nd Edition (Gary Mak, Josh Long and Daniel Rubio)