第一篇:Java回顧之I/O
第二篇:Java回顧之網絡通信
第三篇:Java回顧之多線程
第四篇:Java回顧之多線程同步
第五篇:Java回顧之集合
第六篇:Java回顧之序列化
第七篇:Java回顧之反射
第八篇:Java回顧之一些基礎概念
第九篇:Java回顧之JDBC
第十篇:Java回顧之ORM框架
我計划分兩到三篇文章來描述Spring,這一篇主要講Spring一些基礎的內容。
概述
我印象4、5年前,我還做java開發的時候,Spring是一個非常火的框架,尤其是在Web開發領域,和Struts以及Hibernate構成了SSH三劍客。當時Web開發的另一個組合是LAMP,即Linux+Apache+MySQL+PHP。我在前端方面基本沒有實戰經驗,對js等技術也還是停留在概念和語法方面,所以揚長避短,我對Spring以及Hibernate特別感興趣。
當年Spring是作為EJB的“替代者”橫空出世的,其創始人Rod Johnson還寫了一本《J2EE development without EJB》來推行這個框架,這也是一本關於Spring很經典的書,不過最好是在接觸Spring一段時間后再去閱讀,效果會好一點。
Spring最主要的特點有兩個:IoC和AOP,這也是J2EE開發企業軟件時經常碰到的問題:1)對象太多如何管理;2)共同邏輯和業務邏輯糾纏在一起,錯綜復雜,如何解耦。
這篇文章主要關注3個方面:IoC、AOP和數據庫訪問。這里我們假設所有需要的jar都已經准備就緒。
IoC
IoC的全稱是Inversion of Control,中文稱為控制反轉, Martin Flower由根據它創造了一個新詞:Dependency Injection,中文稱為依賴注入。這兩個詞講的是一回事兒。
IoC的實質是如何管理對象,傳統意義上我們使用new方式來創建對象,但在企業應用開發的過程中,大量的對象創建都在程序中維護很容易造成資源浪費,並且不利於程序的擴展。
實現IoC通常有三種方式:
1)利用接口或者繼承,一般以接口較多。這種實現方式和我們平時提到的lazy load有異曲同工之妙。
2)構造函數注入。
3)屬性注入。
IoC是Spring框架的核心,接下來我們來探索一下Spring中IoC的風采。
IoC簡單示例
我們先來定義一個簡單的接口和實現:
1 public interface UserDao { 2 void save(); 3 } 4 5 public class UserDaoImpl implements UserDao 6 { 7 8 public void save() { 9 System.out.println("save() is called."); 10 } 11 12 }
然后是在classpath下創建一個beans.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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <bean id="userDaoImpl" class = "sample.spring.ioc.UserDaoImpl"/> </beans>
接下來是測試代碼:
1 private static void test1() 2 { 3 ApplicationContext ctx = new ClassPathXmlApplicationContext("sample/spring/ioc/beans.xml"); 4 UserDao userDao = (UserDao)ctx.getBean("userDaoImpl"); 5 userDao.save(); 6 }
輸出結果如下:
save() is called.
我們還可以通過工廠方式來創建對象。
通過靜態工廠創建Bean
添加一個類,如下:
1 public class UserDaoFactory { 2 3 public static UserDao getUserDao() 4 { 5 return new UserDaoImpl(); 6 } 7 }
在beans.xml中,添加如下內容:
1 <bean id="userDaoImpl2" class = "sample.spring.ioc.UserDaoFactory" factory-method = "getUserDao"/>
測試代碼和執行結果和上面類似,不再贅述。
通過實例工廠創建Bean
添加如下類:
1 public class UserDaoFactory2 2 { 3 public UserDao getUserDao() 4 { 5 return new UserDaoImpl(); 6 } 7 }
這個類和UserDaoFactory唯一的區別是這里的getUserDao是實例方法,而不是靜態方法。
在beans.xml中追加如下內容:
1 <bean id="factory" class="sample.spring.ioc.UserDaoFactory2"/> 2 <bean id="userDaoImpl3" factory-bean="factory" factory-method="getUserDao"/>
測試方法和結果同上。
對象的生命周期
我們可以通過設置bean節點的scope屬性來控制對象的聲明周期,它包含兩個可選值:
1)singleton,表明系統中對於同一個對象,只保留一個實例。
2)prototype,表明系統中每次獲取bean時,都新建一個對象。
我們修改beans.xml文件:
1 <bean id="userDaoImpl" class = "sample.spring.ioc.UserDaoImpl" scope="singleton"/> 2 <bean id="userDaoImpl2" class = "sample.spring.ioc.UserDaoImpl" scope="prototype"/>
這兩個bean指向同一個類型,但是scope的設置不同。
下面是測試方法:
1 private static void scopeTest() 2 { 3 ApplicationContext ctx = new ClassPathXmlApplicationContext("sample/spring/ioc/scope.xml"); 4 System.out.println("=====Singleton test====="); 5 UserDao userDao1A = (UserDao)ctx.getBean("userDaoImpl"); 6 UserDao userDao1B = (UserDao)ctx.getBean("userDaoImpl"); 7 System.out.println("userDao1A == userDao1B:" + (userDao1A==userDao1B)); 8 System.out.println("=====Prototype test====="); 9 UserDao userDao2A = (UserDao)ctx.getBean("userDaoImpl2"); 10 UserDao userDao2B = (UserDao)ctx.getBean("userDaoImpl2"); 11 System.out.println("userDao2A == userDao2B:" + (userDao2A==userDao2B)); 12 }
執行結果如下:
=====Singleton test===== userDao1A == userDao1B:true =====Prototype test===== userDao2A == userDao2B:false
如何設置對象屬性
上面的示例中,我們的對象中沒有包含屬性,對於業務對象來說,這一般是不現實。實際中的對象或多或少都會有一些屬性。
Spring支持兩種方式對屬性賦值:set方式和構造函數。
下面我們會分別描述兩種方式,但首先我們需要展示業務對象:

1 public class UserServiceBean 2 { 3 private int userID; 4 private String userName; 5 private UserDao userDao; 6 private List<String> hobbies; 7 private Map<String, Integer> scores; 8 9 public UserServiceBean(int userID, String userName, UserDao userDao, List hobbies,Map scores) 10 { 11 this.userID = userID; 12 this.userName = userName; 13 this.userDao = userDao; 14 this.hobbies = hobbies; 15 this.scores = scores; 16 } 17 18 public UserServiceBean(){} 19 20 public void setUserID(int userID) { 21 this.userID = userID; 22 } 23 public int getUserID() { 24 return userID; 25 } 26 public void setUserName(String userName) { 27 this.userName = userName; 28 } 29 public String getUserName() { 30 return userName; 31 } 32 public void setUserDao(UserDao userDao) { 33 this.userDao = userDao; 34 } 35 public UserDao getUserDao() { 36 return userDao; 37 } 38 public void setHobbies(List<String> hobbies) { 39 this.hobbies = hobbies; 40 } 41 public List<String> getHobbies() { 42 return hobbies; 43 } 44 public void setScores(Map<String, Integer> scores) { 45 this.scores = scores; 46 } 47 public Map<String, Integer> getScores() { 48 return scores; 49 } 50 }
這是一個典型的學生信息,包括學號、姓名、愛好和成績。
通過Set方式為對象屬性賦值
我們在beans.xml中追加如內容:
1 <bean id="userService" class="sample.spring.ioc.UserServiceBean"> 2 <property name="userID" value="1"/> 3 <property name="userName" value="張三"/> 4 <property name="userDao" ref="userDaoImpl"/> 5 <property name="hobbies"> 6 <list> 7 <value>羽毛球</value> 8 <value>看電影</value> 9 <value>彈吉他</value> 10 </list> 11 </property> 12 <property name="scores"> 13 <map> 14 <entry key="數據結構" value="90"/> 15 <entry key="編譯原理" value="85"/> 16 <entry key="離散數學" value="82"/> 17 </map> 18 </property> 19 </bean>
上面是典型的為屬性賦值的示例,其中屬性不僅包括簡單屬性(整數、字符串),也包含了復雜屬性(List、Map),還有其他的bean。
下面是測試代碼:
1 private static void propertyTest1() 2 { 3 ApplicationContext ctx = new ClassPathXmlApplicationContext("sample/spring/ioc/beans.xml"); 4 UserServiceBean userService = (UserServiceBean)ctx.getBean("userService"); 5 printUserService(userService); 6 } 7 8 private static void printUserService(UserServiceBean userService) 9 { 10 System.out.println("編號:" + userService.getUserID()); 11 System.out.println("姓名:" + userService.getUserName()); 12 System.out.println("愛好:"); 13 for(String hobby:userService.getHobbies()) 14 { 15 System.out.println(hobby); 16 } 17 System.out.println("學習成績:"); 18 for(Entry<String,Integer> entry:userService.getScores().entrySet()) 19 { 20 System.out.println(entry.getKey() + "\t" + entry.getValue()); 21 } 22 userService.getUserDao().save(); 23 }
輸出結果如下:
編號:1 姓名:張三 愛好: 羽毛球 看電影 彈吉他 學習成績: 數據結構 90 編譯原理 85 離散數學 82 save() is called.
通過構造函數為對象屬性賦值
我們也可以通過構造函數來為對象賦值,在上面定義UserServiceBean時,我們已經添加了一個構造函數。下面來看beans.xml中的配置:
1 <bean id="userService2" class="sample.spring.ioc.UserServiceBean"> 2 <constructor-arg index="0" value="1"/> 3 <constructor-arg index="1" value="張三"/> 4 <constructor-arg index="2" ref="userDaoImpl"/> 5 <constructor-arg index="3"> 6 <list> 7 <value>羽毛球</value> 8 <value>看電影</value> 9 <value>彈吉他</value> 10 </list> 11 </constructor-arg> 12 <constructor-arg index="4"> 13 <map> 14 <entry key="數據結構" value="90"/> 15 <entry key="編譯原理" value="85"/> 16 <entry key="離散數學" value="82"/> 17 </map> 18 </constructor-arg> 19 </bean>
測試代碼和輸出結果同上。
需要注意:我們定義的業務對象應該保留默認的構造函數。
使用Annotation來定位Bean
在Spring中,除了在xml配置文件中定義對象,我們還可以使用Annotation來定位,這位我們提供了很大的方便。
這里我們使用的Annotation主要包括:@Resource/@Autowried/@Qualifier。
來看下面的示例:

1 public class UserServiceBean2 2 { 3 private String userID; 4 private String userName; 5 @Resource(name="userDaoImpl") 6 private UserDao userDao1; 7 private UserDao userDao2; 8 9 @Autowired(required=false) 10 @Qualifier("userDaoImpl") 11 private UserDao userDao3; 12 13 @Autowired(required=false) 14 @Qualifier("userDaoImpl3") 15 private UserDao userDao4; 16 17 public void setUserID(String userID) { 18 this.userID = userID; 19 } 20 public String getUserID() { 21 return userID; 22 } 23 public void setUserName(String userName) { 24 this.userName = userName; 25 } 26 public String getUserName() { 27 return userName; 28 } 29 @Resource 30 public void setUserDao2(UserDao userDao2) { 31 this.userDao2 = userDao2; 32 } 33 public UserDao getUserDao2() { 34 return userDao2; 35 } 36 37 public void test() 38 { 39 userDao1.save(); 40 userDao2.save(); 41 System.out.println(userDao3.getClass().getName()); 42 userDao3.save(); 43 } 44 }
測試方法:
1 private static void annotationTest() 2 { 3 ApplicationContext ctx = new ClassPathXmlApplicationContext("sample/spring/ioc/annotation.xml"); 4 UserServiceBean2 userService = (UserServiceBean2)ctx.getBean("userService"); 5 6 userService.test(); 7 }
輸出結果如下:
save() is called.
save() is called.
sample.spring.ioc.UserDaoImpl
save() is called.
我們來對上面示例中出現的Annotation來進行說明。
1 @Resource(name="userDaoImpl") 2 private UserDao userDao1;
這是定義在字段上的Annotation,是指userDao1使用xml配置文件中定義的名為“userDaoImpl”的bean進行填充。
1 @Autowired(required=false) 2 @Qualifier("userDaoImpl") 3 private UserDao userDao3;
這是第二種類型的Annotation,它把Autowired和Qualifier組合在一起使用,Qualifier來設置bean的名稱,Autowired來設置bean找不到時的行為,required為true時會拋出異常,required為false時會返回null。
1 @Resource 2 public void setUserDao2(UserDao userDao2) { 3 this.userDao2 = userDao2; 4 }
這是作用在setter上的Annotation,@Resource 可以不寫明name參數,這時Spring會首先按照名字然后按照數據類型的方式去定位bean。
自動加載對象定義
對於大型系統來說,我們可能會創建大量的類,如果這些類的聲明都需要寫在xml文件里的話,會產生額外大量的工作。
Spring提供了一種簡單的機制讓我們的對象可以自動注冊。
我們可以在beans.xml中添加如下內容:
1 <context:component-scan base-package="sample.spring.ioc"/>
然后我們可以在sample.spring.ioc包下的對象,添加@Component/@Service/@Controller/@repository,這樣Spring會自動將帶有這些Annotation的類進行注冊。
下面是一個示例:

1 @Service("userService") 2 public class UserServiceBean3 3 { 4 private String userID; 5 private String userName; 6 @Resource(name="userDaoImpl") 7 private UserDao userDao1; 8 9 @Autowired(required=true) 10 @Qualifier("userDaoImpl") 11 private UserDao userDao3; 12 13 public void setUserID(String userID) { 14 this.userID = userID; 15 } 16 public String getUserID() { 17 return userID; 18 } 19 public void setUserName(String userName) { 20 this.userName = userName; 21 } 22 public String getUserName() { 23 return userName; 24 } 25 // @Resource 26 // public void setUserDao2(UserDao userDao2) { 27 // this.userDao2 = userDao2; 28 // } 29 // public UserDao getUserDao2() { 30 // return userDao2; 31 // } 32 33 public void test() 34 { 35 userDao1.save(); 36 // userDao2.save(); 37 System.out.println(userDao3.getClass().getName()); 38 userDao3.save(); 39 } 40 }
這個類和上面定義的UserServiceBean2非常相似,需要注意在類前面添加的Annotation信息。
我們不需要在xml文件中手動定義這個bean,Spring會進行自動注冊,注冊的bean名稱是userService。
AOP
我們在Java回顧之反射中已經設計了一個簡單的AOP框架,通常情況下,對於AOP,我們有兩種方式來實現。
使用DynamicProxy實現AOP
下面是一個簡單的示例,首先定義業務對象:
1 public interface UserDao { 2 3 void save(); 4 } 5 6 public class UserDaoImpl implements UserDao 7 { 8 private String name; 9 10 public void save() { 11 System.out.println("save() is called for " + name); 12 } 13 14 public void setName(String name) { 15 this.name = name; 16 } 17 18 public String getName() { 19 return name; 20 } 21 }
下面是一個實現了InvocationHandler的類:
1 public class ProxyFactory implements InvocationHandler 2 { 3 private Object target; 4 5 public Object createUserDao(Object target) 6 { 7 this.target = target; 8 return Proxy.newProxyInstance(this.target.getClass().getClassLoader(), 9 this.target.getClass().getInterfaces(), this); 10 } 11 12 public Object invoke(Object proxy, Method method, Object[] args) 13 throws Throwable { 14 15 UserDaoImpl userDao = (UserDaoImpl)target; 16 Object result = null; 17 if(userDao.getName() != null) 18 { 19 result = method.invoke(target, args); 20 } 21 else 22 { 23 System.out.println("The name is null."); 24 } 25 return result; 26 } 27 }
接下來是測試代碼:
1 private static void test1() 2 { 3 ProxyFactory pf = new ProxyFactory(); 4 UserDao userDao = (UserDao)pf.createUserDao(new UserDaoImpl()); 5 userDao.save(); 6 }
執行結果如下:
The name is null.
這是因為userDao的類型是UserDao,它是一個接口,並沒有定義name字段,因此name=null。
使用Cglib實現AOP
同樣是上面的需求,我們假設並沒有繼承的接口,這我們可以使用cglib來實現。
首先我們重新定義一個UserDaoImpl2,它不會實現任何接口:
1 public class UserDaoImpl2 2 { 3 private String name; 4 5 public void save() throws InterruptedException { 6 Thread.sleep(3000); 7 System.out.println("save() is called for " + name); 8 } 9 10 public void setName(String name) { 11 this.name = name; 12 } 13 14 public String getName() { 15 return name; 16 } 17 18 public void raiseException() 19 { 20 throw new RuntimeException("This is test."); 21 } 22 }
然后是創建CglibFactory:
1 public class CglibFactory implements MethodInterceptor 2 { 3 private Object target; 4 public Object createUserDao(Object target) 5 { 6 this.target = target; 7 Enhancer enhancer = new Enhancer(); 8 enhancer.setSuperclass(target.getClass()); 9 enhancer.setCallback(this); 10 return enhancer.create(); 11 } 12 13 public Object intercept(Object proxy, Method method, Object[] args, 14 MethodProxy methodProxy) throws Throwable { 15 UserDaoImpl2 userDao = (UserDaoImpl2)target; 16 if (userDao.getName() != null) 17 { 18 return method.invoke(target, args); 19 } 20 else 21 { 22 System.out.println("The name is null."); 23 } 24 return null; 25 } 26 }
它實現了MethodInterceptor接口,其中包括intercept方法,這個方法就會通過反射的方式來觸發目標方法,同時還可以添加一些其他處理。
下面是測試方法:
1 private static void test2() throws InterruptedException 2 { 3 CglibFactory cf = new CglibFactory(); 4 UserDaoImpl2 temp = new UserDaoImpl2(); 5 UserDaoImpl2 userDao = (UserDaoImpl2)cf.createUserDao(temp); 6 userDao.save(); 7 temp.setName("Zhang San"); 8 userDao = (UserDaoImpl2)cf.createUserDao(temp); 9 userDao.save(); 10 }
輸出結果如下:
The name is null. save() is called for Zhang San
使用Spring實現AOP
Spring框架集合了ProxyFactory和Cglib兩種方式來實現AOP。
我們來看一個示例,還是使用上面定義的UserDaoImpl以及UserDaoImpl2。
首先需要定義一個interceptor:
1 @Aspect 2 public class MyInterceptor { 3 4 @Pointcut("execution (* sample.spring.aop.*.*(..))") 5 public void anyMethod(){} 6 7 @Before("anyMethod()") 8 public void before() 9 { 10 System.out.println("Before"); 11 } 12 13 @After("anyMethod()") 14 public void after() 15 { 16 System.out.println("After"); 17 } 18 19 @Around("anyMethod()") 20 public void Around(ProceedingJoinPoint pjp) throws Throwable 21 { 22 long start = System.currentTimeMillis(); 23 pjp.proceed(); 24 long end = System.currentTimeMillis(); 25 System.out.println("執行時間:" + (end - start)); 26 } 27 28 @Before("anyMethod() && args(name)") 29 public void before(String name) 30 { 31 System.out.println("The name is " + name); 32 } 33 34 @AfterReturning(pointcut="anyMethod()", returning="result") 35 public void afterReturning(String result) 36 { 37 System.out.println("The value is " + result); 38 } 39 40 @AfterThrowing(pointcut="anyMethod()", throwing="e") 41 public void afterThrowing(Exception e) 42 { 43 e.printStackTrace(); 44 } 45 }
我們可以看到上面的代碼中包含了一些Annotation,這些Annotation是用來實現AOP的關鍵。
然后需要修改beans.xml,添加如下內容:
1 <aop:aspectj-autoproxy /> 2 <bean id="userDaoImpl" class = "sample.spring.aop.UserDaoImpl"/> 3 <bean id="userDaoImpl2" class = "sample.spring.aop.UserDaoImpl2"/> 4 <bean id="myInterceptor" class="sample.spring.aop.MyInterceptor"/>
其中第一行是讓Spring打開AOP的功能,下面三行定義了三個bean,這里我們把interceptor也看做是一個bean對象。
接下來是測試代碼:
1 private static void test3() throws InterruptedException 2 { 3 ApplicationContext ctx = new ClassPathXmlApplicationContext("sample/spring/aop/beans.xml"); 4 UserDao userDao = (UserDao)ctx.getBean("userDaoImpl"); 5 userDao.save(); 6 UserDaoImpl2 userDao2 = (UserDaoImpl2)ctx.getBean("userDaoImpl2"); 7 userDao2.save(); 8 userDao2.setName("Zhang San"); 9 String name = userDao2.getName(); 10 // userDao2.raiseException(); 11 }
這里我們可以看到,測試方法中既使用了UserDaoImpl1(這里是UserDao接口),也是用了UserDaoImpl2。正如我們上面所言,在Spring中,如果類實現了接口,Spring會按照ProxyFactory的方式來處理;如果沒有實現接口,Spring會按照Cglib的方式來處理。
上面測試方法的輸出如下:
Before Before save() is called for null 執行時間:1 The value is null After After 執行時間:1 The value is null Before Before save() is called for null 執行時間:3001 The value is null After After 執行時間:3002 The value is null Before The name is Zhang San Before 執行時間:26 The value is null After After 執行時間:27 The value is null Before Before 執行時間:0 The value is null After After 執行時間:1 The value is null
使用Spring配置文件來配置AOP
上面的示例中,我們使用Annotation來配置AOP的信息,同樣我們也可以使用xml文件的方式來配置AOP。
還是以上面定義的interceptor為基礎,我們去掉里面所有的Annotation,然后在beans.xml中添加如下內容:
1 <bean id="myInterceptor2" class="sample.spring.aop.MyInterceptor2"/> 2 <aop:config> 3 <aop:aspect id="asp" ref="myInterceptor2"> 4 <aop:pointcut id="anyMethod" expression="execution (* sample.spring.aop.*.*(..))"/> 5 <aop:before pointcut-ref="anyMethod" method="before"/> 6 <aop:after pointcut-ref="anyMethod" method="after"/> 7 <aop:around pointcut-ref="anyMethod" method="around"/> 8 <aop:after-returning pointcut-ref="anyMethod" method="afterReturning" returning="result"/> 9 <aop:after-throwing pointcut-ref="anyMethod" method="afterThrowing" throwing="e"/> 10 </aop:aspect> 11 </aop:config>
測試方法和輸出結果同上。
Spring和JDBC
Spring中也包含了對JDBC數據訪問的支持,它有一個JdbcTemplate的機制,其中提供了大量的API,對ResultSet進行了封裝,可以大大簡化我們的工作量。
同時Spring還提供了針對事務的支持,包含了一些Annotation,既可以作用在類上,也可以作用在方法上。
下面是一個簡單的示例,我們還是連接MySQL數據庫中的user表,實現對其CRUD操作。
首先是定義業務對象以及DAO接口:
1 public class User { 2 3 private int userID; 4 private String userName; 5 public void setUserID(int userID) { 6 this.userID = userID; 7 } 8 public int getUserID() { 9 return userID; 10 } 11 public void setUserName(String userName) { 12 this.userName = userName; 13 } 14 public String getUserName() { 15 return userName; 16 } 17 } 18 19 public interface UserDao { 20 21 void insertUser(User user); 22 void updateUser(User user); 23 void deleteUser(User user); 24 List<User> getAllUser(); 25 User getUser(int id); 26 }
然后是建立一個Dao的實現類,這里使用了一些JdbcTemplate API:
1 @Transactional 2 public class UserDaoImpl implements UserDao 3 { 4 private JdbcTemplate jdbcTemplate; 5 6 public void setDataSource(DataSource dataSource) throws SQLException 7 { 8 jdbcTemplate = new JdbcTemplate(dataSource); 9 System.out.println(dataSource.getConnection().getMetaData().getDriverName()); 10 } 11 12 public void deleteUser(User user) { 13 jdbcTemplate.update("delete from user where id=?", 14 new Object[]{user.getUserID()}, 15 new int[]{java.sql.Types.INTEGER}); 16 } 17 18 @SuppressWarnings("unchecked") 19 public List<User> getAllUser() { 20 return (List<User>)jdbcTemplate.query("select * from user", 21 new RowMapper() 22 { 23 public Object mapRow(ResultSet rs, int arg) throws SQLException 24 { 25 User user = new User(); 26 user.setUserID(rs.getInt("ID")); 27 user.setUserName(rs.getString("NAME")); 28 29 return user; 30 } 31 }); 32 } 33 34 public User getUser(int id) { 35 try 36 { 37 return (User)jdbcTemplate.queryForObject("select * from user where id=?", 38 new Object[]{id}, 39 new int[]{java.sql.Types.INTEGER}, 40 new RowMapper() 41 { 42 public Object mapRow(ResultSet rs, int arg) throws SQLException 43 { 44 User user = new User(); 45 user.setUserID(rs.getInt("id")); 46 user.setUserName(rs.getString("name")); 47 return user; 48 } 49 }); 50 } 51 catch(Exception ex) 52 { 53 System.out.println(ex.getMessage()); 54 } 55 return null; 56 57 } 58 59 public void insertUser(User user) { 60 jdbcTemplate.update("insert into user (id,name) values(?,?)", 61 new Object[]{user.getUserID(), user.getUserName()}, 62 new int[]{java.sql.Types.INTEGER, java.sql.Types.VARCHAR}); 63 } 64 65 public void updateUser(User user) { 66 jdbcTemplate.update("update user set name=? where id=?", 67 new Object[]{user.getUserName(), user.getUserID()}, 68 new int[]{java.sql.Types.VARCHAR, java.sql.Types.INTEGER}); 69 } 70 71 }
JdbcTemplate還提供了一些其他的API,也非常實用。
接下來需要修改beans.xml:
1 <bean id="theDatasource" class="org.apache.commons.dbcp.BasicDataSource"> 2 <property name="driverClassName" value="com.mysql.jdbc.Driver" /> 3 <property name="url" value="jdbc:mysql://localhost/test" /> 4 <property name="username" value="root" /> 5 <property name="password" value="123" /> 6 <property name="initialSize" value="2" /> 7 <property name="maxActive" value="100" /> 8 <property name="maxIdle" value="2" /> 9 <property name="minIdle" value="1" /> 10 </bean> 11 12 <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 13 <property name="dataSource" ref="theDatasource" /> 14 </bean> 15 16 <tx:annotation-driven transaction-manager="txManager" /> 17 18 <bean id="userDao" class="sample.spring.jdbc.UserDaoImpl"> 19 <property name="dataSource" ref="theDatasource"/> 20 </bean>
這里theDataSource用來配置數據庫連接信息;txManager來配置事務管理器的信息;userDao是我們的Dao實現類信息。
下面是測試方法:
1 public static void test1() 2 { 3 ApplicationContext ctx = new ClassPathXmlApplicationContext("sample/spring/jdbc/beans.xml"); 4 UserDao userDao = (UserDao)ctx.getBean("userDao"); 5 System.out.println("=====get all user====="); 6 List<User> users = userDao.getAllUser(); 7 for(User user:users) 8 { 9 System.out.println("ID:" + user.getUserID() + ";Name:" + user.getUserName()); 10 } 11 System.out.println("=====insert user====="); 12 User user = new User(); 13 user.setUserID(10); 14 user.setUserName("Zhang Fei"); 15 userDao.insertUser(user); 16 user = userDao.getUser(10); 17 System.out.println("ID:" + user.getUserID() + ";Name:" + user.getUserName()); 18 System.out.println("=====update user====="); 19 user.setUserName("Devil"); 20 userDao.updateUser(user); 21 user = userDao.getUser(10); 22 System.out.println("ID:" + user.getUserID() + ";Name:" + user.getUserName()); 23 System.out.println("=====delete user====="); 24 userDao.deleteUser(user); 25 user = userDao.getUser(10); 26 if (user == null) 27 { 28 System.out.println("delete successfully."); 29 } 30 }
輸出結果如下:
MySQL-AB JDBC Driver =====get all user===== ID:1;Name:Zhang San ID:2;Name:TEST =====insert user===== ID:10;Name:Zhang Fei =====update user===== ID:10;Name:Devil =====delete user===== Incorrect result size: expected 1, actual 0 delete successfully.
說到數據庫事務,我們在上面的UserDaoImpl中可以看到,這個類的前面有一個名為@Transcational的Annotation聲明,這是Spring實現事務的關鍵點,它既可以作用在類上,也可以作用在方法上。
@Transactional包含以下參數:
- propagation參數,Propagation類型(枚舉),默認值為Propogation.REQUIRED,支持的值有REQUIRED、MANDATORY、NESTED、NEVER、NOT_SUPPORTED、REQUIRE_NEW、SUPPORTS。
- isolation參數,Isolation類型(枚舉),默認值為Isolation.DEFAULT,支持的值有DEFAULT、READ_COMMITTED、READ_UNCOMMITTED、REPEATABLE_READ、SERIALIZABLE。
- timeout參數,int類型,事務的超時時間,默認值為-1,即不會超時。
- readOnly參數,boolean類型,true表示事務為只讀,默認值為false。
- rollbackFor參數,Class<? extends Throwable>[]類型,默認為空數組。
- rollbackForClassName參數,String[]類型,默認為空數組。
- noRollbackFor參數,Class<? extends Throwable>[]類型,默認為空數組。
- noRollbackForClassName參數,String[]類型,默認為空數組。