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

Java回顾之Spring基础_第1张图片

 

@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的内存布局,如下图:

Java回顾之Spring基础_第2张图片

查看对象内存布局的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++

你可能感兴趣的:(Java回顾之Spring基础)