本文通过一个实例讲解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。