spring @Transactional注解参数详解

 spring  spring @Transactional注解参数详解已关闭评论
2月 072018
 

写的比较清楚,来自:https://www.cnblogs.com/caoyc/p/5632963.html

事物注解方式: @Transactional

当标于类前时, 标示类中所有方法都进行事物处理 , 例子:

1 @Transactional public class TestServiceBean implements TestService {}

当类中某些方法不需要事物时:

@Transactional  
public class TestServiceBean implements TestService { 
    private TestDao dao; 
    public void setDao(TestDao dao) { 
        this.dao = dao; 
    } 
    @Transactional(propagation =Propagation.NOT_SUPPORTED)
    public List getAll() { 
        return null; 
    } 
}

事物传播行为介绍: 

@Transactional(propagation=Propagation.REQUIRED) :如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
@Transactional(propagation=Propagation.NOT_SUPPORTED) :容器不为这个方法开启事务
@Transactional(propagation=Propagation.REQUIRES_NEW) :不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.MANDATORY) :必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.NEVER) :必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS) :如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.

 

事物超时设置:

@Transactional(timeout=30) //默认是30秒

 

事务隔离级别:

@Transactional(isolation = Isolation.READ_UNCOMMITTED):读取未提交数据(会出现脏读, 不可重复读) 基本不使用
@Transactional(isolation = Isolation.READ_COMMITTED):读取已提交数据(会出现不可重复读和幻读)
@Transactional(isolation = Isolation.REPEATABLE_READ):可重复读(会出现幻读)
@Transactional(isolation = Isolation.SERIALIZABLE):串行化

MYSQL: 默认为REPEATABLE_READ级别
SQLSERVER: 默认为READ_COMMITTED

脏读 : 一个事务读取到另一事务未提交的更新数据
不可重复读 : 在同一事务中, 多次读取同一数据返回的结果有所不同, 换句话说, 
后续读取可以读到另一事务已提交的更新数据. 相反, “可重复读”在同一事务中多次
读取数据时, 能够保证所读数据一样, 也就是后续读取不能读到另一事务已提交的更新数据
幻读 : 一个事务读到另一个事务已提交的insert数据

 

@Transactional注解中常用参数说明

参数名称

功能描述

readOnly

该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。例如:@Transactional(readOnly=true)

rollbackFor

该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如:

指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)

指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})

rollbackForClassName

该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。例如:

指定单一异常类名称:@Transactional(rollbackForClassName=”RuntimeException”)

指定多个异常类名称:@Transactional(rollbackForClassName={“RuntimeException”,”Exception”})

noRollbackFor

该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。例如:

指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class)

指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class})

noRollbackForClassName

该属性用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。例如:

指定单一异常类名称:@Transactional(noRollbackForClassName=”RuntimeException”)

指定多个异常类名称:

@Transactional(noRollbackForClassName={“RuntimeException”,”Exception”})

propagation

该属性用于设置事务的传播行为,具体取值可参考表6-7。

例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)

isolation

该属性用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置

timeout

该属性用于设置事务的超时秒数,默认值为-1表示永不超时

 

 

注意的几点:


1、@Transactional 只能被应用到public方法上, 对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能.
2、用 spring 事务管理器,由spring来负责数据库的打开,提交,回滚.默认遇到运行期例外(throw new RuntimeException(“注释”);)会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到需要捕获的例外(throw new Exception(“注释”);)不会回滚,即遇到受检查的例外(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或说受检查异常)时,需我们指定方式来让事务回滚要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其它异常}) .如果让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)
如下:

复制代码
1 @Transactional(rollbackFor=Exception.class) //指定回滚,遇到异常Exception时回滚 2 public void methodName() { 3 throw new Exception("注释"); 4 } 5 @Transactional(noRollbackFor=Exception.class)//指定不回滚,遇到运行期例外(throw new RuntimeException("注释");)会回滚 6 public ItimDaoImpl getItemDaoImpl() { 7 throw new RuntimeException("注释"); 8 }
复制代码

3、@Transactional 注解应该只被应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的方法将不会展示已配置的事务设置。


4、@Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上。然而,请注意仅仅 @Transactional 注解的出现不足于开启事务行为,它仅仅 是一种元数据,能够被可以识别 @Transactional 注解和上述的配置适当的具有事务行为的beans所使用。上面的例子中,其实正是 元素的出现 开启 了事务行为。


5、Spring团队的建议是你在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。你当然可以在接口上使用 @Transactional 注解,但是这将只能当你设置了基于接口的代理时它才生效。因为注解是不能继承的,这就意味着如果你正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装(将被确认为严重的)。因此,请接受Spring团队的建议并且在具体的类上使用 @Transactional 注解。

Spring注解@Component、@Repository、@Service、@Controller @Resource、@Autowired、@Qualifier 解析

 java, spring  Spring注解@Component、@Repository、@Service、@Controller @Resource、@Autowired、@Qualifier 解析已关闭评论
1月 102017
 

这篇文章解释的很清楚http://www.ulewo.com/user/10001/blog/273,记录下:

我们在使用spring的时候经常会用到这些注解,那么这些注解到底有什么区别呢。我们先来看代码

同样分三层来看:

Action 层:

package com.ulewo.ioc; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @Controller public class IocAction {
    @Autowired     private IocService service;
    
    public void add(){
        service.add();
    }
}

service层:(service就直接定义类了,没有定义接口,定义接口也是一样的)

package com.ulewo.ioc; import javax.annotation.Resource; import org.springframework.stereotype.Service; @Service public class IocService {
    @Resource     private IIocDao iocDao;
    public void add(){
        iocDao.add();
    }
}

Dao层

先定义一个接口

package com.ulewo.ioc; public interface IIocDao {
    public void add();
}

然后实现类:

package com.ulewo.ioc; import org.springframework.stereotype.Repository; @Repository public class IocDao implements IIocDao{
    public void add(){
        System.out.println("调用了dao");
    }
}

然后spring的配置,这个配置就很简单了,因为是基于注解的,我们不需要再xml中来定义很多

applicationContext.xml

<?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:jee="http://www.springframework.org/schema/jee"    xmlns:tx="http://www.springframework.org/schema/tx"    xmlns:context="http://www.springframework.org/schema/context"    xmlns:aop="http://www.springframework.org/schema/aop"    xmlns:task="http://www.springframework.org/schema/task"    xsi:schemaLocation="http://www.springframework.org/schema/beans 
                       http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
                       http://www.springframework.org/schema/tx 
                       http://www.springframework.org/schema/tx/spring-tx-3.2.xsd 
                       http://www.springframework.org/schema/jee 
                       http://www.springframework.org/schema/jee/spring-jee-3.2.xsd 
                       http://www.springframework.org/schema/context 
                       http://www.springframework.org/schema/context/spring-context-3.2.xsd
                       http://www.springframework.org/schema/aop 
                       http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
                       http://www.springframework.org/schema/task 
                       http://www.springframework.org/schema/task/spring-task-3.2.xsd">     <context:annotation-config />         <context:component-scan base-package="com.ulewo.ioc" >     </context:component-scan>   </beans>

让spring自动扫描包就行了。

然后是我们的测试类:

IocTest:

package com.ulewo.ioc; import junit.framework.TestCase; import org.springframework.beans.factory.BeanFactory; import org.springframework.context.support.ClassPathXmlApplicationContext; public class IocTest extends TestCase{
    
    public void testIoc(){
        BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml");
        IocAction action = factory.getBean("iocAction", IocAction.class);
        action.add();
    }
}

运行后,我们会发现 控制台打印:调用了dao

@Component、@Repository、@Service、@Controller @Resource、@Autowired、@Qualifier 

这几个基本都用到了 除了 @Component  @Qualifier

我们观察会发现@Repository、@Service、@Controller 这几个是一个类型,其实@Component 跟他们也是一个类型的

Spring 2.5 中除了提供 @Component 注释外,还定义了几个拥有特殊语义的注释,它们分别是:@Repository、@Service和 @Controller 其实这三个跟@Component 功能是等效的

@Service用于标注业务层组件(我们通常定义的service层就用这个)

@Controller用于标注控制层组件(如struts中的action)

@Repository用于标注数据访问组件,即DAO组件

@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。

这几个注解是当你需要定义某个类为一个bean,则在这个类的类名前一行使用@Service(“XXX”),就相当于讲这个类定义为一个bean,bean名称为XXX; 这几个是基于类的,我们可以定义名称,也可以不定义,不定义会默认以类名为bean的名称(类首字母小写)。

然后我们在看后面的几个注解

@Resource、@Autowired、@Qualifier

当需要在某个类中定义一个属性,并且该属性是一个已存在的bean,要为该属性赋值我们就用着三个。我们看上面的代码可以看到这三个都是定义在一个属性上的,比如

@Resource private IIocDao iocDao;
@Autowired private IocService service;

那这几个到底有什么区别呢?

我们先看@Resource,它是javax.annotation.Resource; 这个包中,也就是说是javaEE中的,并不是spring中的

而且@Resource(“xxx”) 是可以定义bean名称的,就是说我这个属性要用那个bean来赋值。

@Autowired,它是org.springframework.beans.factory.annotation.Autowired 是这个包中,它是spring的包。

而且它没有@Autowired(“xxx”),那我要为这个bean定义名称怎么办这个时候可以用@Qualifier(“xxx”) 这个也是spring中的。这个xxx定义bean名称有什么用呢?我们回头看下刚才的代码。

在IIocDao 这个接口中,我们定义的实现类IocDao 只有一个,好那么我们再定义一个实现类:

package com.ulewo.ioc; import org.springframework.stereotype.Repository; @Repository public class IocDao2 implements IIocDao{
    public void add(){
        System.out.println("调用了dao2");
    }
}

其他不变,我们再运行:testIoc(),控制台打印出 调用了dao,所以在service层中

@Resource
private IIocDao iocDao;

这个iocDao 注入的是IocDao 这个实现。奇怪了,它怎么知道我要调用哪个实现呢?

好我们修改一下,把 private IIocDao iocDao;改一下,改成 private IIocDao iocDaox 把属性名改一下,再运行,会报错:

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.ulewo.ioc.IIocDao] is defined: expected single matching bean but found 2: iocDao,iocDao2

错误很明显啊,有两个bean iocDao和iocDao2,但是我们的是iocDaox所以找不到了。所以可以看出来在用 @Repository注解来生成bean的时候,如果没有定义名称那么就会根据类名来生成。所以我们要调用第二个实现的时候可以 定义为private IIocDao iocDao2 。我们再运行:调用了dao2,所以可以根据属性名来区分,到底注入那个bean。但是有的人说,我不想定义bean名称跟类实现一样,我要定义其他的,那怎么玩呢,方法有2种:

第一种:我们在生成bean的时候就给bean定义个名称 

@Repository(“myIocDao”)
public class IocDao implements IIocDao{
    public void add(){
        System.out.println(“调用了dao”);
    }
}

当然@Service是一样的,这样就把这个实现定义为myIocDao了,而不是默认的类名 iocDao。

那么我们在使用这个bean的时候就要这么定义了:

@Resource
private IIocDao myIocDao;

运行 输出:调用了dao

如果你这里不是用的 myIocDao,你又多加了一个x,成了myIocDaox,你运行会是这样的:

 org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.ulewo.ioc.IIocDao] is defined: expected single matching bean but found 2: myIocDao,iocDao2

所以,要自定义bean的名称可以在类注解的时候指定。

第二种:在注入bean的时候指定名称:

先看@Resource

我们这么定义下:

@Resource(name=”iocDao”)
private IIocDao xx;

注意:

@Repository
public class IocDao implements IIocDao{
    public void add(){
        System.out.println(“调用了dao”);
    }
}

这里还是用会默认的,也就是这个实现对应的是 iocDao这bean。如果你要为这个类指定别名bean,@Repository(“myIocDao”),那@Resource(name=”myIocDao”) 就要这么写了。就是这里的name要跟实现类对应的bean名称保持一致。private IIocDao xx; 这个属性名就随便写了。

运行:调用了dao

如果用Autowired就要这么写了

@Autowired
@Qualifier(“iocDao”)
private IIocDao xx;

因为Autowired 不能像Resource 那样带个参数指定一个name,就要用Qualifier来指定了。

而且还可以这么用

@Resource
@Qualifier(“iocDao”)
private IIocDao xx;

等同于

@Resource(name=”iocDao”)
private IIocDao xx;

记住一点:@Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,如果发现找到多个bean,则,又按照byName方式比对,如果还有多个,则报出异常 而@Resource默认按 byName自动注入罢了。其实spring注解,最常用的还是根据名称,根据类型啊,构造方法啊,用的非常少。所以在多个实现的时候我们定义好bean的名称就行,就不会错乱。

说了这么多,不知道对着几个注解是不是了解多一点了呢,貌似这些注解 没有根本上的区别,就看你习惯怎么用了。拿代码多跑几次,然后根据自己的想法改改,你就明白这几个注解的用处啦。

ClassPathXmlApplicationContext的启动

 java, spring  ClassPathXmlApplicationContext的启动已关闭评论
1月 102017
 

好文章,分享下,转自:http://www.cnblogs.com/wade-luffy/p/6072460.html

Spring将ApplicationContext启动的全过程,refresh函数中包含了几乎ApplicationContext中提供的全部功能,而且此函数中逻辑非常清晰明了,很容易分析对应的层次及逻辑。:

ApplicationContext ac = new ClassPathXmlApplicationContext(“application.xml”);

复制代码
 @Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { //准备刷新的上下文环境,例如对系统属性或者环境变量进行准备及验证。  prepareRefresh(); //初始化BeanFactory,并进行XML文件读取, //这一步之后,ClassPathXmlApplicationContext实际上就已经包含了BeanFactory所提供的功能,也就是可以进行Bean的提取等基础操作了。 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); //对BeanFactory进行各种功能填充,@Qualifier与@Autowired这两个注解正是在这一步骤中增加的支持。  //设置@Autowired和 @Qualifier注解解析器QualifierAnnotationAutowireCandidateResolver
       prepareBeanFactory(beanFactory); try { //子类覆盖方法做额外的处理,提供了一个空的函数实现postProcessBeanFactory来方便程序员在业务上做进一步扩展。  postProcessBeanFactory(beanFactory); //激活各种BeanFactory处理器  invokeBeanFactoryPostProcessors(beanFactory); //注册拦截Bean创建的Bean处理器,这里只是注册,真正的调用是在getBean时候  registerBeanPostProcessors(beanFactory); //为上下文初始化Message源,即不同语言的消息体进行国际化处理  initMessageSource(); //初始化应用消息广播器,并放入“applicationEventMulticaster”bean中  initApplicationEventMulticaster(); //留给子类来初始化其它的Bean  onRefresh(); //在所有注册的bean中查找Listener bean,注册到消息广播器中  registerListeners(); //初始化剩下的单实例(非惰性的)  finishBeanFactoryInitialization(beanFactory); //完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人  finishRefresh();
            } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore...  resetCommonCaches();
            }
        }
    }
复制代码

环境准备

复制代码
    protected void prepareRefresh() { this.startupDate = System.currentTimeMillis(); this.closed.set(false); this.active.set(true); if (logger.isInfoEnabled()) {
            logger.info("Refreshing " + this);
        } //留给子类覆盖  initPropertySources();
        //验证需要的属性文件是否都已经放入环境中  getEnvironment().validateRequiredProperties(); //to be published once the multicaster is available... this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
    }
复制代码

假如现在有这样一个需求,工程在运行过程中用到的某个设置(例如classpath)是从系统环境变量中取得的,而如果用户没有在系统环境变量中配置这个参数,那么工程可能不会工作。这一要求可能会有各种各样的解决办法,当然,在Spring中可以这样做,你可以直接修改Spring的源码,例如修改ClassPathXmlApplicationContext。当然,最好的办法还是对源码进行扩展,我们可以自定义类:

复制代码
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext{ public MyClassPathXmlApplicationContext(String... configLocations ){ super(configLocations);
    } protected void initPropertySources() { //添加验证要求 getEnvironment().setRequiredProperties("classpath");
    }
}
复制代码

我们自定义了继承自ClassPathXmlApplicationContext的MyClassPathXmlApplicationContext,并重写了initPropertySources方法,在方法中添加了我们的个性化需求,那么在验证的时候也就是程序走到getEnvironment().validateRequiredProperties()代码的时候,如果系统并没有检测到对应classpath的环境变量,那么将抛出异常。

加载BeanFactory

复制代码
    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { //初始化BeanFactory,并进行XML文件读取,并将得到的BeanFacotry记录在当前实体的属性中  refreshBeanFactory(); //返回当前实体的beanFactory属性 ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) {
            logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
        } return beanFactory;
    }

    AbstractRefreshableApplicationContext.java
    @Override protected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) {
            destroyBeans();
            closeBeanFactory();
        } try { //在介绍BeanFactory的时候,声明方式为:BeanFactory bf = new XmlBeanFactory("beanFactoryTest.xml"),
       //其中的XmlBeanFactory继承自DefaultListableBeanFactory,并提供了XmlBeanDefinitionReader类型的reader属性,
       //也就是说DefaultListableBean Factory是容器的基础。必须首先要实例化,那么在这里就是实例化DefaultListableBeanFactory的步骤。 DefaultListableBeanFactory beanFactory = createBeanFactory(); //为了序列化指定id,如果需要的话,让这个BeanFactory从id反序列化到BeanFactory对象  beanFactory.setSerializationId(getId()); //定制beanFactory,设置相关属性,包括是否允许覆盖同名称的不同定义的对象以及循环依赖以及  customizeBeanFactory(beanFactory); //初始化DodumentReader,并进行XML文件读取及解析  loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { //使用全局变量记录BeanFactory类实例。 this.beanFactory = beanFactory;
            }
        } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    } protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) { //如果属性allowBeanDefinitionOverriding不为空,设置给beanFactory对象相应属性, //此属性的含义:是否允许覆盖同名称的不同定义的对象 if (this.allowBeanDefinitionOverriding != null) {
            beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        } //如果属性allowCircularReferences不为空,设置给beanFactory对象相应属性, //此属性的含义:是否允许bean之间存在循环依赖 if (this.allowCircularReferences != null) {
            beanFactory.setAllowCircularReferences(this.allowCircularReferences);
        } }


    @Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { //为指定beanFactory创建XmlBeanDefinitionReader XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); //对beanDefinitionReader进行环境变量的设置 beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); //对BeanDefinitionReader进行设置,可以覆盖  initBeanDefinitionReader(beanDefinitionReader); //使用XmlBeanDefinitionReader的loadBeanDefinitions方法进行配置文件的加载及注册  loadBeanDefinitions(beanDefinitionReader);
    }
复制代码

功能扩展

复制代码
    protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) { //设置beanFactory的classLoader为当前context的classLoader  beanFactory.setBeanClassLoader(getClassLoader()); //设置beanFactory的表达式语言处理器,Spring3增加了表达式语言的支持,SPEL语言。 //默认可以使用#{bean.xxx}的形式来调用相关属性值。 beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader())); //为beanFactory增加了一个默认的propertyEditor,这个主要是对bean的属性等设置管理的一个工具 beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())); //添加BeanPostProcessor //ApplicationContextAwareProcessor实现了BeanPostProcessor接口,在bean实例化的时候会被调用 //postProcessBeforeInitialization方法中调用了invokeAwareInterfaces。从invokeAwareInterfaces方法中,
     //我们可以看出来,实现这些Aware接口的bean在被初始化之后,可以取得一些对应的资源。 beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)); //Spring将ApplicationContextAwareProcessor注册后,在invokeAwareInterfaces方法中间调用的Aware类已经不是普通的bean了, //如ResourceLoaderAware,ApplicationEventPublisherAware等,需要在Spring做bean的依赖注入的时候忽略它们。 beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
        beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
        beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
        beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
        beanFactory.ignoreDependencyInterface(EnvironmentAware.class); //设置了几个自动装配的特殊规则 //当注册了依赖解析后,例如当注册了对BeanFactory.class的解析后,当bean的属性注入的时候,
     //一旦检测到属性为BeanFactory类型便会将beanFactory的实例注入进去。 beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
        beanFactory.registerResolvableDependency(ResourceLoader.class, this);
        beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
        beanFactory.registerResolvableDependency(ApplicationContext.class, this); //增加对AspectJ的支持 if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
            beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); // Set a temporary ClassLoader for type matching. beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
        } //将相关环境变量及属性注册以单例模式注册,environment,systemProperties,systemEnvironment if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
            beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
        } if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
            beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
        } if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
            beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
        }
    }
复制代码

ResourceEditorRegistrar的registerCustomEditors的调用时机,就是AbstractBeanFactory类中的initBeanWrapper方法,这是在bean初始化时使用的一个方法,主要是在将BeanDefinition转换为BeanWrapper后用于对属性的填充。在bean的初始化后会调用ResourceEditorRegistrar的registerCustomEditors方法进行批量的通用属性编辑器注册。注册后,在属性填充的环节便可以直接让Spring使用这些编辑器进行属性的解析了。
Spring中用于封装bean的是BeanWrapper类型,而它又间接继承了PropertyEditorRegistry类型,也就是我们之前反复看到的方法参数PropertyEditorRegistry,其实大部分情况下都是BeanWrapper,对于BeanWrapper在Spring中的默认实现是BeanWrapperImpl,而BeanWrapperImpl除了实现BeanWrapper接口外还继承了PropertyEditorRegistrySupport,在PropertyEditorRegistrySupport中有这样一个方法:createDefaultEditors,基本的属性编辑器就在此处被注册。

BeanFactory的后处理

BeanFactoryPostProcessor接口跟BeanPostProcessor类似,可以对bean的定义(配置元数据)进行处理。也就是说,Spring IoC容器允许BeanFactoryPostProcessor在容器实际实例化任何其他的bean之前读取配置元数据,并有可能修改它。如果你愿意,你可以配置多个BeanFactoryPostProcessor。你还能通过设置“order”属性来控制BeanFactoryPostProcessor的执行次序(仅当BeanFactoryPostProcessor实现了Ordered接口时你才可以设置此属性,因此在实现BeanFactoryPostProcessor时,就应当考虑实现Ordered接口)。

如果你想改变实际的bean实例(例如从配置元数据创建的对象),那么你最好使用BeanPostProcessor。同样地,BeanFactoryPostProcessor的作用域范围是容器级的。它只和你所使用的容器有关。如果你在容器中定义一个BeanFactoryPostProcessor,它仅仅对此容器中的bean进行后置处理。BeanFactoryPostProcessor不会对定义在另一个容器中的bean进行后置处理,即使这两个容器都是在同一层次上。

在Spring中存在对于BeanFactoryPostProcessor的两种典型应用。

(1)比如PropertyPlaceholderConfigurer

复制代码
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  <property name="locations">
   <list>
    <value>/WEB-INF/mail.properties</value>  
    <value>classpath: conf/sqlmap/jdbc.properties</value>//注意这两种value值的写法 </list>
  </property>
   <property name="fileEncoding">
     <value>UTF-8</value>
   </property>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  <property name="driverClassName"value="${jdbc.driverClassName}" />
  <property name="url" value="${jdbc.url}" />
  <property name="username" value="${jdbc.username}"/>
  <property name="password"value="${jdbc.password}" />
</bean> jdbc.properties文件
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost/mysqldb?useUnicode=true&amp;characterEncoding=UTF-8&amp; jdbc.username=root
jdbc.password=123456
复制代码

PropertyPlaceholderConfigurer是个bean工厂后置处理器的实现,也就是BeanFactoryPostProcessor接口的一个实现。PropertyPlaceholderConfigurer可以将上下文(配置文件)中的属性值放在另一个单独的标准java Properties文件中去。在XML文件中用${key}替换指定的properties文件中的值。这样的话,只需要对properties文件进行修改,而不用对xml配置文件进行修改。
在Spring中,使用PropertyPlaceholderConfigurer可以在XML配置文件中加入外部属性文件,当然也可以指定外部文件的编码,PropertyPlaceholderConfigurer如果在指定的Properties文件中找不到你想使用的属性,它还会在Java的System类属性中查找。可以通过System.setProperty(key, value)或者java中通过-Dnamevalue来给Spring配置文件传递参数。

查看层级结构可以看出PropertyPlaceholderConfigurer这个类间接继承了BeanFactoryPostProcessor接口。这是一个很特别的接口,当Spring加载任何实现了这个接口的bean的配置时,都会在bean工厂载入所有bean的配置之后执行postProcessBeanFactory方法。在PropertyResourceConfigurer类中实现了postProcessBeanFactory方法,在方法中先后调用了mergeProperties、convertProperties、processProperties这3个方法,分别得到配置,将得到的配置转换为合适的类型,最后将配置内容告知BeanFactory。正是通过实现BeanFactoryPostProcessor接口,BeanFactory会在实例化任何bean之前获得配置信息,从而能够正确解析bean描述文件中的变量引用。

(2)使用自定义BeanFactoryPostProcessor 

实现一个BeanFactoryPostProcessor,实现一个简单的回调处理器,它能去除潜在的”流氓”属性值,例如bean定义中留下bollocks这样的字眼。

复制代码
<bean id="bfpp" class="com.spring.ch04.ObscenityRemovingBeanFactoryPostProcessor"> <property name="obscenties"> <set> <value>bollocks</value> <value>winky</value> <value>bum</value> <value>Microsoft</value> </set> </property> </bean> <bean id="simpleBean" class="com.spring.ch04.SimplePostProcessor"> <property name="connectionString" value="bollocks"/> <property name="password" value="imaginecup"/> <property name="username" value="Microsoft"/> </bean> 
复制代码

java代码

复制代码
public class ObscenityRemovingBeanFactoryPostProcessor implements BeanFactoryPostProcessor { private Set<String> obscenties; public ObscenityRemovingBeanFactoryPostProcessor(){ this.obscenties=new HashSet<String>();  
    } public void postProcessBeanFactory(  
            ConfigurableListableBeanFactory beanFactory) throws BeansException {  
        String[] beanNames=beanFactory.getBeanDefinitionNames(); for(String beanName:beanNames){  
            BeanDefinition bd=beanFactory.getBeanDefinition(beanName);  
            StringValueResolver valueResover=new StringValueResolver() { public String resolveStringValue(String strVal) { if(isObscene(strVal)) return "*****"; return strVal;  
                }  
            };  
            BeanDefinitionVisitor visitor=new BeanDefinitionVisitor(valueResover);  
            visitor.visitBeanDefinition(bd);  
        }  
    } public boolean isObscene(Object value){  
        String potentialObscenity=value.toString().toUpperCase(); return this.obscenties.contains(potentialObscenity);  
    } public void setObscenties(Set<String> obscenties) { this.obscenties.clear(); for(String obscenity:obscenties){ this.obscenties.add(obscenity.toUpperCase());  
        }  
    }  
  
} 
复制代码

测试类

复制代码
import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.ClassPathResource; public class PropertyConfigurerDemo { public static void main(String[] args) {  
        ConfigurableListableBeanFactory bf=new XmlBeanFactory(new ClassPathResource("/META-INF/BeanFactory.xml"));  
        BeanFactoryPostProcessor bfpp=(BeanFactoryPostProcessor)bf.getBean("bfpp");  
        bfpp.postProcessBeanFactory(bf);  
        System.out.println(bf.getBean("simpleBean"));  
          
    }  
} 
复制代码

SimplePostProcessor{connectionString=*****,username=*****,password=imaginecup

激活BeanFactoryPostProcessor

1)从invokeBeanFactoryPostProcessors的方法中我们看到,对于BeanFactoryPostProcessor的处理主要分两种情况进行,一个是对于BeanDefinitionRegistry类的特殊处理,另一种是对普通的BeanFactoryPostProcessor进行处理。而对于每种情况都需要考虑硬编码注入注册的后处理器以及通过配置注入的后处理器

对于硬编码注册的后处理器的处理,主要是通过AbstractApplicationContext中的添加处理器方法addBeanFactoryPostProcessor进行添加。

public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor beanFactoryPostProcessor) {

this.beanFactoryPostProcessors.add(beanFactoryPostProcessor);

}

添加后的后处理器会存放在beanFactoryPostProcessors中,而在处理BeanFactoryPostProcessor时候会首先检测beanFactoryPostProcessors是否有数据。当然,BeanDefinitionRegistryPostProcessor继承自BeanFactoryPostProcessor,不但有BeanFactoryPostProcessor的特性,同时还有自己定义的个性化方法,也需要在此调用。所以,这里需要从beanFactoryPostProcessors中挑出BeanDefinitionRegistryPostProcessor的后处理器,并进行其postProcessBeanDefinitionRegistry方法的激活。

(2)记录后处理器主要使用了三个List完成。

registryPostProcessors:记录通过硬编码方式注册的BeanDefinitionRegistryPostProcessor类型的处理器。

regularPostProcessors:记录通过硬编码方式注册的BeanFactoryPostProcessor类型的处理器。

registryPostProcessorBeans:记录通过配置方式注册的BeanDefinitionRegistryPostProcessor类型的处理器。

(3)对以上所记录的List中的后处理器进行统一调用BeanFactoryPostProcessor的postProcessBeanFactory方法。

(4)对beanFactoryPostProcessors中非BeanDefinitionRegistryPostProcessor类型的后处理器进行统一的BeanFactoryPostProcessor的postProcessBeanFactory方法调用。

(5)普通beanFactory处理。BeanDefinitionRegistryPostProcessor只对BeanDefinitionRegistry类型的ConfigurableListableBeanFactory有效,所以如果判断所示的beanFactory并不是BeanDefinitionRegistry,那么便可以忽略BeanDefinitionRegistryPostProcessor,而直接处理BeanFactoryPostProcessor,当然获取的方式与上面的获取类似。

对于硬编码方式手动添加的后处理器是不需要做任何排序的,但是在配置文件中读取的处理器,Spring并不保证读取的顺序。所以,为了保证用户的调用顺序的要求,Spring对于后处理器的调用支持按照PriorityOrdered或者Ordered的顺序调用。

注册BeanPostProcessor

来探索下BeanPostProcessor,但是这里并不是调用,而是注册。真正的调用其实是在bean的实例化阶段进行的。这是一个很重要的步骤,也是很多功能BeanFactory不支持的重要原因。Spring中大部分功能都是通过后处理器的方式进行扩展的,这是Spring框架的一个特性,但是在BeanFactory中其实并没有实现后处理器的自动注册,所以在调用的时候如果没有进行手动注册其实是不能使用的。

对于BeanPostProcessor的处理与BeanFactoryPostProcessor的处理极为相似,但是似乎又有些不一样的地方。经过反复的对比发现,对于BeanFactoryPostProcessor的处理要区分两种情况,一种方式是通过硬编码方式的处理,另一种是通过配置文件方式的处理。那么为什么在BeanPostProcessor的处理中只考虑了配置文件的方式而不考虑硬编码的方式呢?对于BeanFactoryPostProcessor的处理,不但要实现注册功能,而且还要实现对后处理器的激活操作,所以需要载入配置中的定义,并进行激活;而对于BeanPostProcessor并不需要马上调用,再说,硬编码的方式实现的功能是将后处理器提取并调用,这里并不需要调用,当然不需要考虑硬编码的方式了,这里的功能只需要将配置文件的BeanPostProcessor提取出来并注册进入beanFactory就可以了。

对于beanFactory的注册,也不是直接注册就可以的。在Spring中支持对于BeanPostProcessor的排序,比如根据PriorityOrdered进行排序、根据Ordered进行排序或者无序,而Spring在BeanPostProcessor的激活顺序的时候也会考虑对于顺序的问题而先进行排序。

初始化消息资源

在initMessageSource中的方法主要功能是提取配置中定义的messageSource,并将其记录在Spring的容器中,也就是AbstractApplicationContext中。当然,如果用户未设置资源文件的话,Spring中也提供了默认的配置DelegatingMessageSource。

在initMessageSource中获取自定义资源文件的方式为beanFactory.getBean(MESSAGE_ SOURCE_BEAN_NAME, MessageSource.class),在这里Spring使用了硬编码的方式硬性规定了子定义资源文件必须为message,否则便会获取不到自定义资源配置。

复制代码
    protected void initMessageSource() {
        ConfigurableListableBeanFactory beanFactory = getBeanFactory(); //messageSource if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) { //如果在配置中已经配置了messageSource,那么将messageSource提取并记录在this.messageSource中 this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class); if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
                HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource; if (hms.getParentMessageSource() == null) {  hms.setParentMessageSource(getInternalParentMessageSource());
                }
            } if (logger.isDebugEnabled()) {
                logger.debug("Using MessageSource [" + this.messageSource + "]");
            }
        } else { //如果用户并没有定义配置文件,那么使用临时的DelegatingMessageSource以便于作为调用getMessage方法的返回。 DelegatingMessageSource dms = new DelegatingMessageSource();
            dms.setParentMessageSource(getInternalParentMessageSource()); this.messageSource = dms;
            beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource); if (logger.isDebugEnabled()) {
                logger.debug("Unable to locate MessageSource with name '" + MESSAGE_SOURCE_BEAN_NAME +
                        "': using default [" + this.messageSource + "]");
            }
        }
    }
复制代码

初始化ApplicationEventMulticaster

initApplicationEventMulticaster的方式比较简单,无非考虑两种情况:

如果用户自定义了事件广播器,那么使用用户自定义的事件广播器。

如果用户没有自定义事件广播器,那么使用默认的ApplicationEventMulticaster。

复制代码
    protected void initApplicationEventMulticaster() {
        ConfigurableListableBeanFactory beanFactory = getBeanFactory(); //applicationEventMulticaster if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) { this.applicationEventMulticaster = beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class); if (logger.isDebugEnabled()) {
                logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
            }
        } else { this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
            beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster); if (logger.isDebugEnabled()) {
                logger.debug("Unable to locate ApplicationEventMulticaster with name '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
                        "': using default [" + this.applicationEventMulticaster + "]");
            }
        }
    }
复制代码

按照之前介绍的顺序及逻辑,作为广播器,一定是用于存放监听器并在合适的时候调用监听器,那么进入默认的广播器实现SimpleApplicationEventMulticaster来一探究竟。

复制代码
 @Override public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            Executor executor = getTaskExecutor(); if (executor != null) {
                executor.execute(new Runnable() {
                    @Override public void run() {
                        invokeListener(listener, event);
                    }
                });
            } else {
                invokeListener(listener, event);
            }
        }
    } protected void invokeListener(ApplicationListener listener, ApplicationEvent event) {
   ErrorHandler errorHandler = getErrorHandler(); if (errorHandler != null) { try {
         listener.onApplicationEvent(event);
      } catch (Throwable err) {
         errorHandler.handleError(err);
      }
   } else {
      listener.onApplicationEvent(event);
   }
}
复制代码

可以推断,当产生Spring事件发生的时候会默认使用SimpleApplicationEventMulticaster的multicastEvent来广播事件,遍历所有监听器,并使用监听器中的onApplicationEvent方法来进行监听器的处理。而对于每个监听器来说其实都可以获取到产生的事件,但是是否进行处理则由事件监听器来决定。

注册监听器

复制代码
    protected void registerListeners() { //硬编码方式注册的监听器处理 for (ApplicationListener<?> listener : getApplicationListeners()) {
            getApplicationEventMulticaster().addApplicationListener(listener);
        } //配置文件注册的监听器处理 String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false); for (String listenerBeanName : listenerBeanNames) {
            getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
        }
        
     //广播早期的事件进行广播和事件处理  // Publish early application events now that we finally have a multicaster... Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents; this.earlyApplicationEvents = null; if (earlyEventsToProcess != null) { for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
                getApplicationEventMulticaster().multicastEvent(earlyEvent);
            }
        }
    }
复制代码

初始化非延迟加载单例

finishBeanFactoryInitialization完成BeanFactory的初始化工作,其中包括ConversionService的设置、配置冻结以及非延迟加载的bean的初始化工作。

复制代码
    protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { //conversionService的bean会被注册 if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) && beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
            beanFactory.setConversionService(
                    beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
        } // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early. String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false); for (String weaverAwareName : weaverAwareNames) {
            getBean(weaverAwareName);
        } // Stop using the temporary ClassLoader for type matching. beanFactory.setTempClassLoader(null); //冻结所有的bean定义,说明注册的bean定义将不被修改或任何进一步的处理。  beanFactory.freezeConfiguration(); //初始化剩下的单实例(非惰性的)  beanFactory.preInstantiateSingletons();
    }
复制代码

ConversionService的设置,之前我们提到过使用自定义类型转换器从String转换为Date的方式,使用属性编辑器,那么,在Spring中还提供了另一种转换方式:使用Converter。

ApplicationContext实现的默认行为就是在启动时将所有单例bean提前进行实例化。提前实例化意味着作为初始化过程的一部分,ApplicationContext实例会创建并配置所有的单例bean。通常情况下这是一件好事,因为这样在配置中的任何错误就会即刻被发现(否则的话可能要花几个小时甚至几天)。而这个实例化的过程就是在finishBeanFactoryInitialization中完成的,详细流程见Spring框架的设计理念章节。

finishRefresh

在Spring中还提供了Lifecycle接口,Lifecycle中包含start/stop方法,实现此接口后Spring保证在启动的时候调用其start方法开始生命周期,并在Spring关闭的时候调用stop方法来结束生命周期,通常用来配置后台程序,在启动后一直运行(如对MQ进行轮询等)。而ApplicationContext的初始化最后正是保证了这一功能的实现。

复制代码
    protected void finishRefresh() { // Initialize lifecycle processor for this context.  //lifecycleProcessor的bean会被注册
     initLifecycleProcessor(); // Propagate refresh to lifecycle processor first.  getLifecycleProcessor().onRefresh(); // Publish the final event. publishEvent(new ContextRefreshedEvent(this)); // Participate in LiveBeansView MBean, if active. LiveBeansView.registerApplicationContext(this);
    }
复制代码

initLifecycleProcessor

当ApplicationContext启动或停止时,它会通过LifecycleProcessor来与所有声明的bean周期做状态更新,而在LifecycleProcessor的使用前首先需要初始化。

onRefresh

启动所有实现了Lifecycle接口的bean。

publishEvent

当完成ApplicationContext初始化的时候,要通过Spring中的事件发布机制来发出ContextRefreshedEvent事件,以保证对应的监听器可以做进一步的逻辑处理。

Spring JdbcTemplate批量操作数据库

 spring  Spring JdbcTemplate批量操作数据库已关闭评论
12月 062016
 

解决什么问题

提升数据操作性能,因为批量操作可以减少网络来回次数。

怎么做

方法1:使用jdbcTempalte的batchUpdate方法,第二个参数传入接口BatchPreparedStatementSetter接口,该接口需要实现两个方法,getBatchSize()用于获得该批数量,setValues(PreapredStatement ps, int i)用于设置每个PreparedStatement,以插入为例:

int batchInsert(final List<Stock> stockList)
    {
        logger.info(“batchInsert() begin, stockList.size=”+stockList.size());
        int[] updatedCountArray = getJdbcTemplate().batchUpdate(“insert into stock(id,code,name) value(?,?,?)”, new BatchPreparedStatementSetter() {
            
            public void setValues(PreparedStatement ps, int i) throws SQLException {
                // TODO Auto-generated method stub
                ps.setLong(1, stockList.get(i).getId());//要注意,下标从1开始
                ps.setString(2, stockList.get(i).getCode());
                ps.setString(3, stockList.get(i).getName());
            }
            
            public int getBatchSize() {
                
                return stockList.size();
            }
        });
        int sumInsertedCount = 0;
        for(int a: updatedCountArray)
        {
            sumInsertedCount+=a;
        }
        logger.info(“batchInsert() end, stockList.size=”+stockList.size()+”,success inserted “+sumInsertedCount+” records”);
        return sumInsertedCount;
    }

 

方法2:使用内置的SqlParamterSouce,从上面可以看出,代码还是写起来还是挺麻烦,赋值的时候很明显都是与Bean的属性名称有对应关系的,Spring因此提供了内置的方法来简化开发。因为需要足够的信息判断如何将对象的属性映射到sql中去,因此需要使用NamedJdbcTemplate。


int batchDelete(final List<Stock> stockList)
    {
        logger.info(“batchDelete() begin, codeList.size=”+stockList.size());
        SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(stockList.toArray());
        int[] updatedCountArray = getNamedParameterJdbcTemplate().batchUpdate(“delete from stock where code=:code”, batch);
        int sumInsertedCount = 0;
        for(int a: updatedCountArray)
        {
            sumInsertedCount+=a;
        }
        logger.info(“batchInsert() end, stockList.size=”+stockList.size()+”,success deleted “+sumInsertedCount+” records”);
        return sumInsertedCount;
    }

转自:http://www.cnblogs.com/lidabnu/p/5769732.html

Spring Boot中配置使用 Servlet

 spring  Spring Boot中配置使用 Servlet已关闭评论
12月 052016
 

当使用Spring-Boot时,嵌入式Servlet容器通过扫描注解的方式注册Servlet、Filter和Servlet规范的所有监听器(如HttpSessionListener监听器)。 
Spring boot 的主 Servlet 为 DispatcherServlet,其默认的url-pattern为“/”。也许我们在应用中还需要定义更多的Servlet,该如何使用SpringBoot来完成呢?

在spring boot中添加自己的Servlet有两种方法,代码注册Servlet和注解自动注册(Filter和Listener也是如此)。 
一、代码注册通过ServletRegistrationBean、 FilterRegistrationBean 和 ServletListenerRegistrationBean 获得控制。 
也可以通过实现 ServletContextInitializer 接口直接注册。

二、在 SpringBootApplication 上使用@ServletComponentScan 注解后,Servlet、Filter、Listener 可以直接通过 @WebServlet、@WebFilter、@WebListener 注解自动注册,无需其他代码。

通过代码注册Servlet示例代码:

SpringBootSampleApplication.java

package org.springboot.sample;

import org.springboot.sample.servlet.MyServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.DispatcherServlet;

@SpringBootApplication
public class SpringBootSampleApplication {

    /**
     * 使用代码注册Servlet(不需要@ServletComponentScan注解)
     *
     */
    @Bean
    public ServletRegistrationBean servletRegistrationBean() {
        return new ServletRegistrationBean(new MyServlet(), "/xs/*");// ServletName默认值为首字母小写,即myServlet
    }

    public static void main(String[] args) {
        SpringApplication.run(SpringBootSampleApplication.class, args);
    }
}

MyServlet.java
package org.springboot.sample.servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet
 *
 */
//@WebServlet(urlPatterns="/xs/*", description="Servlet的说明")
public class MyServlet extends HttpServlet{

    private static final long serialVersionUID = -8685285401859800066L;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println(">>>>>>>>>>doGet()<<<<<<<<<<<");
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println(">>>>>>>>>>doPost()<<<<<<<<<<<");
        resp.setContentType("text/html");  
        PrintWriter out = resp.getWriter();  
        out.println("<html>");  
        out.println("<head>");  
        out.println("<title>Hello World</title>");  
        out.println("</head>");  
        out.println("<body>");  
        out.println("<h1>大家好,我的名字叫Servlet</h1>");  
        out.println("</body>");  
        out.println("</html>"); 
    }

}



使用注解注册Servlet示例代码

SpringBootSampleApplication.java

package org.springboot.sample;

import org.springboot.sample.servlet.MyServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.DispatcherServlet;

@SpringBootApplication
@ServletComponentScan
public class SpringBootSampleApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootSampleApplication.class, args);
    }
}

MyServlet2.java

package org.springboot.sample.servlet;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet
 */
@WebServlet(urlPatterns=”/xs/myservlet”, description=”Servlet的说明”) // 不指定name的情况下,name默认值为类全路径,即org.springboot.sample.servlet.MyServlet2
public class MyServlet2 extends HttpServlet{

    private static final long serialVersionUID = -8685285401859800066L;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println(“>>>>>>>>>>doGet2()<<<<<<<<<<<“);
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println(“>>>>>>>>>>doPost2()<<<<<<<<<<<“);
        resp.setContentType(“text/html”);  
        PrintWriter out = resp.getWriter();  
        out.println(“<html>”);  
        out.println(“<head>”);  
        out.println(“<title>Hello World</title>”);  
        out.println(“</head>”);  
        out.println(“<body>”);  
        out.println(“<h1>大家好,我的名字叫Servlet2</h1>”);  
        out.println(“</body>”);  
        out.println(“</html>”); 
    }

}

使用 @WebServlet 注解,其中可以设置一些属性。

有个问题:DispatcherServlet 默认拦截“/”,MyServlet 拦截“/xs/*”,MyServlet2 拦截“/xs/myservlet”,那么在我们访问 http://localhost:8080/xs/myservlet 的时候系统会怎么处理呢?如果访问 http://localhost:8080/xs/abc 的时候又是什么结果呢?这里就不给大家卖关子了,其结果是“匹配的优先级是从精确到模糊,复合条件的Servlet并不会都执行”

既然系统DispatcherServlet 默认拦截“/”,那么我们是否能做修改呢,答案是肯定的,我们在SpringBootSampleApplication中添加代码:

 /**
     * 修改DispatcherServlet默认配置
     */
    @Bean
    public ServletRegistrationBean dispatcherRegistration(DispatcherServlet dispatcherServlet) {
        ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet);
        registration.getUrlMappings().clear();
        registration.addUrlMappings(“*.do”);
        registration.addUrlMappings(“*.json”);
        return registration;
    }

当然,这里可以对DispatcherServlet做很多修改,并非只是UrlMappings。

转自:http://blog.csdn.net/catoop/article/details/50501686

通过Spring Boot快速开发spring web项目(实例介绍,生产监控)

 spring  通过Spring Boot快速开发spring web项目(实例介绍,生产监控)已关闭评论
11月 292016
 

今天试用了下spring-boot,发现确实开发spring web小应用快了不是一点点。以下举个小例子:

——————————————————————————————————————————————————————

创建项目

首先使用Maven创建一个普通Maven应用即可,不必是web的。

 

添加Spring Boot相关POM配置

在pom.xml中添加如下配置

Java代码  收藏代码
  1. <!– Inherit defaults from Spring Boot –>  
  2. <parent>  
  3.     <groupId>org.springframework.boot</groupId>  
  4.     <artifactId>spring-boot-starter-parent</artifactId>  
  5.     <version>1.2.3.RELEASE</version>  
  6. </parent>  
  7.  <properties>
            <tomcat.version>7.0.52</tomcat.version>
     </properties>

  8. <!– Add typical dependencies for a web application –>  
  9. <dependencies>  
  10.     <dependency>  
  11.         <groupId>org.springframework.boot</groupId>  
  12.         <artifactId>spring-boot-starter-web</artifactId>  
  13.     </dependency>  
  14. </dependencies>  
  15.   
  16. <!– Package as an executable JAR –>  
  17. <build>  
  18.     <plugins>  
  19.         <plugin>  
  20.             <groupId>org.springframework.boot</groupId>  
  21.             <artifactId>spring-boot-maven-plugin</artifactId>  
  22.         </plugin>  
  23.     </plugins>  
  24. </build>  
  25.   
  26. <!– Allow access to Spring milestones and snapshots –>  
  27. <!– (you don’t need this if you are using anything after 0.5.0.RELEASE) –>  
  28. <repositories>  
  29.     <repository>  
  30.         <id>spring-snapshots</id>  
  31.         <url>http://repo.spring.io/snapshot</url>  
  32.         <snapshots><enabled>true</enabled></snapshots>  
  33.     </repository>  
  34.     <repository>  
  35.         <id>spring-milestones</id>  
  36.         <url>http://repo.spring.io/milestone</url>  
  37.         <snapshots><enabled>true</enabled></snapshots>  
  38.     </repository>  
  39. </repositories>  
  40. <pluginRepositories>  
  41.     <pluginRepository>  
  42.         <id>spring-snapshots</id>  
  43.         <url>http://repo.spring.io/snapshot</url>  
  44.     </pluginRepository>  
  45.     <pluginRepository>  
  46.         <id>spring-milestones</id>  
  47.         <url>http://repo.spring.io/milestone</url>  
  48.     </pluginRepository>  
  49. </pluginRepositories>  

继承spring-boot-starter-parent后我们可以继承一些默认的依赖,这样就无需添加一堆相应的依赖,把依赖配置最小化;spring-boot-starter-web提供了对web的支持,spring-boot-maven-plugin提供了直接运行项目的插件,我们可以直接mvn spring-boot:run运行。

 

实体

Java代码  收藏代码

  1. package com.sishuok.entity;  
  2.   
  3. public class User {  
  4.     private Long id;  
  5.     private String name;  
  6.   
  7.     public Long getId() {  
  8.         return id;  
  9.     }  
  10.   
  11.     public void setId(Long id) {  
  12.         this.id = id;  
  13.     }  
  14.   
  15.     public String getName() {  
  16.         return name;  
  17.     }  
  18.   
  19.     public void setName(String name) {  
  20.         this.name = name;  
  21.     }  
  22.   
  23.     @Override  
  24.     public boolean equals(Object o) {  
  25.         if (this == o) return true;  
  26.         if (o == null || getClass() != o.getClass()) return false;  
  27.   
  28.         User user = (User) o;  
  29.   
  30.         if (id != null ? !id.equals(user.id) : user.id != nullreturn false;  
  31.   
  32.         return true;  
  33.     }  
  34.   
  35.     @Override  
  36.     public int hashCode() {  
  37.         return id != null ? id.hashCode() : 0;  
  38.     }  
  39. }  

 

控制器

Java代码  收藏代码

  1. package com.sishuok.controller;  
  2.   
  3. import com.sishuok.entity.User;  
  4. import org.springframework.boot.SpringApplication;  
  5. import org.springframework.boot.autoconfigure.EnableAutoConfiguration;  
  6. import org.springframework.web.bind.annotation.PathVariable;  
  7. import org.springframework.web.bind.annotation.RequestMapping;  
  8. import org.springframework.web.bind.annotation.RestController;  
  9.   
  10. //@EnableAutoConfiguration  
  11. @RestController  
  12. @RequestMapping(“/user”)  
  13. public class UserController {  
  14.   
  15.     @RequestMapping(“/{id}”)  
  16.     public User view(@PathVariable(“id”) Long id) {  
  17.         User user = new User();  
  18.         user.setId(id);  
  19.         user.setName(“zhang”);  
  20.         return user;  
  21.     }  
  22.   
  23.     //public static void main(String[] args) {  
  24.     //    SpringApplication.run(UserController.class);  
  25.     //}  
  26.   
  27. }  

 

运行  

第一种方式

通过在UserController中加上@EnableAutoConfiguration开启自动配置,然后通过SpringApplication.run(UserController.class);运行这个控制器;这种方式只运行一个控制器比较方便;

第二种方式

通过@Configuration+@ComponentScan开启注解扫描并自动注册相应的注解Bean

Java代码  收藏代码

  1. package com.sishuok;  
  2.   
  3. import com.sishuok.controller.UserController;  
  4. import org.springframework.boot.SpringApplication;  
  5. import org.springframework.boot.autoconfigure.EnableAutoConfiguration;  
  6. import org.springframework.context.annotation.ComponentScan;  
  7. import org.springframework.context.annotation.Configuration;  
  8.     
  9. @Configuration  
  10. @ComponentScan  
  11. @EnableAutoConfiguration  
  12. public class Application {  
  13.     public static void main(String[] args) {  
  14.         SpringApplication.run(Application.class);  
  15.     }  
  16. }  

到此,一个基本的REST风格的web应用就构建完成了。

 

地址栏输入http://localhost:8080/user/1即可看到json结果。

 

 

如果大家查看其依赖,会发现自动添加了需要相应的依赖(不管你用/不用),但是开发一个应用确实变得非常快速,对于想学习/体验Spring的新手,快速建立项目模型等可以考虑用这种方式。当然如果不想依赖这么多的jar包,可以去掉parent,然后自己添加依赖。 

————————————————————————————————————————————

spring-boot-actuator模块提供了一个监控和管理生产环境的模块,可以使用http、jmx、ssh、telnet等拉管理和监控应用。审计(Auditing)、健康(health)、数据采集(metrics gathering)会自动加入到应用里面


pom.xml添加下面语句:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

以下是所有监控描述:

HTTP方法

路径

描述

鉴权

GET

/autoconfig

查看自动配置的使用情况,该报告展示所有auto-configuration候选者及它们被应用或未被应用的原因

true

GET

/configprops

显示一个所有@ConfigurationProperties的整理列表

true

GET

/beans

显示一个应用中所有Spring Beans的完整列表

true

GET

/dump

打印线程栈

true

GET

/env

查看所有环境变量

true

GET

/env/{name}

查看具体变量值

true

GET

/health

查看应用健康指标

false

GET

/info

查看应用信息

false

GET

/mappings

查看所有url映射

true

GET

/metrics

查看应用基本指标

true

GET

/metrics/{name}

查看具体指标

true

POST

/shutdown

允许应用以优雅的方式关闭(默认情况下不启用)

true

GET

/trace

查看基本追踪信息

true


————————————————————————————————————————————————


默认启动的端口是8080,可以通过下面方法修改端口

1. java 命令行 添加,如:   java -Dserver.port=8888 -jar  demo.jar (8888为端口号)

2. 在classpath目录的根目录或config目录下(优先根目录)的application.propertiesapplication.yml文件添加下面内容:

server.port=8888 (8888为端口号)


3. 通过实现EmbeddedServletContainerCustomizer接口来实现:
public class Application extends SpringBootServletInitializer implements EmbeddedServletContainerCustomizer {  
  
    @Override  
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {  
        return builder.sources(Application.class);  
    }  
      
    public static void main(String[] args) {  
        SpringApplication.run(Application.class, args);  
    }  
  
  
    @Override  
    public void customize(ConfigurableEmbeddedServletContainer container) {  
        container.setPort(8888);  
    }  
}  


servlet, spring, filter, listenr执行顺序

 java, spring  servlet, spring, filter, listenr执行顺序已关闭评论
11月 282016
 

web.xml 文件中一般包括 servlet, spring, filter, listenr的配置。那么他们是按照一个什么顺序加载呢?

加载顺序会影响对spring bean 的调用。

    比如filter 需要用到 bean ,但是加载顺序是 先加载filter 后加载spring,则filter中初始化操作中的bean为null;

首先可以肯定 加载顺序与他们在web.xml 文件中的先后顺序无关。

web.xml 中 listener 和 serverlet 的加载顺序为 先 listener 后serverlet

最终得出结果:先 listener >> filter >> servlet >>  spring 

 所以,如果过滤器中要使用到 bean,可以将spring 的加载 改成 Listener的方式

<listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>

 搞定!

 

 

 

 关于他们的内部执行顺序,也需要注意,如下遇到的乱码问题

web.xml的filter执行顺序导致的乱码,切记!
发现引起bug的原因是web.xml的下面几行:
    <filter-mapping>
        <filter-name>SecurityFilter</filter-name>
        <url-pattern>*.do</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>CharacterEncoding</filter-name>
        <url-pattern>*.do</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>CharacterEncoding</filter-name>
        <url-pattern>*.jsp</url-pattern>
    </filter-mapping>
根据servlet2.3规范filter执行是按照web.xml配置的filter-mapping先后顺序进行执行,所以上面的配置会导致遇见*.do的url请求,先进行SecurityFilter的过滤器处理,这时候没有做编码处理,已经是乱码,到下面的filter处理时已经时乱码,再做编码处理已经没有用处。
修正方式,调整filter-mapping顺序,如下:
    <filter-mapping>
        <filter-name>CharacterEncoding</filter-name>
        <url-pattern>*.do</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>CharacterEncoding</filter-name>
        <url-pattern>*.jsp</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <filter-name>SecurityFilter</filter-name>
        <url-pattern>*.do</url-pattern>
    </filter-mapping>

在ServletContextListener 获取spring上下文

 spring  在ServletContextListener 获取spring上下文已关闭评论
8月 122016
 

ServletContextListener 不受spring管理我们应该如何获取呢?

 

实际上spring同样使用了ServletContextListener接口,我们可以通过实现一个自己的ServletContextListener

来得到spring上下文

  1. import java.util.List;  
  2.   
  3. import javax.servlet.ServletContextEvent;  
  4. import javax.servlet.ServletContextListener;  
  5.   
  6. import org.springframework.context.ApplicationContext;  
  7. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  8. import org.springframework.web.context.support.WebApplicationContextUtils;  
  9.   
  10. public class InitialData implements ServletContextListener {  
  11.       
  12.       
  13.     private ApplicationContext app;  
  14.   
  15.     public void contextDestroyed(ServletContextEvent arg0) {  
  16.         // TODO Auto-generated method stub  
  17.   
  18.     }  
  19.   
  20.     public void contextInitialized(ServletContextEvent event) {  
  21.         app = WebApplicationContextUtils.getWebApplicationContext(event.getServletContext()); //获取spring上下文!  
  22.         app.getBean(“xxxService”); //获取到bean了, DONE
  23.     }  
  24.   

Spring 使用注解方式进行事务管理

 spring  Spring 使用注解方式进行事务管理已关闭评论
4月 282016
 

转自:http://www.cnblogs.com/younggun/archive/2013/07/16/3193800.html

使用步骤:

步骤一、在spring配置文件中引入<tx:>命名空间
<beans xmlns=”http://www.springframework.org/schema/beans”
 xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
 xmlns:tx=”http://www.springframework.org/schema/tx”
 xsi:schemaLocation=”http://www.springframework.org/schema/beans
 http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
 http://www.springframework.org/schema/tx
 http://www.springframework.org/schema/tx/spring-tx-2.0.xsd”>

步骤二、具有@Transactional 注解的bean自动配置为声明式事务支持
 

复制代码
<!-- 事务管理器配置, Hibernate单数据源事务 --> <bean id="defaultTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <!-- 使用annotation定义事务 --> <tx:annotation-driven transaction-manager="defaultTransactionManager" proxy-target-class="true" />
复制代码

 

步骤三、在接口或类的声明处 ,写一个@Transactional.
要是只在接口上写, 接口的实现类就会继承下来、接口的实现类的具体方法,可以覆盖类声明处的设置
@Transactional   //类级的注解、适用于类中所有的public的方法

事务的传播行为和隔离级别

大家在使用spring的注解式事务管理时,对事务的传播行为和隔离级别可能有点不知所措,下边就详细的介绍下以备方便查阅。

事物注解方式: @Transactional

当标于类前时, 标示类中所有方法都进行事物处理 , 例子:

@Transactional public class TestServiceBean implements TestService {} 

当类中某些方法不需要事物时:

复制代码
@Transactional public class TestServiceBean implements TestService { private TestDao dao; public void setDao(TestDao dao) { this.dao = dao;
    }   
    @Transactional(propagation = Propagation.NOT_SUPPORTED) public List<Object> getAll() { return null;
    }   
}
复制代码

事物传播行为介绍: 
@Transactional(propagation=Propagation.REQUIRED) 
如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
@Transactional(propagation=Propagation.NOT_SUPPORTED) 
容器不为这个方法开启事务
@Transactional(propagation=Propagation.REQUIRES_NEW) 
不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.MANDATORY) 
必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.NEVER) 
必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS) 
如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.

事物超时设置:
@Transactional(timeout=30) //默认是30秒

事务隔离级别:
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
读取未提交数据(会出现脏读, 不可重复读) 基本不使用
@Transactional(isolation = Isolation.READ_COMMITTED)
读取已提交数据(会出现不可重复读和幻读)
@Transactional(isolation = Isolation.REPEATABLE_READ)
可重复读(会出现幻读)
@Transactional(isolation = Isolation.SERIALIZABLE)
串行化

MYSQL: 默认为REPEATABLE_READ级别
SQLSERVER: 默认为READ_COMMITTED

脏读 : 一个事务读取到另一事务未提交的更新数据
不可重复读 : 在同一事务中, 多次读取同一数据返回的结果有所不同, 换句话说, 
后续读取可以读到另一事务已提交的更新数据. 相反, “可重复读”在同一事务中多次
读取数据时, 能够保证所读数据一样, 也就是后续读取不能读到另一事务已提交的更新数据
幻读 : 一个事务读到另一个事务已提交的insert数据

@Transactional注解中常用参数说明

参 数 名 称

功 能 描 述

readOnly

该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。例如:@Transactional(readOnly=true)

rollbackFor

该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如:

指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)

指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})

 续表)

参 数 名 称

功 能 描 述

rollbackForClassName

该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。例如:

指定单一异常类名称:@Transactional(rollbackForClassName=”RuntimeException”)

指定多个异常类名称:@Transactional(rollbackForClassName={“RuntimeException”,”Exception”})

noRollbackFor

该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。例如:

指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class)

指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class})

noRollbackForClassName

该属性用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。例如:

指定单一异常类名称:@Transactional(noRollbackForClassName=”RuntimeException”)

指定多个异常类名称:

@Transactional(noRollbackForClassName={“RuntimeException”,”Exception”})

propagation

该属性用于设置事务的传播行为,具体取值可参考表6-7。

例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)

isolation

该属性用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置

timeout

该属性用于设置事务的超时秒数,默认值为-1表示永不超时

注意的几点:
1 @Transactional 只能被应用到public方法上, 对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能.
2用 spring 事务管理器,由spring来负责数据库的打开,提交,回滚.默认遇到运行期例外(throw new RuntimeException(“注释”);)会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到需要捕获的例外(throw new Exception(“注释”);)不会回滚,即遇到受检查的例外(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或说受检查异常)时,需我们指定方式来让事务回滚 要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其它异常}) .如果让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)
如下:
@Transactional(rollbackFor=Exception.class) //指定回滚,遇到异常Exception时回滚
public void methodName() {
throw new Exception(“注释”);
}
@Transactional(noRollbackFor=Exception.class)//指定不回滚,遇到运行期例外(throw new RuntimeException(“注释”);)会回滚
public ItimDaoImpl getItemDaoImpl() {
throw new RuntimeException(“注释”);
}

3、@Transactional 注解应该只被应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的方法将不会展示已配置的事务设置。


4、@Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上。然而,请注意仅仅 @Transactional 注解的出现不足于开启事务行为,它仅仅 是一种元数据,能够被可以识别 @Transactional 注解和上述的配置适当的具有事务行为的beans所使用。上面的例子中,其实正是 <tx:annotation-driven/>元素的出现 开启 了事务行为。


5、Spring团队的建议是你在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。你当然可以在接口上使用 @Transactional 注解,但是这将只能当你设置了基于接口的代理时它才生效。因为注解是 不能继承 的,这就意味着如果你正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装(将被确认为严重的)。因 此,请接受Spring团队的建议并且在具体的类上使用 @Transactional 注解。

Spring的ApplicationEvent的使用

 spring  Spring的ApplicationEvent的使用已关闭评论
3月 102016
 

    Spring 3.0中提供了很多类似*Aware的类,其中ApplicationContextAware接口可以实现我们在初始化bean的时候给bean注入ApplicationConxt(Spring上下文对象)对象。ApplicationContextAware接口提供了publishEvent方法,实现了Observe(观察者)设计模式的传播机制,实现了对bean的传播。通过ApplicationContextAware我们可以把系统中所有ApplicationEvent传播给系统中所有的ApplicationListener。因此,我们只需要构造好我们自己的ApplicationEvent和ApplicationListener,就可以在系统中实现相应的监听器。

   下面以增加学生的示例来演示如何构造Spring的监听器,StudentAddEvent是监听的事件对象,StudentAddListener是事件的监听器(负责处理接收到的监听事件),StudentAddBean负责触发StudentAddEvent事件。具体步骤如下:

1.  定义StudentAddEvent监听事件

新建StudentAddEvent类,实现抽象类

org.springframework.context.ApplicationEvent

StudentAddEvent类中需要实现自己的构造函数,具体代码如下:

[java] view plain copy

  1. package com.trs.spring.event;  
  2.   
  3. import org.springframework.context.ApplicationEvent;  
  4.   
  5. /** 
  6.  * 增加学生的监听事件 
  7.  */  
  8. public class StudentAddEvent extends ApplicationEvent {  
  9.   
  10.     /** 
  11.      *  
  12.      */  
  13.     private static final long serialVersionUID = 20L;  
  14.   
  15.     /** 
  16.      * 学生姓名 
  17.      */  
  18.     private String m_sStudentName;  
  19.   
  20.     /** 
  21.      * @param source 
  22.      */  
  23.     public StudentAddEvent(Object source, String _sStudentName) {  
  24.         super(source);  
  25.         this.m_sStudentName = _sStudentName;  
  26.     }  
  27.   
  28.     /** 
  29.      * 获取学生姓名 
  30.      *  
  31.      * @return 
  32.      */  
  33.     public String getStudentName() {  
  34.         return m_sStudentName;  
  35.     }  
  36.   
  37. }  


 

2.  定义StudentAddListener监听器

新建StudentAddListener类,实现接口

org.springframework.context.ApplicationListener

中的onApplicationEvent方法,在该方法中只处理StudentAddEvent类型的ApplicationEvent事件,代码如下:

[java] view plain copy

  1. package com.trs.spring.event;  
  2.   
  3. import org.springframework.context.ApplicationEvent;  
  4. import org.springframework.context.ApplicationListener;  
  5.   
  6. public class StudentAddListener implements ApplicationListener {  
  7.   
  8.     /* 
  9.      * (non-Javadoc) 
  10.      *  
  11.      * @see 
  12.      * org.springframework.context.ApplicationListener#onApplicationEvent(org 
  13.      * .springframework.context.ApplicationEvent) 
  14.      */  
  15.     public void onApplicationEvent(ApplicationEvent _event) {  
  16.         // 1.判断是否是增加学生对象的事件  
  17.         if (!(_event instanceof StudentAddEvent)) {  
  18.             return;  
  19.         }  
  20.   
  21.         // 2.是增加学生事件的对象,进行逻辑处理,比如记日志、积分等  
  22.         StudentAddEvent studentAddEvent = (StudentAddEvent) _event;  
  23.         System.out.println(“增加了学生:::” + studentAddEvent.getStudentName());  
  24.     }  
  25.   
  26. }  

 

3.  定义StudentAddBean触发StudentAddEvent事件

新建StudentAddBean类,实现接口

org.springframework.context.ApplicationContextAware

中的setApplicationContext方法,在构造bean的时候注入Spring的上下文对象,以便通过Spring上下文对象的publishEvent方法来触发StudentAddEvent事件,具体代码如下:

[java] view plain copy

  1. package com.trs.spring.event;  
  2.   
  3. import org.springframework.beans.BeansException;  
  4. import org.springframework.context.ApplicationContext;  
  5. import org.springframework.context.ApplicationContextAware;  
  6. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  7.   
  8. public class StudentAddBean implements ApplicationContextAware {  
  9.     /** 
  10.      * 定义Spring上下文对象 
  11.      */  
  12.     private ApplicationContext m_applicationContext = null;  
  13.   
  14.     /* 
  15.      * (non-Javadoc) 
  16.      *  
  17.      * @see 
  18.      * org.springframework.context.ApplicationContextAware#setApplicationContext 
  19.      * (org.springframework.context.ApplicationContext) 
  20.      */  
  21.     public void setApplicationContext(ApplicationContext _applicationContext)  
  22.             throws BeansException {  
  23.         this.m_applicationContext = _applicationContext;  
  24.   
  25.     }  
  26.   
  27.     /** 
  28.      * 增加一个学生 
  29.      *  
  30.      * @param _sStudentName 
  31.      */  
  32.     public void addStudent(String _sStudentName) {  
  33.         // 1.构造一个增加学生的事件  
  34.         StudentAddEvent aStudentEvent = new StudentAddEvent(  
  35.                 m_applicationContext, _sStudentName);  
  36.         // 2.触发增加学生事件  
  37.         m_applicationContext.publishEvent(aStudentEvent);  
  38.     }  
  39.   
  40.     /** 
  41.      * @param args 
  42.      */  
  43.     public static void main(String[] args) {  
  44.         String[] xmlConfig = new String[] { “applicationContext.xml” };  
  45.         // 使用ApplicationContext来初始化系统  
  46.         ApplicationContext context = new ClassPathXmlApplicationContext(  
  47.                 xmlConfig);  
  48.         StudentAddBean studentBean = (StudentAddBean) context  
  49.                 .getBean(“StudentAddBean”);  
  50.         studentBean.addStudent(“我是第一个学生”);  
  51.         studentBean.addStudent(“第二个学生已经添加”);  
  52.   
  53.     }  
  54.   
  55. }  

 

4.  applicationContext.xml配置文件

<bean id=”StudentAddBean” class=”com.trs.spring.event.StudentAddBean”></bean>

<bean id=”StudentAddListener” class=”com.trs.spring.event.StudentAddListener”></bean>

 

5.  说明

ApplicationContext在运行期会自动检测到所有实现了ApplicationListener的bean对象,并将其作为事件接收对象。当ApplicationContext的publishEvent方法被触发时,每个实现了ApplicationListener接口的bean都会收到ApplicationEvent对象,每个ApplicationListener可根据事件类型只接收处理自己感兴趣的事件,比如上面的StudentAddListener只接收StudentAddEvent事件。

6.  执行StudentAddBean的main函数,结果如下:

增加了学生:::我是第一个学生

增加了学生:::第二个学生已经添加

 

转自:http://blog.csdn.net/wgw335363240/article/details/7202320

 Posted by at 下午2:45

spring mvc 使用post方法, @RequestBody 和 @RequestParam 单独使用及混合使用时差别

 spring  spring mvc 使用post方法, @RequestBody 和 @RequestParam 单独使用及混合使用时差别已关闭评论
10月 102015
 

spring mvc 使用RequestMethod.POST方法时,@RequestBody 和 @RequestParam 单独使用及混合使用时差别汇总如下:

Using HTTP dev client with Post request and Content-Type application/x-www-form-urlencoded

1) Only @RequestBody

Request – localhost:8080/SpringMVC/welcome In Body – name=abc

Code-

@RequestMapping(method = RequestMethod.POST) public String printWelcome(@RequestBody String body, Model model) { model.addAttribute("message", body); return "hello"; }

// Gives body as ‘name=abc’ as expected

2) Only @RequestParam

Request – localhost:8080/SpringMVC/welcome In Body – name=abc

Code-

@RequestMapping(method = RequestMethod.POST) public String printWelcome(@RequestParam String name, Model model) { model.addAttribute("name", name); return "hello"; }

// Gives name as ‘abc’ as expected

3) Both together

Request – localhost:8080/SpringMVC/welcome In Body – name=abc

Code-

@RequestMapping(method = RequestMethod.POST) public String printWelcome(@RequestBody String body, @RequestParam String name, Model model) { model.addAttribute("name", name); model.addAttribute("message", body); return "hello"; }

// HTTP Error Code 400 – The request sent by the client was syntactically incorrect.

4) Above with params position changed

Request – localhost:8080/SpringMVC/welcome In Body – name=abc

Code-

@RequestMapping(method = RequestMethod.POST) public String printWelcome(@RequestParam String name, @RequestBody String body, Model model) { model.addAttribute("name", name); model.addAttribute("message", body); return "hello"; }

// No Error. Name is ‘abc’. body is empty

5) Together but get type url parameters

Request – localhost:8080/SpringMVC/welcome?name=xyz In Body – name=abc

Code-

@RequestMapping(method = RequestMethod.POST) public String printWelcome(@RequestBody String body, @RequestParam String name, Model model) { model.addAttribute("name", name); model.addAttribute("message", body); return "hello"; }

// name is ‘xyz’ and body is ‘name=abc’

6) Same as 5) but with parameters position changed

Code –

@RequestMapping(method = RequestMethod.POST) public String printWelcome(@RequestParam String name, @RequestBody String body, Model model) { model.addAttribute("name", name); model.addAttribute("message", body); return "hello"; }

// name = ‘xyz,abc’ body is empty

Spring-3 @Autowired,@Qualifier @Required @Resource @Component,@Service,@Controller,@Repository

 spring  Spring-3 @Autowired,@Qualifier @Required @Resource @Component,@Service,@Controller,@Repository已关闭评论
8月 192015
 

记录下,以备不时之需。

@PostConstruct,@PreDestroy

1、@Autowired 注解:
首先在使用时候需要引入配置:

<!-- 该 BeanPostProcessor 将自动起作用,对标注 @Autowired 的 Bean 进行自动注入 --> <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>

在Spring MVC 中 由于配置了“ <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>”

故不使用引入上面自动注入也可实现指定注入功能。

@Autowired 可以标注在属性,以及set方法上,以及构造函数上.进行标注,然后自动完成注入。

 

2、@Qualifier 注解:
如果配置了多个类型为 User 的 Bean, 那么我们在

1
2
3
4
@Autowired
private User setUser(User user){
//….
}

会出现错误,这个时候我们就需要对注入的对象指定读取Bean的名称,则所以 @Autowired 和 @Qualifier 结合使用时,自动注入的策略就从 byType 转变成 byName 了。

1
2
3
4
5
6
7
8
9
10
@Autowired
private User setUser(@Qualifier(“user1”) User user){
//….
}
 
public class Recommender {
    @Autowired
    @Qualifier(“main”)
    private MovieCatalog log;
}

@Autowired 可以对成员变量、方法以及构造函数进行注释,而 @Qualifier 的标注对象是成员变量、方法入参、构造函数入参。正是由于注释对象的不同,所以 Spring 不将 @Autowired 和 @Qualifier 统一成一个注释类。


3、@Required注解:
@Required注解检查 但他只检查属性是否已经设置而不会测试属性是否非空

4、@Resource 写在属性或者是setter方法上,按照名称注入 可以配置name=”…”,
如果不配置则提取 setter方法名,该bean必须存在。
@Resource(name=”user”)
private User user;

5、@Component,@Service,@Controller,@Repository 注解:
Spring 2.5 中除了提供 @Component 注释外,还定义了几个拥有特殊语义的注释,它们分别是:@Repository、@Service 和 @Controller。在目前的 Spring 版本中,这 3 个注释和 @Component 是等效的,但是从注释类的命名上,很容易看出这 3 个注释分别和持久层、业务层和控制层(Web 层)相对应。虽然目前这 3 个注释和 @Component 相比没有什么新意,但 Spring 将在以后的版本中为它们添加特殊的功能。所以,如果 Web 应用程序采用了经典的三层分层结构的话,最好在持久层、业务层和控制层分别采用 @Repository、@Service 和 @Controller 对分层中的类进行注释,而用 @Component 对那些比较中立的类进行注释。

6、@PostConstruct 注解,标识Bean在初始化的时候执行,
@PreDestroy 注解,表示在Bean在对象销毁的时候执行。

1
2
3
4
5
6
7
8
9
@PostConstruct
public void init() {
 
}
 
@PreDestroy
public void destory() {
 
}

7、@Scope 注解 设置作用域:

prototype,singleton 注意spring2.0后 又增加了request ,session和global session 4个作用区域

转自:http://www.cnblogs.com/rhythmK/p/3412549.html

解决使用spring restful 获取匹配path包含(.)时path ariable被截断问题

 java, spring  解决使用spring restful 获取匹配path包含(.)时path ariable被截断问题已关闭评论
6月 192015
 

Spring considers that anything behind the last dot is a file extension such as .jsonor .xml and trucate it to retrieve your parameter.

So if you have /somepath/{variable} :

  • /somepath/param, /somepath/param.json, /somepath/param.xml or /somepath/param.anything will result in a param with value param
  • /somepath/param.value.json, /somepath/param.value.xml or /somepath/param.value.anything will result in a param with value param.val

翻译下就是sprng会将任何在最后一个”.”字符后的部分当作文件类型并截断。

解决方法如下:

1.  将匹配路径有原来的 /path/{variable} 修改为  /path/{variable}/   ,即最后添加/, 虽然简单但也要求请求时需要最后的/ 

2.  将匹配路径有原来的 /path/{variable} 修改为 /path/{variable:.+} , 及使用正则表达式

3.  如果项目不需要通过后缀区分类型, 可修改配置文件,修改RequestMappingHandlerMapping类属性useSuffixPatternMatch为false
<bean class=”org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping”>
        <property name=”useSuffixPatternMatch” value=”false”/>
    </bean>

done!

spring-rabbit配置多vhost,多connect-factory时配置exchange, queue需使用declared-by

 java, spring  spring-rabbit配置多vhost,多connect-factory时配置exchange, queue需使用declared-by已关闭评论
3月 132015
 

大家在使用spring-rabbit 包配置时一般是这样使用的:

<?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:context=”http://www.springframework.org/schema/context”
       xmlns:rabbit=”http://www.springframework.org/schema/rabbit”
       xsi:schemaLocation=”
            http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
                http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/rabbit
                http://www.springframework.org/schema/rabbit/spring-rabbit-1.2.xsd”>

   <rabbit:connection-factory id=”connectionFactory” host=”queue.o-u-u.com” username=”o-u-u”
        password=”o-u-u” port=”5611″ virtual-host=”ouu_vh1″/>
         
   <rabbit:admin connection-factory=”connectionFactory”/>
    
   <!– queue 队列声明–>
   <rabbit:queue id=”ouu.request.queue” durable=”false” auto-delete=”false” exclusive=”false” name=”ouu.request.queue” />
   
   <!– exchange queue binging key 绑定 –>
    <rabbit:direct-exchange name=”ouu-request-exchange” durable=”false” auto-delete=”false” id=”ouu-request-exchange” >
        <rabbit:bindings>
            <rabbit:binding queue=”ouu.request.queue” key=”ouu.request.queue.key”/>
        </rabbit:bindings>
    </rabbit:direct-exchange>
    

    <!– spring template声明 –>
    <rabbit:template exchange=”ouu-request-exchange” id=”ouuamqpTemplate”  connection-factory=”connectionFactory” />

</beans>

那么如果我有令一个exchange,需要使用第二个vhost,应该怎么配呢?

如果直接添加使用下面的

 <rabbit:connection-factory id=”connectionFactory2″ host=”queue.o-u-u.com” username=”o-u-u”
        password=”o-u-u” port=”5611″ virtual-host=”ouu_vh2″/>
    
   <!– queue 队列声明–>
   <rabbit:queue id=”ouu2.request.queue” durable=”false” auto-delete=”false” exclusive=”false” name=”ouu2.request.queue” />
   
   <!– exchange queue binging key 绑定 –>
    <rabbit:direct-exchange name=”ouu-request-exchange2″ durable=”false” auto-delete=”false” id=”ouu2-request-exchange” >
        <rabbit:bindings>
            <rabbit:binding queue=”ouu2.request.queue” key=”ouu2.request.queue.key”/>
        </rabbit:bindings>
    </rabbit:direct-exchange>
    

    <!– spring template声明 –>
    <rabbit:template exchange=”ouu2-request-exchange” id=”ouu2amqpTemplate”  connection-factory=”connectionFactory2″ />

</beans>

结果会如何? 

你会发现ouu2.request.queue只有在手动创建后才能写入,并且在virtual-host=”ouu_vh1″ 竟然建立了一个ouu2.request.queue和ouu2-request-exchange



正确的代码应该使用declared-by,记得还要使用 http://www.springframework.org/schema/rabbit/spring-rabbit-1.2.xsd

<?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:context=”http://www.springframework.org/schema/context”
       xmlns:rabbit=”http://www.springframework.org/schema/rabbit”
       xsi:schemaLocation=”
            http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
                http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/rabbit
                http://www.springframework.org/schema/rabbit/spring-rabbit-1.2.xsd”>

   <rabbit:connection-factory id=”connectionFactory” host=”queue.o-u-u.com” username=”o-u-u”
        password=”o-u-u” port=”5611″ virtual-host=”ouu_vh1″/>
         
   <rabbit:admin  id=”connectionFactory-admin”  connection-factory=”connectionFactory”/>
    
   <!– queue 队列声明–>
   <rabbit:queue id=”ouu.request.queue” durable=”false” auto-delete=”false” exclusive=”false” name=”ouu.request.queue” declared-by=”connectionFactory-admin” />
   
   <!– exchange queue binging key 绑定 –>
    <rabbit:direct-exchange name=”ouu-request-exchange” durable=”false” auto-delete=”false” id=”ouu-request-exchange” declared-by=”connectionFactory-admin” >
        <rabbit:bindings>
            <rabbit:binding queue=”ouu.request.queue” key=”ouu.request.queue.key”/>
        </rabbit:bindings>
    </rabbit:direct-exchange>
    

    <!– spring template声明 –>
    <rabbit:template exchange=”ouu-request-exchange” id=”ouuamqpTemplate”  connection-factory=”connectionFactory” />

    

 <rabbit:connection-factory id=”connectionFactory2″ host=”queue.o-u-u.com” username=”o-u-u”
        password=”o-u-u” port=”5611″ virtual-host=”ouu_vh2″/>
    

 <rabbit:admin  id=”connectionFactory2-admin”  connection-factory=”connectionFactory2″/>
   <!– queue 队列声明–>
   <rabbit:queue id=”ouu2.request.queue” durable=”false” auto-delete=”false” exclusive=”false” name=”ouu2.request.queue” declared-by=”connectionFactory2-admin” />
   
   <!– exchange queue binging key 绑定 –>
    <rabbit:direct-exchange name=”ouu-request-exchange2″ durable=”false” auto-delete=”false” id=”ouu2-request-exchange” declared-by=”connectionFactory2-admin” >
        <rabbit:bindings>
            <rabbit:binding queue=”ouu2.request.queue” key=”ouu2.request.queue.key”/>
        </rabbit:bindings>
    </rabbit:direct-exchange>
    

    <!– spring template声明 –>
    <rabbit:template exchange=”ouu2-request-exchange” id=”ouu2amqpTemplate”  connection-factory=”connectionFactory2″ />

</beans>

详细可参考spring文档: http://docs.spring.io/spring-amqp/reference/html/amqp.html    (3.8.1 Conditional Declaration一节)