java之其他题

 

 

三、Spring相关

44、IOC(Inversion of Control)的理解

 (1)、IoC(Inversion of Control)是指容器控制程序对象之间的关系,而不是传统实现中,由程序代码直接操控。控制权由应用代码中转到了外部容器,控制权的转移是所谓反转。 对于Spring而言,就是由Spring来控制对象的生命周期和对象之间的关系;IoC还有另外一个名字——“依赖注入(Dependency Injection)”。从名字上理解,所谓依赖注入,即组件之间的依赖关系由容器在运行期决定,即由容器动态地将某种依赖关系注入到组件之中。  

(2)、在Spring的工作方式中,所有的类都会在spring容器中登记,告诉spring这是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。

(3)、在系统运行中,动态的向某个对象提供它所需要的其他对象。  

(4)、依赖注入的思想是通过反射机制实现的,在实例化一个类时,它通过反射调用类中set方法将事先保存在HashMap中的类属性注入到类中。 总而言之,在传统的对象创建方式中,通常由调用者来创建被调用者的实例,而在Spring中创建被调用者的工作由Spring来完成,然后注入调用者,即所谓的依赖注入or控制反转。 注入方式有两种:依赖注入和设置注入; IoC的优点:降低了组件之间的耦合,降低了业务对象之间替换的复杂性,使之能够灵活的管理对象。

IOC的实现原理

Spring中的IOC的实现原理就是工厂模式加反射机制。 我们首先看一下不用反射机制时的工厂模式:

interface fruit{  
    public abstract void eat();  
}   
class Apple implements fruit{  
     public void eat(){  
         System.out.println("Apple");  
     }  
}   
class Orange implements fruit{  
     public void eat(){  
         System.out.println("Orange");  
     }  
}  
//构造工厂类  
//也就是说以后如果我们在添加其他的实例的时候只需要修改工厂类就行了  
class Factory{  
     public static fruit getInstance(String fruitName){  
         fruit f=null;  
         if("Apple".equals(fruitName)){  
             f=new Apple();  
         }  
         if("Orange".equals(fruitName)){  
             f=new Orange();  
         }  
         return f;  
     }  
}  
class hello{  
     public static void main(String[] a){  
         fruit f=Factory.getInstance("Orange");  
         f.eat();  
     }  
}  

上面写法的缺点是当我们再添加一个子类的时候,就需要修改工厂类了。如果我们添加太多的子类的时候,改动就会很多。下面用反射机制实现工厂模式:

interface fruit{  
     public abstract void eat();  
}  
class Apple implements fruit{  
public void eat(){  
         System.out.println("Apple");  
     }  
}  
class Orange implements fruit{  
public void eat(){  
        System.out.println("Orange");  
    }  
}  
class Factory{  
    public static fruit getInstance(String ClassName){  
        fruit f=null;  
        try{  
            f=(fruit)Class.forName(ClassName).newInstance();  
        }catch (Exception e) {  
            e.printStackTrace();  
        }  
        return f;  
    }  
}  
class hello{  
    public static void main(String[] a){  
        fruit f=Factory.getInstance("Reflect.Apple");  
        if(f!=null){  
            f.eat();  
        }  
    }  
}  

     现在就算我们添加任意多个子类的时候,工厂类都不需要修改。使用反射机制实现的工厂模式可以通过反射取得接口的实例,但是需要传入完整的包和类名。而且用户也无法知道一个接口有多少个可以使用的子类,所以我们通过属性文件的形式配置所需要的子类。

     下面编写使用反射机制并结合属性文件的工厂模式(即IoC)。首先创建一个fruit.properties的资源文件:

apple=Reflect.Apple  
orange=Reflect.Orange  

 然后编写主类代码:

interface fruit{  
    public abstract void eat();  
}  
class Apple implements fruit{  
    public void eat(){  
        System.out.println("Apple");  
    }  
}  
class Orange implements fruit{  
    public void eat(){  
        System.out.println("Orange");  
    }  
}  
//操作属性文件类  
class init{  
    public static Properties getPro() throws FileNotFoundException, IOException{  
        Properties pro=new Properties();  
        File f=new File("fruit.properties");  
        if(f.exists()){  
            pro.load(new FileInputStream(f));  
        }else{  
            pro.setProperty("apple", "Reflect.Apple");  
            pro.setProperty("orange", "Reflect.Orange");  
            pro.store(new FileOutputStream(f), "FRUIT CLASS");  
        }  
        return pro;  
    }  
}  
class Factory{  
    public static fruit getInstance(String ClassName){  
        fruit f=null;  
        try{  
            f=(fruit)Class.forName(ClassName).newInstance();  
        }catch (Exception e) {  
            e.printStackTrace();  
        }  
        return f;  
    }  
}  
class hello{  
    public static void main(String[] a) throws FileNotFoundException, IOException{  
        Properties pro=init.getPro();  
        fruit f=Factory.getInstance(pro.getProperty("apple"));  
        if(f!=null){  
            f.eat();  
        }  
    }  
}  

运行结果:Apple

IOC容器的技术剖析

    IOC中最基本的技术就是“反射(Reflection)”编程,通俗来讲就是根据给出的类名(字符串方式)来动态地生成对象,这种编程方式可以让对象在生成时才被决定到底是哪一种对象。只是在Spring中要生产的对象都在配置文件中给出定义,目的就是提高灵活性和可维护性。

    目前C#、Java和PHP5等语言均支持反射,其中PHP5的技术书籍中,有时候也被翻译成“映射”。有关反射的概念和用法,大家应该都很清楚。反射的应用是很广泛的,很多的成熟的框架,比如像Java中的Hibernate、Spring框架,.Net中NHibernate、Spring.NET框架都是把”反射“做为最基本的技术手段。

    反射技术其实很早就出现了,但一直被忽略,没有被进一步的利用。当时的反射编程方式相对于正常的对象生成方式要慢至少得10倍。现在的反射技术经过改良优化,已经非常成熟,反射方式生成对象和通常对象生成方式,速度已经相差不大了,大约为1-2倍的差距。

    我们可以把IOC容器的工作模式看做是工厂模式的升华,可以把IOC容器看作是一个工厂,这个工厂里要生产的对象都在配置文件中给出定义,然后利用编程语言提供的反射机制,根据配置文件中给出的类名生成相应的对象。从实现来看,IOC是把以前在工厂方法里写死的对象生成代码,改变为由配置文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。

IOC底层实现原理

底层实现使用的技术:

(1)xml配置文件

(2)dom4j解析xml

(3)工厂模式

(4)反射

 

    首先,通过dom4j将我们的配置文件读取,这时我们就可以解析到所有相关的类的全路径了。然后,它再利用反射机制通过如下代码完成类的实例化:类1=Class.forName("类1的全路径")。这时,我们就得到了类1。(这也是为啥当我们的类的全路径写错了会导致出现classNotfind的错误。)

    当我们得到了类1以后,通过调用类1的set方法,将属性给对象进行注入。而且,需要遵循首字母大写的set规范。例如:我们的类中有个字段的属性为name那么set方法必须写成setName(name 的首字母要大写)否则会报一个属性找不到的错误:

public void setName(String name){ 
     this.name= name; 
} 

    对象创建后,我们将我们的对象id和我们的对象物理地址,一起存入类似于HashMap的容器中,然后呢,我们是如何获得我们需要的对象,然后执行对象中的方法呢?我们通过getBean的方法,通过对象Id获得对象的物理地址,得到对象,然后调用对象的方法,完成对方法的调用。

 

SpringAOP 的具体加载步骤

(1)当 spring 容器启动的时候,加载了 spring 的配置文件。

(2)为配置文件中的所有 bean 创建对象。

(3)spring 容器会解析 aop:config 的配置

    解析切入点表达式,用切入点表达式和纳入 spring 容器中的 bean 做匹配。如果匹配成功,则会为该 bean 创建代理对象,代理对象的方法=目标方法+通知,如果匹配不成功,不会创建代理对象。

(4)在客户端利用 context.getBean() 获取对象时,如果该对象有代理对象,则返回代理对象;如果没有,则返回目标对象。

说明:如果目标类没有实现接口,则 spring 容器会采用 cglib 的方式产生代理对象,如果实现了接口,则会采用 jdk 的方式

 

46、BeanFactroy 与 ApplicationContext 的区别

    BeanFactory是Spring框架最核心的接口,它提供了高级IOC的配置机制。ApplicationContext建立在BeanFactory基础之上,提供了更多面向应用的功能,它提供了国际化支持和框架事件体系,更易于创建实际应用。一般称BeanFactory为IOC容器,而称ApplicationContext为应用上下文。

    BeanFactory是一个类工厂,可以创建并管理各种类的对象,Spring称这些创建和管理的Java对象为Bean。ApplicationContext由BeanFactory派生而来,提供了更多面向实际应用的功能。在BeanFactory中,很多功能需要以编程的方式方式实现,而在ApplicationContext中则可以通过配置的方式实现。

    BeanFactory负责读取bean配置文档,管理bean的加载,实例化,维护bean之间的依赖关系,负责bean的声明周期。ApplicationContext除了提供上述BeanFactory所能提供的功能之外,还提供了更完整的框架功能:

a. MessageSource, 提供国际化的消息访问  
b. 资源访问,如URL和文件  
c. 事件传播特性,即支持aop特性
d. 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层 

(1)BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化,这样,我们就不能发现一些存在的Spring的配置问题。而ApplicationContext则相反,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误。 相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。

BeanFacotry延迟加载,如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常;而ApplicationContext则在初始化自身是检验,这样有利于检查所依赖属性是否注入;所以通常情况下我们选择使用 ApplicationContext。
应用上下文则会在上下文启动后预载入所有的单实例Bean。通过预载入单实例bean,确保当你需要的时候,你就不用等待,因为它们已经创建好了。

(2)BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。(Applicationcontext比 beanFactory 加入了一些更好使用的功能。而且 beanFactory 的许多功能需要通过编程实现而 Applicationcontext 可以通过配置实现。比如后处理 bean , Applicationcontext 直接配置在配置文件即可而 beanFactory 这要在代码中显示的写出来才可以被容器识别。 )

(3)beanFactory主要是面对与 spring 框架的基础设施,面对 spring 自己。而 Applicationcontex 主要面对与 spring 使用的开发者。基本都会使用 Applicationcontex 并非 beanFactory 。

47、Bean的作用域

作用域描述
singleton

在每个Spring IoC容器中一个bean定义对应一个对象实例。

(默认)在spring IOC容器中仅存在一个Bean实例,Bean以单实例的方式存在。

prototype

一个bean定义对应多个对象实例。

每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行new XxxBean()的操作。

request

在一次HTTP请求中,一个bean定义对应一个实例;即每次HTTP请求将会有各自的bean实例,它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。

session

在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。

同一个HTTP session共享一个Bean,不同HTTP session使用不同的Bean,该作用域仅适用于webApplicationContext环境。

globalSession

在一个全局的HTTP Session中,一个bean定义对应一个实例。典型情况下,仅在使用portlet context的时候有效。该作用域仅在基于web的Spring ApplicationContext情形下有效。

  

依赖注入方式

    对于spring配置一个bean时,如果需要给该bean提供一些初始化参数,则需要通过依赖注入方式,所谓的依赖注入就是通过spring将bean所需要的一些参数传递到bean实例对象的过程,spring的依赖注入有3种方式:

·使用属性的setter方法注入 ,这是最常用的方式。属性注入即通过setXxx()方法注入Bean的属性值或依赖对象,由于属性注入方式具有可选择性和灵活性高的优点,因此属性注入是实际应用中最常采用的注入方式。

·使用构造器注入。在类中,不用为属性设置setter方法,但是需要生成该类带参的构造方法。同时,在配置文件中配置该类的bean,并配置构造器,在配置构造器中用到了<constructor-arg>节点,可以指定按类型匹配入参还是按索引匹配入参。

·使用Filed注入(用于注解方式)。使用注解注入依赖对象不用再在代码中写依赖对象的setter方法或者该类的构造方法,并且不用再配置文件中配置大量的依赖对象,使代码更加简洁,清晰,易于维护。在Spring IOC编程的实际开发中推荐使用注解的方式进行依赖注入。

自动装配

    在应用中,我们常常使用<ref>标签为JavaBean注入它依赖的对象,同时也Spring为我们提供了一个自动装配的机制,在定义Bean时,<bean>标签有一个autowire属性,我们可以通过指定它来让容器为受管JavaBean自动注入依赖对象。

自动装配是在配置文件中实现的,如下:<bean id="***" class="***" autowire="byType">

只需要配置一个autowire属性即可完成自动装配,不用再配置文件中写<property>,但是在类中还是要生成依赖对象的setter方法。

<bean>的autowire属性有如下六个取值,他们的说明如下:

(1)No:即不启用自动装配。Autowire默认的值。默认情况下,需要通过"ref"来装配bean。

(2)byName:按名称装配。可以根据属性的名称在容器中查询与该属性名称相同的bean,如果没有找到,则属性值为null。假设Boss类中有一个名为car的属性,如果容器中刚好有一个名为car的Bean,Spring就会自动将其装配给Boss的car属性。

(3)byType:按类型装配。可以根据属性类型,在容器中寻找该类型匹配的bean,如有多个,则会抛出异常,如果没有找到,则属性值为null。假设Boss类中有一个Car类型的属性,如果容器中刚好有一个Car类型的Bean,Spring就会自动将其装配给Boss的这个属性。

(4)constructor:与byType方式相似,不同之处在与它应用于构造器参数,如果在容器中没有找到与构造器参数类型一致的bean,那么将抛出异常。(根据构造函数参数的数据类型,进行byType模式的自动装配。)

(5)autodetect:通过bean类的自省机制(introspection)来决定是使用constructor还是byType的方式进行自动装配。如果Bean有空构造器那么将采用“byType”自动装配方式,否则使用“constructor”自动装配方式。

(6)default:由上级标签<beans>的default-autowire属性确定。

注:不是所有类型都能自动装配,不能自动装配的数据类型:Object、基本数据类型(Date、CharSequence、Number、URI、URL、Class、int)等。

Spring常用注解

传统的Spring做法是使用.xml文件来对bean进行注入或者是配置aop、事物,这么做有两个缺点:

(1)如果所有的内容都配置在.xml文件中,那么.xml文件将会十分庞大;如果按需求分开.xml文件,那么.xml文件又会非常多。总之这将导致配置文件的可读性与可维护性变得很低。

(2)在开发中在.java文件和.xml文件之间不断切换,是一件麻烦的事,同时这种思维上的不连贯也会降低开发的效率。

为了解决这两个问题,Spring引入了注解,通过"@XXX"的方式,让注解与Java Bean紧密结合,既大大减少了配置文件的体积,又增加了Java Bean的可读性与内聚性。

Spring常用注解总结:

@Configuration把一个类作为一个IoC容器,它的某个方法头上如果注册了@Bean,就会作为这个Spring容器中的Bean。

     @Configuration标注在类上,相当于把该类作为spring的xml配置文件中的<beans>,作用为:配置spring容器(应用上下文)。@Bean标注在方法上(返回某个实例的方法),等价于spring的xml配置文件中的<bean>,作用为:注册bean对象。用@Configuration注解类,等价于XML中配置beans;用@Bean标注方法等价于XML中配置bean。

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

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

@Service用于标注业务层组件。

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

@Autowired:顾名思义,就是自动装配,其作用是为了消除代码Java代码里面的getter/setter与bean属性中的property。当然,getter看个人需求,如果私有属性需要对外提供的话,应当予以保留。@Autowired默认按类型匹配的方式,在容器查找匹配的Bean,当有且仅有一个匹配的Bean时,Spring将其注入@Autowired标注的变量中。

@Qualifier:用来指定注入Bean的名称。如果容器中有一个以上匹配的Bean,则可以通过@Qualifier注解限定Bean的名称。通常@Qualifier可以结合@Autowired注解一起使用。如下:@Autowired @Qualifier("personDaoBean") 存在多个实例配合使用。

@Resource默认按名称装配,当找不到与名称匹配的bean才会按类型装配。

说一下@Resource的装配顺序:

(1)@Resource后面没有任何内容,默认通过name属性去匹配bean,找不到再按type去匹配。

(2)指定了name或者type则根据指定的类型去匹配bean。

(3)指定了name和type则根据指定的name和type去匹配bean,任何一个不匹配都将报错。

然后,区分一下@Autowired和@Resource两个注解的区别:

(1)@Autowired默认按照byType方式进行bean匹配,@Resource默认按照byName方式进行bean匹配。

(2)@Autowired是Spring的注解,@Resource是J2EE的注解,这个看一下导入注解的时候这两个注解的包名就一清二楚了。

Spring属于第三方的,J2EE是Java自己的东西,因此,建议使用@Resource注解,以减少代码和Spring之间的耦合。

Spring事务管理:

spring支持编程式事务管理和声明式事务管理两种方式。

    编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。

    声明式事务管理建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。

    显然声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。和编程式事务相比,声明式事务唯一不足地方是,后者的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。

    声明式事务管理也有两种常用的方式,一种是基于tx和aop名字空间的xml配置文件,另一种就是基于@Transactional注解。显然基于注解的方式更简单易用,更清爽。

spring事务回滚规则

     指示spring事务管理器回滚一个事务的推荐方法是在当前事务的上下文内抛出异常。spring事务管理器会捕捉任何未处理的异常,然后依据规则决定是否回滚抛出异常的事务。

     默认配置下,spring只有在抛出的异常为运行时unchecked异常时才回滚该事务,也就是抛出的异常为RuntimeException的子类(Errors也会导致事务回滚),而抛出checked异常则不会导致事务回滚。可以明确的配置在抛出哪些异常时回滚事务,包括checked异常。也可以明确定义哪些异常抛出时不回滚事务。还可以编程性的通过setRollbackOnly()方法来指示一个事务必须回滚,在调用完setRollbackOnly()后你所能执行的唯一操作就是回滚。

checked异常: 表示无效,不是程序中可以预测的。比如无效的用户输入,文件不存在,网络或者数据库链接错误。这些都是外在的原因,都不是程序内部可以控制的。 必须在代码中显式地处理。比如try-catch块处理,或者给所在的方法加上throws说明,将异常抛到调用栈的上一层。 继承自java.lang.Exception(java.lang.RuntimeException除外)。

unchecked异常: 表示错误,程序的逻辑错误。是RuntimeException的子类,比如IllegalArgumentException, NullPointerException和IllegalStateException。 不需要在代码中显式地捕获unchecked异常做处理。 继承自java.lang.RuntimeException(而java.lang.RuntimeException继承自java.lang.Exception)。

    java里面将派生于Error或者RuntimeException(比如空指针,1/0)的异常称为unchecked异常,其他继承自java.lang.Exception的异常统称为Checked Exception,如IOException、TimeoutException等。那么再通俗一点:你写代码出现的空指针等异常,会被回滚,文件读写,网络出问题,spring就没法回滚了。

@Transactional注解

@Transactional属性 

属性类型描述
value String 可选的限定描述符,指定使用的事务管理器
propagation enum: Propagation 可选的事务传播行为设置
isolation enum: Isolation 可选的事务隔离级别设置
readOnly boolean 读写或只读事务,默认读写
timeout int (in seconds granularity) 事务超时时间设置
rollbackFor Class对象数组,必须继承自Throwable 导致事务回滚的异常类数组
rollbackForClassName 类名数组,必须继承自Throwable 导致事务回滚的异常类名字数组
noRollbackFor Class对象数组,必须继承自Throwable 不会导致事务回滚的异常类数组
noRollbackForClassName 类名数组,必须继承自Throwable 不会导致事务回滚的异常类名字数组

 

 

 

 

 

 

 

 

 

 

 

 

 

 

用法:

    @Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。

    虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。

    默认情况下,只有来自外部的方法调用才会被AOP代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用@Transactional注解进行修饰。

   Spring使用声明式事务处理,默认情况下,如果被注解的数据库操作方法中发生了unchecked异常,所有的数据库操作将rollback;如果发生的异常是checked异常,默认情况下数据库操作还是会提交的。

如何改变默认规则: 

(1)让checked例外也回滚:在整个方法前加上 @Transactional(rollbackFor=Exception.class) 

(2)让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class) 

(3)不需要事务管理的(只查询的)方法:@Transactional(propagation=Propagation.NOT_SUPPORTED) 

在整个方法运行前就不会开启事务 ,还可以加上:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true),这样就做成一个只读事务,可以提高效率。 

@Autowired  
private MyBatisDao dao;  
  
@Transactional  
@Override  
public void insert(Test test) {  
    dao.insert(test);  
    throw new RuntimeException("test");//抛出unchecked异常,触发事物,回滚  
}  

noRollbackFor

@Transactional(noRollbackFor=RuntimeException.class)  
@Override  
public void insert(Test test) {  
    dao.insert(test);  
    //抛出unchecked异常,触发事物,noRollbackFor=RuntimeException.class,不回滚  
    throw new RuntimeException("test");  
}

类,当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性

@Transactional  
public class MyBatisServiceImpl implements MyBatisService {  
    @Autowired  
    private MyBatisDao dao;  
    @Override  
    public void insert(Test test) {  
        dao.insert(test);  
        //抛出unchecked异常,触发事物,回滚  
        throw new RuntimeException("test");  
    }
}

propagation=Propagation.NOT_SUPPORTED

@Transactional(propagation=Propagation.NOT_SUPPORTED)  
@Override  
public void insert(Test test) {  
    //事物传播行为是PROPAGATION_NOT_SUPPORTED,以非事务方式运行,不会存入数据库  
    dao.insert(test);  
}  

例子:

@Service
public class SysConfigService {
    @Autowired
    private SysConfigRepository sysConfigRepository;
    public SysConfigEntity getSysConfig(String keyName) {
        SysConfigEntity entity = sysConfigRepository.findOne(keyName);
        return entity;
    }
    public SysConfigEntity saveSysConfig(SysConfigEntity entity) {
        
        if(entity.getCreateTime()==null){
            entity.setCreateTime(new Date());
        }
        
        return sysConfigRepository.save(entity);
                
    }
    
    @Transactional
    public void testSysConfig(SysConfigEntity entity) throws Exception {
        //不会回滚
        this.saveSysConfig(entity);
        throw new Exception("sysconfig error");
        
    }
    
    @Transactional(rollbackFor = Exception.class)
    public void testSysConfig1(SysConfigEntity entity) throws Exception {
        //会回滚
        this.saveSysConfig(entity);
        throw new Exception("sysconfig error");
        
    }
    
    @Transactional
    public void testSysConfig2(SysConfigEntity entity) throws Exception {
        //会回滚
        this.saveSysConfig(entity);
        throw new RuntimeException("sysconfig error");
        
    }
    
    @Transactional
    public void testSysConfig3(SysConfigEntity entity) throws Exception {
        //事务仍然会被提交
        this.testSysConfig4(entity);
        throw new Exception("sysconfig error");
    }
    
    @Transactional(rollbackFor = Exception.class)
    public void testSysConfig4(SysConfigEntity entity) throws Exception {
        
        this.saveSysConfig(entity);
    }
}

@Transactional事务使用总结:

(1)异常在A方法内抛出,则A方法就得加注解

(2)多个方法嵌套调用,如果都有 @Transactional 注解,则产生事务传递,默认 Propagation.REQUIRED

(3)如果注解上只写 @Transactional 默认只对 RuntimeException 回滚,而非 Exception 进行回滚。如果要对 checked Exceptions 进行回滚,则需要 @Transactional(rollbackFor = Exception.class),rollbackFor这属性指定了,即使你出现了checked这种例外,那么它也会对事务进行回滚。

解决@Transactional注解不回滚

(1)检查你方法是不是public的。

(2)你的异常类型是不是unchecked异常。如果我想check异常也想回滚怎么办,注解上面写明异常类型即可。

@Transactional(rollbackFor=Exception.class) 

类似的还有norollbackFor,自定义不回滚的异常。

(3)数据库引擎要支持事务,如果是MySQL,注意表要使用支持事务的引擎,比如innodb,如果是myisam,事务是不起作用的。

(4)是否开启了对注解的解析。

 <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>

(5)spring是否扫描到你这个包,如下是扫描到org.test下面的包。

<context:component-scan base-package="org.test" ></context:component-scan>

(6)检查是不是同一个类中的方法调用(如a方法调用同一个类中的b方法)。 

(7)异常是不是被你catch住了。 

SpringMVC工作流程

(1)用户发起请求到前端控制器(Controller)

(2)前端控制器没有处理业务逻辑的能力,需要找到具体的模型对象处理(Handler),到处理器映射器(HandlerMapping)中查找Handler对象(Model)。

(3)HandlerMapping返回执行链,包含了2部分内容: ① Handler对象、② 拦截器数组

(4)前端处理器通过处理器适配器包装后执行Handler对象。

(5)处理业务逻辑。

(6)Handler处理完业务逻辑,返回ModelAndView对象,其中view是视图名称,不是真正的视图对象。

(7)将ModelAndView返回给前端控制器。

(8)视图解析器(ViewResolver)返回真正的视图对象(View)。

(9)(此时前端控制器中既有视图又有Model对象数据)前端控制器根据模型数据和视图对象,进行视图渲染。

(10)返回渲染后的视图(html/json/xml)返回。

(11)给用户产生响应。

(1)用户发送请求至前端控制器DispatcherServlet。

(2)DispatcherServlet收到请求调用HandlerMapping处理器映射器。

(3)处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。

(4)DispatcherServlet调用HandlerAdapter处理器适配器。

(5)HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。

(6)Controller执行完成返回ModelAndView。

(7)HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。

(8)DispatcherServlet将ModelAndView传给ViewReslover视图解析器。

(9)ViewReslover解析后返回具体View。

(10)DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。

(11)DispatcherServlet响应用户。

(1)用户向服务器发送HTTP请求,请求被前端控制器 DispatcherServlet 捕获。

(2)DispatcherServlet 根据 <servlet-name>-servlet.xml 中的配置对请求的URL进行解析,得到请求资源标识符(URI)。 然后根据该URI,调用 HandlerMapping 获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以 HandlerExecutionChain 对象的形式返回。

(3)DispatcherServlet 根据获得的Handler,选择一个合适的 HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(…​)方法)。

(4)提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:

HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息。

数据转换:对请求消息进行数据转换。如String转换成Integer、Double等。

数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等。

数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中。

(5)Handler(Controller)执行完成后,向 DispatcherServlet 返回一个 ModelAndView 对象;

(6)根据返回的ModelAndView,选择一个适合的 ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet。

(7)ViewResolver 结合Model和View,来渲染视图。

(8)视图负责将渲染结果返回给客户端。

 

组件:

(1)前端控制器DispatcherServlet(不需要工程师开发),由框架提供

作用:接收请求,响应结果,相当于转发器,中央处理器。有了dispatcherServlet减少了其它组件之间的耦合度。
用户请求到达前端控制器,它就相当于mvc模式中的c,dispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。

(2)处理器映射器HandlerMapping(不需要工程师开发),由框架提供

作用:根据请求的url查找Handler。HandlerMapping负责根据用户请求找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。

(3)处理器适配器HandlerAdapter

作用:按照特定规则(HandlerAdapter要求的规则)去执行Handler。通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

(4)处理器Handler(需要工程师开发)

注意:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以去正确执行Handler。Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。由于Handler涉及到具体的用户业务请求,所以一般情况需要工程师根据业务需求开发Handler。

(5)视图解析器View resolver(不需要工程师开发),由框架提供

作用:进行视图解析,根据逻辑视图名解析成真正的视图(view)。View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。 springmvc框架提供了很多的View视图类型,包括:jstlView、freemarkerView、pdfView等。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由工程师根据业务需求开发具体的页面。

(6)视图View(需要工程师开发jsp...)

View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf...)。

SpringMVC注解

@RequestMapping:RequestMapping是一个用来处理请求地址映射的注解(将请求映射到对应的控制器方法中),可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。RequestMapping请求路径映射,如果标注在某个controller的类级别上,则表明访问此类路径下的方法都要加上其配置的路径;最常用是标注在方法上,表明哪个具体的方法来接受处理某次请求。

RequestMapping的属性

value:指定请求的实际url

method:指定请求的method类型, GET、POST、PUT、DELETE等;
@RequestMapping(value="/get/{bookid}",method={RequestMethod.GET,RequestMethod.POST})

params:指定request中必须包含某些参数值是,才让该方法处理。
@RequestMapping(params="action=del"),请求参数包含“action=del”,如:http://localhost:8080/book?action=del

headers:指定request中必须包含某些指定的header值,才能让该方法处理请求。

consumes:指定处理请求的提交内容类型(Content-Type),例如application/json, text/html。

produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回。 

@RequestParam:绑定单个请求参数值

@RequestParam有以下三个参数:

value:参数名字,即入参的请求参数名字,如username表示请求的参数区中的名字为username的参数的值将传入;

required:是否必须,默认是true,表示请求中一定要有相应的参数,否则将抛出异常;

defaultValue:默认值,表示如果请求中没有同名参数时的默认值,设置该参数时,自动将required设为false。

@PathVariable:绑定URI模板变量值

@PathVariable用于将请求URL中的模板变量映射到功能处理方法的参数上。

@ModelAttribute:ModelAttribute可以应用在方法参数上或方法上,他的作用主要是当注解在方法参数上时会将注解的参数对象添加到Model中;当注解在请求处理方法Action上时会将该方法变成一个非请求处理的方法,但其它Action被调用时会首先调用该方法。

@Responsebody:@Responsebody表示该方法的返回结果直接写入HTTP response body中。一般在异步获取数据时使用,在使用@RequestMapping后,返回值通常解析为跳转路径,加上@Responsebody后返回结果不会被解析为跳转路径,而是直接写入HTTP response body中。比如异步获取json数据,加上@Responsebody后,会直接返回json数据。

@RequestBody:将HTTP请求正文插入方法中,使用适合的HttpMessageConverter将请求体写入某个对象。

posted on 2016-03-28 11:43  左手指月  阅读(7863)  评论(0编辑  收藏  举报