Spring 总结:
主要从讲述以下几个知识点:
目录结构:
1,Spring 概述
1.1 Spring 的主要作用:
1.2 Spring 的核心是控制反转(IOC)和 面向切面编程(AOP)
2,Spring 与 IOC
2.1 IoC的定义
2.2 创建容器
2.3 ApplicationContext 与 BeanFactory 容器的区别
2.4 Bean 的装配
2.5 容器中 Bean 的作用域
2.6 Bean 的后处理器
2.7 Bean 的生命周期
2.8 <bean /> 标签的 id 属性与 name 属性(了解即可)
2.9 基于 XML 的 DI
2.10 基于注解的 DI
2.11 注解 与 XML 共同使用
3,Spring 与 AOP
3.1 AOP概述
3.2 通知 Advice(spring 框架自己实现的 AOP)
3.3 顾问 Advisor(spring 框架自己实现的 AOP)
3.4 自动代理生成器
3.5 AspectJ 框架对 AOP 的实现(重点)
4,Spring 与 DAO
4.1 Spring 与 JDBC 模板(对 IOC 的应用)
4.2 从属性文件读取数据库连接信息
4.3 配置 JDBC 模板
4.4 Dao 实现类继承 jdbcDaoSupport
正文:
1,Spring 概述
Spring 是一个容器,用于降低代码之间的耦合度,根据不同的代码采用 IOC 和 AOP 两种技术来解耦合
1.1 Spring 的主要作用:
是为代码“解耦”,降低代码间的耦合度。
可以管理对象的生命周期、对象与对象之间的依赖关系。可以通过配置文件,来定义对象,以及设置与其他对象的依赖关系。
1.2 Spring 的核心是控制反转(IOC)和 面向切面编程(AOP)
根据功能的不同,可以将一个系统中的代码分为,主业务逻辑与系统级业务逻辑两类。
主业务逻辑:
主业务逻辑代码间逻辑联系紧密,有具体的专业业务应用场景,复用性相对比较低
系统级业务:(交叉业务)
系统级业务相对功能独立,没有具体的专业业务应用场景,主要是为主业务提供系统级服务,如日志、安全、事物等,复用性强。
Spring 根据代码的功能特点,将降低耦合度的方式分为了两类:IOC 与 AOP。
IOC 使得主业务在相互调用过程中,不用再自己维护关系了,即不用再自己创建要使用的对象了。而是有 Spring 容器统一管理,自动“注入”。
2,Spring 与 IOC
2.1 IoC的定义
控制反转(IoC,Inversion of Control),是一个概念,是一种思想。指将传统上有程序代码直接操控的对象调用权交给容器,通过容器来实现
对象的装配和管理。控制反转就是对对象控制权的转移,从程序代码本身反转到了外部容器。
IoC 是一个概念,是一种思想,其实现方式多种多样。当前比较流行的实现方式有两种:依赖注入和依赖查找。依赖注入应用更为广泛。
依赖查找:
Dependency Lookup,DL,容器提供调用接口和上下文环境给组件,程序代码则需要提供具体的查找方式。比较典型的是依赖于 JNDI 服务
接口的查找
依赖注入:
Dependency Injection,DI,程序代码不做定位查询,这些工作容器自行完成。
依赖注入是目前最优秀的解耦方式。
2.2 创建容器
ApplicationContext ac = null
方式1:(在类路径下(src下)找配置文件)
ac = new ClassPathXmlApplicationContext("applicationContext.xml");
方式2:(在项目根路径下找配置文件,或盘符下)
ac = new FileSystemXmlApplicationContext("applicationContext.xml");
2.3 ApplicationContext 与 BeanFactory 容器的区别:
这两个容器对于其中创建 Bean 的时机不同,通常是使用 ApplicationContext 容器
1,ApplicationContext 容器在进行初始化时,会将其中的所有Bean(对象)进行创建
缺点:占用系统资源(内存、CPU等)
优点:响应速度快
2,BeanFactory容器中的对象,在容器初始化时不会创建,而是在真正获取该对象时才被创建
缺点:相对来说,响应速度慢
优点:不多占用系统资源
一个空的对象,占 8 个字节。
在执行无参构造方法执行时,已经完成了给对象在内存的堆中分配好了空间,并且已经给成员变量做完了初始化赋值工作。
在执行无参构造方法之前先执行了动态代码块
2.4 Bean 的装配
即 Bean 对象的创建。容器根据代码要求创建 Bean 对象后再传递给代码的过程,称为 Bean 的装配
1,默认的装配方式:
代码通过 getBean()方法从容器获取指定的 Bean 实例,容器首先会调用 Bean 类的无参构造器,创建空值的实例对象。
注:当需要通过spring来获取 bean 对象时,这个类给出了带参构造器后一定要记得定义一个无参构造器,原因是底层使用的反射机制,在反射机制中用到了无参构造器
2,动态工厂 Bean
<bean id="factory" class="com.shz.ServiceFactory" />
<bean id="myService" factory-bean="factory" factory-method="getMyService" />
3,静态工厂 Bean
静态工厂是不需要创建对象的,直接通过类名点
<bean id="myService" class="com.hsz.ServiceFactory" factory-method="getMyService" />
2.5 容器中 Bean 的作用域
1,单例模式:只在容器中创建一个对象,每次获取的都是同一个对象
其对象的创建 时机 是在 Spring容器初始化时创建的,是默认值。
<bean id="myService" class="com.hsz.SomeServiceImpl" scope="singleton" />
2,原型模式:其对象的创建时机不是在 Spring容器初始化时创建,而是在代码中真正访问时才创建
<bean d="myService" class="com.hsz.SomeServiceImpl" scope="prototype" />
不常用的两个作用域
3,request:对于每次http请求,都将会产生一个一个不同的<bean />
4,session:对于每一个不同的HTTP session,都将会产生一个不同的 bean 实例
2.6 Bean 的后处理器
1,实现 BeanPostProcessor 接口
2,注册 bean 后处理器
没有 id
<bean class="com.hsz.MyBeanPostProcessor" />
3,实现接口中的方法
public Object postProcessBeforeInitialization(Object bean,String beanName){}
public Object postProcessAfterInittialization(Object bean,String beanName){}
bean:表示当前正在进行初始化的 Bean 对象
beanName:表示当前正在进行初始化的 Bean 对象的 id
4,在 after 方法中使用 JDK 自带的 proxy 动态代理来给代码做增强处理
2.7 Bean 的生命周期
1,定制 Bean 的生命周期始末
<bean id="myService" class="xxx" init-method="setUp" destroy-method="teamDown" />
init-mothod:对应的方法是在无参构造方法执行完后就执行的方法
destroy-method:在 bean 销毁之前,执行的方法
执行这个方法是有条件的:
1)当前的Bean需要是singleton的
2)要手工关闭容器(关闭时使用实现类的 close 方法来关闭)
2,
2.8 标签的 id 属性与 name 属性(了解即可)
id 的命名需要满足 xml 对 id 属性命名规范:必须以字母开头,可以以字母开头,可以包含字母、数字、句号、冒号(新版本的spring可以使用 /)
name 属性值则可以包含各种字符
2.9 基于 XML 的 DI
1,注入的分类:
1)设值注入(比较常用)
在 applicationContext.xml 中
<bean id="myStudent" class="com.hsz.MyStudent">
<property name="name" value="张三">
<property name="age" value="18">
<property name="shool" ref="myShool">
</bean>
在对应类中定义对应的属性及 set 方法
底层原理是通过反射机制,找到set方法进行赋值的
2)构造注入
方式一:使用索引
<bean id="myStudent" class="com.hsz.MyStudent">
<constructor index="0" value="张三">
<constructor index="1" value="18">
<constructor index="2" ref="myShool">
</bean>
index对应的是对应类的带参构造的顺序,和属性的 set 方法无关。
方式二:使用 name 属性
<bean id="myStudent" class="com.hsz.MyStudent">
<constructor name="name" value="张三">
<constructor name="age" value="18">
<constructorname="shool" ref="myShool">
</bean>
2,命名空间注入(了解即可)
分类:P命名空间注入,C命名空间注入
在使用之前需要事先添加对应的头部 约束声明
1)P命名空间注入:
<bean id="myStudent" class="com.hsz.Shool" p:name="清华大学"/>
底层调用的还是 set 方法注入
2)C命名空间注入:
<bean id="myStudent" class="com.hsz.MyStudent" c:name="张三" c:age="18"/>
在对应的类中都没有无参构造器也可以,在底层中直接调用的是带参构造器。
经验:只要加了带参构造,都加上无参构造器。
3)实现特定接口注入
这种方式采用侵入式编程,污染了代码,所以几乎不用。
3,集合属性注入
array
list
set
map
property
方式一:传统的写法
<bean id="mySome" class="com.hsz.MySome">
<property name="shools">
<array>
<ref bean="mySchool" />
<ref bean="myShool2" />
</array>
</property>
<property name="shools">
<list>
<value >篮球 </value>
<value >足球 </value>
</list>
</property>
。。。。。。。。。
</bean>
方式二:比较简单的方式
使用 value 属性,中间用逗号分开即可,但是只能在 数组,list,set 中使用
<bean id="mySome" class="com.hsz.MySome">
<property name="myList" value="篮球,足球">
。。。。。。
</bean>
注:Map 和 property 中的 key 和 value 之间的区别
Map:key 和 value 是 obejct 类型
property:中的 key 和 value 只能是 String 类型
4,对于域属性的自动注入
1)byName
<bean id="mySome" class="com.hsz.MySome" autowire="byName">
<property name="name" value="张三">
<property name="age" value="18">
</bean>
会自动在容器中查找与实体类的域属性同名的 bean 的 id,并将该 bean 的属性赋值给该域属性
2)byType
<bean id="mySome" class="com.hsz.MySome" autowire="byType">
<property name="name" value="张三">
<property name="age" value="18">
</bean>
会自动在容器中查找与实体类的域属性具有 is - a关系的 bean,并将该 bean 的属性赋值给
5,使用SPEL注入
Spring 的 EL 表达式
作用:
1)可以使用 JavaEE 中的静态方法 #{ T(java.lang.Math).random() * 50 }
2)可以调用其他 bean 的属性,方法
3)可以在 表达式中进行计算
<bean id="mySome" class="com.hsz.MySome" autowire="byType">
<property name="name" value="#{ myPerson.pname }">
<property name="age" value="#{ myPerson.score > 25 ? 25 : myPerson.score}">
<property name="score" value="#{ myPerson.scoreAvg()}">
</bean>
myPerson 是一个 bean 的 id,pname 和 score 是其属性,scoreAvg()是其方法,给mySome 中的属性注入值
6,使用内部 Bean注入(了解即可)
7,使用同类抽象 Bean 注入(了解即可)
和 继承差不多,父类定义成 abstract
8,使用异类抽象 Bean 注入(了解即可)
9,位应用指定多个 Spring 配置文件(了解即可)
1)在读取配置文件时,以可变长参数 或 数组来传入配置文件 来解析
2)使用通配符
2.10 基于注解的 DI
要使用注解开发,需要在 Spring 的配置文件中定义,注解扫描器
注解的注入方式 和 set 方法没有关系,和 xml 的实现方式不一样。
1,定义 Bean @Compinent 组件
1)在一个类的上方添加 @Compinent 注解,表示当前类被 Spring 容器所管理
2)给这个类的属性赋值:
在属性的上方添加 @Value("xxx")
3)与@Component 注解功能相同,但意义不同的注解还有三个:
1,@Repository:注解在Dao实现类上
2,@Service:注解在Service实现类上
3,@Controller:注解在 SpringMVC 的处理器上
2,Bean 的作用域 @Scope
@Scope("")
3,基本类型注入域属性 @Value
4,按类型注入域属性 @Autowired
byType 方式的注解式注入,根据在 配置文件中的指定要扫描的包下,找这个类型的类并创建。
5,按名称注入域属性 @Autowired 与 @Qualifier
需要在对应的 注解中事先取一个名字
6,域属性注解 @Resource
JSR-250 规范要求,要求 JDK 要在1.6 以上
@Resource byType 方式注入
@Resource(name="xxx") byName 方式注入
7,Bean 的生命始末 @PostConstruct 与 @PreDestroy
// 了解即可
8,使用 JavaConfig 进行配置(了解即可)
在类的上方添加 @Configuration 注解
表示当前类充当 Spring 容器,即所有的 Bean 将由这个类来创建
9,使用Spring 的 Junit4 测试 Spring
在测试类的上方添加注解
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:com.hsz.applicationContext.xml")
Public class MyTest{
@Aotuwired
private Student student;
@Test
public void test01(){
System.out.println(student);
}
}
2.11 注解 与 XML 共同使用
XML 的优先级要高于 注解,原因是:对于程序的发布,和维护方便,不需要重新编译。
经验:即使是使用注解式开发,最好也保留属性的 set 方法。
3,Spring 与 AOP
3.1 AOP概述
1,AOP 面向切面编程,是面向对象编程 OOP 的一种补充。面向对象编程是从静态角度考虑程序的结构,而面向切面编程是从动态角度考虑程序
运行过程。
AOP 底层,就是采用动态代理模式实现的。采用了两种代理:JDK的动态代理,与 CGLIB 的动态代理。
主要是把通用的与主业务逻辑无关的代码,如安全检查、事务、日志等织入到主业务逻辑代码中。
2,AOP编程术语
切面(Aspect):
织入:
连接点:
切入点:
被标记为 final 的方法是不能作为连接点和切入点的。因为最终的是不能被修改的,不能被增强的。
目标对象:
通知:
顾问:
3,AOP 编程环境搭建
3.2 通知 Advice(spring 框架自己实现的 AOP)
缺点:通知只能指定增强的时机,即增强的,不能指定切入点。
1,前置增强
需要 代理类 实现 MethodBeforeAdvice 接口
使用 ProxyFactoryBean 实现
2,后置增强
需要 代理类 实现 AfterReturningAdvice 接口
可以获得目标方法的返回结果,但是无法改变目标方法的结果。
3,环绕增强
需要 代理类 实现 MethodInterceptor 接口
4,异常增强
需要目标类实现 ThrowsAdvice 接口
try。。。catch 和 throws 的区别:
1,发生在 try。。。catch。。。 中的异常不会往外抛,如果是使用 Junit 进行测试的话,就算是出现异常,结果都是绿条,但是在
控制台中会打印出异常信息
2,异常通过 throws 抛出去后,如果发生异常,Junit 的结果是红条,但是在控制台中不会打印异常信息。因为当前异常已经被抛出去了,
当前类不知道有异常的发生。
3.3 顾问 Advisor(spring 框架自己实现的 AOP)
1,方法名称匹配切入点顾问
匹配的对象时简单方法名
2,使用正则表达式方法切入点顾问
<bean id = "myAdvisor" class = "org.springframework.aop.RegexpMethodPoincutAdvisor">
<property name = "advice" ref = "myAdvice" />
这里得的正则表达式匹配的对象是 权限定性方法名,不包括方法名的小括号
<!-- <property name = "pattern" value = ".*doFirst" /> -->
<!-- <property name = "patterns" value = ".*doFirst,.*doSecond" /> -->
<!-- <property name = "pattern" value = ".*doFirst | .*doSecond" /> -->
<!-- <property name = "pattern" value = ".*S.*" /> -->
</ bean>
当前代码存在的两个问题:
1,若存在多个目标对象,就需要使用多次 ProxyFactoryBean 来创建多个代理对象,这会使配置文件变得臃肿,不便于管理
2,用户真正想调用的是目标对象,而真正可以调用的却是代理对象,这不符合正常的逻辑
以上这两个问题,均为 ProxyFactoryBean 类的功能太简单引起的
3.4 自动代理生成器
1,默认代理 advisor 自动代理生成器
注册自动代理生成器
<bean class = "org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />
在使用时,直接用目标对象,底层代码实现了 BeanPostProcessor 接口(bean 后处理器接口)
存在三个问题:
1)不能选择目标对象
2)不能选择切面类型,切面只能是advisor
3)因为不能选择advisor,所以advisor均将被作为切面织入到目标方法
2,Bean 名称自动代理生成器
注册自动代理生成器
<bean class = "org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name = "beanNames" value = "someService" />
<property name = "interceptorNames" value = "myAdvice" />
</bean>
3.5 AspectJ 框架对 AOP 的实现(重点)
1,AspectJ概述
对于 AOP 这种编程思想,很多框架都进行了实现。spring就是其中之一,可以完成面向切面编程,然而,AspectJ 也实现了 AOP 的功能,
也是一个比较小的框架,且实现方式更为简捷,使用更为方便,而且还支持注解式开发。所以,spring 又将 AspectJ 的对于 AOP 的实现
也引入到了自己的框架中。
在 Spring 中使用 AOP 开发时,一般使用 AspectJ 的实现方式。
2,Aspectj的通知类型
前置通知:
后置通知:
环绕通知:
异常通知:
最终通知:
3,AspectJ 的切入点表达式
表达式原型:
execution([modifiwes-pattern] 访问权限类型
ret-type-pattern 返回值类型
[declaring(param-pattern)] 全限定性类名
name-pattern(param-pattern) 方法名(参数名)
[throws-pattern] 抛出异常类型 )
符号:
* :0至多个任意字符
.. :用在方法参数中,表示任意多个参数用在包名后,表示当前包及其子包路径
+ :用在类名后,表示当前类及其子类
用在接口后,表示当前接口及其实现类
用的比较常用的表达式:
execution(* *..service.*.*(..))
指定所有包下的 service 子包下所有类(接口)中所有方法为切入点
execution(* *..service.*(..))
指定所有包下的 service 接口中所有方法为切入点
3.4 Aspectj 基于注解的 AOP 实现(不常用)
使用步骤:
1)在一个类的上方添加 @Aspect 注解,表示当前类为切面
2)在 xml 文件中注册 这个类
3)定义目标类,即需要增强的类
4)在这个类中定义相应的方法上方添加对应的注解,如:
前置通知:
@Before("execution(* *..ISomeService.doFirst(..))")
public void MyBefore(JoinPoint jp){
// 入参的是具体的与表达式匹配的执行的目标类的权限定性方法名 和返回值
}
后置通知:
@AfterReturning(value = "execution(* *..ISomeService.doSecond(..))",returning = "result")
public void myAfterReturning(Object result){
// result 就是执行方法后的返回值,但是没有返回值。
}
环绕通知:
@Around("execution(* *..ISomeService.doFirst(..))")
public Object myAround(ProceedingJoinPoint pjp){
Object result = pjp.proceed(); // 执行方法,这里需要进行一场处理
return result; // 可以修改返回值
}
异常通知:
@AfterThrowing(value = "execution(* *..ISomeService.doFirst(..), throwing = "ex")")
public void myAfterThrowing(Exception ex){
// 表达式中的 throwing 可以指定也可以不指定,即可以指定异常类型
}
最终通知:
@After("execution(* *..ISomeService.doFirst(..))")
// 定义一个切入点,叫 doThirdPointcut(),上面的表达式都可以换成这个名字
@Pointcut("execution(* *..ISomeService.doFirst(..))")
public void doThirdPointcut(){}
5)在 spring 的注配置文件中注册 AspectJ 的自动代理
<aop:aspectj-autoproxy />
3.5 Aspectj 基于XML 的 AOP 实现
实现步骤:
1)定义一个增强类
2)定义一个需要被增强的类
3)配置 spring 的主配置文件
<aop:config>
<aop:pointcut expression = "execution(* *..ISomeService.doFirst(..))" id="doFirstPointcut">
<aop:pointcut expression = "execution(* *..ISomeService.doFirst(..))" id="doSecondPointcut">
<aop:pointcut expression = "execution(* *..ISomeService.doFirst(..))" id="doThirdPointcut"> // 切入点可以定义多个
<aop:aspect ref = "myAspct"> // 注意这里不是用 id
<aop:before method = "myBefore" pointcut = "execution(* *..ISomeService.doFirst(..))" /> // 表达式的方式一
<aop:before method = "myBefore(org.aspect.lang.JoinPoint)" pointcut-ref = "doFirstPointcut"> // 使用表达式的方式二 // 前置增强
<aop:after " method = "myBefore(org.aspect.lang.JoinPoint)" pointcut-ref = "doFirstPointcut" returning = "result"> // 后置增强
<aop:after-throwing method = "myAfterThrowing" pointcut-ref = "doThirdPointcut" throwing = "ex"> // 异常增强
<aop:after method="doThirdPointcut">
</aop:aspect>
</aop:config>
4,Spring 与 DAO
4.1 Spring 与 JDBC 模板(对 IOC 的应用)
JDBC 模板也是一个框架,也可以对数据库进行增删改查操作,只是这个框架太小了,所以一般不叫它框架。
1,数据源的配置
数据源有三种:
1)Spring 默认的数据源(内置的连接池) DriverManagerDataSource
<bean id = "myDataSource" class = "org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name = "driverClassName" value="com.mysql.jdbc.Driver">
<property name = "url" value="jdbc:mysql://127.0.0.0:3306/test">
<property name = "userName" value="root">
<property name = "password" value="111">
</bean>
2)DBCP 数据源 BasicDataSource
<bean id = "myDataSource" class = "org.springframework.jdbc.datasource.BasicDataSource">
<property name = "driverClassName" value="com.mysql.jdbc.Driver">
<property name = "url" value="jdbc:mysql://127.0.0.0:3306/test">
<property name = "userName" value="root">
<property name = "password" value="111">
</bean>
3)C3P0 数据源 ComboPooledDataSource
<bean id = "myDataSource" class = "org.springframework.jdbc.datasource.ComboPooledDataSource">
<property name = "driverClass" value="com.mysql.jdbc.Driver">
<property name = "jdbcUrl" value="jdbc:mysql://127.0.0.0:3306/test">
<property name = "user" value="root">
<property name = "password" value="111">
</bean>
数据库连接池:
连接池中的连接的创建时机是,应用一启动就先创建好。
1,设置初始创建 连接 数量
2,设置 连接下限 数量
当连接池中的连接数量剩余的数量低于这个数量时,再次创建连接
3,设置一次创建的连接数量
低于连接下限的时候一次创建的连接数量
4,设置连接池中的连接上限
回收连接时的最大数量,当回收的连接数超过这个数量,就会释放超出的连接
5,设置最大空闲连接数
数据库中的连接一直没有被使用,只保留的在连接池中的连接数
6,设置最大空闲时间
4.2 从属性文件读取数据库连接信息
1)<bean/> 方式(不常用)
使用class 为PropertyPlaceholderConfigurer
eg:
<bean class = "org.springframewrk.beans.factory.config.PropertyPlaceholderConfigurer">
<property name = "location" value = "classpath:jdbc.properties" />
</bena>
2)<context:property-placeholder> (常用)
1,引入 context 约束
2,<context:property-placeholder location="classpath:jdbc.properties"/>
4.3 配置 JDBC 模板
把 JDBC 模板,注入到 Dao 中
方式一:
注册 jdbcTemplate
<bean id = "myJdbcTemplate" class = "org.springframework.jdbc.core.JdbcTemplate">
<property name = "dataSource" ref = "myDataSource" />
</bean>
注册 Dao
<bean id = "studetnDao" class = "com.hsz.dao.StudentDaoImpl">
<property name = "jdbcTemplate" ref = "myJdbcTemplate" />
</bean>
方式二:
注册 Dao,直接把数据源注册到 Dao 中,底层源码会自动注入
<bean id = "studetnDao" class = "com.hsz.dao.StudentDaoImpl">
<property name = "dataSource" ref = "myDataSource" />
</bean>
注册 Service
<bean id = "studentService" class = "com.hsz.service.StudentServiceImpl">
<property name = "dao" ref = "studnetDao"/>
</bean>
4.4 Dao 实现类继承 jdbcDaoSupport
继承之后,可以通过 get.JdbcTemplate() 方法获得模板,然后通过调用模板的 update 方法(增删改都是使用 update 方法,和 mybatis 的底层原理一样),和 query 方法
eg: SQL 语句使用 ? 作为 占位符
public void insertStudent(Student student){
String sql = "insert into studnet(name,age) values(?,?)";
this.getJdbcTemplate().update(sql,student.getName(),studnet.getAge());
}
// 查询所有学生的名字,String.class 是指定返回类型
public void selectAllStudentsNames(){
String sql = "select name from studnet";
this.getJdbcTemplate().queryForList(sql,String.class);
}
// 查询单个学生的名字
public void selectAllStudentsNames(){
String sql = "select name from studnet where id = ?;
this.getJdbcTemplate().queryForObject(sql,String.class,id);
}
// 查询所有学生的信息,返回的是学生对象的集合
注:因为 jdbc 模板这个框架功能过于简单,所以需要自己手动进行封装对象。
①,定义一个实现 RowMapper 接口的类
public class StudentRowMapper implements RowMapper<Student>{
// rs:当查询出总的结果集后,框架会自动遍历这个结果集,每次遍历的一行数据,都会被存放到这个方法的 rs 参数中。也就是说,这里的 rs 代表的是一行数据,并非所有的查询结果。换一个角度来说,只要能执行到这个方法,就说明这里的 rs 不会是空的。
public Student mapRow(ResultSet rs,int rowNum) throws SQLException{
Student student = new Studnet();
student.setId(rs.getInt("id"));
studnet.setName(rs.getString("name"));
studnet.setAge(rs.getInt("age"));
return student;
}
}
②,
public Studnet selectAllStudnts(){
String sql = "select id,name,age from studnet;
this.getJdbcTemplate().query(sql,new StudentRowMapper());
}
5,注意:JDBC 模板对象是多例的
jdbcTemplate 对象时多例的,即系统会为每一个使用模板对象的线程(方法)创建一个 jdbcTemplate 实例,并且在该线程(方法)结束时,自动释放 jdbcTemplate 实例。所以在每次使用 jdbcTemplate 对象时,都需要通过 getJdbcTemplate()方法获取。
2)Spring 的事务管理(对 AOP ,IOC 的应用)
1,spring 事务管理的概述 及 API:
1)事务原本是数据库中的概念,在 Dao 层中,但一般情况下,需要经事务提升到 业务层。这样做事为了能够使用事务的特殊性来管理具体的业务。
2)事务管理器接口
PlatformTransactionManager 接口对象。其主要作用是用于完成事务的提交,回滚,及获取事务的状态信息。
常用的两个实现类:
DataSourceTransactionManager:使用JDBC 或 iBatis 进行持久化数据时使用
HibernateTransactionManager:使用 Hibernate 进行持久化数据时使用
Spring 的默认回滚方式是:发生运行时异常时回滚,发生受查时异常时提交。不过,对于受查异常,程序员也可以手动设置其回滚方式。
3)事务定义接口
事务的隔离级别:
trnasaction_default 默认的隔离级别根据不同数据库自动选择
transaction_none 0
transaction_read_committed 2 oracle 的事务隔离级别
transaction_read_uncommited 1
transaction_repeatable_read 4 MySQL的事务隔离级别
transaction_serializable 8
事务的传播行为:
如:a 方法在 a 事务下运行,b 方法在 b 事务下运行。当 a 方法调用 b 方法时,b 方法运行的是哪一个事务环境下运行?
① REQUIRED 默认的
指定的方法必须在事务内执行。若当前存在事务,就加入到当前的事务中;若当前没有事务,则创建一个新事务。这种传播行为是最常见的选择,也是 Spring 默认的事务传播行为。
如该传播行为加在 doOther()方法上。若 doSome()方法在调用 doOther()方法时就是在事务内运行的,则 doOther()方法的执行也
加入到该事务内执行。若 doSome()方法在调用 doOther()方法时没有在事务内执行,则 doOther()方法会创建一个事务,并在其中执行。
② REQUIRES_NEW
总是新建一个事务,若当前存在事务,就将当前事务挂起,直到新事务执行完毕。
③ SUPPPORTS
指定的方法支持当前事务,但若当前没有事务,也可以以非事务方式执行。
④ NOT_SUPPORTED
指定的方法不能再事务环境中执行,若当前存在事务,就将当前事务挂起。
⑤ NESTED
指定的方法必须在事务内执行。若当前存在事务,则在嵌套事务内执行;若当前没有事务,则创建一个新事务。
定义了默认事务超时时限,一般是使用默认的设置值。
⑥MANDATORY
指定的方法必须在当前事务内执行,若当前没有事务,则直接抛出异常。
⑦ NEVER
指定的方法不能在事务环境下执行,若当前存在事务,就直接抛出异常。
在Spring 中通常可以通过以下三种方式来实现对事务的管理:
2,使用 spring 的事务代理工厂管理事务(了解即可)
使用 JDBC 模板的时候,使用的事务管理
缺点:配置文件容易臃肿,在测试类中使用的是代理类
注册事务管理器
<bean id = "myTransactionManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name = "dataSource" ref = "myDataSource" />
</bean>
// 生成事务代理对象
<bean id = "serviceProxy" class = "org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name = "transactionManager" ref = "myTransactionManager" />
<property name = "target" ref ="buyStockService" />
<property name = "transactionAttributes">
<props>
<prop key = "open*">ISOLATION_DEFAULT,PROPAGATION_REQUIRED</prop>
// -异常:表示发生指定异常后回滚,这时的异常通常是受查时异常
// +异常:表示发生指定异常后提交,这时的异常通常是运行时异常
<prop key = "buyStock*">ISOLATION_DEFAULT,PROPAGATION_REQUIRED,-BuyStockException</prop>
</props>
</property>
</bean>
3,使用 Spring 的事务注解管理事务
1)生成事务注解对象
<bean id = "myTransactionManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name = "dataSource" ref = "myDataSource" />
</bean>
2)注册事务注解驱动
需要加 tx 约束
<tx:annotation-driven transaction-manager = "myTransactionManager" />
3)在 service 层中对应的方法上方添加 事务 注解
@Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED,rollbackFor=BuyStockException.class)
public void buyStock(String aname,double money,String sname,int amount) throws Exception{
boolean isBuy = true;
adao.updateAccount(aname,money,isBuy);
if(1 == 1){ // 临时定义的异常
throw new BuyStockException("购买股票异常");
}
sdao.updateStock(sname,amount,isBuy);
}
4,使用 AspectJ 的 AOP 配置管理事务
1)生成事务注解对象
<bean id = "myTransactionManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name = "dataSource" ref = "myDataSource" />
</bean>
2)AOP 配置
注册事务通知,这里指定的是为每一个连接点指定所要应用的事务属性
<tx:advice id = "txAdvice" transaction-manager="myTransactionManager">
<tx:attributes>
<tx:method name="open*" isolation="DEFAULT" propagation="REQUIRED" />
<tx:method name="buyStock" isolation="DEFAULT" propagation="REQUIRED" rollback="BuyStockException">
<tx:attributes>
<tx:advice>
<aop:config>
这里指定的是切入点,没有具体指定方法,当指定方法时,上面的通知就不起作用了,但是一般都在通知中具体配置
<aop:pointcut expression="execution(* *..service.*.*(..))" id="myPointcut">
<aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut">
</aop:config>
5,Spring 与 MyBatis 整合
就是要把 mybatis 的 sqlSessionFactoryBean 交给 spring 来管理
1)Mapper动态代理方式(这种方式不常用,配置文件过于臃肿)
<bean id = "mySqlSessionFactory" class = "org.mybatis.spring.SqlSessionFactoryBean">
<property name = "configLocation" value = "classpath:mybatis.xml">
<property name = "dataSource" value = "myDataSource">
</bean>
针对某一个 Dao 而生成的代理对象
<bean id = "studnetDao" class = "org.mybatis.spring.mapper.MapperFaactoryBean">
<property name = "sqlSessionFactory" ref = "mySqlSessionFactory" />
<property name = "mapperInterface" value = "com.hsz.dao.IstudentDao">
</bean>
注册 Service
<bean id = "studentService" class ="com.hsz.service.StudentServiceImpl">
<properyt name = "dao" ref ="studentDao" />
</bean>
2)支持扫描的 Mapper 动态代理
// 以下配置会为指定的基本包中所有的接口生成代理对象
<bean class = "org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name = "sqlSessionFactoryBeanName" value = "mySessionFactory" />
<property name = "basePackage" value = "com.hsz.dao" />
</bean>
// 注册 service
<bean id = "studentService" class = "com.hsz.service.StudentServiceImpl">
// 这里的 Dao 接口的注入需要使用 ref 属性,
// 若 Dao 的接口名的前两个字母是大写,则这里的值为接口的简单类名
// 若 Dao 的接口名的首字母是大写,第二个字母为小写,则这里的值为简单类名,但首字母要小写
</bean>
6,Spring 与 Web
目的:是为了让 spring 容器的生命周期 和 整个应用的生命周期一致,并且 使创建的 spring 容器是单例的(因为 spring 容器在创建完后,会创建容器中所有的对象实例,如果不是单例的话,就会创建大量的对象,浪费内容),所以把创建 spring 容器的这个工作交给 web 容器来完成。
1)在 web.xml 配置文件中注册 ServletContext监听器,完成两件工作
① 在 ServletContext 被创建时,创建 Spring 容器对象
② 将创建好的 Spring 容器对象放入到 ServletContext 的域属性空间
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
2)配置 <context-param>
指定 spring 配置文件的位置及名称,如果不进行指定只能放在 WEB-INF 目录下
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
3)使用工具类获取 Spring 容器
WebApplicationContext ac = WebApplicationContextUtils.getRequiredWebApplicationContext( getServletContext() );