Skip to content

spring framework

来源 spring framework 是 java 平台上一个开源应用框架,它有深厚的历史根基。 spring framework 最初起源于 Rod Jhonjson 于 2002 年所注的《expert one on one: J2EE Design and Development》一书中的基础性代码。在该书中,Rod Jhonson 阐述了大量 Spring 框架的涉及思想,并对 J2EE 平台进行了深层次的思考,指出 EJB 存在的结构臃肿的问题。他认为采用一种轻量级的、基于 JavaBean 的框架就可以满足大多数程序开发的需要。 2003 年,Rod Jhonson 公开了所述框架的源代码,这个框架逐渐演变成了我们所熟知 Spring 框架。2004年3月发布的1.0版本是 spring framework 的第一个具有里程碑意义的版本,这个版本发布之后,spring framework 框架在 java 社区中变得的异常流程。现在,spring framework 已经获得了广泛的欢迎,并对许多公司人为是具有战略意义的重要框架。

优势

  • spring framework 能有效地组织中间层对象。
  • spring framework 实现了真正意义上地面向接口编程,可实现组件之间的高度解耦。
  • spring framework 所秉承的设计思想就是让使用 spring framework 创建的那些应用都京可能少的依赖于它的 APIs。
  • 使用 spring framework 构建的应用程序易于进行单元测试。
  • spring framework 提高了代码的可重用性,它京可能避免在程序中硬编码。
  • spring framework 为数据存取提供了一个一致的框架,简化了底层数据库的访问方式。

Spring Framework 简介

核心

org.springframework.spring-context
├── org.springframework.spring-aop
├── org.springframework.spring-beans
├── org.springframework.spring-core
└── org.springframework.spring-expression

application-

IoC DI IoC, Inversion of Control 控制反转,DI, Dependency Injection 依赖注入 实际上是同一个概念。 在传统的程序涉及中,通常由调用者来创建被调用者的实例,而在依赖注入或控制反转的定义中,调用者不负责被调用者的实例创建工作,该工作由 spring framework 中的容器负责,它通过开发者的配置来判断实例的类型,创建后再注入调用者。由于 spring framework 中的容器负责创建被调用者实例,实例创建后由负责将实例注入调用者,因此称为依赖注入;而被调用者的实例创建工作不再有调用者来创建而是由 spring framework 来创建,因此成为控制反转。

AOP AOP, Aspect-Oriented Programming,面向切面编程,他是 OOP, Object Oriented Programming,面向对象编程的补充和完善。 在 OOP 中通过封装、继承和多态等概念建立起了对各对象之间的层次结构,但当需要为这些分散的对象加入一些公共行为时,OOP 就显得力不从心了。换句话说就是,OOP 擅长的时定义从上到下的关系,但是并不适合定义从左到右的关系。 AOP 的出现恰好解决了 OOP 技术的这种局限性。AOP 利用了一种称为“横切”的技术,将封装好的对象剖开,找出千种对各对象产生影响的公共行为,并将其封装为一个可重用的模块,这个模块被命名为 Aspect “切面”。切面将那些于业务无关,却被业务共同调用的逻辑提取并封装起来,减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。

org.springframework:spring-tx

ACID

  • Atomic,一个事务中的所有操作是一个不可分割的整体,要么一起执行成功,要么一起执行失败,不可能停滞在中间某个环节。
  • Consistency,事务开始之前和结束之后,数据库的完整性约束没有被破坏。
  • Isolation,并发的事务时相互隔离的,如果有两个事务运行在相同的时间内执行相同的功能,事务的隔离性将确保每一事务在系统中认为只有该事务在使用系统。
  • Durable,事务一旦提交,数据将会被持久化。

本地事务 Local 只使用一个事务

全局事务 Global 可跨越多个数据库

Propagation 被定义了声明式事务的方法,有权知道当前是否有事务开启,并由权决定是否加入当前事务。某个方法增加到按当前事务,相当于事务被“传播”。传播的实质,也就是对事务的边界,在程序运行期间“动态地”控制。

设定值说明当前事务状态被声明的方法
REQUIRED如果没有事务,则开始新的事务,事务已经存在,则加入当前事务。tx0tx0
REQUIREDnonetx0
SUPPORTS如果事务已经存在,则加入当前事务,如果没有事务,不会开始新事务。tx0tx0
SUPPORTSnonenone
MANDATORY必须由事务存在,并加入当前事务,否则抛出异常。tx0tx0
MANDATORYnoneexception
REQUIRES_NEW每次都开始一个全新事务。tx0tx0
REQUIRES_NEWnonetx1
NOT_SUPPORTED不会开始或加入事务。tx0none
NOT_SUPPORTEDnonenone
NESTED如果事务已经存在,则运行在一个嵌套的事务中。如果没有活动事务,则开始新的事务。tx0tx1
NESTEDnonetx0
NEVER表示不会开始或加入事务,如果事务已存在,则抛出异常。tx0exception
NEVERnonenone

Isolation

级别说明
READ_UNCOMMITTED表示会读取到未提交的数据
READ_COMMITED表示读取到的时已经提交的数据
REPEATABLE_READ表示多次读取的数据时相同的
SERIALIZABLE表示就好像没有别的用户在修改数据库中的数据一样

concurrent & serialize

dirty read

unrepeatable readphantom read

springframework-tx
├── junit
│   └──hamcrest-core
├── commons-logging
├── log4j
├── hibernate-core
│   ├── jboss-logging
│   ├── jboss-logging-annotations
│   ├── jboss-transaction-api
│   ├── dom4j
│   │   └── xml-apis
│   ├── hibernate-commons-annotations
│   ├── hibernate-jpa-api
│   ├── javassist
│   ├── antlr
│   └── jandex
└── hibernate-c3p0
    ├── c3p0
    │   └── mchange-commons-java
    ├── ojdbc
    ├── h2
    ├── spring-context
    │   ├── spring-aop
    │   │   └── aopalliance
    │   ├── spring-beans
    │   ├── spring-core
    │   └── spring-expression
    ├── commons-dbcp
    │   └── commons-pool
    └── spring-orm
        │   └── spring-jpa
        └── spring-tx

HibernateTransactionManager

mermaid
sequenceDiagram
    HibernateTransactionManager->>SessionFactory: openSession()
    SessionFactory->>DataSource: getConntection()
    DataSource->Database: get session connection
    DataSource->>SessionFactory: connection
    SessionFactory->>HibernateTransactionManager: session

BasicDataSource commons-dbcp 是 apache 下的开源数据库连接池 jar 包

LocalSessionFactoryBean spring-orm 是负责对 ORM 框架的支持,如 jpa,hibernate

  • SessionFactory 中 getCuttentSession() 和 openSession() 区别
    • openSession() 适合编程事务,getCurrentSession() 适合声明事务。
    • openSession() 返回新的 session, getCurrentSession() 返回当前线程绑定的 session 如果没有则抛出错误 No Session found for current thread
    • openSession() 需要手动调用 close() 进行释放 getCurrentSession() 在事务提交时会自动
java
public class LocalSessionFactoryBean extends HibernateExceptionTranslator implements FactoryBean<SesstionFactory>, ResourceLoaderAware, InitializingBean, DisposeableBean {
    @Nullable
    private DataSource dataSource;
    @Nullable
    private Properties hibernateProperties;
    // 实体所在包路径
    @Nullable
    private String[] packagesToScan;
    @Nullable
    public SessionFactory getObject() {
        return this.sessionFactory;
    }
}

FactoryBeanorg.springframework.bean.factory.FactoryBean springframework 中用户可以通过实现该工厂接口定制实例化 bean 的逻辑。

java
public interface FactoryBean<T> {  
    String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";  
	// 返回由 FactoryBean 创建的 Bean 的实例,如果 isSingleton() 方法返回 ture,是单例的实例,该实例将放入 spring 的缓冲池。
    @Nullable  
    T getObject() throws Exception;  
	// 返回 FactoryBean 创建的 Bean 类型
    @Nullable  
    Class<?> getObjectType();  
	// 确定由 FactoryBean 创建的 Bean 作用域是 singleton 还是 prototype
    default boolean isSingleton() {  
        return true;  
    }  
}

public class Car {
	private String name;
	public Car(){}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

public class CarFactory implements FactoryBean<Car> {
	@Override
	public Car getObject() throw Exception {
		Car car = new Car();
		car.setName("myCar");
		return car;
	}
	@Override
	public Class getObjectType() {
		return Car.class;
	}
	@Override
	public boolean isSingleton() {
		return true;
	}
}

// test
// <bean id="car" class="com.rz.spring.CarFactoryBean"/>
@Test 
public void test() {
	ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:application-context.xml");
	Car car = (Car)ac.getBean("car");
}

编程式

java
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
try {
    session.save(user);
    tx.commit();
} catch (Exception e) {
	tx.rollback();
	e.printStackTrace();
} finally {
	session.close();
}

声明式

java

@Configuration
@PropertySource("classpath:application.properties")
@ComponentScan
@EnableAspectJAutoProxy
// 启用基于注解的声明式事务
@EnableTransactionManagement
public class AppConfig {
	private static final Logger LOGGER = LOGGER.getLogger(AppConfig.class);
	@Autowired
	private Environment env;
	
	// DataSource
	@Bean
	public DataSource getDataSource() {
		LOGGER.debug("getDataSource() run...");
		BasicDataSource basicDataSource = new BasicDataSource();
		// basicDataSource.setDriverClassName("oracle.jdbc.OracleDriver");
		// basicDataSource.setUrl("jdbc:oracle:thin:@localhost:1521:XE");
		// basicDataSource.setUsername("username");
		// basicDataSource.setPassword("password");
		
		basicDataSource.setDriverClassName(env.getProperty("driver"));
		basicDataSource.setUrl(env.getProperty("url"));
		basicDataSource.setUsername(env.getProperty("username"));
		basicDataSource.setPassword(env.getProperty("password"));
		# basicDataSource.setInitialSize(10);
		return basicDataSource;
	}
	// SessionFactory 的封装类 FactoryBean
	// 当被使用时,对外所暴露的时 org.hibernate.SessionFactory
	@Bean
	public LocalSessionFactoryBean getLocalSessionFactoryBean(DataSource dataSource) {
		LOGGER.debug("getLocalSessionFactoryBean() run...");
		LocalSessionFactoryBean localSessionFactoryeBean = new LocalSessionFactoryBean();
		// localsessionFactoryBean.setDataSource(this.getDataSource());
		localsessionFactoryBean.setDataSource(dataSource);
		Properties props = new Properties();
		props.setProperty("hibernate.dialect", "org.hibernate.Oracle10gDialect");
		localSessionFactoryBean.setHibernateProperties(props);
		return localSessionFactoryBean;
	}
	@Bean
	@Autowired
	public HibernateTransactionManager getHibernateTransactionManager(SessionFactory sessionFactory) {
		LOGGER.debug("getHibernateTransactionManager() run...");
		HibernateTransactionManager htm = new HibernateTransactionManager(sessionFactory);
		return htm;
	}
}

@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITED) 位置:方法名或类名上方 作用:该方法具有声明式事务,或该类的所有方法具有声明式事务。 效果:

  • 在方法运行前,自动开启事务将 session 存入线程。
  • 在方法运行成功后,自动提交事务。
  • 在方法异常后,自动回滚事务。 细节:
  • 方法几倍的定义,可以覆盖类级别的定义。
  • 其中可以自定义传播行为,隔离级别,只读等属性。

xml

xml
<!-- spring-framework-reference -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:aop="http://www.springframework.org/schema/aop"
	   xmlns:tx="http://www.springframework.org/schema/tx"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
	   http://www.springframework.org/schema/beans/spring-beans.xsd
	   http://www.springframework.org/schema/tx
	   http://www.springframework.org/schema/tx/spring-tx.xsd
	   http://www.springframework.org/schema/aop
	   http://www.springframework.org/schema/aop/spring-aop.xsd">

	<!-- c3p0 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
		<property name="url" value="jdbc:oracle:thin:@localhost:1521:XE"/>
		<property name="username" value="username"/>
		<property name="password" value="password"/>
	</bean>
	<!-- BasicDataSource -->
	<bean id="dataSource" class="org.apache.common.dbcp.BasicDataSource">
		<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
		<property name="url" value="jdbc:oracle:thin:@localhost:1521:XE"/>
		<property name="username" value="username"/>
		<property name="password" value="password"/>
	</bean>
	<!-- SessionFactory -->
	<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
		<property value="dataSource" ref="dataSource"/>
		<property value="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
				<prop key="hibernate.show_sql">true</prop>
			</props>
		</property>
		<!-- 注册 ORM -->
		<property name="packagesToScan" value="com.tz.spring.entity"/>
	</bean>
	<!-- TransactionManager -->
	<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
		<property name="sessionFactory" ref="sessionFactory"/>
	</bean>

	<bean id="accountDao" class="com.tz.spring.dao.AccountDaoImpl">
		<property name="sessionFactory" ref="sessionFactory"/>
	</bean>
	<bean id="accountService" class="com.tz.spring.service.AccountServiceImpl">
		<property name="accountDao" ref="accountDao"/>
	</bean>
	<!-- txAdvice -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<!-- 定义切面涉及的方法,怎么来参与声明式事务 -->
			<tx:method name="*" isolation="READ_COMMITTED" propagation="REQUIRED"/>
		</tx:attributes>
	</tx:advice>
	<!-- 配置 声明式事务切面 -->
	<aop:config>
		<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.tz.spring.service.*ServiceImpl.*(..))"/>
	</aop:config>
</beans>

tx:method/

属性是否必须默认值说明
nameyes定义方法名,可以使用 * 通配符。
propagetionnoREQUIRED定义传播行为。
isolationnoREAD_COMMITED定义隔离级别。
timeoutno-1定义超时时间,超过时间事务自动回滚。
read-onlynofalse是否制度事务,把查询操作定义为只读事务可以减少开销,优化性能。
rollback-forno定义会触发事务回滚的异常类型,用逗号分隔。
java
public class AccountDaoImpl implements AccountDao {
	private SessionFactory sessionFactory;
	public viod setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}
	private Session getSession() {
		return sessionFactory.getCurrentSession();
	}
	@Override
	public Account get(int id) {
		return (Account) getSession().get(Account.class, id);
	}
	@Override
	public void update (Account acc) {
		getSession().update(acc);
	}
}