Spring是一个非怼U的轻量框架Q通过Spring的IoC容器Q我们的x点便攑ֈ了需要实现的业务逻辑 上。对AOP的支持则能让我们动态增Z务方法。编写普通的业务逻辑Bean是非常容易而且易于试的,因ؓ它能qJ2EE容器Q如 ServletQjsp环境Q单独进行单元测试。最后的一步便是在Spring框架中将q些业务Bean以XML配置文g的方式组lv来,它们按照我? 预定的目标正常工作了Q非常容易!
本文给Z个基本的Spring入门CZQƈ演示如何使用Spring的AOP复杂的业务逻辑分离到每个方面中?/p>
1Q开发环境配|?Q编写Bean接口及其实现3Q在Spring中配|Beanq获得Bean的实?Q编写Advisor以增? ServiceBean5Qȝ
1Q开发环境配|?首先Q需要正配|Java环境。推荐安装JDK1.4.2Qƈ正确配置环境变量Q?/p>
JAVA_HOME=<JDK安装目录>CLASSPATH=.Path=%JAVA_HOME%"bin;……
我们用免费的Eclipse 3.1作ؓIDE。新Z个Java ProjectQ将Spring的发布包spring.jar以及commons-logging-1.0.4.jar复制到Project目录下,q在 Project > Properties中配|好Java Build PathQ?/p>
我们实现一个管理用L业务Bean。首先定义一个ServiceBean接口Q声明一些业务方法:
/** * Copyright_2006, Liao Xuefeng * Created on 2006-3-9 * For more information, please visit: http://www.crackj2ee.com */package com.crackj2ee.example.spring;
/** * Interface of service facade. * * @author Xuefeng */public interface ServiceBean { void addUser(String username, String passWord); void deleteUser(String username); boolean findUser(String username); String getPassword(String username);}
然后在MyServiceBean中实现接口:
/** * Copyright_2006, Liao Xuefeng * Created on 2006-3-9 * * For more information, please visit: http://www.crackj2ee.com */package com.crackj2ee.example.spring;
import java.util.*;
public class MyServiceBean implements ServiceBean {
private String dir; private Map map = new HashMap();
public void setUserDir(String dir) { this.dir = dir; System.out.println("Set user dir to: " + dir); }
public void addUser(String username, String password) { if(!map.containsKey(username)) map.put(username, password); else throw new RuntimeException("User already exist."); }
public void deleteUser(String username) { if(map.remove(username)==null) throw new RuntimeException("User not exist."); }
public boolean findUser(String username) { return map.containsKey(username); }
public String getPassword(String username) { return (String)map.get(username); }}
Z化逻辑Q我们用一个Map保存用户名和口o?/p>
现在Q我们已l有了一个业务Bean。要试它非常容易,因ؓ到目前ؓ止,我们q没有涉及到Spring容器Q也没有涉及CQ何Web容器Q假定这 是一个Web应用E序关于用户理的业务BeanQ。完全可以直接进行Unit试Q或者,单地写个mainҎ试Q?/p>
/** * Copyright_2006, Liao Xuefeng * Created on 2006-3-9 * For more information, please visit: http://www.crackj2ee.com */package com.crackj2ee.example.spring;
public class Main {
public static void main(String[] args) throws Exception { ServiceBean service = new MyServiceBean(); service.addUser("bill", "hello"); service.addUser("tom", "goodbye"); service.addUser("tracy", "morning"); System.out.println("tom's password is: " + service.getPassword("tom")); if(service.findUser("tom")) { service.deleteUser("tom"); } }}
执行l果Q?img src="http://www.knowsky.com/UploadFiles/20071223/2007122311173837577802.jpg" alt="Spring入门Q图二)" border="0" height="184" width="566" />
3Q在Spring中配|Beanq获得Bean的实?我们已经在一个mainҎ中实C业务Q不q,对象的生命周期交给容器理是更好的办法Q我们就不必为初始化对象和销毁对象进行硬~码Q从而获 得更大的灉|性和可测试性?/p>
惌把ServiceBean交给Spring来管理,我们需要一个XML配置文g。新Z个beans.xmlQ放到src目录下,保? classpath中能扑ֈ此配|文Ӟ输入以下内容Q?/p>
<?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="service" class="com.crackj2ee.example.spring.MyServiceBean" /></beans>
以上XML声明了一个id为service的BeanQ默认地QSpring为每个声明的Bean仅创Z个实例,q过id来引用这? Bean。下面,我们修改mainҎQ让Spring来管理业务BeanQ?/p>
/** * Copyright_2006, Liao Xuefeng * Created on 2006-3-9 * For more information, please visit: http://www.crackj2ee.com */package com.crackj2ee.example.spring;
import org.springframework.beans.factory.xml.XmlBeanFactory;import org.springframework.core.io.ClassPathResource;
public class Main {
public static void main(String[] args) throws Exception { // init factory: XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml")); // use service bean: ServiceBean service = (ServiceBean)factory.getBean("service"); service.addUser("bill", "hello"); service.addUser("tom", "goodbye"); service.addUser("tracy", "morning"); System.out.println("tom's password is """ + service.getPassword("tom") + """"); if(service.findUser("tom")) { service.deleteUser("tom"); } // close factory: factory.destroySingletons(); }}
执行l果Q?nbsp;
׃我们要通过mainҎ启动Spring环境Q因此,首先需要初始化一个BeanFactory。红色部分是初始化Spring? BeanFactory的典型代码,只需要保证beans.xml文g位于classpath中?/p>
然后Q在BeanFactory中通过id查找Q即可获得相应的Bean的实例,q将光当转型为合适的接口?/p>
接着Q实Cpd业务操作Q在应用E序l束前,让Spring销毁所有的Bean实例?/p>
Ҏ上一个版本的MainQ可以看出,最大的变化是不需要自q理Bean的生命周期。另一个好处是在不更改实现cȝ前提下,动态地为应用程序增? 功能?/p> 4Q编写Advisor以增强ServiceBean
所谓AOPx分散在各个Ҏ处的公共代码提取C处,q过cM拦截器的机制实现代码的动态织入。可以简单地惌成,在某个方法的调用前、返? 前、调用后和抛出异常时Q动态插入自q代码。在弄清楚Pointcut、Advice之类的术语前Q不如编写一个最单的AOP应用来体验一下?/p>
考虑一下通常的Web应用E序都会有日志记录,我们来编写一个LogAdvisorQ对每个业务Ҏ调用前都作一个记录:
/** * Copyright_2006, Liao Xuefeng * Created on 2006-3-9 * For more information, please visit: http://www.crackj2ee.com */package com.crackj2ee.example.spring;
import java.lang.reflect.Method;import org.springframework.aop.MethodBeforeAdvice;
public class LogAdvisor implements MethodBeforeAdvice { public void before(Method m, Object[] args, Object target) throws Throwable { System.out.println("[Log] " + target.getClass().getName() + "." + m.getName() + "()"); }}
然后Q修改beans.xmlQ?/p>
<?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="serviceTarget" class="com.crackj2ee.example.spring.MyServiceBean" />
<bean id="logAdvisor" class="com.crackj2ee.example.spring.LogAdvisor" />
<bean id="service" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"><value>com.crackj2ee.example.spring.ServiceBean</value></property> <property name="target"><ref local="serviceTarget"/></property> <property name="interceptorNames"> <list> <value>logAdvisor</value> </list> </property> </bean></beans>
注意观察修改后的配置文gQ我们用了一个ProxyFactoryBean作ؓservice来与客户端打交道Q而真正的业务Bean?
MyServiceBean被声明ؓserviceTargetq作为参数对象传递给ProxyFactoryBeanQproxyInterfaces
指定了返回的接口cd。对于客L而言Q将感觉不出M变化Q但却动态加入了LogAdvisorQ关pd下:
q行l果如下Q可以很Ҏ看到调用了哪些方法:
要截h定的某些Ҏ也是可以的。下面的例子修改getPassword()Ҏ的返回|
/** * Copyright_2006, Liao Xuefeng * Created on 2006-3-9 * For more information, please visit: http://www.crackj2ee.com */package com.crackj2ee.example.spring;
import org.aopalliance.intercept.MethodInterceptor;import org.aopalliance.intercept.MethodInvocation;
public class PasswordAdvisor implements MethodInterceptor { public Object invoke(MethodInvocation invocation) throws Throwable { Object ret = invocation.proceed(); if(ret==null) return null; String password = (String)ret; StringBuffer encrypt = new StringBuffer(password.length()); for(int i=0; i<password.length(); i++) encrypt.append('*'); return encrypt.toString(); }}
q个PasswordAdvisor截获ServiceBean的getPassword()Ҏ的返回|q将其改?***"。l? 修改beans.xmlQ?/p>
<?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="serviceTarget" class="com.crackj2ee.example.spring.MyServiceBean" />
<bean id="logAdvisor" class="com.crackj2ee.example.spring.LogAdvisor" />
<bean id="passwordAdvisorTarget" class="com.crackj2ee.example.spring.PasswordAdvisor" />
<bean id="passwordAdvisor" class="org.springframework.aop.support.RegeXPMethodPointcutAdvisor"> <property name="advice"> <ref local="passwordAdvisorTarget"/> </property> <property name="patterns"> <list> <value>.*getPassword</value> </list> </property> </bean>
<bean id="service" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"><value>com.crackj2ee.example.spring.ServiceBean</value></property> <property name="target"><ref local="serviceTarget"/></property> <property name="interceptorNames"> <list> <value>logAdvisor</value> <value>passwordAdvisor</value> </list> </property> </bean></beans>
利用Spring提供的一个RegexMethodPointcutAdvisor可以非常Ҏ地指定要截获的方法。运行结果如下,可以看到q回l果?
?******"Q?nbsp;
q需要l增强ServiceBeanQ我们编写一个ExceptionAdvisorQ在业务Ҏ抛出异常时能做一些处理:
/** * Copyright_2006, Liao Xuefeng * Created on 2006-3-9 * For more information, please visit: http://www.crackj2ee.com */package com.crackj2ee.example.spring;
import org.springframework.aop.ThrowsAdvice;
public class ExceptionAdvisor implements ThrowsAdvice { public void afterThrowing(RuntimeException re) throws Throwable { System.out.println("[Exception] " + re.getMessage()); }}
此Adviced到beans.xml中,然后在业务Bean中删除一个不存在的用P故意抛出异常Q?/p>
service.deleteUser("not-exist");
再次q行Q注意到ExceptionAdvisor记录下了异常Q?nbsp;
利用Spring非常强大的IoC容器和AOP功能Q我们能实现非常灉|的应用,让Spring容器理业务对象的生命周期,利用AOP增强功能Q? 却不影响业务接口Q从而避免更改客L代码?/p>
Z实现q一目标Q必dl牢讎ͼ面向接口~程。而Spring默认的AOP代理也是通过Java的代理接口实现的。虽然Spring也可以用 CGLIB实现Ҏ通类的代理,但是Q业务对象只要没有接口,׃变得难以扩展、维护和试?/p>
Ƣ迎来信与作者交:asklxf@163.com
可以从此处下载完整的Eclipse工程Q?/p>
springbasic.rar
Q出处:http://www.jzwiki.com/article_1215945431010.shtml#Q?/p>