频道栏目
首页 > 资讯 > Java > 正文

Spring 实践之Java与设计模式 -拾遗

16-03-14        来源:[db:作者]  
收藏   我要投稿

 

Spring 实践

标签: Java与设计模式


Junit集成

前面多次用到@RunWith@ContextConfiguration,在测试类添加这两个注解,程序就会自动加载Spring配置并初始化Spring容器,方便Junit与Spring集成测试.使用这个功能需要在pom.xml中添加如下依赖:

pom.xml

    org.springframework
    spring-test
    4.2.0.RELEASE
@RunWith@ContextConfiguration加载Spring容器
/**
 * Spring 整合 Junit
 * Created by jifang on 15/12/9.
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring/applicationContext.xml")
public class BeanTest {

    @Autowired
    private Bean bean;

    @Test
    public void testConstruct() {
        Car car = bean.getCar();
        System.out.println(car);
    }
}

Web集成

我们可以利用ServletContext容器保存数据的唯一性, 以及ServletContextListener会在容器初始化时只被调用一次的特性. 在web.xml中配置spring-web包下的ContextLoaderListener来加载Spring配置文件/初始化Spring容器:

pom.xml/spring-web

    org.springframework
    spring-web
    4.2.0.RELEASE
配置监听器(web.xml)

    org.springframework.web.context.ContextLoaderListener
加载Spring配置文件

    contextConfigLocation
    classpath:spring/applicationContext.xml

附: 完整web.xml文件git地址.

测试Servlet
@WebServlet(urlPatterns = "/servlet")
public class Servlet extends HttpServlet {

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
        Bean bean = context.getBean("bean", Bean.class);
        Car car = bean.getCar();
        System.out.println(car);
    }
}

在应用中,普通的JavaBean由Spring管理,可以使用@Autowired自动注入.但FilterServlet例外,他们都是由Servlet容器管理,因此其属性不能用Spring注入,所以在实际项目中,一般都不会直接使用Servlet,而是用SpringMVC/WebX/Struts2之类的MVC框架以简化开发,后面会有专门的博客介绍这类框架,在此就不做深入介绍了.

注: 运行Servlet不要忘记添加servlet-api依赖:

    javax.servlet
    javax.servlet-api
    3.1.0

文件加载

1. 引入properties

可以将需要经常修改的属性参数值放到properties文件, 并在Spring文件中引入.

db.properties
## Data Source
mysql.driver.class=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://host:port/db?useUnicode=true&characterEncoding=UTF8
mysql.user=user
mysql.password=password

注意: value后不能有空格.

1.1 property-placeholde引入

在Spring配置文件中使用标签引入properties文件,XML文件可通过${key}引用, Java可通过@Value("${key}")引用:

XML



    
    
    
    
Java
@Component
public class AccessLog {

    @Value("${mysql.url}")
    private String value;

    // ...
}

1.2 PropertiesFactoryBean引入

Spring提供了org.springframework.beans.factory.config.PropertiesFactoryBean,以加载properties文件, 方便在JavaBean中注入properties属性值.

XML

    
        
            classpath*:common.properties
        
    
Java
@Controller
public class Bean {

    @Value("#{commonProperties['bean.properties.name']}")
    private String name;

    // ...
}

2. import其他Spring配置

如果Spring的配置项过多,可以按模块将配置划分多个配置文件(-datasource.xml/-dubbo-provider.xml/-bean.xml), 并由主配置applicationContext.xml文件引用他们,此时可用标签引入:




事务管理

Spring事务管理高层抽象主要由PlatformTransactionManager/TransactionDefinition/TransactionStatus三个接口提供支持:


PlatformTransactionManager(事务管理器)

PlatformTransactionManager的主要功能是事务管理,Spring为不同的持久层框架提供了不同的PlatformTransactionManager实现:

事务 描述
DataSourceTransactionManager JDBCTemplate/MyBatis/iBatis持久化使用
HibernateTransactionManager Hibernate持久化使用
JpaTransactionManager JPA持久化使用
JdoTransactionManager JDO持久化使用
JtaTransactionManager JTA实现管理事务,一个事务跨越多个资源时使用

因此使用Spring管理事务,需要为不同持久层配置不同事务管理器实现.


TransactionDefinition(事务定义信息)

TransactionDefinition提供了对事务的相关配置, 如事务隔离级别/传播行为/只读/超时等:

隔离级别(isolation)
为解决事务并发引起的问题(脏读/幻读/不可重复读),引入四个隔离级别:
隔离级别 描述
DEFAULT 使用数据库默认的隔离级别
READ_UNCOMMITED 读未提交
READ_COMMITTED 读已提交(Oracle默认)
REPEATABLE_READ 可重复读(MySQL默认)
SERIALIZABLE 串行化
传播行为(propagation)
传播行为不是数据库的特性, 而是为了在业务层解决两个事务相互调用的问题:
传播类型 描述
REQUIRED 支持当前事务,如果不存在就新建一个(默认)
SUPPORTS 支持当前事务,如果不存在就不使用事务
MANDATORY 支持当前事务,如果不存在则抛出异常
REQUIRES_NEW 如果有事务存在,则挂起当前事务新建一个
NOT_SUPPORTED 以非事务方式运行,如果有事务存在则挂起当前事务
NEVER 以非事务方式运行,如果有事务存在则抛出异常
NESTED 如果当前事务存在,则嵌套事务执行(只对DataSourceTransactionManager有效)
超时时间(timeout) 只读(read-only)
只读事务, 不能执行INSERT/UPDATE/DELETE操作.

TransactionStatus(事务状态信息)

获得事务执行过程中某一个时间点状态.


声明式事务管理

Spring声明式事务管理:无需要修改原来代码,只需要为Spring添加配置(XML/Annotation),就可以为目标代码添加事务管理功能.

需求: 转账案例(使用MyBatis).

AccountDAO




    
        UPDATE account
        SET money = money + #{0}
        WHERE name = #{1};
    

    
        UPDATE account
        SET money = money - #{0}
        WHERE name = #{1};
    
/**
 * @author jifang
 * @since 16/3/3 上午11:16.
 */
public interface AccountDAO {

    void transferIn(Double inMoney, String name);

    void transferOut(Double outMoney, String name);
}
Service
public interface AccountService {

    void transfer(String from, String to, Double money);
}
@Service("service")
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDAO dao;

    @Override
    public void transfer(String from, String to, Double money) {
        dao.transferOut(money, from);

        // 此处抛出异常, 没有事务将导致数据不一致
        int a = 1 / 0;

        dao.transferIn(money, to);
    }
}
mybatis-configuration.xml/applicationContext-datasource.xml



    
    
        
    


    

    
    
        
        
        
        
        
        
        
        
        
            
                com.mysql.jdbc.jdbc2.optional.MysqlDataSource
                true
                250
                2048
            
        
    

    
        
    

    
    
        
        
    

    
    
        
        
    

applicationContext.xml(没有事务)



    

    
Client
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring/applicationContext.xml")
public class SpringClient {

    @Autowired
    private AccountService service;

    @Test
    public void client() {
        service.transfer("from", "to", 10D);
    }
}

执行以上代码, 将会导致数据前后不一致.


XML配置

Spring事务管理依赖AOP,而AOP需要定义切面(Advice+PointCut),在Spring内部提供了事务管理的默认Adviceorg.springframework.transaction.interceptor.TransactionInterceptor,并且Spring为了简化事务配置,引入tx标签:

引入tx的命名空间,配置Advice:

    



    
    
        
        
    
配置切面
Spring事务管理Advice基于SpringAOP,因此使用配置:

    

注解配置

使用注解配置事务, 可以省略切点的定义(因为注解放置位置就已经确定了PointCut的置), 只需配置Advice即可:

激活注解事务管理功能

    


在需要管理事务的业务类/业务方法上添加@Transactional注解
@Override
@Transactional(transactionManager = "transactionManger", readOnly = true)
public void transfer(String from, String to, Double money) {
    // ...
}

可以在注解@Transactional中配置与XML相同的事务属性(isolation/propagation等).


实践

更推荐使用XML方式来配置事务,实际开发时一般将事务集中配置管理. 另外, 事务的isolation/propagation一般默认的策略就已经足够, 反而我们需要配置是否只读(比如MySQL主从备份时,主库一般提供读写操作,而从库只提供读操作), 因此其配置可以如下:



    



    
    
        
        
        
        
        
    




    
    
    
    
主从

    
    
        
    



    
    
        
        
        
        
        
    

 
 
相关TAG标签
上一篇:设计原则的相关介绍
下一篇:使用category 为 AppDelegate 的代码分层
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站