好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Java回顾之Spring基础

Java回顾之Spring基础

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       public   void   save() {
   8          System.out.println("save() is called." );
   9       }
  10  }

  然后是在classpath下创建一个beans.xml文件(这个文件名不是必须这样的):

  1   <?  xml version="1.0" encoding="UTF-8"  ?> 
  2   <  beans   xmlns  ="http://www.springframework.org/schema/beans" 
  3           xmlns:xsi  ="http://www.w3.org/2001/XMLSchema-instance" 
  4           xmlns:context  ="http://www.springframework.org/schema/context" 
  5           xmlns:tx  ="http://www.springframework.org/schema/tx" 
  6           xsi:schemaLocation  ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
   7                   http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
   8                   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"  > 
  9  
 10       <  bean   id  ="userDaoImpl"   class   = "sample.spring.ioc.UserDaoImpl"  /> 
 11   </  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

  添加如下类:

 public   class   UserDaoFactory2 
{
      public   UserDao getUserDao()
    {
          return   new   UserDaoImpl();
    }
} 

  这个类和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方式和构造函数。

  下面我们会分别描述两种方式,但首先我们需要展示业务对象:

定义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。

  来看下面的示例:

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中添加如下内容:

 <  context:component-scan   base-package  ="sample.spring.ioc"  /> 

  然后我们可以在sample.spring.ioc包下的对象,添加@Component/@Service/@Controller/@repository,这样Spring会自动将带有这些Annotation的类进行注册。

  下面是一个示例:

自动注册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  
 20   public interface UserDao {
  21  
 22       void insertUser(User user);
  23       void updateUser(User user);
  24       void deleteUser(User user);
  25      List <  User  >   getAllUser();
  26       User getUser(int id);
  27  }

  然后是建立一个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[]类型,默认为空数组。

    

作者: 李胜攀

    

出处: http://wing011203.cnblogs.com/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

 

分类:  Java相关

标签:  面试 ,  Java ,  Annotation ,  AOP ,  JDBC ,  Spring ,  Ioc ,  DI ,  Transaction

C++多态、继承的简单分析

 

一直以为自己对多态和继承已经比较了解,当遇到虚继承的时候,发现有点犯晕,想不通了,于是在微博上向几个大神请教,很快得到了他们的回复,高兴之情无以言表。之后自己查了一些资料,结合大神的回复,在这里做一下简单的记录。

我的问题如下:

为什么虚继承类的sizeof要大些啊,是因为虚继承中,子类有指向父类的指针和指向父类的虚函数表的指针吗,比非虚继承多了这两个指针?  @左耳朵耗子   @简悦云风   @GeniusVczh

@GeniusVczh :调用的时候给的this和函数实际需要的this的指针不一定是一样的,多重继承的时候已经这样了。再加上你还有virtual继承,所以需要很多描述。

V福尔摩斯  回复  @GeniusVczh :看了B(第三个图中的B)的内存布局,的确有vfptr和vbptr两个指针

简悦云风 :和编译器实现有关。实现上虚继承更象是组合,因为它可以被菱形继承而只有一份,所以加上一个额外指针引用这个对象。没有虚函数时不生成虚表,所以 2 里就是 a 对象加额外指针。3里面 b 有虚表,就再加一个虚表指针。字数不够不吐槽了。

左耳朵耗子 :1) int a :4字节;2)虚函数增加一个虚表指针:4字节。3)虚继承还会增加一个指针:4字节。但是为什么最后会是16个字呢?你是在用VC++吧?看一下我的这篇文章( http://t.cn/a1lMjd  最后一个示例)你会知道VC++的对象布局是有点诡异。G++下应该是正常的。

看了 简悦云风 和 左耳朵耗子 两个大神的回复之后,自己觉得还是有点晕,于是看了一下《Effective C++》和《More Effective C++》,在这里做一下记录。

多态的实现原理 :

1:含有虚方法的类都有一个虚函数表

2:子类的虚方法会覆盖父类对应的虚方法

3:含有虚方法的类的每个实例都有一个指向虚方法表的指针,如果虚继承的话可能会有多个

4:根据3中的指针调用虚方法表中对应的虚方法

多态的实现差不多就是上面几点。面试中经常遇到的就是调用哪个方法的问题,一句话告诉你是怎么调用的: 在继承关系中,非虚方法调用指针类型的方法;虚方法调用指针所指的对象类型的方法。 非虚方法和默认参数都是静态绑定,在继承关系中只跟指针类型有关,跟指针所指的对象的实际类型无关。还有一点就是非虚方法就像C方法一样,不用太在意,证明非虚方法就像C方法的一个方式就是,用一个空指针调用一个非虚方法,只要这个对象没有用到对象的数据,就不会有任何问题。

再来看看我发问的哪个图,为什么图3中sizeof(B)=16,于是用VS自带的工具看了一下B的内存布局,如下图:

查看对象内存布局的VS命令:cl [filename].cpp /d1reportSingleClassLayout[className]

看到这个图,其实还是不太好理解,int a占4字节,B有自己的虚函数表,虚函数指针占4字节,另外多出一个vfptr和vbptr,那就只能这样理解了:vfptr指向父类的虚函数表,B每多虚继承一个类,就多一个vfptr,不信你可以试试,vbptr指向A,但是每多虚继承一个类,并不多出一个vbptr,这是和解呢?

@pop_Atry :干嘛不把B到A的偏移量放到虚表里面,何必为每个对象添加一个额外引用?

@简悦云风 :回复 @pop_Atry : 1. 性能原因; 2.有的编译器的确是放偏移量的.

风神一语中的,但如果不按照 @pop_Atry ,就是我们现在看到的,B变大了。

妹的,不同的编译器有不同的实现,咋们讨论这个问题有什么意义呢,千万不要把不同编译器编译的代码放到同一个程序中啊,啊哥。

虚拟继承的出现就是为了解决重复继承中多个间接父类的问题的,保证每个父类都只有一份。

Effective C++的作者建议尽量避免多继承,如果不能避免也要避免菱形继承,各种莫名其妙的复杂啊。所以说多继承也就算了,还许多(虚多)继承,那就要搞死人了。

更多了解请看 左耳朵耗子 的相关博客: http://blog.csdn.net/haoel/article/details/3081385

我的博客目录

作者: 陈太汉
出处: http://hlxs.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载

 

分类:  C/C++

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于Java回顾之Spring基础的详细内容...

  阅读:46次