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 为数据存取提供了一个一致的框架,简化了底层数据库的访问方式。
核心
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 | 如果没有事务,则开始新的事务,事务已经存在,则加入当前事务。 | tx0 | tx0 |
REQUIRED | none | tx0 | |
SUPPORTS | 如果事务已经存在,则加入当前事务,如果没有事务,不会开始新事务。 | tx0 | tx0 |
SUPPORTS | none | none | |
MANDATORY | 必须由事务存在,并加入当前事务,否则抛出异常。 | tx0 | tx0 |
MANDATORY | none | exception | |
REQUIRES_NEW | 每次都开始一个全新事务。 | tx0 | tx0 |
REQUIRES_NEW | none | tx1 | |
NOT_SUPPORTED | 不会开始或加入事务。 | tx0 | none |
NOT_SUPPORTED | none | none | |
NESTED | 如果事务已经存在,则运行在一个嵌套的事务中。如果没有活动事务,则开始新的事务。 | tx0 | tx1 |
NESTED | none | tx0 | |
NEVER | 表示不会开始或加入事务,如果事务已存在,则抛出异常。 | tx0 | exception |
NEVER | none | none |
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
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() 在事务提交时会自动
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 的逻辑。
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");
}
编程式
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
try {
session.save(user);
tx.commit();
} catch (Exception e) {
tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
声明式
@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
<!-- 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>
属性 | 是否必须 | 默认值 | 说明 |
---|---|---|---|
name | yes | 定义方法名,可以使用 * 通配符。 | |
propagetion | no | REQUIRED | 定义传播行为。 |
isolation | no | READ_COMMITED | 定义隔离级别。 |
timeout | no | -1 | 定义超时时间,超过时间事务自动回滚。 |
read-only | no | false | 是否制度事务,把查询操作定义为只读事务可以减少开销,优化性能。 |
rollback-for | no | 定义会触发事务回滚的异常类型,用逗号分隔。 |
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);
}
}