Spring是一个开源框架。Spring为简化企业级开发而生,使用Spring开发可以将Bean对象,Dao组件对象,Service组件对象等交给Spring容器来管理,这样使得很多复杂的代码在Spring中开发却变得非常的优雅和简洁,有效的降低代码的耦合度,极大的方便项目的后期维护、升级和扩展。Spring是一个IOC(DI)和AOP容器框架。
[1]非侵入式:基于Spring开发的应用中的对象可以不依赖于Spring的API。
[2]控制反转:IOC——Inversion of Control,指的是将对象交给Spring容器去创建。
[3]依赖注入:DI——Dependency Injection,是指依赖的对象不需要手动调用setXX方法去设置,而是通过配置赋值。
[4]面向切面编程:Aspect Oriented Programming——AOP面向切面编程。
[5]容器:Spring是一个容器,因为它包含并且管理应用对象的生命周期。
[6]组件化:Spring使用简单的组件配置组合成一个复杂的应用。在Spring中可以使用XML和Java注解组合这些对象。
[7]一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(实际上Spring 自身也提供了表述层的SpringMVC和持久层的Spring JDBC)。
Core Container(核心容器):
Data Access/Integration(数据访问/集成):
Web:
AOP:
Aspects:
Instrumentation(设备):
Messaging(消息):
Test(测试):
在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取所需要的资源,在这样的模式下开发人员往往需要知道在具体容器中特定资源的获取方式,增加了学习成本,同时降低了开发效率。
反转控制的思想完全颠覆了应用程序组件获取资源的传统方式:反转了资源的获取方向——改由容器主动的将资源推送给需要的组件,开发人员不需要知道容器是如何创建资源对象的,只需要提供接收资源的方式即可,极大的降低了学习成本,提高了开发的效率。这种行为也称为查找的被动形式。
IOC的另一种表述方式:即组件以一些预先定义好的方式(例如:setter 方法)接受来自于容器的资源注入。相对于IOC而言,这种表述更直接。
在通过IOC容器读取Bean的实例之前,需要先将IOC容器本身实例化。
Spring提供了IOC容器的两种实现方式:
ClassPathXmlApplicationContext:对应类路径下的XML格式的配置文件;
FileSystemXmlApplicationContext:对应文件系统中的XML格式的配置文件;
在初始化时就创建单例的bean,也可以通过配置的方式指定创建的Bean是多实例的。
它是ApplicationContext的子接口,包含一些扩展方法;
refresh()和close()让ApplicationContext具有启动、关闭和刷新上下文的能力;
专门为WEB应用而准备的,它允许从相对于WEB根目录的路径中完成初始化工作;
通过id获取对象:
@Test
public void test1() {
// 在使用Spring框架的时候,一定要先获取Spring容器对象( Spring IOC 容器对象 )
// 在Spring中,容器对象由接口ApplicationContext表示
// ClassPathXmlApplicationContext类表示从ClassPath类路径下加载xml配置文件创建Spring容器对象
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
// getBean()从容器中根据id获取Bean对象,获取出来的对象是Object类型,需要进行强转
Person person = (Person) applicationContext.getBean("p1");
System.out.println(person);
}
通过类型获取对象:
@Test
public void test2() {
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
/**
* 通过具体的类型获取
* 1 如果通过class类型找到唯一一个,就返回
* 2 如果没有找到就报错
* 3 如果通过class类型找到多个,也报错
* 4 获取出来的对象直接是该类型
*/
Person person = applicationContext.getBean(Person.class);
System.out.println(person);
}
通过id和类型获取对象:
@Test
public void test2() {
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
Person person = applicationContext.getBean("p1",Person.class);
System.out.println(person);
}
<bean class="com.JavaEE.pojo.Person" id="p4">
<constructor-arg index="0" value="4" />
<constructor-arg index="2" value="phone属性" />
<constructor-arg index="1" value="name属性" />
<constructor-arg index="3" value="18" />
bean>
<bean class="com.JavaEE.pojo.Person" id="p5">
<constructor-arg index="0" value="5" type="java.lang.Integer" />
<constructor-arg index="1" value="类型赋值" type="java.lang.String" />
<constructor-arg index="2" value="110" type="java.lang.String"/>
<constructor-arg index="3" value="120" type="java.lang.Integer" />
bean>
<bean class="com.JavaEE.pojo.Person" id="p14">
<property name="id" value="14" />
<property name="car">
<bean class="com.JavaEE.pojo.Car" id="car">
<property name="name" value="拖拉机" />
<property name="carNo" value="京C333333" />
bean>
property>
<property name="car.name" value="GTR" />
bean>
为了简化XML文件的配置,越来越多的XML文件采用属性而非子元素配置信息。
Spring从2.5版本开始引入了一个新的p命名空间,可以通过
元素属性的方式配置Bean的属性。
使用p命名空间后,基于XML的配置方式将进一步简化。
<bean class="com.JavaEE.pojo.Person" id="p6"
p:id="6" p:name="刘优" p:age="40" p:phone="电话"
/>
字面量:
把字面值包裹起来;null值:
<bean class="com.JavaEE.pojo.Person" id="p7">
<property name="id" value="7" />
<property name="name" >
<null>null>
property>
bean>
外部已声明的bean:
<bean class="com.JavaEE.pojo.Car" id="c1">
<property name="name" value="GTR" />
<property name="carNo" value="京B66666" />
bean>
<bean class="com.JavaEE.pojo.Person" id="p8">
<property name="id" value="8" />
<property name="name" value="强子" />
<property name="car" ref="c1" />
bean>
内部bean:
bean实例仅仅给一个特定的属性使用时,可以将其声明为内部bean,但其不能使用在其他任何地方,不能被spring 容器直接获取。内部bean声明直接包含在
或
元素里,不需要设置任何id或name属性。
<bean class="com.JavaEE.pojo.Person" id="p9">
<property name="id" value="9" />
<property name="name" value="庞家伟" />
<property name="car" >
<bean class="com.JavaEE.pojo.Car">
<property name="name" value="大众"/>
<property name="carNo" value="京A111111" />
bean>
property>
bean>
在Spring中可以通过一组内置的XML标签来配置集合属性,例如:
,
或。
标签,在标签里包含一些元素。这些标签可以通过
指定简单的常量值,通过
指定对其他Bean的引用。通过
指定内置bean定义。通过
指定空元素。甚至可以内嵌其他集合。 <bean id="shop" class="com.JavaEE.spring.bean.Shop" >
<list>
<value> 历史value >
<value> 军事value >
list>
property>
<list>
list>
property>
bean >
配置java.util.Set需要使用
标签,定义的方法与List一样。
数组的定义和List一样,都使用
元素。
Java.util.Map通过标签定义,
标签里可以使用多个
作为子标签。每个条目包含一个键和一个值。
必须在
标签里定义键。因为键和值的类型没有限制,所以可以自由地为它们指定
、、
或
元素。
可以将Map的键和值作为
的属性定义:简单常量使用key和value来定义;bean引用通过key-ref和value-ref属性定义。
<bean class="com.JavaEE.pojo.Person" id="p11">
<property name="id" value="11" />
<property name="map1">
<map>
<entry key="key1" value="value1" />
<entry key="key2" value="value2" />
<entry key="key3" value="value3" />
map>
property>
<property name="map2">
<map>
<key>
<value>bookKey01value>
key>
<ref bean="book"/>
map>
property>
bean>
定义java.util.Properties,该标签使用多个
作为子标签。每个
标签必须定义key属性。<bean class="com.JavaEE.spring.bean.DataSource" id="dataSource">
<property name="properties">
<props>
<prop key="userName">rootprop>
<prop key="password">rootprop>
<prop key="url">jdbc:mysql://localhost:3306/mybatisprop>
<prop key="driverClass">com.mysql.jdbc.Driverprop>
props>
property>
bean>
如果只能将集合对象配置在某个bean内部,则这个集合的配置将不能重用。我们需要将集合bean的配置拿到外面,供其他bean引用。引入util名称空间,可以定义全局公共的集合信息,方便容器直接获取,或者是给属性赋值使用。
<util:list id="bookList">
<ref bean="book01"/>
<ref bean="book02"/>
<ref bean="book03"/>
<ref bean="book04"/>
<ref bean="book05"/>
util:list>
<util:list id="categoryList">
<value>编程value>
<value>极客value>
<value>相声value>
<value>评书value>
util:list>
调用静态工厂方法创建bean是将对象创建的过程封装到静态方法中。当客户端需要对象时,只需要简单地调用静态方法,而不用关心创建对象的细节。
声明通过静态方法创建的bean需要在bean的class属性里指定静态工厂类的全类名,同时在factory-method属性里指定工厂方法的名称,最后使用
元素为该方法传递方法参数。
工厂类:
public class PersonFactory {
public static Person createPerson(){
return new Person(id,name,phone, age);
}
}
xml:
<bean id="p15" class="com.JavaEE.factory.PersonFactory" factory-method="createPerson">
<constructor-arg index="0" value="15" />
<constructor-arg index="1" value="name属性" />
<constructor-arg index="2" value="phone属性" />
<constructor-arg index="3" value="22" />
bean>
实例工厂方法:将对象的创建过程封装到另外一个对象实例的方法里。当客户端需要请求对象时,只需要简单的调用该实例方法而不需要关心对象的创建细节。
实现方式:先配置工厂类实例的bean,然后在factory-bean指定工厂类实例bean,在factory-method属性里指定该工厂方法的名称,最后使用
元素为工厂方法传递方法参数。
工厂类:
public class PersonFactory {
public Person createPerson2(){
return new Person(id,name,phone, age);
}
}
xml:
<bean class="com.JavaEE.factory.PersonFactory" id="personFactory"/>
<bean id="p16" factory-bean="personFactory" factory-method="createPerson2" >
<constructor-arg index="0" value="16" />
<constructor-arg index="1" value="name属性" />
<constructor-arg index="2" value="phone属性" />
<constructor-arg index="3" value="23" />
bean>
Spring中有两种类型的bean,一种是普通bean,另一种是工厂bean,即FactoryBean。工厂bean跟普通bean不同,其返回的对象不是指定类的一个实例,其返回的是该工厂bean的getObject方法所返回的对象。
具体实现:创建一个类去实现FactoryBean接口—>实现里面的方法—>到Spring的配置文件中去配置
FactroryBean接口实现类:
public class PersonFactoryBean implements FactoryBean<Person> {
/**
* 创建Bean对象时创建的方法
* @return
* @throws Exception
*/
@Override
public Person getObject() throws Exception {
return new Person(17,"FactoryBean接口方式","电话",18);
}
/**
* 获取对象的Class类型的方法
* @return
*/
@Override
public Class<?> getObjectType() {
return Person.class;
}
/**
* 是否是单例
* @return
*/
@Override
public boolean isSingleton() {
return false;
}
}
xml:
<bean id="p17" class="com.JavaEE.factory.PersonFactoryBean" />
背景:查看下面两个Employee的配置,其中dept属性是重复的。
<bean id="dept" class="com.JavaEE.parent.bean.Department">
<property name="deptId" value="100"/>
<property name="deptName" value="IT"/>
bean>
<bean id="emp01" class="com.JavaEE.parent.bean.Employee">
<property name="empId" value="1001"/>
<property name="empName" value="Tom"/>
<property name="age" value="20"/>
<property name="detp" ref="dept"/>
bean>
<bean id="emp02" class="com.JavaEE.parent.bean.Employee">
<property name="empId" value="1002"/>
<property name="empName" value="Jerry"/>
<property name="age" value="25"/>
<property name="detp" ref="dept"/>
bean>
配置信息的继承:以emp01作为父bean,继承后可以省略公共属性值的配置
<bean id="emp02" parent="emp01">
<property name="empId" value="1002"/>
<property name="empName" value="Jerry"/>
<property name="age" value="25"/>
bean>
的abstract 属性为true,这样Spring将不会实例化这个bean。
元素里的所有属性都会被继承。比如:autowire,abstract等。
<bean id="emp01" class="com.JavaEE.parent.bean.Employee" abstract="true">
<property name="empId" value="1001"/>
<property name="empName" value="Tom"/>
<property name="age" value="20"/>
<property name="detp" ref="dept"/>
bean>
有的时候创建一个bean的时候需要保证另外一个bean也被创建,这时我们称前面的bean对后面的bean有依赖。例如:要求创建Employee对象的时候必须创建Department。这里需要注意的是依赖关系不等于引用关系,Employee即使依赖Department也可以不引用它。
<bean id="emp03" class="com.JavaEE.parent.bean.Employee" depends-on="dept">
<property name="empId" value="1003"/>
<property name="empName" value="Kate"/>
<property name="age" value="21"/>
bean>
<bean id="dept" class="com.JavaEE.parent.bean.Department">
<property name="deptId" value="100"/>
<property name="deptName" value="IT"/>
bean>
在Spring中,可以在
元素的scope属性里设置bean的作用域,以决定这个bean是单实例的还是多实例的。
默认情况下,Spring只为每个在IOC容器里声明的bean创建唯一一个实例,整个IOC容器范围内都能共享该实例:所有后续的getBean()调用和bean引用都将返回这个唯一的bean实例。该作用域被称为singleton,它是所有bean的默认作用域。
<bean class="com.JavaEE.pojo.Person" id="p20" scope="singleton">
<property name="id" value="20" />
bean>
Spring
IOC容器可以管理bean的生命周期,Spring允许在bean生命周期内特定的时间点执行指定的任务。
Spring IOC容器对bean的生命周期进行管理的过程:
具体实现:
JavaBean类:
public class Person {
private Integer id;
private String name;
private String phone;
private Integer age;
private Car car;
/*初始化方法 */
public void init(){
System.out.println(" init() 这里可以做一些初始化操作 ");
}
/* 销毁方法 */
public void destroy(){
System.out.println(" destroy() 这里可以做一些销毁操作 ");
}
/* 以下省略其他javabean方法 */
}
xml:
<bean id="p22" class="com.JavaEE.pojo.Person" scope="singleton"
init-method="init" destroy-method="destroy">bean>
bean后置处理器允许在调用初始化方法前后对bean进行额外的处理。
bean后置处理器对IOC容器里的所有bean实例逐一处理,而非单一实例。其典型应用是:检查bean属性的正确性或根据特定的标准更改bean的属性。
bean后置处理器时需要实现接口:org.springframework.beans.factory.config.BeanPostProcessor。
在初始化方法被调用前后,Spring将把每个bean实例分别传递给上述接口的以下两个方法:
具体实现:
后置处理器类:
public class MyBeanPostProcessor implements BeanPostProcessor {
/**
* 在初始化方法之前执行,做一些操作
* @param bean 当前初始化的对象实例
* @param beanName 当前初始化对象的id值
* @return 返回值是当前初始化对象( 它会替代当前初始化对象 )
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println( " 初始化之前 obj => " + bean + " , id =>" + beanName );
return bean;
}
/**
* 在初始化方法之后执行,做一些操作
* @param bean 当前初始化的对象实例
* @param beanName 当前初始化对象的id值
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println( " 初始化之后 obj => " + bean + " , id =>" + beanName );
if ("p22".equals(beanName)) {
Person p = (Person) bean;
p.setCar(new Car("QQ卡丁车", "京C444444"));
}
return bean;
}
}
xml:
<bean class="com.JavaEE.pojo.Person" id="p22" scope="prototype"
init-method="init" destroy-method="destroy">bean>
<bean class="com.JavaEE.processor.MyBeanPostProcessor" />
添加bean后置处理器后bean的生命周期:
注意:相对于使用注解实现的自动装配,在XML文档中进行的自动装配略显笨拙,在项目中更多的使用注解的方式实现。
<bean class="com.JavaEE.pojo.Car" id="car3">
<property name="name" value="本田" />
<property name="carNo" value="京B22222" />
bean>
<bean class="com.JavaEE.pojo.Car" id="car">
<property name="name" value="东风日产" />
<property name="carNo" value="京B33333" />
bean>
<bean class="com.JavaEE.pojo.Person" id="p21" autowire="constructor">
<property name="id" value="21" />
bean>
当bean的配置信息逐渐增多时,查找和修改一些bean的配置信息就变得愈加困难。这时可以将一部分信息提取到bean配置文件的外部,以properties格式的属性文件保存起来,同时在bean的配置文件中引用properties属性文件中的内容,从而实现一部分属性值在发生变化时仅修改properties属性文件即可。这种技术多用于连接数据库的基本信息的配置。
具体实现:(转到:五、Spring管理数据库连接池)
jdbc.properteis属性配置文件:
jdbc.username=root
jdbc.password=123456
jdbc.url=jdbc:mysql://localhost:3306/<数据库名>?useEncode=true&characterEncoding=utf-8
jdbc.driver=com.mysql.jdbc.Driver
jdbc.initialSize=5
jdbc.maxActive=10
Spring配置文件:
<context:property-placeholder location="classpath:jdbc.properties" />
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="username" value="${user}" />
<property name="password" value="${password}" />
<property name="url" value="${url}" />
<property name="driverClassName" value="${driverClassName}" />
<property name="initialSize" value="${initialSize}" />
<property name="maxActive" value="${maxActive}" />
bean>
测试类:
@Test
public void test1() throws SQLException {
//读取配置文件,创建Spring容器
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
DataSource dataSource = (DataSource) applicationContext.getBean("dataSource");
System.out.println(dataSource.getConnection());
}
基本语法:SpEL使用**#{…}**作为定界符,所有在大框号中的字符都将被认为是SpEL表达式。
使用字面量:
整数:<property name="count" value="#{5}"/>
小数:<property name="frequency" value="#{89.7}"/>
科学计数法:<property name="capacity" value="#{1e4}"/>
String类型:<property name=“name” value="#{
'Chuck'}"/> <property name='name' value='#{
"Chuck"}'/>
Boolean:<property name="enabled" value="#{false}"/>
引用其他bean:
<bean id="emp04" class="com.JavaEE.parent.bean.Employee">
<property name="empId" value="1003"/>
<property name="empName" value="Kate"/>
<property name="age" value="21"/>
<property name="detp" value="#{dept}"/>
bean>
引用其他bean的属性值作为自己某个属性的值:
<bean id="emp05" class="com.JavaEE.parent.bean.Employee">
<property name="empId" value="1003"/>
<property name="empName" value="Kate"/>
<property name="age" value="21"/>
<property name="deptName" value="#{dept.deptName}"/>
bean>
调用非静态方法:
<bean id="salaryGenerator" class="com.JavaEE.spel.bean.SalaryGenerator"/>
<bean id="employee" class="com.JavaEE.spel.bean.Employee">
<property name="salayOfYear" value="#{salaryGenerator.getSalaryOfYear(5000)}"/>
bean>
调用静态方法:
<bean id="employee" class="com.JavaEE.spel.bean.Employee">
<property name="circle" value="#{T(java.lang.Math).PI*20}"/>
bean>
SpEL运算符:
①算术运算符:+、-、*、/、%、^
②字符串连接:+
③比较运算符:<、>、==、<=、>=、lt、gt、eq、le、ge
④逻辑运算符:and, or, not, |
⑤三目运算符:判断条件?判断结果为true时的取值:判断结果为false时的取值
⑥正则表达式:matches
Spring扫描到被注解标识的组件,就会在Spring容器启动时自动创建相对应的bean。
普通组件:@Component 标识一个受Spring IOC容器管理的组件
持久化层组件:@Respository 标识一个受Spring IOC容器管理的持久化层组件
业务逻辑层组件:@Service 标识一个受Spring IOC容器管理的业务逻辑层组件
表述层控制器组件:@Controller 标识一个受Spring IOC容器管理的表述层控制器组件
组件作用域:@Scope 配置Bean的作用域 (单例,多例)
singleton:默认单例模式,即:Spring IOC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例,一个容器对应一个bean;
prototype 表示多例:每一次请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)都会产生一个新的bean实例,相当与一个new的操作;
组件命名规则:
[1] 默认使用组件的简单类名首字母小写后得到的字符串作为bean的id;
[2] 使用组件注解的value属性指定bean的id(起别名);
注意:事实上Spring并没有能力识别一个组件到底是不是它所标记的类型,即使将@Respository注解用在一个表述层控制器组件上面也不会产生任何错误,所以@Respository、@Service、@Controller这几个注解仅仅是为了让开发人员自己明确当前的组件扮演的角色。
组件被上述注解标识后还需要通过Spring进行扫描才能够侦测到。
①指定被扫描的package
<context:component-scan base-package="com.JavaEE.component"/>
②详细说明
[1]base-package属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包及其子包中的所有类。
[2]当需要扫描多个包时可以使用逗号分隔。
[3]如果仅希望扫描特定的类而非基包下的所有类,可使用resource-pattern属性过滤特定的类。
<context:component-scan base-package="com.JavaEE.component" resource-pattern="autowire/*.class"/>
[4]包含与排除
下可以拥有若干个include-filter和exclude-filter子节点;
①
子节点表示要包含的目标类;
注意:通常需要与use-default-filters属性配合使用才能够达到“仅包含某些组件”这样的效果。
即:通过将use-default-filters属性设置为false,禁用默认过滤器,然后扫描的就只是include-filter中的规则指定的组件了。
②
子节点表示要排除在外的目标类;
类别 | 示例 | 说明 |
---|---|---|
annotation | com.JavaEE.XxxAnnotation | 过滤所有标注了XxxAnnotation的类。这个规则根据目标组件是否标注了指定类型的注解进行过滤。 |
assignable | com.JavaEE.BaseXxx | 过滤所有BaseXxx类的子类。这个规则根据目标组件是否是指定类型的子类的方式进行过滤。 |
aspectj | com.JavaEE.*Service+ | 所有类名是以Service结束的,或这样的类的子类。这个规则根据AspectJ表达式进行过滤。 |
regex | com.JavaEE.anno.* | 所有com.JavaEE.anno包下的类。这个规则根据正则表达式匹配到的类名进行过滤。 |
custom | com.JavaEE.XxxTypeFilter | 使用XxxTypeFilter类通过编码的方式自定义过滤规则。该类必须实现org.springframework.core.type.filter.TypeFilter接口 |
Controller组件中往往需要用到Service组件的实例,Service组件中往往需要用到Repository组件的实例。Spring可以通过注解的方式帮我们实现属性的装配。
在指定要扫描的包时,
元素会自动注册一个bean的后置处理器:AutowiredAnnotationBeanPostProcessor的实例。该后置处理器可以自动装配标记了**@Autowired**、@Resource或@Inject注解的属性。
@Autowired 注解默认按照byType方式在Spring容器中匹配资源类型兼容的bean:
1、如果找到唯一的类型,则自动装配;
2、如果资源类型兼容的bean不止一个,默认根据@Autowired注解标记的成员变量名作为id查找bean;
3、如果找不到资源类型兼容的bean,则抛出异常;
4、如果某一属性允许不被设置,即找不到资源类型兼容的bean时不抛出异常,而是该属性=null,则设置 @Autowired(required = false);
5、如果希望@Autowired 注解按照byName方式,则配合@Qualifier 注解使用,在里面指定注入bean的名称;
@Autowired注解一般用在组件属性的装配上。
@Autowired注解也可以应用在方法,此时Spring将会把所有形参进行自动装配。
@Autowired注解也可以应用在方法的形参上,此时Spring将会把指定形参进行自动装配。
@Autowired注解也可以应用在数组类型的属性上,此时Spring将会把所有匹配的bean进行自动装配。
@Autowired注解也可以应用在集合属性上,此时Spring读取该集合的类型信息,然后自动装配所有与之兼容的bean。
@Autowired注解用在java.util.Map上时,若该Map的键值为String,那么 Spring将自动装配与值类型兼容的bean作为值,并以bean的id值作为键。
@Resource默认按照byName方式进行bean匹配;
1).若@Resource后面没有内容,则采用标注处的变量名或方法名作为name去匹配bean,找不到再按type去匹配;
2).指定了name或者type则根据指定的类型去匹配bean;
3).指定了name和type则根据指定的name和type去匹配bean,任何一个不匹配都将报错;
@Inject和@Autowired注解一样也是按类型注入匹配的bean,但没有reqired属性。
Spring为了让Junit测试变得更佳简单,写的测试代码更少,为Junit做了一些扩展操作。
import calculator.Calculator;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
// @ContextConfiguration注解的作用是指定Spring容器需要的配置文件路径
@ContextConfiguration("classpath:ioc01.xml")
// @RunWith表示使用Spring扩展的Junit测试类来测试代码
@RunWith(SpringJUnit4ClassRunner.class)
public class AopTest {
@Autowired
private Calculator calculator;
@Test
public void test1(){
calculator.add(1,0);
}
}
在Spring 4.x中可以为子类注入子类对应的泛型类型的成员变量的引用。
创建两个带泛型的类,并配置两者的依赖关系,对于继承这两个类的子类,如果泛型相同,则会继承这种依赖关系:
BaseRepository:
public class BaseRepository<T> {
public void save(){
System.out.println("BaseRepository save...");
}
}
BaseService:
import org.springframework.beans.factory.annotation.Autowired;
public class BaseService<T> {
@Autowired
private BaseRepository<T> repository;
public void save(){
repository.save();
UserRepository:
import org.springframework.stereotype.Repository;
@Repository
public class UserRepository extends BaseRepository<User> {
public void save(){
System.out.println("UserRepository save...");
}
}
UserService:
import org.springframework.stereotype.Service;
@Service
public class UserService extends BaseService<User> {
}
test:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-generic-di.xml");
UserService userService = (UserService) ctx.getBean("userService");
userService.save();
}
}
结果:UserRepository save...
AOP(Aspect-Oriented Programming,面向切面编程):程序在运行期间,动态地将某段代码插入到原来方法代码的某些位置中。
在应用AOP编程时,仍然需要定义公共功能,但可以明确的定义这个功能应用在哪里,以什么方式应用,并且不必修改受影响的类。这样一来横切关注点就被模块化到特殊的类里——这样的类我们通常称之为“切面”。
AOP的好处:
1、每个事物逻辑位于一个位置,代码不分散,便于维护和升级;
2、业务模块更简洁,只包含核心业务代码;
代理设计模式的原理:使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
优点:这种方式可以把非核心业务抽取出来,方便的在后期进行维护和升级。
缺点:使用jdk动态代理,需要有接口。如果没有接口。就无法使用jdk动态代理。
非核心业务类:
import java.util.Arrays;
//日志,验证等功能实现类
public class LogUtil {
public static void logBefore(String methodName,Object...args){
System.out.println("执行"+methodName+",参数:"+ Arrays.asList(args));
}
public static void logAfter(String methodName){
System.out.println("执行"+methodName+"完毕");
}
public static void logAfterReturning(String methodName, Object res) {
System.out.println("执行"+methodName+",结果:" + res);
}
public static void logAfterThrowing(String methodName, Exception e) {
System.out.println("执行"+methodName+"时,发生异常:"+e.getMessage());
}
}
代理类:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//代理类
public class JDKProxy {
/**
*
* @param target 目标对象
* @return
*/
public static Object creatProxy(Object target){
//获取类加载器
ClassLoader classLoader = target.getClass().getClassLoader();
//获取被目标对象接口信息
Class<?>[] interfaces = target.getClass().getInterfaces();
/**
* 核心方法:生产代理对象
* @param classLoader 类加载器,通常用被代理类的类加载器,我们希望被代理和代理类使用同一个类加载器
* @param interfaces 目标对象的接口信息
* @param InvocationHandler 处理器
* Proxy:提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
*/
return Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
/**
* 拦截器
* @param proxy 代理对象
* @param method 目标方法
* @param args 目标方法中的参数
* @return 目标方法执行之后的结果
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res = null;
LogUtil.logBefore(method.getName(),args);//前置增强
try{
res = method.invoke(target, args);//目标方法
LogUtil.logAfterReturning(method.getName(), res);//返回增强
} catch (Exception e) {
LogUtil.logAfterThrowing(method.getName(), e);// 异常增强
throw new RuntimeException(e);
}finally {
LogUtil.logAfter(method.getName());//后置增强
}
return res;
}
});
}
}
Cglib动态代理它不管目标对象有没有实现接口.它都可以实现代理技术.
Cglib动态代理是通过修改目标对象的字节码程序产生一个子类.生成一个代理对象实例.
Cglib产生的代理对象是目标对象的子类.
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
//用cglib创建动态代理对象
public class CglibProxy {
public static Object creatCglibProxy(Object target){
// 增强器 它负责产生一个Cglib代理对象实例
Enhancer enhancer = new Enhancer();
//指定具体的目标对象(加载目标对象形成对应的字节码文件)
enhancer.setSuperclass(target.getClass());
// 设置方法拦截器==跟InvocationHandler接口功能一样,
//是代理对象调用方法时就会执行的接口(专门对目标方法进行增强)
enhancer.setCallback(new MethodInterceptor() {
/**
* intercept 会拦截所有的目标方法 等价于invoke()
* 只要代理对象方法调用,就会执行intercept()方法
* @param proxy 代理对象
* @param method 目标方法
* @param args 目标方法中的参数
* @param methodProxy 方法反射对象的代理对象
* @return 返回值是代理对象调用方法的返回值
* @throws Throwable
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 前置增强
LogUtil.logBefore(method.getName(),args);
//调用目标方法
Object res = method.invoke(target, args);
// 后置增强
LogUtil.logAfter(method.getName());
return res;
}
});
// 返回Cglib创建的代理对象实例
return enhancer.create();
}
}
通知(Advice)
切面必须要完成的各个具体工作。
切面(Aspect)
封装横切关注点信息的类,每个关注点体现为一个通知方法。
横切关注点
从每个方法中抽取出来的同一类非核心业务。
目标(Target)
目标对象就是被关注的对象。或者被代理的对象。
代理(Proxy)
为了拦截目标对象方法,而被创建出来的那个对象,就叫做代理对象。
连接点(Joinpoint)
横切关注点在程序代码中的具体体现,对应程序执行的某个特定位置。例如:类某个方法调用前、调用后、方法捕获到异常后等。
切入点(pointcut)
定位连接点的方式。每个类的方法中都包含多个连接点,所以连接点是类中客观存在的事物。如果把连接点看作数据库中的记录,那么切入点就是查询条件——AOP可以通过切入点定位到特定的连接点。
在Spring中切入点通过org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。
AOP术语图解:
①导入jar包
需要代入的jar包:
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
junit_4.12.jar
org.hamcrest.core_1.3.0.jar
spring-aop-5.2.5.RELEASE.jar
spring-aspects-5.2.5.RELEASE.jar
spring-beans-5.2.5.RELEASE.jar
spring-context-5.2.5.RELEASE.jar
spring-core-5.2.5.RELEASE.jar
spring-expression-5.2.5.RELEASE.jar
spring-jcl-5.2.5.RELEASE.jar
②在配置文件引入aop名称空间
<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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="aop,calculator">context:component-scan>
<aop:aspectj-autoproxy>aop:aspectj-autoproxy>
beans>
①要在Spring中声明AspectJ切面,只需要在IOC容器中将切面声明为bean实例。
②当在Spring IOC容器中初始化AspectJ切面之后,Spring IOC容器就会为那些与 AspectJ切面相匹配的bean创建代理。
③在AspectJ注解中,切面只是一个带有@Aspect注解的Java类,它往往要包含很多通知。
④通知是标注有某种注解的简单的Java方法。
⑤AspectJ支持5种类型的通知注解:
[1]@Before:前置通知,在方法执行之前执行
[2]@After:后置通知,在方法执行之后执行
[3]@AfterRunning:返回通知,在方法返回结果之后执行
[4]@AfterThrowing:异常通知,在方法抛出异常之后执行
[5]@Around:环绕通知,围绕着方法执行
切入点表达式语法格式:
execution(public int com.JavaEE.aop.Calculator.add(int, int)
通配符:
*
表示任意的意思;切入点表达式连接:&& 、||
通知:在具体的连接点上要执行的操作。
一个切面可以包括一个或者多个通知。
通知所使用的注解的值往往是切入点表达式。
前置通知:在方法执行之前执行的通知;
后置通知:后置通知是在连接点完成之后执行的,即连接点返回结果或者抛出异常的时候;
返回通知:在连接点进行正常返回时才执行的通知;
在返回通知中访问连接点的返回值
在返回通知中,只要将returning属性添加到@AfterReturning注解中,就可以访问连接点的返回值。该属性的值即为用来传入返回值的参数名称
必须在通知方法的签名中添加一个同名参数。在运行时Spring AOP会通过这个参数传递返回值
原始的切点表达式需要出现在pointcut属性中
异常通知:只在连接点抛出异常时才执行异常通知;
将throwing属性添加到@AfterThrowing注解中,也可以访问连接点抛出的异常。
如果只对某种特殊的异常类型感兴趣,可以将参数声明为其他异常的参数类型。然后通知就只在抛出这个类型及其子类的异常时才被执行。
环绕通知:环绕通知是所有通知类型中功能最为强大的,能够全面地控制连接点,甚至可以控制是否执行连接点。
对于环绕通知来说,连接点的参数类型必须是ProceedingJoinPoint。它是 JoinPoint的子接口,允许控制何时执行,是否执行连接点。
在环绕通知中需要明确调用ProceedingJoinPoint的proceed()方法来执行被代理的方法。如果忘记这样做就会导致通知被执行了,但目标方法没有被执行。
注意:环绕通知的方法需要返回目标方法执行之后的结果,即调用 joinPoint.proceed();的返回值,否则会出现空指针异常。
@Component //用组件标注Component,让容器自动创建对象
@Aspect //把类标注成切面
public class LogAop {
//切入点表达式:通过表达式的方式定位一个或多个具体的连接点。
@Pointcut("execution(public Integer calculator.CalculatorImpl.*(..))")
public void pointcut(){
//注解需要使用环境,所以绑定一个空方法
}
@Before("pointcut()")
public void logBefore(){
System.out.println("前置通知————方法准备开始");
}
@After("pointcut()")
public void logAfter(){
System.out.println("后置通知————方法执行完毕");
}
@AfterReturning(value = "pointcut()",returning = "res")
public Object afterReturning(Object res){
System.out.println("结果为:"+res);
return res;
}
@AfterThrowing(value = "pointcut()",throwing = "e")
public void afterThrowing(Throwable e){
System.out.println("错误异常信息:"+e.getMessage());
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint){
try {
System.out.println("around前置通知————"+joinPoint.getSignature().getName()+"准备开始");
Object res = joinPoint.proceed();
System.out.println("around返回通知:"+res);
return res;
} catch (Throwable e) {
System.out.println("around错误异常信息:"+e.getMessage());
}finally {
System.out.println("around后置通知————"+joinPoint.getSignature().getName()+"执行完毕");
}
return null;
}
}
切入点表达式通常都会是从宏观上定位一组方法,和具体某个通知的注解结合起来就能够确定对应的连接点。那么就一个具体的连接点而言,我们可能会关心这个连接点的一些具体信息,例如:当前连接点所在方法的方法名、当前传入的参数值等等。这些信息都封装在JoinPoint接口的实例对象中。
@After("pointcut()")
public static void logAfter(JoinPoint joinPoint){
System.out.println("后置通知执行"+joinPoint.getSignature().getName());
}
只需要在通知方法的参数中,加入一个JoinPoint参数。就可以获取到拦截方法的信息。
编写AspectJ切面时,可以直接在通知注解中书写切入点表达式。但同一个切点表达式可能会在多个通知中重复出现。
在AspectJ切面中,可以通过@Pointcut注解将一个切入点声明成简单的方法。
@Pointcut("execution(public Integer calculator.CalculatorImpl.*(..))")
public void pointcut(){
//注解需要使用环境,所以绑定一个空方法
}
切入点方法的访问控制符同时也控制着这个切入点的可见性。如果切入点要在多个切面中共用,最好将它们集中在一个公共的类中。在这种情况下,它们必须被声明为public。在引入这个切入点时,必须将类名也包括在内。如果类没有与这个切面放在同一个包中,还必须包含包名。
在同一个连接点上应用不止一个切面时,除非明确指定,否则它们的优先级是不确定的。
切面的优先级可以通过实现Ordered接口或利用@Order注解指定。
实现Ordered接口,getOrder()方法的返回值越小,优先级越高。
使用@Order注解决定通知执行的顺序(值越小,越先执行)。
@Aspect
@Order(1)
public class LogAop {
...}
在Spring中,可以对有接口的对象和无接口的对象分别进行代理。在使用上有些细微的差别。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="calculator" class="com.JavaEE.xml.calculator.CalculatorImpl">bean>
<bean id="logaspect" class="com.JavaEE.xml.aop.LogAop">bean>
<aop:config>
<aop:aspect ref="logaspect" order="0">
<aop:pointcut id="cut" expression="execution(public int com.JavaEE.xml.calculator.Calculator.*(..))"/>
<aop:before method="logBefore" pointcut-ref="cut">aop:before>
<aop:after method="logAfter" pointcut-ref="cut">aop:after>
<aop:after-returning method="afterReturning" pointcut-ref="cut" returning="a">aop:after-returning>
<aop:after-throwing method="afterThrowing" pointcut-ref="cut" throwing="e">aop:after-throwing>
<aop:around method="around" pointcut-ref="cut">aop:around>
aop:aspect>
aop:config>
beans>
为了使JDBC更加易于使用,Spring在JDBC API上定义了一个抽象层,以此建立一个JDBC存取框架。作为Spring JDBC框架的核心,JDBC模板的设计目的是为不同类型的JDBC操作提供模板方法,通过这种方式,可以在尽可能保留灵活性的情况下,将数据库存取的工作量降到最低。可以将Spring的JdbcTemplate看作是一个小型的轻量级持久化层框架,和我们之前使用过的DBUtils风格非常接近。
1)导入jar包
①IOC容器所需要的JAR包
commons-logging-1.1.1.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
②JdbcTemplate所需要的JAR包
spring-jdbc-4.0.0.RELEASE.jar
spring-orm-4.0.0.RELEASE.jar
spring-tx-4.0.0.RELEASE.jar
③数据库驱动和数据源
druid-1.1.9.jar
mysql-connector-java-5.1.7-bin.jar
④Aop
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
spring-aop-5.2.5.RELEASE.jar
spring-aspects-5.2.5.RELEASE.jar
2)创建连接数据库基本信息属性文件(porperties)
jdbc.username=root
jdbc.password=123456
jdbc.url=jdbc:mysql://localhost:3306/jdbctemplate?useEncode=true&characterEncoding=utf-8
jdbc.driver=com.mysql.jdbc.Driver
3)在Spring配置文件中配置相关的bean
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.JavaEE">context:component-scan>
<context:property-placeholder location="classpath:jdbc.properties">context:property-placeholder>
<bean id="datasource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${jdbc.username}">property>
<property name="password" value="${jdbc.password}">property>
<property name="url" value="${jdbc.url}">property>
<property name="driverClassName" value="${jdbc.driver}">property>
bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="datasource">property>
bean>
<bean id="namedParameterJdbcTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg name="dataSource" ref="datasource">constructor-arg>
bean>
beans>
@Autowired //对jdbcTempplate自动装配
private JdbcTemplate jdbcTemplate;
//将id=5的记录的salary字段更新为1300.00
@Test
public void t1() {
String sql="update employee set salary=? where id=?";
int count = jdbcTemplate.update(sql, 1300, 5);
System.out.println(count);
}
Object[]封装了SQL语句每一次执行时所需要的参数;List集合封装了SQL语句多次执行时的所有参数;
@Autowired
private JdbcTemplate jdbcTemplate;
//批量插入
@Test
public void t2() {
String sql="insert into employee values(null,?,?)";
List<Object[]> params=new ArrayList<>();
params.add(new Object[]{
"tom",12000});
params.add(new Object[]{
"jack",120000});
params.add(new Object[]{
"rose",12});
int[] count = jdbcTemplate.batchUpdate(sql, params);
for (int i : count) {
System.out.println(i);
}
}
@Autowired
private JdbcTemplate jdbcTemplate;
//查询id=5的数据库记录,封装为一个Java对象返回
@Test
public void t3() {
String sql="select * from employee where id=?";
Employee employee = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Employee.class), 5);
System.out.println(employee);
}
JdbcTemplate.query(String sql, RowMapper, Object… args)
RowMapper对象依然可以使用BeanPropertyRowMapper
@Autowired
private JdbcTemplate jdbcTemplate;
//查询salary>4000的数据库记录,封装为List集合返回
@Test
public void t4(){
List<Employee> emps = jdbcTemplate.query("select * from employee where salary>?", new BeanPropertyRowMapper<>(Employee.class), 4000);
System.out.println(emps);
}
@Autowired
private JdbcTemplate jdbcTemplate;
//查询最大salary 单行单列 queryForObject
@Test
public void t5(){
BigDecimal bigDecimal = jdbcTemplate.queryForObject("select max(salary) from employee", BigDecimal.class);
System.out.println(bigDecimal);
}
在Hibernate的HQL查询中我们体验过具名参数的使用,相对于基于位置的参数,具名参数具有更好的可维护性,在SQL语句中参数较多时可以考虑使用具名参数。在Spring中可以通过NamedParameterJdbcTemplate类的对象使用带有具名参数的SQL语句。
<bean
id="namedTemplate"
class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
<constructor-arg ref="dataSource" ref="datasource"/>
bean>
INSERT INTO depts (dept_name) VALUES (:deptName)
@Autowired
//使用带有命名参数的SQL语句插入一条员工记录,并以Map形式传入参数值
@Test
public void t6(){
String sql="insert into employee values(null,:name,:sal)";
Map<String,Object> params=new HashMap<>();
params.put("name","jerry");
params.put("sal",1200000);
int count = namedParameterJdbcTemplate.update(sql, params);
System.out.println(count);
}
JdbcTemplate类是线程安全的,所以可以在IOC容器中声明它的单个实例,并将这个实例注入到所有的Dao实例中。
@Repository
public class EmployeeDao {
//通过IOC容器自动注入
@Autowired
private JdbcTemplate jdbcTemplate;
public Employee get(Integer id){
//…
}
}
将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。
事务管理代码的固定模式作为一种横切关注点,可以通过AOP方法模块化,进而借助Spring AOP框架实现声明式事务管理。Spring在不同的事务管理API之上定义了一个抽象层,通过配置的方式使其生效,从而让应用程序开发人员不必了解事务管理API的底层实现细节,就可以使用Spring的事务管理机制。
Spring事务管理底层原理
Spring的核心事务管理抽象是PlatformTransactionManager。它为事务管理封装了一组独立于技术的方法。无论使用Spring的哪种事务管理策略(编程式或声明式),事务管理器都是必须的。
①DataSourceTransactionManager:在应用程序中只需要处理一个数据源,而且通过JDBC存取。
②JtaTransactionManager:在JavaEE应用服务器上用JTA(Java Transaction API)进行事务管理。
③HibernateTransactionManager:用Hibernate框架存取数据库。
配置文件:
<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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="com.JavaEE">context:component-scan>
<context:property-placeholder location="classpath:jdbc.properties">context:property-placeholder>
<bean id="ds" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${jdbc.username}">property>
<property name="password" value="${jdbc.password}">property>
<property name="url" value="${jdbc.url}">property>
<property name="driverClassName" value="${jdbc.driver}">property>
bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="ds">property>
bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="ds">property>
bean>
<tx:annotation-driven transaction-manager="transactionManager">tx:annotation-driven>
beans>
在需要进行事务控制的方法上加注解:
@Transactional
public void updateUserBook(){
bookDao.updateBook();
userDao.updateUser();
}
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”})
timeout
该属性用于设置事务的超时秒数,以秒为单位,默认值为-1表示永不超时。
设置超时秒数为两秒:@Transactional(timeout = 2)
propagation:事务传播行为。
isolation:事务隔离级别。
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。
方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。
事务的传播行为可以由传播属性指定。
常量名称 | 解释 |
---|---|
PROPAGATION_REQUIRED | 先判断当前方法有没有事务,如果有就是用当前的事务,如果没有事务,开启一个新的事务。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。新建的事务将和被挂起的事务没有任何关系,是两个独立的事务,外层事务失败回滚之后,不能回滚内层事务执行的结果,内层事务失败抛出异常,外层事务捕获,也可以不处理回滚操作 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 支持当前事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果事务在运行,则当前方法就在这个事务的嵌套事务内运行。否则,就气动一个新的事务,并在它自己的事务内运行。它使用了一个单独的事务,这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效。 |
数据库系统必须具有隔离并发运行各个事务的能力,使它们不会相互影响,避免各种并发问题。一个事务与其他事务隔离的程度称为隔离级别。SQL标准中规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性越弱。
常量名称 | 解释 |
---|---|
ISOLATION_DEFAULT 默认 | 这是个 PlatfromTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别。另外四个与 JDBC 的隔离级别相对应。 |
ISOLATION_READ_UNCOMMITTED 读未提交 | 这是事务最低的隔离级别,它充许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。 |
ISOLATION_READ_COMMITTED 读已提交 | 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。可以防止脏读,但可能出现不可重复读。 |
ISOLATION_REPEATABLE_READ 可重复读 | 确保一个事务可以多次从一个字段中读取到相同的值,即这个事务执行期间禁止其它事务对这个字段进行更新。这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。 |
ISOLATION_SERIALIZABLE 串行化 | 确保一个事务可以多次从一个表中读取到相同的行,在这个事务执行期间,禁止其它事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。 |
补充:
<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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.JavaEE.xml">context:component-scan>
<context:property-placeholder location="classpath:jdbc.properties">context:property-placeholder>
<bean id="ds" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${jdbc.username}">property>
<property name="password" value="${jdbc.password}">property>
<property name="url" value="${jdbc.url}">property>
<property name="driverClassName" value="${jdbc.driver}">property>
bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="ds">property>
bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="ds">property>
bean>
<tx:advice id="transAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="updateBookUser" read-only="false" isolation="DEFAULT" propagation="REQUIRED" timeout="-1"/>
<tx:method name="save*">tx:method>
<tx:method name="del*">tx:method>
<tx:method name="update*">tx:method>
<tx:method name="query*" read-only="true">tx:method>
<tx:method name="*" read-only="true">tx:method>
tx:attributes>
tx:advice>
<aop:config>
<aop:pointcut id="cut" expression="execution(* com.JavaEE.xml.service.*.*(..))"/>
<aop:advisor advice-ref="transAdvice" pointcut-ref="cut">aop:advisor>
aop:config>
beans>
整合Spring和Web容器分三个步骤:
基本思路:
通过注册监听器ContextLoaderListener,Web应用可以在启动时加载Spring的ApplicationContext对象。这个监听器会将加载好的ApplicationContext对象保存到Web应用的ServletContext中。随后Servlet或可以访问ServletContext的任意对象就能通过一个辅助方法来访问Spring的应用程序上下文了。
监听器类:
org.springframework.web.context.ContextLoaderListener
监听器通过查找Web应用初始化参数contextConfigLocation来获取bean配置文件的位置。如果有多个bean配置文件,可以通过逗号或空格进行分隔。contextConfigLocation的默认值为/WEB-INF/applicationContext.xml。若实际的文件和默认值一致则可以省略这个Web应用的初始化参数
获取ApplicationContext对象:
WebApplicationContextUtils.getWebApplicationContext(getServletContext())
具体实现:
xml配置文件:
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:bean.xmlparam-value>
context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
web-app>
Servlet层:
@WebServlet(value = "/springServlet")
public class SpringServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
}
}
.jar
2. 在web.xml中配置org.springframework.web.context.ContextLoaderListener监听器监听ServletContext的初始化
3. 在web.xml中配置contextConfigLocation上下文参数。配置Spring配置文件的位置,以用于初始化Spring容器
基本思路:
通过注册监听器ContextLoaderListener,Web应用可以在启动时加载Spring的ApplicationContext对象。这个监听器会将加载好的ApplicationContext对象保存到Web应用的ServletContext中。随后Servlet或可以访问ServletContext的任意对象就能通过一个辅助方法来访问Spring的应用程序上下文了。
监听器类:
org.springframework.web.context.ContextLoaderListener
监听器通过查找Web应用初始化参数contextConfigLocation来获取bean配置文件的位置。如果有多个bean配置文件,可以通过逗号或空格进行分隔。contextConfigLocation的默认值为/WEB-INF/applicationContext.xml。若实际的文件和默认值一致则可以省略这个Web应用的初始化参数
获取ApplicationContext对象:
WebApplicationContextUtils.getWebApplicationContext(getServletContext())
具体实现:
xml配置文件:
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:bean.xmlparam-value>
context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
web-app>
Servlet层:
@WebServlet(value = "/springServlet")
public class SpringServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
}
}