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://HdhCmsTestspringframework.org/schema/beans" 3 xmlns:xsi ="http://HdhCmsTestw3.org/2001/XMLSchema-instance" 4 xmlns:context ="http://HdhCmsTestspringframework.org/schema/context" 5 xmlns:tx ="http://HdhCmsTestspringframework.org/schema/tx" 6 xsi:schemaLocation ="http://HdhCmsTestspringframework.org/schema/beans http://HdhCmsTestspringframework.org/schema/beans/spring-beans-2.5.xsd 7 http://HdhCmsTestspringframework.org/schema/context http://HdhCmsTestspringframework.org/schema/context/spring-context-2.5.xsd 8 http://HdhCmsTestspringframework.org/schema/tx http://HdhCmsTestspringframework.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测试数据mons.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测试数据/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
分类: 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测试数据/
本文版权归作者和博客园共有,欢迎转载
分类: C/C++
作者: Leo_wl
出处: http://HdhCmsTestcnblogs测试数据/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息