用Mockito测试SpringMVC+Hibernate

译自:Spring 4 MVC+Hibernate 4+MySQL+Maven integration + Testing example using annotations

2017-01-19

目录:

1 目录结构
2 pom.xml
3 Testing Controller Layer
  3.1 com.websystique.springmvc.controller.AppControllerTest
4 Testing Service Layer
  4.1 com.websystique.springmvc.service.EmployeeServiceImplTest
5 Testing Data Layer
  5.1 com.websystique.springmvc.configuration.HibernateTestConfiguration
  5.2 com.websystique.springmvc.dao.EntityDaoImplTest
  5.3 com.websystique.springmvc.dao.EmployeeDaoImplTest
  5.4 src/test/resources/Employee.xml

源代码 : SpringHibernateExample.zip

1 目录结构


返回

2 pom.xml


返回

与 被测项目 Spring 4 MVC+Hibernate 4+MySQL+Maven使用注解集成实例中pom.xml 一样。

其中,

  • Spring-test : 在测试类中使用 spring-test annotations
  • TestNG : 使用testNG作为测试框架
  • Mockito : 使用mockito模拟外部依赖, 比如当测试service时mock dao,关于mockito,请参考Mockito教程
  • DBUnit : 使用DBUnit管理数据,当测试data/dao层时
  • H2 Database : 对数据库层测试,与其说是单元测试不如说是集成测试,使用H2 Database对数据库层进行测试

3 Testing Controller Layer


返回

3.1 com.websystique.springmvc.controller.AppControllerTest

package com.websystique.springmvc.controller;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify; import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List; import org.joda.time.LocalDate;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import static org.mockito.Mockito.atLeastOnce; import org.springframework.context.MessageSource;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test; import com.websystique.springmvc.model.Employee;
import com.websystique.springmvc.service.EmployeeService; public class AppControllerTest { @Mock
EmployeeService service; @Mock
MessageSource message; @InjectMocks
AppController appController; @Spy
List<Employee> employees = new ArrayList<Employee>(); @Spy
ModelMap model; @Mock
BindingResult result; @BeforeClass
public void setUp(){
MockitoAnnotations.initMocks(this);
employees = getEmployeeList();
} @Test
public void listEmployees(){
when(service.findAllEmployees()).thenReturn(employees);
Assert.assertEquals(appController.listEmployees(model), "allemployees");
Assert.assertEquals(model.get("employees"), employees);
verify(service, atLeastOnce()).findAllEmployees();
} @Test
public void newEmployee(){
Assert.assertEquals(appController.newEmployee(model), "registration");
Assert.assertNotNull(model.get("employee"));
Assert.assertFalse((Boolean)model.get("edit"));
Assert.assertEquals(((Employee)model.get("employee")).getId(), 0);
} @Test
public void saveEmployeeWithValidationError(){
when(result.hasErrors()).thenReturn(true);
doNothing().when(service).saveEmployee(any(Employee.class));
Assert.assertEquals(appController.saveEmployee(employees.get(0), result, model), "registration");
} @Test
public void saveEmployeeWithValidationErrorNonUniqueSSN(){
when(result.hasErrors()).thenReturn(false);
when(service.isEmployeeSsnUnique(anyInt(), anyString())).thenReturn(false);
Assert.assertEquals(appController.saveEmployee(employees.get(0), result, model), "registration");
} @Test
public void saveEmployeeWithSuccess(){
when(result.hasErrors()).thenReturn(false);
when(service.isEmployeeSsnUnique(anyInt(), anyString())).thenReturn(true);
doNothing().when(service).saveEmployee(any(Employee.class));
Assert.assertEquals(appController.saveEmployee(employees.get(0), result, model), "success");
Assert.assertEquals(model.get("success"), "Employee Axel registered successfully");
} @Test
public void editEmployee(){
Employee emp = employees.get(0);
when(service.findEmployeeBySsn(anyString())).thenReturn(emp);
Assert.assertEquals(appController.editEmployee(anyString(), model), "registration");
Assert.assertNotNull(model.get("employee"));
Assert.assertTrue((Boolean)model.get("edit"));
Assert.assertEquals(((Employee)model.get("employee")).getId(), 1);
} @Test
public void updateEmployeeWithValidationError(){
when(result.hasErrors()).thenReturn(true);
doNothing().when(service).updateEmployee(any(Employee.class));
Assert.assertEquals(appController.updateEmployee(employees.get(0), result, model,""), "registration");
} @Test
public void updateEmployeeWithValidationErrorNonUniqueSSN(){
when(result.hasErrors()).thenReturn(false);
when(service.isEmployeeSsnUnique(anyInt(), anyString())).thenReturn(false);
Assert.assertEquals(appController.updateEmployee(employees.get(0), result, model,""), "registration");
} @Test
public void updateEmployeeWithSuccess(){
when(result.hasErrors()).thenReturn(false);
when(service.isEmployeeSsnUnique(anyInt(), anyString())).thenReturn(true);
doNothing().when(service).updateEmployee(any(Employee.class));
Assert.assertEquals(appController.updateEmployee(employees.get(0), result, model, ""), "success");
Assert.assertEquals(model.get("success"), "Employee Axel updated successfully");
} @Test
public void deleteEmployee(){
doNothing().when(service).deleteEmployeeBySsn(anyString());
Assert.assertEquals(appController.deleteEmployee("123"), "redirect:/list");
} public List<Employee> getEmployeeList(){
Employee e1 = new Employee();
e1.setId(1);
e1.setName("Axel");
e1.setJoiningDate(new LocalDate());
e1.setSalary(new BigDecimal(10000));
e1.setSsn("XXX111"); Employee e2 = new Employee();
e2.setId(2);
e2.setName("Jeremy");
e2.setJoiningDate(new LocalDate());
e2.setSalary(new BigDecimal(20000));
e2.setSsn("XXX222"); employees.add(e1);
employees.add(e2);
return employees;
}
}

右击该测试类,得到结果如下:

PASSED: deleteEmployee
PASSED: editEmployee
PASSED: listEmployees
PASSED: newEmployee
PASSED: saveEmployeeWithSuccess
PASSED: saveEmployeeWithValidationError
PASSED: saveEmployeeWithValidationErrorNonUniqueSSN
PASSED: updateEmployeeWithSuccess
PASSED: updateEmployeeWithValidationError
PASSED: updateEmployeeWithValidationErrorNonUniqueSSN ===============================================
Default test
Tests run: 10, Failures: 0, Skips: 0
===============================================

解读:

因为被测类AppController依赖EmployeeService , MessageSource, Employee, ModelMap & BindingResult。因此,为了测试AppController,需要提供这些依赖。

    @Mock  //Mock不是真实的对象,它只是用类型的class创建了一个虚拟对象,并可以设置对象行为
EmployeeService service; @Mock
MessageSource message; @InjectMocks //InjectMocks创建这个类的对象并自动将标记@Mock、@Spy等注解的属性值注入到这个中
AppController appController; @Spy //Spy是一个真实的对象,但它可以设置对象行为
List<Employee> employees = new ArrayList<Employee>(); @Spy
ModelMap model; @Mock
BindingResult result;

其中,when..then 模式用于设置对象行为。

另外,需要加入以下代码:

    MockitoAnnotations.initMocks(this); //初始化被注释的[@Mock, @Spy, @Captor, @InjectMocks] 对象

4 Testing Service Layer


返回

4.1 com.websystique.springmvc.service.EmployeeServiceImplTest

package com.websystique.springmvc.service;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify; import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List; import static org.mockito.Mockito.when; import org.joda.time.LocalDate;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test; import com.websystique.springmvc.dao.EmployeeDao;
import com.websystique.springmvc.model.Employee; public class EmployeeServiceImplTest { @Mock
EmployeeDao dao; @InjectMocks
EmployeeServiceImpl employeeService; @Spy
List<Employee> employees = new ArrayList<Employee>(); @BeforeClass
public void setUp(){
MockitoAnnotations.initMocks(this);
employees = getEmployeeList();
} @Test
public void findById(){
Employee emp = employees.get(0);
when(dao.findById(anyInt())).thenReturn(emp);
Assert.assertEquals(employeeService.findById(emp.getId()),emp);
} @Test
public void saveEmployee(){
doNothing().when(dao).saveEmployee(any(Employee.class));
employeeService.saveEmployee(any(Employee.class));
verify(dao, atLeastOnce()).saveEmployee(any(Employee.class));
} @Test
public void updateEmployee(){
Employee emp = employees.get(0);
when(dao.findById(anyInt())).thenReturn(emp);
employeeService.updateEmployee(emp);
verify(dao, atLeastOnce()).findById(anyInt());
} @Test
public void deleteEmployeeBySsn(){
doNothing().when(dao).deleteEmployeeBySsn(anyString());
employeeService.deleteEmployeeBySsn(anyString());
verify(dao, atLeastOnce()).deleteEmployeeBySsn(anyString());
} @Test
public void findAllEmployees(){
when(dao.findAllEmployees()).thenReturn(employees);
Assert.assertEquals(employeeService.findAllEmployees(), employees);
} @Test
public void findEmployeeBySsn(){
Employee emp = employees.get(0);
when(dao.findEmployeeBySsn(anyString())).thenReturn(emp);
Assert.assertEquals(employeeService.findEmployeeBySsn(anyString()), emp);
} @Test
public void isEmployeeSsnUnique(){
Employee emp = employees.get(0);
when(dao.findEmployeeBySsn(anyString())).thenReturn(emp);
Assert.assertEquals(employeeService.isEmployeeSsnUnique(emp.getId(), emp.getSsn()), true);
} public List<Employee> getEmployeeList(){
Employee e1 = new Employee();
e1.setId(1);
e1.setName("Axel");
e1.setJoiningDate(new LocalDate());
e1.setSalary(new BigDecimal(10000));
e1.setSsn("XXX111"); Employee e2 = new Employee();
e2.setId(2);
e2.setName("Jeremy");
e2.setJoiningDate(new LocalDate());
e2.setSalary(new BigDecimal(20000));
e2.setSsn("XXX222"); employees.add(e1);
employees.add(e2);
return employees;
}
}

右击该测试类,得到结果如下:

PASSED: deleteEmployeeBySsn
PASSED: findAllEmployees
PASSED: findById
PASSED: findEmployeeBySsn
PASSED: isEmployeeSsnUnique
PASSED: saveEmployee
PASSED: updateEmployee ===============================================
Default test
Tests run: 7, Failures: 0, Skips: 0
===============================================

Test Service Layer和Test Control Layer类似,不再详述

5 Testing Data Layer


返回

DAO 或 data Layer测试一直是有争议的话题。我们到底要如何测试?把它当做单元测试的话,就要测试它的每一行代码,这样的话,要mocking所有的外部依赖。但是,我们没有与数据库本身的交互,就没法测data-layer。那么它就变成了集成测试。

通常,我们对DAO Layer做集成测试。这里,我们用in-memory H2 database做集成测试。

5.1 com.websystique.springmvc.configuration.HibernateTestConfiguration

package com.websystique.springmvc.configuration;

import java.util.Properties;

import javax.sql.DataSource;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate4.HibernateTransactionManager;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement; /*
* This class is same as real HibernateConfiguration class in sources.
* Only difference is that method dataSource & hibernateProperties
* implementations are specific to Hibernate working with H2 database.
*/ @Configuration
@EnableTransactionManagement
@ComponentScan({ "com.websystique.springmvc.dao" })
public class HibernateTestConfiguration { @Autowired
private Environment environment; @Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(new String[] { "com.websystique.springmvc.model" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
} @Bean(name = "dataSource")
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
} private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
properties.put("hibernate.hbm2ddl.auto", "create-drop");
return properties;
} @Bean
@Autowired
public HibernateTransactionManager transactionManager(SessionFactory s) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
}

解读:

  • 上面的类与HibernateConfiguration类非常相似,区别仅在 dataSource() & hibernateProperties()这两个方法的实现。
  • 在Sources folder中,它做了几乎同样事情:它用dataSource创建了SessionFacoty,其中,dataSource被配置成可与in-memory database H2一起工作。为了使hibernate与H2一起工作,设置hibernate.dialect为H2Dialect。
  • SessionFacoty会被注入到AbstractDao,而后当测试EmployeeDaoImpl类时,EmployeeDaoImpl会使用SessionFacoty。

5.2 com.websystique.springmvc.dao.EntityDaoImplTest

该类是所有测试累的基类

package com.websystique.springmvc.dao;

import javax.sql.DataSource;

import org.dbunit.database.DatabaseDataSourceConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.IDataSet;
import org.dbunit.operation.DatabaseOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests;
import org.testng.annotations.BeforeMethod; import com.websystique.springmvc.configuration.HibernateTestConfiguration; @ContextConfiguration(classes = { HibernateTestConfiguration.class })
public abstract class EntityDaoImplTest extends AbstractTransactionalTestNGSpringContextTests { @Autowired
DataSource dataSource; @BeforeMethod
public void setUp() throws Exception {
IDatabaseConnection dbConn = new DatabaseDataSourceConnection(
dataSource);
DatabaseOperation.CLEAN_INSERT.execute(dbConn, getDataSet());
} protected abstract IDataSet getDataSet() throws Exception; }

解读:

  • AbstractTransactionalTestNGSpringContextTests在某种程度上可以更JUnit的RunWith等价。这个抽象类集成Spring TestContext support到TestNG environment中。
  • 为了在测试中提供数据访问层的支持,它也需要在ApplicationContext中定义datasource和transactionManager。我们已在上面的Configuration类中定义了datasource和transactionManager。
  • 由于事物支持,每次测试前一个事物会被默认启动,在每次测试结束后这个事物会被回滚。你可以override这个回滚行为。
  • BeforeTest在测试用执行前,我们将使用DBUnit去clean-insert测试数据库[h2]中的数据样例。这样避免各个测试方法之间的影响
  • 抽象方法getDataSet会在测试类中实现为了在测试前提供真实的测试数据

5.3 com.websystique.springmvc.dao.EmployeeDaoImplTest

package com.websystique.springmvc.dao;

import java.math.BigDecimal;

import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSet;
import org.joda.time.LocalDate;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.Assert;
import org.testng.annotations.Test; import com.websystique.springmvc.model.Employee; public class EmployeeDaoImplTest extends EntityDaoImplTest{ @Autowired
EmployeeDao employeeDao; @Override
protected IDataSet getDataSet() throws Exception{
IDataSet dataSet = new FlatXmlDataSet(this.getClass().getClassLoader().getResourceAsStream("Employee.xml"));
return dataSet;
} /* In case you need multiple datasets (mapping different tables) and you do prefer to keep them in separate XML's
@Override
protected IDataSet getDataSet() throws Exception {
IDataSet[] datasets = new IDataSet[] {
new FlatXmlDataSet(this.getClass().getClassLoader().getResourceAsStream("Employee.xml")),
new FlatXmlDataSet(this.getClass().getClassLoader().getResourceAsStream("Benefits.xml")),
new FlatXmlDataSet(this.getClass().getClassLoader().getResourceAsStream("Departements.xml"))
};
return new CompositeDataSet(datasets);
}
*/ @Test
public void findById(){
Assert.assertNotNull(employeeDao.findById(1));
Assert.assertNull(employeeDao.findById(3));
} @Test
public void saveEmployee(){
employeeDao.saveEmployee(getSampleEmployee());
Assert.assertEquals(employeeDao.findAllEmployees().size(), 3);
} @Test
public void deleteEmployeeBySsn(){
employeeDao.deleteEmployeeBySsn("11111");
Assert.assertEquals(employeeDao.findAllEmployees().size(), 1);
} @Test
public void deleteEmployeeByInvalidSsn(){
employeeDao.deleteEmployeeBySsn("23423");
Assert.assertEquals(employeeDao.findAllEmployees().size(), 2);
} @Test
public void findAllEmployees(){
Assert.assertEquals(employeeDao.findAllEmployees().size(), 2);
} @Test
public void findEmployeeBySsn(){
Assert.assertNotNull(employeeDao.findEmployeeBySsn("11111"));
Assert.assertNull(employeeDao.findEmployeeBySsn("14545"));
} public Employee getSampleEmployee(){
Employee employee = new Employee();
employee.setName("Karen");
employee.setSsn("12345");
employee.setSalary(new BigDecimal(10980));
employee.setJoiningDate(new LocalDate());
return employee;
} }

5.4 src/test/resources/Employee.xml

<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<employee id="1" NAME="SAMY" JOINING_DATE="2014-04-16" SALARY="20000" SSN="11111" />
<employee id="2" NAME="TOMY" JOINING_DATE="2014-05-17" SALARY="23000" SSN="11112" />
</dataset>

右击该测试类,得到结果如下:

PASSED: deleteEmployeeByInvalidSsn
PASSED: deleteEmployeeBySsn
PASSED: findAllEmployees
PASSED: findById
PASSED: findEmployeeBySsn
PASSED: saveEmployee ===============================================
Default test
Tests run: 6, Failures: 0, Skips: 0
===============================================

我们以saveEmployee为例解读下执行过程:

1. 在测试方法运行前,Spring会通过@ContextConfiguration注释的EntityDaoImplTest类加载text context,还会通过AbstractTransactionalTestNGSpringContextTests创建beans实例。这只会发生一次。

2. 在bean实例创建前,Spring会创建SessionFactory Bean,并且SessionFactory Bean会被注入dataSource bean(在HibernateTestConfiguration类中定义),见下面属性设置

properties.put("hibernate.hbm2ddl.auto", "create-drop");  

注意:由于hbm2ddl属性,当SessionFactory被创建,与Model类相关的schema会被验证并导出到数据库。这意味着Employee表会在H2数据库中创建。

3. 在测试前,@BeforeMethod注释的方法会被调用,该方法会通知DBUnit连接数据库执行clean-insert,在Employee表插入两个记录(见Employee.xml内容)

4. 现在测试用例saveEmployee将开始执行,在执行开始前,事物将被启动,saveEmployee方法本身将在事物中运行。一旦saveEmployee方法运行完毕,事物会回滚到默认的setup。

5. 测试用例saveEmployee开始执行。它会调用employeeDao.saveEmployee(getSampleEmployee()),被调用者会通过hibernate插入预先定义的Employee到H2 database中。这是关键的一步。在这一个之后,就会有3条记录在H2 database中。

6. 在下一个用例中,@BeforeMethod又会被调用

7. 当所有用例测试完后,session会被关掉,schema会被去除

用Mockito测试SpringMVC+Hibernate的更多相关文章

  1. Maven搭建SpringMVC+Hibernate项目详解 【转】

    前言 今天复习一下SpringMVC+Hibernate的搭建,本来想着将Spring-Security权限控制框架也映入其中的,但是发现内容太多了,Spring-Security的就留在下一篇吧,这 ...

  2. Maven搭建SpringMVC+Hibernate项目详解

    前言 今天复习一下SpringMVC+Hibernate的搭建,本来想着将Spring-Security权限控制框架也映入其中的,但是发现内容太多了,Spring-Security的就留在下一篇吧,这 ...

  3. Maven搭建SpringMVC+Hibernate项目详解(转)

    前言 今天复习一下SpringMVC+Hibernate的搭建,本来想着将Spring-Security权限控制框架也映入其中的,但是发现内容太多了,Spring-Security的就留在下一篇吧,这 ...

  4. 框架篇:Spring+SpringMVC+hibernate整合开发

    前言: 最近闲的蛋疼,搭个框架写成博客记录下来,拉通一下之前所学知识,顺带装一下逼. 话不多说,我们直接步入正题. 准备工作: 1/ IntelliJIDEA的安装配置:jdk/tomcat等..(本 ...

  5. Spring+SpringMVC+Hibernate小案例(实现Spring对Hibernate的事务管理)

    原文地址:https://blog.csdn.net/jiegegeaa1/article/details/81975286 一.工作环境 编辑器用的是MyEclipse,用Mysql数据库,mave ...

  6. springmvc+hibernate

    <本文摘要他人> 1.设计数据库:设计好表结构,最好符合3NF,采用Hibernate tools将设计好的表自动生成对应的实体entity. 1.创建Maven项目,按需映入Maven包 ...

  7. spring+springmvc+hibernate 整合

    三大框架反反复复搭了很多次,虽然每次都能搭起来,但是效率不高.最近重新搭了一次,理顺了思路,整理了需要注意的地方,分享出来. 工具:Eclipse(jdk 1.7) spring和hibernate版 ...

  8. Spring MVC 学习笔记12 —— SpringMVC+Hibernate开发(1)依赖包搭建

    Spring MVC 学习笔记12 -- SpringMVC+Hibernate开发(1)依赖包搭建 用Hibernate帮助建立SpringMVC与数据库之间的联系,通过配置DAO层,Service ...

  9. spring+springmvc+hibernate 礼品管理系统

    spring+springmvc+hibernate template礼品管理系统 1.简单介绍 如标题所示,这篇文章简单写了一个基于spring+springmvc+hibernate templa ...

随机推荐

  1. margin 塌陷现象 与 注意事项

    1.在标准文档流中,块级标签之间竖直方向的margin会以大的为准,这就是margin的塌陷现象. 但是,脱标之后就不会出现margin的塌陷现象. 2.margin:0 auto;  会让盒子水平居 ...

  2. cocos2dx 3.2 的中国象棋游戏

    改编来源:http://cn.cocos2d-x.org/tutorial/lists?id=103 在cocos2dx官网看到了这么个教程,是cocos2dx 2.x版本的,于是用 cocos2dx ...

  3. MySQL的 explain 解析

    EXPLAIN 的每个输出行提供一个表的相关信息,并且每个行包括下面的列: 项 说明 id MySQL Query Optimizer 选定的执行计划中查询的序列号.表示查询中执行 select 子句 ...

  4. (简单) POJ 2492 A Bug's Life,二分染色。

    Description Background Professor Hopper is researching the sexual behavior of a rare species of bugs ...

  5. [iOS Animation]-CALayer 图层性能

    图层性能 要更快性能,也要做对正确的事情. ——Stephen R. Covey 在第14章『图像IO』讨论如何高效地载入和显示图像,通过视图来避免可能引起动画帧率下降的性能问题.在最后一章,我们将着 ...

  6. iOS 为label添加删除线

    UILabel *testLabel = [[ UILabel alloc] initWithFrame:CGRectMake(, , , )]; testLabel.numberOfLines = ...

  7. HTML学习(四)样式

    通过使用 HTML4.0,所有的格式化代码均可移出 HTML 文档,然后移入一个独立的样式表. 实例:例1:本例演示如何使用添加到 <head> 部分的样式信息对 HTML 进行格式化.& ...

  8. 关于eclipse创建Maven项目创建的问题

    1.问题: 为什么Maven Update Project JDK变回1.5 解释:官方文档 The Compiler Plugin is used to compile the sources of ...

  9. linux分区-df

    转自:http://baike.baidu.com/link?url=tyonI3NCB3F-ytIQz72PY-8uAaUQgfFFXbyKAea1e2NiB_t5AsE0MLOLc2LcqOiS ...

  10. C++指针与const

    在C++中,const修饰符一般用于修饰常量.常量在定义的时候必须初始化,而且值一旦定义之后就不能修改,这样就能保证常量的值在程序运行过程中不会发生变换. 1.指向const对象的指针 指向const ...