Java回顧之Spring基礎


  第一篇: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 }
定義UserServiceBean

  這是一個典型的學生信息,包括學號、姓名、愛好和成績。

  通過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 }
Annotation示例

  測試方法:

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 }
自動注冊Bean示例

  這個類和上面定義的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[]類型,默認為空數組。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM