本文通过一个实例讲解spring开发过程中的一些特性。
1.通过一个refactoring的过程来体会使用spring的组件化和依赖注入的特性。
2.一个简单的web mvc demo。
1.问题是从hello world开始的。
一个简单的hello world程序:
package getstart; /** * */ /** * 这是一个最简单的设计,但是在这个程序中这个类的职责 * 比较复杂,在下面的重构的重构中,将这个类分解为:消息 * 的提供者和消息的显示者。 * * @author jefferyxu * */ public class HelloWorld { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub System.out.println("Hello World!"); } }
将上面的类重构成下面的两个类:
IMessageDisplayer:
package getstart; public interface IMessageDisplayer { public void display() throws Exception ; public void setSupplier(IMessageSupplier supplier); public IMessageSupplier getSupplier(); }
IMessageSupplier:
package getstart; public interface IMessageSupplier { public String getMessage() ; }
MessageDisplayer:
package getstart; /** * */ /** * @author jefferyxu * */ public class MessageDisplayer implements IMessageDisplayer { private IMessageSupplier supplier; /* (non-Javadoc) * @see IMessageDisplayer#display() */ @Override public void display() throws Exception { if(supplier == null) { throw new Exception("supplier must be set."); } System.out.println(supplier.getMessage()); } /** * @return the supplier */ public IMessageSupplier getSupplier() { return supplier; } /** * @param supplier the supplier to set */ public void setSupplier(IMessageSupplier supplier) { this.supplier = supplier; } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub } }
MessageSupplier:
package getstart; public class MessageSupplier implements IMessageSupplier { @Override public String getMessage() { // TODO Auto-generated method stub return "Hello World !"; } }
客户程序如下:
package getstart; /** * */ /** * * 现在将这个程序进行重构,将这个程序分解成一个消息的提供者和 * 消息的显示 者,并且将这个依赖关系进行解耦。现在的问题是 主程序 * 需要依赖于具体类的实现(主要问题是new MessageSupplier()),下面 * 对这个依赖解耦,具体的做法是将具体类的产生通过一个工厂的方法来 * 代替new的实现,同时工厂类在实现的时候通过java中的反射机制来实现 * 根据配置文件来加载对应类 . * * @author jefferyxu * */ public class HelloWorldBetter { /** * @param args * @throws Exception */ public static void main(String[] args) throws Exception { // TODO Auto-generated method stub IMessageSupplier supplier = new MessageSupplier(); IMessageDisplayer displayer = new MessageDisplayer(); displayer.setSupplier(supplier); displayer.display(); } }
但是上面中依旧存在的问题是主程序需要依赖具体类的实现(主要问题是new MessageSupplier())。解耦的方法是使用一个factory方法来实现对象的产生。实现代码如下:
package getstart; import java.io.FileInputStream; import java.util.Properties; import org.aspectj.apache.bcel.generic.ReturnaddressType; import org.aspectj.weaver.patterns.ThisOrTargetAnnotationPointcut; /** * @author jefferyxu * */ public class MessageSupportFactory { /** * 单件模式的全局变量 */ private static MessageSupportFactory factory = null; /** * private构造函数 */ private MessageSupportFactory() { properties = new Properties(); try { properties.load(new FileInputStream("src/msgbean.properties")); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } private Properties properties = null; /** * 单件模式产生factory对象 * @return */ public static MessageSupportFactory getInstance() { if(factory == null) { return new MessageSupportFactory(); } else { return factory; } } /** * 通过配置文件产生一个displayer的实例. * @return */ public MessageDisplayer makeDisplayer() { String displayerClass = properties.getProperty("displayer.class"); try { return (MessageDisplayer) Class.forName(displayerClass).newInstance(); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } return null; } public MessageSupplier makeSupplier() { String displayerClass = properties.getProperty("supplier.class"); try { return (MessageSupplier)Class.forName(displayerClass).newInstance(); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } return null; } }
msgbean.properties :
displayer.class=getstart.MessageDisplayer supplier.class=getstart.MessageSupplier
客户程序如下:
package getstart; /** * @author jefferyxu * */ public class HelloWorldWidthFactory { public static void main(String[] args) throws Exception { MessageSupplier supplier = MessageSupportFactory.getInstance().makeSupplier(); MessageDisplayer displayer = MessageSupportFactory.getInstance().makeDisplayer(); displayer.setSupplier(supplier); displayer.display(); } }
通过上面的几步重构的话,客户端的程序基本上和实现类实现了解耦。但是上面种存在的问题是实现起来较为的复杂,如果改用spring框架来实现,大大简化上面的步骤.
applicationContext.xml :
<?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:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="displayer" name="displayer" class="getstart.MessageDisplayer"> </bean> <bean id="supplier" name="supplier" class="getstart.MessageSupplier"> </bean> </beans>
客户端程序:
package getstart; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Properties; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.PropertiesBeanDefinitionReader; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @author jefferyxu * */ public class HelloWorldWithSpringUsgae { /** * @param args */ public static void main(String[] args) { /** * 加载spring的运行环境 */ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); /** * 使用bean工厂,产生具体类 */ MessageSupplier supplier = (MessageSupplier)context.getBean("supplier"); MessageDisplayer displayer = (MessageDisplayer)context.getBean("displayer"); /** * 这里直接在代码中直接将两者的依赖关系写入,所以 * 整段代码的可重用性不好。 */ displayer.setSupplier(supplier); try { displayer.display(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } private static BeanFactory getFactory() { DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); PropertiesBeanDefinitionReader reader = new PropertiesBeanDefinitionReader(factory); Properties properties = new Properties(); try { properties.load(new FileInputStream("src/msgbean.properties")); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } reader.registerBeanDefinitions(properties); return factory; } }
继续重构 上面的程序,上面的程序现在存在的问题主要是下面的这句:
displayer.setSupplier(supplier);
这句的主要问题是在代码中直接将两个类的依赖关系写入。改进的办法是使用spring的“依赖注入”机制。实现如下:
applicationContext.xml:
<?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:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="displayer" name="displayer" class="getstart.MessageDisplayer"> <property name="supplier"><ref local="supplier"/></property> </bean> <bean id="supplier" name="supplier" class="getstart.MessageSupplier"> </bean> </beans>
客户端程序:
/** * */ package getstart; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Properties; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.PropertiesBeanDefinitionReader; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @author jefferyxu * */ public class HelloWorldWithSpringDIUsgae { /** * @param args */ public static void main(String[] args) { /** * 加载spring的运行环境 */ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); /** * 使用bean工厂,产生具体类 */ MessageSupplier supplier = (MessageSupplier)context.getBean("supplier"); MessageDisplayer displayer = (MessageDisplayer)context.getBean("displayer"); /** * 这里直接在代码中直接将两者的依赖关系写入,所以 * 整段代码的可重用性不好。 */ // displayer.setSupplier(supplier); try { displayer.display(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } private static BeanFactory getFactory() { DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); PropertiesBeanDefinitionReader reader = new PropertiesBeanDefinitionReader(factory); Properties properties = new Properties(); try { properties.load(new FileInputStream("src/msgbean.properties")); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } reader.registerBeanDefinitions(properties); return factory; } }
2. 一个简单spring web mvc demo。
网站的目录结构:
web.xml :
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> <!-- tomcat将符合url pattern的请求分发到这个dispatchter 上,然后这个dispatchter会默认的寻找以这个servlet-name -servlet.xml命名的xml文件 --> <servlet> <servlet-name>helloworld</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>helloworld</servlet-name> <url-pattern>/helloworld/*</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <login-config> <auth-method>BASIC</auth-method> </login-config> </web-app>
HelloWorldController.java :
package getstart.web; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller; /** * @author jefferyxu * */ public class HelloWorldController implements Controller { public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { // TODO Auto-generated method stub return new ModelAndView("/test.jsp"); } }
helloworld-servlet.xml :
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 这个文件是在web.xml配置文件的helloworld/的子路径 中定义的环境变量.按照下面的定义的话,将/test的路径 请求,转发到getstart.web.HelloWorldController上。 --> <bean name="/test" class="getstart.web.HelloWorldController"> </bean> </beans>
test.jsp :
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'test.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> </head> <body> test.jsp from the HelloWorldController </body> </html>
可以使用下面的连接来访问:http://xuqiang-pc:8080/spring/helloworld/test。