一、IOC
-
IoC (Inversion of Control) : 控制反转, 是一个理论,概念,思想。
描述的:把对象的创建,赋值,管理工作都交给代码之外的容器实现, 也就是对象的创建是有其它外部资源完成。控制: 创建对象,对象的属性赋值,对象之间的关系管理。
反转: 把原来的开发人员管理,创建对象的权限转移给代码之外的容器实现。 由容器
代替开发人员管理对象。创建对象,
给属性赋值。正转:由开发人员在代码中,使用new 构造方法创建对象, 开发人员主动管理对象。
-
目的就是减少对代码的改动, 也能实现不同的功能。 实现解耦合。
-
IoC的技术实现 ,
DI 是ioc的技术实现,
DI(Dependency Injection) :依赖注入, 只需要在程序中提供要使用的对象名称就可以, 至于对象如何在容器中创建,
赋值,查找都由容器内部实现。 -
spring是使用的di实现了ioc的功能, spring底层创建对象,使用的是反射机制。
spring是一个容器,管理对象,给属性赋值, 底层是反射创建对象。
二、容器配置文件
<!--告诉spring创建对象
声明bean , 就是告诉spring要创建某个类的对象
id:对象的自定义名称,唯一值。 spring通过这个名称找到对象
class:类的全限定名称(不能是接口,因为spring是反射机制创建对象,必须使用类)
spring就完成 SomeService someService = new SomeServiceImpl();
spring是把创建好的对象放入到map中, spring框架有一个map存放对象的。
springMap.put(id的值, 对象);
例如 springMap.put("someService", new SomeServiceImpl());
一个bean标签声明一个对象。
-->
<bean id="someService" class="com.bjpowernode.service.impl.SomeServiceImpl" />
三、创建容器对象
//使用spring容器创建的对象
//1.指定spring配置文件的名称
String config="beans.xml";
//2.创建表示spring容器的对象, ApplicationContext
// ApplicationContext就是表示Spring容器,通过容器获取对象了
// ClassPathXmlApplicationContext:表示从类路径中加载spring的配置文件
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
//从容器中获取某个对象, 你要调用对象的方法
//getBean("配置文件中的bean的id值")
SomeService service = (SomeService) ac.getBean("someService");
//使用spring创建好的对象
service.doSome();
//获取容器中对象的数量
int nums = ac.getBeanDefinitionCount();
//获取容器中对象中对象的名称
String names[] = ac.getBeanDefinitionNames();
四、注入
-
简单类型注入
<!--声明student对象 注入:就是赋值的意思 简单类型: spring中规定java的基本数据类型和String都是简单类型。 di:给属性赋值 1. set注入(设值注入) :spring调用类的set方法, 你可以在set方法中完成属性赋值 1)简单类型的set注入 <bean id="xx" class="yyy"> <property name="属性名字" value="此属性的值"/> 一个property只能给一个属性赋值 <property....> </bean> --> <bean id="myStudent" class="com.bjpowernode.ba01.Student" > <property name="name" value="李四lisi" /><!--setName("李四")--> <property name="age" value="22" /><!--setAge(21)--> <property name="email" value="lisi@qq.com" /><!--setEmail("lisi@qq.com")--> </bean>
-
引用类型注入
<!-- 2) 引用类型的set注入 : spring调用类的set方法 <bean id="xxx" class="yyy"> <property name="属性名称" ref="bean的id(对象的名称)" /> </bean> --> <!--声明School对象--> <bean id="mySchool" class="com.bjpowernode.ba02.School"> <property name="name" value="北京大学"/> <property name="address" value="北京的海淀区" /> </bean>
-
有参构造注入
<!-- 2.构造注入:spring调用类有参数构造方法,在创建对象的同时,在构造方法中给属性赋值。 构造注入使用 <constructor-arg> 标签 <constructor-arg> 标签:一个<constructor-arg>表示构造方法一个参数。 <constructor-arg> 标签属性: name:表示构造方法的形参名 index:表示构造方法的参数的位置,参数从左往右位置是 0 , 1 ,2的顺序 value:构造方法的形参类型是简单类型的,使用value ref:构造方法的形参类型是引用类型的,使用ref --> <!--使用name属性实现构造注入--> <bean id="myStudent" class="com.bjpowernode.ba03.Student" > <constructor-arg name="myage" value="20" /> <constructor-arg name="mySchool" ref="myXueXiao" /> <constructor-arg name="myname" value="周良"/> </bean> <!--使用index属性--> <bean id="myStudent2" class="com.bjpowernode.ba03.Student"> <constructor-arg index="1" value="22" /> <constructor-arg index="0" value="李四" /> <constructor-arg index="2" ref="myXueXiao" /> </bean> <!--省略index--> <bean id="myStudent3" class="com.bjpowernode.ba03.Student"> <constructor-arg value="张强强" /> <constructor-arg value="22" /> <constructor-arg ref="myXueXiao" /> </bean> <!--声明School对象--> <bean id="myXueXiao" class="com.bjpowernode.ba03.School"> <property name="name" value="清华大学"/> <property name="address" value="北京的海淀区" /> </bean> <!--创建File,使用构造注入--> <bean id="myfile" class="java.io.File"> <constructor-arg name="parent" value="D:\course\JavaProjects\spring-course\ch01-hello-spring" /> <constructor-arg name="child" value="readme.txt" /> </bean>
-
按名称自动注入
<!-- 1.byName(按名称注入) : java类中引用类型的属性名和spring容器中(配置文件)<bean>的id名称一样, 且数据类型是一致的,这样的容器中的bean,spring能够赋值给引用类型。 语法: <bean id="xx" class="yyy" autowire="byName"> 简单类型属性赋值 </bean> --> <!--byName--> <bean id="myStudent" class="com.bjpowernode.ba04.Student" autowire="byName"> <property name="name" value="李四" /> <property name="age" value="26" /> <!--引用类型--> <!--<property name="school" ref="mySchool" />--> </bean> <!--声明School对象--> <bean id="school" class="com.bjpowernode.ba04.School"> <property name="name" value="清华大学"/> <property name="address" value="北京的海淀区" /> </bean>
-
按类型注入
<!-- 2.byType(按类型注入) : java类中引用类型的数据类型和spring容器中(配置文件)<bean>的class属性 是同源关系的,这样的bean能够赋值给引用类型 同源就是一类的意思: 1.java类中引用类型的数据类型和bean的class的值是一样的。 2.java类中引用类型的数据类型和bean的class的值父子类关系的。 3.java类中引用类型的数据类型和bean的class的值接口和实现类关系的 语法: <bean id="xx" class="yyy" autowire="byType"> 简单类型属性赋值 </bean> 注意:在byType中, 在xml配置文件中声明bean只能有一个符合条件的, 多余一个是错误的 <!--byType--> <bean id="myStudent" class="com.bjpowernode.ba05.Student" autowire="byType"> <property name="name" value="张飒" /> <property name="age" value="26" /> <!--引用类型--> <!--<property name="school" ref="mySchool" />--> </bean> <!--声明School对象--> <bean id="mySchool" class="com.bjpowernode.ba05.School"> <property name="name" value="人民大学"/> <property name="address" value="北京的海淀区" /> </bean> <!--声明School的子类--> <!--<bean id="primarySchool" class="com.bjpowernode.ba05.PrimarySchool"> <property name="name" value="北京小学" /> <property name="address" value="北京的大兴区" /> </bean>--> -->
五、多个配置文件整合
<!--
包含关系的配置文件:
spring-total表示主配置文件 : 包含其他的配置文件的,主配置文件一般是不定义对象的。
语法:<import resource="其他配置文件的路径" />
关键字:"classpath:" 表示类路径(class文件所在的目录),
在spring的配置文件中要指定其他文件的位置, 需要使用classpath,告诉spring到哪去加载读取文件。
-->
<!--加载的是文件列表-->
<import resource="classpath:ba06/spring-school.xml" />
<import resource="classpath:ba06/spring-student.xml" />
<!--
在包含关系的配置文件中,可以通配符(*:表示任意字符)
注意: 主的配置文件名称不能包含在通配符的范围内(不能叫做spring-total.xml)
-->
<import resource="classpath:ba06/spring-*.xml" />
六、注解
-
组件扫描器
<!--声明组件扫描器(component-scan),组件就是java对象 base-package:指定注解在你的项目中的包名。 component-scan工作方式: spring会扫描遍历base-package指定的包, 把包中和子包中的所有类,找到类中的注解,按照注解的功能创建对象,或给属性赋值。 加入了component-scan标签,配置文件的变化: 1.加入一个新的约束文件spring-context.xsd 2.给这个新的约束文件起个命名空间的名称 --> <context:component-scan base-package="com.bjpowernode.ba02" /> <!--指定多个包的三种方式--> <!--第一种方式:使用多次组件扫描器,指定不同的包--> <context:component-scan base-package="com.bjpowernode.ba01"/> <context:component-scan base-package="com.bjpowernode.ba02"/> <!--第二种方式:使用分隔符(;或,)分隔多个包名--> <context:component-scan base-package="com.bjpowernode.ba01;com.bjpowernode.ba02" /> <!--第三种方式:指定父包--> <context:component-scan base-package="com.bjpowernode" /> <!--加载属性配置文件--> <context:property-placeholder location="classpath:test.properties" />
-
@Component
-
@Respotory
-
@Service
-
@Controller
/** * @Component: 创建对象的, 等同于<bean>的功能 * 属性:value 就是对象的名称,也就是bean的id值, * value的值是唯一的,创建的对象在整个spring容器中就一个 * 位置:在类的上面 * * @Component(value = "myStudent")等同于 * <bean id="myStudent" class="com.bjpowernode.ba01.Student" /> * * spring中和@Component功能一致,创建对象的注解还有: * 1.@Repository(用在持久层类的上面) : 放在dao的实现类上面, * 表示创建dao对象,dao对象是能访问数据库的。 * 2.@Service(用在业务层类的上面):放在service的实现类上面, * 创建service对象,service对象是做业务处理,可以有事务等功能的。 * 3.@Controller(用在控制器的上面):放在控制器(处理器)类的上面,创建控制器对象的, * 控制器对象,能够接受用户提交的参数,显示请求的处理结果。 * 以上三个注解的使用语法和@Component一样的。 都能创建对象,但是这三个注解还有额外的功能。 * @Repository,@Service,@Controller是给项目的对象分层的。 * * */ //使用value属性,指定对象名称 //@Component(value = "myStudent") //省略value @Component("myStudent") //不指定对象名称,由spring提供默认名称: 类名的首字母小写 //@Component public class Student { private String name; private Integer age; public Student() { System.out.println("==student无参数构造方法==="); } public void setName(String name) { this.name = name; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; }
-
@Value
@Component("myStudent") public class Student { /** * @Value: 简单类型的属性赋值 * 属性: value 是String类型的,表示简单类型的属性值 * 位置: 1.在属性定义的上面,无需set方法,推荐使用。 * 2.在set方法的上面 */ //@Value("李四" ) @Value("${myname}") //使用属性配置文件中的数据 private String name; @Value("${myage}") //使用属性配置文件中的数据 private Integer age; public Student() { System.out.println("==student无参数构造方法==="); } public void setName(String name) { this.name = name; } //@Value("30") public void setAge(Integer age) { System.out.println("setAge:"+age); this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
-
@Autowired
@Component("myStudent") public class Student { @Value("李四" ) private String name; private Integer age; /** * 引用类型 * @Autowired: spring框架提供的注解,实现引用类型的赋值。 * spring中通过注解给引用类型赋值,使用的是自动注入原理 ,支持byName, byType * @Autowired:默认使用的是byType自动注入。 * * 位置:1)在属性定义的上面,无需set方法, 推荐使用 * 2)在set方法的上面 * * 属性:required ,是一个boolean类型的,默认true * required=true:表示引用类型赋值失败,程序报错,并终止执行。 * required=false:引用类型如果赋值失败, 程序正常执行,引用类型是null * * 如果要使用byName方式,需要做的是: * 1.在属性上面加入@Autowired * 2.在属性上面加入@Qualifier(value="bean的id") :表示使用指定名称的bean完成赋值。 */ @Autowired(required="true") @Qualifier(value="bean的id") private School school; public Student() { System.out.println("==student无参数构造方法==="); } public void setName(String name) { this.name = name; } @Value("30") public void setAge(Integer age) { System.out.println("setAge:"+age); this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", school=" + school + '}'; } }
-
@ReSource
@Component("myStudent") public class Student { @Value("李四" ) private String name; private Integer age; /** * 引用类型 * @Resource: 来自jdk中的注解,spring框架提供了对这个注解的功能支持,可以使用它给引用类型赋值 * 使用的也是自动注入原理,支持byName, byType .默认是byName * 位置: 1.在属性定义的上面,无需set方法,推荐使用。 * 2.在set方法的上面 */ //默认是byName: 先使用byName自动注入,如果byName赋值失败,再使用byType /** @Resource只使用byName方式,需要增加一个属性 name * name的值是bean的id(名称) */ @Resource private School school; public Student() { System.out.println("==student无参数构造方法==="); } public void setName(String name) { this.name = name; } @Value("30") public void setAge(Integer age) { System.out.println("setAge:"+age); this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", school=" + school + '}'; } }
七、aop简介
-
怎么理解面向切面编程 ?
1)需要在分析项目功能时,找出切面。
2)合理的安排切面的执行时间(在目标方法前, 还是目标方法后)
3)合理的安全切面执行的位置,在哪个类,哪个方法增加增强功能 -
术语:
1)Aspect:切面,表示增强的功能, 就是一堆代码,完成某个一个功能。非业务功能,
常见的切面功能有日志, 事务, 统计信息, 参数检查, 权限验证。2)JoinPoint:连接点 ,连接业务方法和切面的位置。 就某类中的业务方法
3)Pointcut : 切入点 ,指多个连接点方法的集合。多个方法
4)目标对象: 给哪个类的方法增加功能, 这个类就是目标对象
5)Advice:通知,通知表示切面功能执行的时间。 -
说一个切面有三个关键的要素:
1)切面的功能代码,切面干什么
2)切面的执行位置,使用Pointcut表示切面执行的位置
3)切面的执行时间,使用Advice表示时间,在目标方法之前,还是目标方法之后。
八 aspectj切入点表达式
- execution(访问修饰符 返回值,包名.类名.方法名称(方法的参数)异常)
- *表示0或多个任意字符
- 。。用在方法参数中,表示任意多个参数,用在包名后,表示当前包及其任意子包路径
- +用在类明后,表示当前类及其子类,用在接口后,表示当前接口及其实现类
九、aspectj通知注解
-
声明自动代理生成器
<!--声明自动代理生成器:使用aspectj框架内部的功能,创建目标对象的代理对象。 创建代理对象是在内存中实现的, 修改目标对象的内存中的结构。 创建为代理对象 所以目标对象就是被修改后的代理对象. aspectj-autoproxy:会把spring容器中的所有的目标对象,一次性都生成代理对象。 --> <aop:aspectj-autoproxy /> <!-- 如果你期望目标类有接口,使用cglib代理 proxy-target-class="true":告诉框架,要使用cglib动态代理 --> <aop:aspectj-autoproxy proxy-target-class="true"/>
-
@Before
/** * @Aspect : 是aspectj框架中的注解。 * 作用:表示当前类是切面类。 * 切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码 * 位置:在类定义的上面 */ @Aspect public class MyAspect { /** * 定义方法,方法是实现切面功能的。 * 方法的定义要求: * 1.公共方法 public * 2.方法没有返回值 * 3.方法名称自定义 * 4.方法可以有参数,也可以没有参数。 * 如果有参数,参数不是自定义的,有几个参数类型可以使用。 */ /** * @Before: 前置通知注解 * 属性:value ,是切入点表达式,表示切面的功能执行的位置。 * 位置:在方法的上面 * 特点: * 1.在目标方法之前先执行的 * 2.不会改变目标方法的执行结果 * 3.不会影响目标方法的执行。 */ /** * 指定通知方法中的参数 : JoinPoint * JoinPoint:业务方法,要加入切面功能的业务方法 * 作用是:可以在通知方法中获取方法执行时的信息, 例如方法名称,方法的实参。 * 如果你的切面功能中需要用到方法的信息,就加入JoinPoint. * 这个JoinPoint参数的值是由框架赋予, 必须是第一个位置的参数 */ @Before(value = "execution(void *..SomeServiceImpl.doSome(String,Integer))") public void myBefore(JoinPoint jp){ //获取方法的完整定义 System.out.println("方法的签名(定义)="+jp.getSignature()); System.out.println("方法的名称="+jp.getSignature().getName()); //获取方法的实参 Object args [] = jp.getArgs(); for (Object arg:args){ System.out.println("参数="+arg); } //就是你切面要执行的功能代码 System.out.println("2=====前置通知, 切面功能:在目标方法之前输出执行时间:"+ new Date()); } }
-
@AfterReturning
@Aspect public class MyAspect { /** * 后置通知定义方法,方法是实现切面功能的。 * 方法的定义要求: * 1.公共方法 public * 2.方法没有返回值 * 3.方法名称自定义 * 4.方法有参数的,推荐是Object ,参数名自定义 */ /** * @AfterReturning:后置通知 * 属性:1.value 切入点表达式 * 2.returning 自定义的变量,表示目标方法的返回值的。 * 自定义变量名必须和通知方法的形参名一样。 * 位置:在方法定义的上面 * 特点: * 1。在目标方法之后执行的。 * 2. 能够获取到目标方法的返回值,可以根据这个返回值做不同的处理功能 * Object res = doOther(); * 3. 可以修改这个返回值 * * 后置通知的执行 * Object res = doOther(); * 参数传递: 传值, 传引用 * myAfterReturing(res); * System.out.println("res="+res) * */ @AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))", returning = "res") public void myAfterReturing( JoinPoint jp ,Object res ){ // Object res:是目标方法执行后的返回值,根据返回值做你的切面的功能处理 System.out.println("后置通知:方法的定义"+ jp.getSignature()); System.out.println("后置通知:在目标方法之后执行的,获取的返回值是:"+res); if(res.equals("abcd")){ //做一些功能 } else{ //做其它功能 } //修改目标方法的返回值, 看一下是否会影响 最后的方法调用结果 if( res != null){ res = "Hello Aspectj"; } }
-
@Around
@Aspect public class MyAspect { /** * 环绕通知方法的定义格式 * 1.public * 2.必须有一个返回值,推荐使用Object * 3.方法名称自定义 * 4.方法有参数,固定的参数 ProceedingJoinPoint */ /** * @Around: 环绕通知 * 属性:value 切入点表达式 * 位置:在方法的定义什么 * 特点: * 1.它是功能最强的通知 * 2.在目标方法的前和后都能增强功能。 * 3.控制目标方法是否被调用执行 * 4.修改原来的目标方法的执行结果。 影响最后的调用结果 * * 环绕通知,等同于jdk动态代理的,InvocationHandler接口 * * 参数: ProceedingJoinPoint 就等同于 Method * 作用:执行目标方法的 * 返回值: 就是目标方法的执行结果,可以被修改。 * * 环绕通知: 经常做事务, 在目标方法之前开启事务,执行目标方法, 在目标方法之后提交事务 */ @Around(value = "execution(* *..SomeServiceImpl.doFirst(..))") public Object myAround(ProceedingJoinPoint pjp) throws Throwable { String name = ""; //获取第一个参数值 Object args [] = pjp.getArgs(); if( args!= null && args.length > 1){ Object arg= args[0]; name =(String)arg; } //实现环绕通知 Object result = null; System.out.println("环绕通知:在目标方法之前,输出时间:"+ new Date()); //1.目标方法调用 if( "zhangsan".equals(name)){ //符合条件,调用目标方法 result = pjp.proceed(); //method.invoke(); Object result = doFirst(); } System.out.println("环绕通知:在目标方法之后,提交事务"); //2.在目标方法的前或者后加入功能 //修改目标方法的执行结果, 影响方法最后的调用结果 if( result != null){ result = "Hello AspectJ AOP"; } //返回目标方法的执行结果 return result; } }
-
@AfterThrowing
@Aspect public class MyAspect { /** * 异常通知方法的定义格式 * 1.public * 2.没有返回值 * 3.方法名称自定义 * 4.方法有个一个Exception, 如果还有是JoinPoint, */ /** * @AfterThrowing:异常通知 * 属性:1. value 切入点表达式 * 2. throwinng 自定义的变量,表示目标方法抛出的异常对象。 * 变量名必须和方法的参数名一样 * 特点: * 1. 在目标方法抛出异常时执行的 * 2. 可以做异常的监控程序, 监控目标方法执行时是不是有异常。 * 如果有异常,可以发送邮件,短信进行通知 * * 执行就是: * try{ * SomeServiceImpl.doSecond(..) * }catch(Exception e){ * myAfterThrowing(e); * } */ @AfterThrowing(value = "execution(* *..SomeServiceImpl.doSecond(..))", throwing = "ex") public void myAfterThrowing(Exception ex) { System.out.println("异常通知:方法发生异常时,执行:"+ex.getMessage()); //发送邮件,短信,通知开发人员 } }
-
@After
@Aspect public class MyAspect { /** * 最终通知方法的定义格式 * 1.public * 2.没有返回值 * 3.方法名称自定义 * 4.方法没有参数, 如果还有是JoinPoint, */ /** * @After :最终通知 * 属性: value 切入点表达式 * 位置: 在方法的上面 * 特点: * 1.总是会执行 * 2.在目标方法之后执行的 * * try{ * SomeServiceImpl.doThird(..) * }catch(Exception e){ * * }finally{ * myAfter() * } * */ @After(value = "execution(* *..SomeServiceImpl.doThird(..))") public void myAfter(){ System.out.println("执行最终通知,总是会被执行的代码"); //一般做资源清除工作的。 } }
-
@Pointcut
@Aspect public class MyAspect { @After(value = "mypt()") public void myAfter(){ System.out.println("执行最终通知,总是会被执行的代码"); //一般做资源清除工作的。 } @Before(value = "mypt()") public void myBefore(){ System.out.println("前置通知,在目标方法之前先执行的"); } /** * @Pointcut: 定义和管理切入点, 如果你的项目中有多个切入点表达式是重复的,可以复用的。 * 可以使用@Pointcut * 属性:value 切入点表达式 * 位置:在自定义的方法上面 * 特点: * 当使用@Pointcut定义在一个方法的上面 ,此时这个方法的名称就是切入点表达式的别名。 * 其它的通知中,value属性就可以使用这个方法名称,代替切入点表达式了 */ @Pointcut(value = "execution(* *..SomeServiceImpl.doThird(..))" ) private void mypt(){ //无需代码, } }
十、整合mybatis
-
声明数据源
<!-- 把数据库的配置信息,写在一个独立的文件,编译修改数据库的配置内容 spring知道jdbc.properties文件的位置 --> <context:property-placeholder location="classpath:jdbc.properties" /> <!--声明数据源DataSource, 作用是连接数据库的--> <bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <!--set注入给DruidDataSource提供连接数据库信息 --> <!-- 使用属性配置文件中的数据,语法 ${key} --> <property name="url" value="${jdbc.url}" /><!--setUrl()--> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.passwd}" /> <property name="maxActive" value="${jdbc.max}" /> </bean>
-
声明sqlSessionFactory类
<!--声明的是mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory的 SqlSessionFactory sqlSessionFactory = new .. --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!--set注入,把数据库连接池付给了dataSource属性--> <property name="dataSource" ref="myDataSource" /> <!--mybatis主配置文件的位置 configLocation属性是Resource类型,读取配置文件 它的赋值,使用value,指定文件的路径,使用classpath:表示文件的位置 --> <property name="configLocation" value="classpath:mybatis.xml" /> </bean>
-
配置dao类
<!--创建dao对象,使用SqlSession的getMapper(StudentDao.class) MapperScannerConfigurer:在内部调用getMapper()生成每个dao接口的代理对象。 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!--指定SqlSessionFactory对象的id--> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> <!--指定包名, 包名是dao接口所在的包名。 MapperScannerConfigurer会扫描这个包中的所有接口,把每个接口都执行 一次getMapper()方法,得到每个接口的dao对象。 创建好的dao对象放入到spring的容器中的。 dao对象的默认名称是 接口名首字母小写 --> <property name="basePackage" value="com.bjpowernode.dao"/> </bean>
十一、事务
-
事务管理器
事务内部提交,回滚事务,使用的事务管理器对象,代替你完成commit,rollback事务管理器是一个接口和他的众多实现类。
接口:PlatformTransactionManager ,定义了事务重要方法 commit ,rollback
实现类:spring把每一种数据库访问技术对应的事务处理类都创建好了。
mybatis访问数据库—spring创建好的是DataSourceTransactionManager
hibernate访问数据库----spring创建的是HibernateTransactionManager声明数据库访问技术对于的事务管理器实现类, 在spring的配置文件中使用声明就可以了
例如,你要使用mybatis访问数据库,你应该在xml配置文件中
<bean id=“xxx" class="…DataSourceTransactionManager"> -
事务的隔离级别
事务的隔离级别:有4个值。
DEFAULT:采用 DB 默认的事务隔离级别。MySql 的默认为 REPEATABLE_READ; Oracle默认为 READ_COMMITTED。
➢ READ_UNCOMMITTED:读未提交。未解决任何并发问题。
➢ READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。
➢ REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读
➢ SERIALIZABLE:串行化。不存在并发问题。 -
事务的超时时间: 表示一个方法最长的执行时间,如果方法执行时超过了时间,事务就回滚。
单位是秒, 整数值, 默认是 -1. -
事务的传播行为 : 控制业务方法是不是有事务的, 是什么样的事务的。
7个传播行为,表示你的业务方法调用时,事务在方法之间是如果使用的。PROPAGATION_REQUIRED (会接受外部事务) PROPAGATION_REQUIRES_NEW (会创建新事务) PROPAGATION_SUPPORTS (支持事务) 以上三个需要掌握的 PROPAGATION_MANDATORY PROPAGATION_NESTED PROPAGATION_NEVER PROPAGATION_NOT_SUPPORTED
-
事务提交事务,回滚事务的时机
-
当你的业务方法,执行成功,没有异常抛出,当方法执行完毕,spring在方法执行后提交事务。事务管理器commit
-
当你的业务方法抛出运行时异常或ERROR, spring执行回滚,调用事务管理器的rollback
运行时异常的定义: RuntimeException 和他的子类都是运行时异常, 例如NullPointException , NumberFormatException -
当你的业务方法抛出非运行时异常, 主要是受查异常时,提交事务
受查异常:在你写代码中,必须处理的异常。例如IOException, SQLException
-
-
配置事务管理器和开启事务注解驱动
<!--使用spring的事务处理--> <!--1. 声明事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--连接的数据库, 指定数据源--> <property name="dataSource" ref="myDataSource" /> </bean> <!--2. 开启事务注解驱动,告诉spring使用注解管理事务,创建代理对象 transaction-manager:事务管理器对象的id --> <tx:annotation-driven transaction-manager="transactionManager" />
-
给方法添加注解
/** * * rollbackFor:表示发生指定的异常一定回滚. * 处理逻辑是: * 1) spring框架会首先检查方法抛出的异常是不是在rollbackFor的属性值中 * 如果异常在rollbackFor列表中,不管是什么类型的异常,一定回滚。 * 2) 如果你的抛出的异常不在rollbackFor列表中,spring会判断异常是不是RuntimeException, * 如果是一定回滚。 * */ /* @Transactional( propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false, rollbackFor = { NullPointerException.class, NotEnoughException.class } )*/ //使用的是事务控制的默认值, 默认的传播行为是REQUIRED,默认的隔离级别DEFAULT //默认抛出运行时异常,回滚事务。 @Transactional @Override public void buy(Integer goodsId, Integer nums) {
-
配置文件添加事务
<tx:advice id="myAdvice" transaction-manager="transactionManager"> <!--tx:attributes:配置事务属性--> <tx:attributes> <!--tx:method:给具体的方法配置事务属性,method可以有多个,分别给不同的方法设置事务属性 name:方法名称,1)完整的方法名称,不带有包和类。 2)方法可以使用通配符,* 表示任意字符 propagation:传播行为,枚举值 isolation:隔离级别 rollback-for:你指定的异常类名,全限定类名。 发生异常一定回滚 --> <tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT" rollback-for="java.lang.NullPointerException,com.bjpowernode.excep.NotEnoughException"/> <!--使用通配符,指定很多的方法--> <tx:method name="add*" propagation="REQUIRES_NEW" /> <!--指定修改方法--> <tx:method name="modify*" /> <!--删除方法--> <tx:method name="remove*" /> <!--查询方法,query,search,find--> <tx:method name="*" propagation="SUPPORTS" read-only="true" /> </tx:attributes> </tx:advice> <!--配置aop--> <aop:config> <!--配置切入点表达式:指定哪些包中类,要使用事务 id:切入点表达式的名称,唯一值 expression:切入点表达式,指定哪些类要使用事务,aspectj会创建代理对象 com.bjpowernode.service com.crm.service com.service --> <aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/> <!--配置增强器:关联adivce和pointcut advice-ref:通知,上面tx:advice哪里的配置 pointcut-ref:切入点表达式的id --> <aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt" /> </aop:config>
十二、监听器
-
web.xml文件配置
<!--注册监听器ContextLoaderListener 监听器被创建对象后,会读取/WEB-INF/spring.xml 为什么要读取文件:因为在监听器中要创建ApplicationContext对象,需要加载配置文件。 /WEB-INF/applicationContext.xml就是监听器默认读取的spring配置文件路径 可以修改默认的文件位置,使用context-param重新指定文件的位置 配置监听器:目的是创建容器对象,创建了容器对象, 就能把spring.xml配置文件中的所有对象都创建好。 用户发起请求就可以直接使用对象了。 --> <context-param> <!-- contextConfigLocation:表示配置文件的路径 --> <param-name>contextConfigLocation</param-name> <!--自定义配置文件的路径--> <param-value>classpath:spring.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
-
拿到容器对象
//创建spring的容器对象 //String config= "spring.xml"; //ApplicationContext ctx = new ClassPathXmlApplicationContext(config); WebApplicationContext ctx = null; //获取ServletContext中的容器对象,创建好的容器对象,拿来就用 /*String key = WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE; Object attr = getServletContext().getAttribute(key); if( attr != null){ ctx = (WebApplicationContext)attr; }*/ //使用框架中的方法,获取容器对象 ServletContext sc = getServletContext(); ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(sc); System.out.println("容器对象的信息========"+ctx);