Spring介绍
Spring是一个非常活跃的开源框架;它是一个基于IOC和AOP来构架多层JavaEE系统的框架,它的主要目地是简化企业开发.
Spring以一种非侵入式的方式来管理你的代码,Spring提倡”最少侵入”,这也就意味着你可以适当的时候安装或卸载Spring
官网:http://www.springsource.org/
(1)应用Spring的好处
l 方便解耦,简化开发
- Spring就是一个大工厂,可以将所有对象创建和依赖关系维护,交给Spring管理
l AOP编程的支持
- Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能
l 声明式事务的支持
- 只需要通过配置就可以完成对事务的管理,而无需手动编程
l 方便程序的测试
- Spring对Junit4支持,可以通过注解方便的测试Spring程序
l 方便集成各种优秀框架
- Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz等)的直接支持
l 降低JavaEE API的使用难度
- Spring 对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低
(2)Spring模块介绍
Spring 框架是一个分层架构,,它包含一系列的功能要素并被分为大约20个模块。这些模块分为Core Container、Data Access/Integration、Web、AOP(Aspect Oriented Programming)、Instrumentation和测试部分,如下图所示:
功能(一) IOC 控制反转
分为两部分
【IOC】
IOC就是 Inversion of Control
所谓控制反转就是应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护是由外部容器负责的。这样控制权就由应用转移到了外部容器,控制权的转移就是所谓反转,目的是为了获得更好的扩展性和良好的可维护性。
【DI】
Dependency Injection
所谓依赖注入就是指:在运行期,由外部容器动态地将依赖对象注入到组件中。
a: 配置(IOC、DI)
到http://www.springsource.org/download下载spring,然后进行解压缩,在解压目录中找到下面jar文件,拷贝到类路径下
* org.springframework.beans-3.2.0.RELEASE.jar
所有应用都要用到的,它包含访问配置文件、创建和管理bean
以及进行Inversion of Control / Dependency Injection(IoC/DI)操作相关的所有类
* org.springframework.context-3.2.0.RELEASE.jar
Spring提供在基础IoC功能上的扩展服务,此外还提供许多企业级服务的支持,
如邮件服务、任务调度、JNDI定位、EJB集成、远程访问、缓存以及各种视图层框架的封装等。
* org.springframework.core-3.2.0.RELEASE.jar
包含Spring框架基本的核心工具类,Spring其它组件要都要使用到这个包里的类,是其它组件的基本核心。
* org.springframework.expression-3.2.0.RELEASE.jar
Spring表达式语言
* org.apache.commons.logging-1.1.1.jar(spring-framework-3.0.2.RELEASE-dependencies)
第三方的主要用于处理日志
提示:spring3.0.X 版本 asm jar包 已经被合并到 spring core包中
创建spring的配置模板
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd“>
</beans>
该配置模版可以从spring的参考手册或spring的例子中得到。配置文件的取名可以任意,文件可以存放在任何目录下,但考虑到通用性,一般放在类路径下。
在spring的配置文件中增加如下配置 –ioc
<bean id="boy" class="cn.itcast.ioc.Boy"></bean>
bean节点中Id和name的区别:
如果spring当前版本是3.0的时候,id和name有以下区别
如果spring当前版本是3.2的时候,id和name的属性可以通用
Spring3.0区别一:
id:指定唯一实例引用
name:可以指定多个实例引用,例如name=“boy,boy1”
Spring3.0区别二:
id :id的命名要满足XML对ID属性命名规范
例如:必须以字母开始,可以使用字母、数字、连字符、下划线、句话、冒号
name:如果Bean的名称中含有特殊字符,就需要使用name属性
例如:<bean name="# boy " class="cn.itcast.ioc.Boy"/>
在spring的beans.xml文件中增加如下配置,红色的为新增加的—di
<bean id="boy" class="cn.itcast.di.Boy"></bean>
<!-- 创建girl的对象 -->
<bean id="girl" class="cn.itcast.di.Girl">
<!-- 依赖注入 把boy对象传递给girl property name="boy1"使用 public void
setBoy1(Boy boy1) à
<property name="boy">
<!-- 引用 bean id="boy"中的boy -->
<ref bean="boy"/>
</property>
</bean>
实例化spring容器的方式
方法一:
在类路径下寻找配置文件来实例化容器
ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"beans.xml"});
可以在整个类路径中寻找xml文件
* 通过这种方式加载。需要将spring的配置文件放到当前项目的classpath路径下
* classpath路径指的是当前项目的src目录,该目录是java源文件的存放位置。
方法二:
在文件系统路径下寻找配置文件来实例化容器
ApplicationContext ctx = new FileSystemXmlApplicationContext(new String[]{“d:\beans.xml“});
Spring的配置文件可以指定多个,可以通过String数组传入。
BeanFactory和ApplicationContext
当使用spring时,我们可以使用容器提供的众多服务
b.三种实例化bean的方式
1.使用类构造器实例化(默认无参数)
<bean id=“personService" class="cn.itcast.bean.impl.PersonServiceImpl"/>
2.使用静态工厂方法实例化(简单工厂模式)
<bean id="personService"
class="com.itcast.factory.PersonServiceFactory" factory-method="createPersonService" />
public class PersonServiceFactory {
public static PersonService createPersonService(){
return new PersonServiceImpl();
}
}
3.使用实例工厂方法实例化(工厂方法模式):
<bean id=“personServiceFactory" class="com.itcast.factory.PersonServiceFactory"/>
<bean id="personService" factory-bean=“personServiceFactory"
factory-method="createPersonService" />
public class PersonServiceFactory {
public PersonService createPersonService(){
return new PersonServiceImpl();
}
Bean的作用域
* singleton(默认值)
在每个Spring IoC容器中一个bean定义只有一个对象实例(共享)。
默认情况下会在容器启动时初始化bean,但我们可以指定Bean节点的lazy-init=“true”来延迟初始化bean,这时候,只有第一次获取bean会才初始化bean。如:
<bean id="xxx" class="cn.itcast.OrderServiceBean" lazy-init="true"/>
如果想对所有bean都应用延迟初始化,可以在根节点beans设置default-lazy-init=“true“,如下:
<beans default-lazy-init="true“ ...>
* prototype
允许bean可以被多次实例化(使用一次就创建一个实例)
* request每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境(一个系统request.setAttribute () )
* session同一个HTTP Session 共享一个Bean,不同Session使用不同Bean,仅适用于WebApplicationContext 环境(一个系统session.setAttribute())
* global session一般用于Porlet应用环境,该作用域仅适用于WebApplicationContext 环境(多个系统共享一个session)
Bean的生命周期
- instantiate bean对象实例化
- populate properties 封装属性
- 如果Bean实现BeanNameAware 执行 setBeanName
- 如果Bean实现BeanFactoryAware 或者 ApplicationContextAware 设置工厂 setBeanFactory 或者上下文对象 setApplicationContext
- 如果存在类实现 BeanPostProcessor(后处理Bean) ,执行如果Bean实现InitializingBean 执行 afterPropertiesSet
接口 BeanPostProcessor 后处理Bean 提供两个钩子函数,用于开发者 动态去修改Bean对象 (动态代理) - 如果bean实现InitializingBean,执行afterPropertiesSet()初始化
- 调用<bean init-method=“userInitMethod"> 指定初始化方法 userInitMethod
- 如果存在类实现 BeanPostProcessor(处理Bean) ,执行postProcessAfterInitialization
- 执行业务处理,即用户自定义的业务操作
- 如果Bean实现 DisposableBean 执行 destroy
- 调用<bean destroy-method=“userDestroyMethod"> 指定销毁方法 userDestroyMethod
依赖注入
手工装配
* 使用xml配置文件
* 使用属性setter方法注入 1.1
* 使用构造器注入 1.2
* 使用注解
* autowired注解 2.1
* resource注解 2.2
* 自动装配 2.3
* byType:按类型装配
* byName:按名称装配
* constructor装配,
* autodetect 不确定装配。
注入依赖对象可以采用手工装配或自动装配,在实际应用中建议使用手工装配,因为自动装配会产生未知情况,开发人员无法预见最终的装配结果。
依赖注入
1.1使用属性setter方法注入
通过setter方法注入依赖
<bean>元素的< property >子元素指明了使用它们的set方法来注入。可以注入任何东西,从基本类型到集合类,甚至是应用系统的bean。
【1】简单bean配置
配置bean的简单属性,基本数据类型和String。
<bean id="personService" class="com.itcast.bean.impl.PersonServiceImpl">
<!-- 基本类型,string类型 -->
<property name="age" value="20"></property>
<property name="name" value="张无忌"></property>
</bean>
【2】引用其它bean(外部bean)
<bean id="person" class="com.itcast.bean.Person" />
<bean id="personService" class="com.itcast.bean.impl.PersonServiceImpl">
<!-- 引用类型 -->
<property name="person" ref="person" />
</bean>
【3】引用其它bean(内部bean)
<bean id="personService" class="com.itcast.bean.impl.PersonServiceImpl">
<!-- 内部bean注入 -->
<property name="personClass">
<bean class="com.itcast.bean.PersonClass" />
</propert>
</bean>
这种方式的缺点是你无法在其它地方重用这个personClass实例,原因是它是专门为personService而用。
【4】使用p命名空间
为了简化XML文件配置,Spring从2.5开始引入一个新的p名称空间
p:<属性名>="xxx" 引入常量值
p:<属性名>_ref="xxx" 引用其它Bean对象
【5】装配集合List和数组
若bean的属性是集合类型,按如下处理:
<!-- 装配list -->
<property name="lists">
<list>
<value>list1</value>
<value>list2</value>
<ref bean="person"/>
</list>
</property>
<!-- 装配数组 -->
<property name="obj">
<array>
<value>obj1</value>
<value>obj2</value>
<ref bean="person"/>
</array>
</property>
【6】装配set
<!-- 装配set -->
<property name="sets">
<set>
<value>set1</value>
<value>set2</value>
<ref bean="person"/>
</set>
</property>
set使用方法和list一样,不同的是对象被装配到set中,而list是装配到List或数组中装配。
【7】装配map:
<!-- 装配map-->
<property name="maps">
<map>
<entry key="01">
<value>map01</value>
</entry>
<entry key="02">
<value>map02</value>
</entry>
</map>
</property>
简化为:
<property name="maps">
<map>
<entry key="01" value="map01"></entry>
<entry key="02" value="map02"></entry>
</map>
</property>
map中的<entry>的数值和<list>以及<set>的一样,可以使任何有效的属性元素,需要注意的是key值必须是String的。
【8】装配集合Property
<!--装配Properties -->
<property name="props">
<props>
<prop key="01">prop1</prop>
<prop key="02">prop2</prop>
</props>
</property>
【9】装配集合List(null)
<!-- 装配null -->
<property name="listnull">
<null/>
</property>
1.2使用构造器注入
【1】通过参数的顺序:
<constructor-arg index="0">
<value>张三</value>
</constructor-arg>
<constructor-arg index="1">
<value>56</value>
</constructor-arg>
【2】通过参数的类型:
<!-- 通过参数的类型 -->
<constructor-arg type="java.lang.Integer">
<value>56</value>
</constructor-arg>
<constructor-arg type="java.lang.String">
<value>张三</value>
</constructor-arg>
2.1 autowired注解
在java代码中使用@Autowired或@Resource注解方式进行装配的前提条件是。
【1】引入context命名空间 需要在xml配置文件中配置以下信息:
<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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config/>
</beans>
【2】在配置文件中添加context:annotation-config标签
<context:annotation-config/>
这个配置隐式注册了多个对注解进行解析处理的处理器
AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,
PersistenceAnnotationBeanPostProcessor,RequiredAnnotationBeanPostProcessor
注: @Resource注解在spring安装目录的
spring-framework-3.0.2.RELEASE-dependenciesjavax.annotationcom.springsource.javax.annotation1.0.0包下com.springsource.javax.annotation-1.0.0.jar
但是这里:
如果使用jdk1.5,需要引入com.springsource.javax.annotation-1.0.0.jar包
如果使用jdk1.6,不需要引入com.springsource.javax.annotation-1.0.0.jar包
【3】使用注解@autowired标注在属性上
Autowired标注在字段上
* @Autowired注解默认按类型进行装配
* 获取该注解标注的字段的类型---PersonDao类型
* 以该类型为条件到spring容器(beans.xml)中去查找bean的id节点的类型是PersonDao类型.
* 找到以后,获取该节点对应的对象,利用反射直接为personDao变量赋值
@Qualifier(“personDao”)该注解以名字为条件查找依赖对象
* 以该注解的的参数personDao条件到spring容器(beans.xml)中去查找bean的id节点的值是personDao的对象
* 找到以后,获取该节点对应的对象, 利用反射直接为personDao变量赋值,如果不存在该名称,抛出异常
例如:
【4】使用注解@autowired标注在set方法上
Autowired标注在setter方法上
@Autowired注解默认按类型进行装配
* 获取 setPersonDao()方法的参数的类型---PersonDao类型
* 以该类型为条件到spring容器(beans.xml)中去查找bean的id节点的类型是PersonDao类型.
* 找到以后,获取该节点对应的对象, 把该对象作为实参传递给该setPersonDao(
PersonDao personDao)的形参.
@Qualifier("personDao")该注解以名字为条件查找依赖对象
* 以该注解的的参数personDao条件到spring容器(beans.xml)中去查找bean的id节点的值是PersonDao的对象
* 找到以后,获取该节点对应的对象, 把该对象作为实参传递给该setPersonDao(
PersonDao personDao)的形参.
* 如果不存在该名称,抛出异常
例如:
2.2 resource注解
1】使用注解@Resource标注在属性上
Resource注解标注在字段上
* @Resource注解默认按名称装配。
* 如果没有指定name属性
* 获取该注解标注的字段值---personDao
* 以该字段值为条件到spring容器(beans.xml)中去查找bean的id节点的值是 personDao的节点
* 找到以后,获取该节点对应的对象, 利用反射直接为personDao变量赋值
* 如果没有找到.并且按照默认的名称找不到依赖对象时, @Resource注解会回退到按类型装配
* 获取该注解标注的字段类型--PersonDao
* 以该类型为条件到spring容器(beans.xml)中去查找bean的节点的类型是PersonDao类型的对象
* 找到以后,获取该节点对应的对象,利用反射直接为personDao变量赋值
* 如果指定name属性,只能按名称装配
* 获取name属性的值 personDao
* 以该值为条件到spring容器(beans.xml)中去查找bean的id节点的值是PersonDao的对象
* 找到以后,获取该节点对应的对象, 利用反射直接为personDao变量赋值
* 如果不存在该名称,抛出异常
例如:
【2】使用@Value注解装配值
@Value注解
在UserServiceImpl的类中定义
@Value(value="金毛狮王")
private String name;
@Value(value="50")
private Integer age;
注意:这里可以省略set()方法
相当于spring容器中配置:
【3】使用注解@Resource标注在set方法上
resource注解标注在setter方法上
* @Resource注解默认按名称装配。
* 如果没有指定name属性
* 获取setPersonDao()方法的属性名---personDao
* 以该属性名为条件到spring容器(beans.xml)中去查找bean的id节点的值是personDao的节点
* 找到以后,获取该节点对应的对象, 把该对象作为实参传递给该setPersonDao(
PersonDao personDao)的形参.
* 如果没有找到.并且按照默认的名称找不到依赖对象时,@Resource注解会回退到按类型装配
* 获取setPersonDao()方法的参数类型---PersonDao
* 以该类型为条件到spring容器(beans.xml)中去查找bean的节点的类型是PersonDao类型对象
* 找到以后,获取该节点对应的对象, 把该对象作为实参传递给该setPersonDao(
PersonDao personDao)方法的形参
* 如果指定name属性 只能按名称装配
* 获取name属性的值
* 以该值为条件到spring容器(beans.xml)中去查找bean的id节点的值是PersonDao的对象
* 找到以后,获取该节点对应的对象, 把该对象作为实参传递给该setPersonDao(
PersonDao personDao)的形参.
* 如果不存在该名称,抛出异常
例如:
3.1)手工装配:bean之间的关系(了解知识)
【1】继承
继承,如果多个Bean具有相同的方法和属性,则可以引入父类Bean,配置父子bean关系
【2】依赖
依赖,一个类内部运行依赖另一个Bean初始化一些数据
SystemInfo 会对 SystemInfo的 taskPeroid 任务周期进行初始化
TaskManager 需要使用 SystemInfo提供 taskPeroid 指定任务周期
总结:TaskManager 依赖 SystemInfo 初始化数据 依赖关系Bean,必须先初始化 SystemInfo 后初始化 TaskManager
(4)手工装配,配置多个XML文件(spring容器)
使用多个XML配置文件
方式一 可以在创建ApplicationContext对象时传入多个配置文件
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("beans1.xml", "beans2.xml");
方式二 可以在配置文件中通过<import>引入其他配置文件
<import resource="classpath:bean2.xml"/>
我们再开发中,一般使用第二种方式加载多个XML文件:例如:
在:src的beans.xml中添加:
<import resource="classpath:cn/itcast/beans.xml"/>
【3】引用
引用,一个Bean可以将另一个Bean的id注入到程序中,在运行期获得其实例
classpath自动扫描把组件纳入spring容器中管理
<context:component-scan base-package="cn.itcast"/>
其中base-package为需要扫描的包(含子包)。
注:
1、在使用组件扫描元素时,AutowiredAnnotationBeanPostProcessor 和CommonAnnotationBeanPostProcessor会隐式地被包括进来。 也就是说,每个组件都会被自动检测并织入 - 所有这一切都不需要在XML中提供任何bean配置元数据。
2、功能介绍
@Service用于标注业务层组件、
@Controller用于标注控制层组件(如struts中的action)、
@Repository用于标注数据访问组件,即DAO组件。
而@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
3.2)手工装配,配置多个XML文件(spring容器)
使用多个XML配置文件
方式一 可以在创建ApplicationContext对象时传入多个配置文件
ApplicationContext applicationContext = new
ClassPathXmlApplicationContext("beans1.xml", "beans2.xml");
方式二 可以在配置文件中通过<import>引入其他配置文件
<import resource="classpath:bean2.xml"/>
我们再开发中,一般使用第二种方式加载多个XML文件:例如:
在:src的beans.xml中添加:
<import resource="classpath:cn/itcast/beans.xml"/>
功能(二)AOP 面向切面编程
1 :AOP代理模式
代理模式:代理模式的英文叫做Proxy或Surrogate,中文都可译为”代理“,所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用
抽象主题角色:声明了真实主题和代理主题的共同接口,这样一来在任何可以使用真实主题的地方都可以是使用代理主题
代理主题(Proxy)角色:代理主题角色内部含有对真实主题的引用,从而可以在任何时候操作真实主题对象;代理主题角色提供一个与真实主题角色相同的接口,以便可以在任何时候都可以替代真实主题控制对真实主题的引用,负责在需要的时候创建真实主题对象(和删除真实主题对象);代理角色通常在将客户端调用传递给真实的主题之前或之后,都要执行某个操作,而不是单纯地将调用传递给真实主题对象。
真实主题角色:定义了代理角色所代表地真实对象
2:JDK动态代理
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JDKProxy implements InvocationHandler{
private Object targerObject;//代理的目标对象
/**
* 创建代理对象
* @param targerObject 目标对象
* @return
*/
public Object createProxyInstance(Object targerObject){
this.targerObject=targerObject;
/*
* 第一个参数设置代码使用的类加载器,一般采用跟目标类相同的类加载器
* 第二个参数设置代理类实现的接口,跟目标类使用相同的接口
* 第三个参数设置回调对象,当代理对象的方法被调用时,会调用该参数指定对象的invoke方法
*/
return Proxy.newProxyInstance(
targerObject.getClass().getClassLoader(),
targerObject.getClass().getInterfaces(),
this);
}
/**
* @param proxy 目标对象的代理类实例
* @param method 对应于在代理实例上调用接口方法的Method实例
* @param args 传入到代理实例上方法参数值的对象数组
* @return 方法的返回值 没有返回值时是null
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("代理类实例 "+proxy.getClass());
System.out.println("方法名称 "+method.getName());
if(args!=null&&args.length>0){//方法的参数值
for(int i=0;i<args.length;i++){
System.out.println("方法参数值 "+args[i].toString()); }
}
Object returnvalue=null; //定义方法的返回值 没有返回值时是null
returnvalue=method.invoke(this.targerObject, args); //调用目标对象的方法
System.out.println("方法的返回值 "+returnvalue);
return returnvalue;
}
}
3:CGLIB代理
CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class UserManagerCglibProxy implements MethodInterceptor {
private Object targerObject; //代理的目标对象
/**
* 创建目标对象的代理对象
* @param targerObject 目标对象
* @return
*/
public Object createProxyInstance(Object targerObject){
this.targerObject=targerObject;
Enhancer enhancer=new Enhancer(); //该类用于生成代理对象
enhancer.setSuperclass(this.targerObject.getClass()); //设置父类
enhancer.setCallback(this); //设置回调用对象为本身
return enhancer.create(); //创建代理对象
}
/**
* @param obj 目标对象代理类的实例
* @param method 代理实例上调用父类方法的Method实例
* @param args 传入到代理实例上方法参数值的对象数组
* @param methodProxy 使用它调用父类的方法
* @return
* @throws Throwable
*/
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
System.out.println("代理类 "+obj.getClass());
System.out.println("方法名称 "+method.getName());
if(args!=null&&args.length>0){//方法的参数值
for(int i=0;i<args.length;i++){
System.out.println("方法参数 "+args[i]); }}
Object returnvalue =null; //方法的返回值,无返回类型时,为null
returnvalue=methodProxy.invoke(this.targerObject, args); //调用目标对象的方法
System.out.println("方法的返回值 "+returnvalue);
return returnvalue;
}
}
这里要引入cglib的相关包:com.springsource.net.sf.cglib-2.1.3.jar,从spring提供的第三方包中获取。
spring中AOP的思想和概念
概念
Aspect(切面): 是通知和切入点的结合,通知和切入点共同定义了关于切面的全部内容---它的功能、在何时和何地完成其功能
joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点.
Pointcut(切入点):所谓切入点是指我们要对哪些joinpoint进行拦截的定义.
通知定义了切面的”什么”和”何时”,切入点就定义了”何地”.
Advice(通知):所谓通知是指拦截到joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
Target(目标对象):代理的目标对象
Weaving(织入):是指把切面应用到目标对象来创建新的代理对象的过程.切面在指定的连接点织入到目标对象
Introduction(引入):在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
spring在运行期创建代理,不需要特殊的编译器.
4:spring面向切面的编程
Spring提供2个代理模式,一个是jdk代理,另一个cglib代理
1.若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。
2.若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。
注意:开发时尽量使用接口的编程,
(1)对接口创建代理优于对类创建代理,因为会产生更加松耦合的系统。
(2)标记为final的方法不能够被通知。spring是为目标类产生子类。任何需要被通知的方法都被复写,将通知织入。final方法是不允许重写的。
(3) spring只支持方法连接点,不支持属性的连接点
(1)配置:
要进行AOP编程,首先我们要在spring的配置文件中引入aop命名空间:
<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-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
</beans>
Spring提供了两种切面使用方式,实际工作中我们可以选用其中一种:
l 基于XML配置方式进行AOP开发。
l 基于注解方式进行AOP开发。
SpringAOP编程,需要引入的jar包
(2)基于XML方式的配置通知
通知类别
before:前置通知(应用:各种校验)
在方法执行前执行,如果其中抛出异常
after:后通知(应用:清理现场)
方法执行完毕后执行,无论方法中是否出现异常
afterReturning:返回后通知(应用:常规数据处理)
方法正常返回后执行,如果方法中抛出异常,无法执行
afterThrowing:抛出异常后通知(应用:包装异常信息)
方法抛出异常后执行,如果方法没有抛出异常,无法执行
around:环绕通知(应用:十分强大,可以做任何事情)
方法执行前后分别执行,可以阻止方法的执行
//环绕通知
public void around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("around before......");
//调用原始方法
pjp.proceed();
System.out.println("around after......");
}
1.通知的配置格式
<aop:before pointcut-ref="pt2" method="before"/>
<aop:after pointcut-ref="pt2" method="after"/>
<aop:after-returning pointcut-ref="pt2" method="afterReturning"/>
<aop:after-throwing pointcut-ref="pt2" method="afterThrowing"/>
<aop:around pointcut-ref="pt2" method="around"/>
2.通知顺序:与配置顺序有关
多个切面间
先声明的before先运行,
后声明的before后运行
先声明的after后运行
后声明的after先运行
总结:配置时以最终运行顺序为准
总结:
AOP:关注共性功能的开发
共性功能抽出,运行时原始功能错误,必须加入共性功能,原始对象已经无法完成该功能,AOP代理负责 创建一个新的对象,该对象完成原始功能,通过织入操作完成。
Spring加载上下文时,会读取AOP的配置aop:config,Spring已经知道了哪些方法需要被监控,由切入点实现,当被监控的方法运行时,完成上面一段的操作,加入的功能需要根据配置完成。
织入:A模块中缺少B功能 BA A.class
编译期植入:A.class中对应的功能已经有BA
类加载期植入:A.class中不具有B功能,但是加载A.class时,内存中的类A.class具有B功能
运行期植入:执行到时,添加(Spring实现)
前置通知
前置通知:在访问目标对象方法之前,先执行通知,再执行目标对象的方法!
* 如果通知的方法抛出异常,此时不会执行目标对象中的方法
* 案例:权限控制,事务处理,校验
配置文件如下
<bean id="security" class="com.itcast.service.Security" /> <!-- 定义切面对象 -->
<bean id=“userManager” class=“com.itcast.service.UserManagerImpl” /> <!-- 创建接口实现类目标对象 -->
<aop:config> <!--所有的切面和通知都必须定义在aop:config元素内部 -->
<aop:aspect ref="security"> <!-- 配置切面 -->
<!-- 声明切入点 -->
<aop:pointcut id="userManagerPointcut"
expression="execution(* com.itcast.service..*.save*(..))"/>
<!--声明前置通知,在匹配方法执行前运行-->
<aop:before method="checkSecurity" pointcut-ref="userManagerPointcut"/>
</aop:aspect>
</aop:config>
附录:切入点表达式的练习
<!--
理解切入点中的表达式
execution(* cn.itcast.e_xml.a_before.UserServiceImpl.saveUser(..))
execution(
modifiers-pattern?
ret-type-pattern
declaring-type-pattern?
name-pattern(param-pattern)
throws-pattern?
)
除了返回类型模式(上面代码片断中的ret-type-pattern),名字模式和参数模式以外, 所有的部分都是可选的。
返回类型模式决定了方法的返回类型必须依次匹配一个连接点。 你会使用的最频繁的返回类型模式是*,它代表了匹配任意的返回类型。
一个全限定的类型名将只会匹配返回给定类型的方法。
名字模式匹配的是方法名。 你可以使用*通配符作为所有或者部分命名模式。
参数模式稍微有点复杂:()匹配了一个不接受任何参数的方法,
而(..)匹配了一个接受任意数量参数的方法(零或者更多)。
模式(*)匹配了一个接受一个任何类型的参数的方法。
模式(*,String)匹配了一个接受两个参数的方法,第一个可以是任意类型, 第二个则必须是String类型
5个参数:带?号表示非必填项
参数一modifiers-pattern?(非必填项):方法的修饰符
execution(public * cn.itcast.e_xml.a_before.UserServiceImpl.saveUser(..)):
方法是公用方法,返回类型任意,在cn.itcast.e_xml.a_before包中的UserServiceImpl,类中的saveUser的方法,参数任意
execution(private * cn.itcast.e_xml.a_before.UserServiceImpl.saveUser(..)):
方法是私有方法,返回类型任意,在cn.itcast.e_xml.a_before包中的UserServiceImpl,类中的saveUser的方法,参数任意
参数二ret-type-pattern (必填项):方法的返回值类型
execution(void cn.itcast.e_xml.a_before.UserServiceImpl.saveUser(..))
方法是无返回值,在cn.itcast.e_xml.a_before包中的UserServiceImpl,类中的saveUser的方法,参数任意
execution(java.lang.String cn.itcast.e_xml.a_before.UserServiceImpl.saveUser(..))
方法的返回值是String类型,在cn.itcast.e_xml.a_before包中的UserServiceImpl,类中的saveUser的方法,参数任意
execution(* cn.itcast.e_xml.a_before.UserServiceImpl.saveUser(..))
方法的返回值类型任意,在cn.itcast.e_xml.a_before包中的UserServiceImpl,类中的saveUser的方法,参数任意
参数三declaring-type-pattern? (非必填项):包及其包中的类
execution(* cn.itcast.e_xml.a_before.UserServiceImpl.saveUser(..))
方法的返回值类型任意,在cn.itcast.e_xml.a_before包中的UserServiceImpl,类中的saveUser的方法,参数任意
execution(* cn.itcast.e_xml.a_before.User*.saveUser(..))
方法的返回值类型任意,在cn.itcast.e_xml.a_before包中,以User开头的类,类中的saveUser的方法,参数任意
execution(* *.saveUser(..))
方法的返回值类型任意,在任意包中,任意类,类中的saveUser的方法,参数任意
execution(* saveUser(..))
方法的返回值类型任意,在任意包中,任意类,类中的saveUser的方法,参数任意
参数四name-pattern(param-pattern)(必填项):方法,括号中表示方法的参数
方法:
execution(* cn.itcast.e_xml.a_before.UserServiceImpl.saveUser(..))
方法的返回值类型任意,在cn.itcast.e_xml.a_before包中的UserServiceImpl,类中的saveUser的方法,参数任意
execution(* cn.itcast.e_xml.a_before.UserServiceImpl.save*(..))
方法的返回值类型任意,在cn.itcast.e_xml.a_before包中的UserServiceImpl,类中以save开头的方法,参数任意
execution(* cn.itcast.e_xml.a_before.UserServiceImpl.*(..))
方法的返回值类型任意,在cn.itcast.e_xml.a_before包中的UserServiceImpl,类中的所有方法,参数任意
参数:
execution(* cn.itcast.e_xml.a_before.UserServiceImpl.saveUser(java.lang.String,java.lang.Integer))
方法的返回值类型任意,在cn.itcast.e_xml.a_before包中的UserServiceImpl,类中的saveUser的方法,参数有2个,一个是String类型,一个Integetr类型
execution(* cn.itcast.e_xml.a_before.UserServiceImpl.saveUser())
方法的返回值类型任意,在cn.itcast.e_xml.a_before包中的UserServiceImpl,类中的saveUser的方法,参数没有参数
execution(* cn.itcast.e_xml.a_before.UserServiceImpl.saveUser(java.lang.String,*))
方法的返回值类型任意,在cn.itcast.e_xml.a_before包中的UserServiceImpl,类中的saveUser的方法,参数有2个,一个是String类型,第二个参数是任意类型
execution(* cn.itcast.e_xml.a_before.UserServiceImpl.saveUser(*))
方法的返回值类型任意,在cn.itcast.e_xml.a_before包中的UserServiceImpl,类中的saveUser的方法,参数有1个,一个是任意类型的参数
execution(* cn.itcast.e_xml.a_before.UserServiceImpl.saveUser(..))
方法的返回值类型任意,在cn.itcast.e_xml.a_before包中的UserServiceImpl,类中的saveUser的方法,参数任意
参数五:throws-pattern?(非必填项):方法抛出的异常
重点写法:
execution(* cn.itcast.e_xml..*.*(..))
方法的返回值类型任意,在 cn.itcast.e_xml包及其子包中的所有类,类中的所有方法,参数任意
例子:
任意公共方法的执行:
execution(public * *(..))
任何一个名字以“set”开始的方法的执行:
execution(* set*(..))
AccountService接口定义的任意方法的执行:
execution(* com.xyz.service.AccountService.*(..))
在service包中定义的任意方法的执行:
execution(* com.xyz.service.*.*(..))
重点:
在service包或其子包中定义的任意方法的执行:
execution(* com.xyz.service..*.*(..))
-->
后置通知
后置通知:在访问目标对象方法之后,再执行通知对应的方法!
* 如果目标对象方法抛出异常,此时不会执行通知的方法
* 案例:日志备份
* 特点:可以在通知的方法中添加目标对象方法的返回值!
public class Security {
public void checkSecurity(JoinPoint point,Object retval){
System.out.println("进行安全性检查");
//获取目标对象方法的返回值
System.out.println("retval返回值: "+retval);
}
}
配置文件如下
<bean id="security" class="com.itcast.service.Security" /><!-- 定义切面对象 -->
<bean id="userManager" class="com.itcast.service.UserManagerImpl" /><!--创建接口实现类对象-->
<aop:config> <!--所有的切面和通知都必须定义在aop:config元素内部 -->
<aop:aspect ref="security"> <!-- 声明切面 -->
<!-- 声明切入点 -->
<aop:pointcut id="userManagerPointcut"
expression="execution(* com.itcast.service..*.save*(..))"/>
<!--声明后置通知,在匹配的方法完全执行后运行-->
<aop:after-returning method="checkSecurity" pointcut-ref="userManagerPointcut“ returning="retval"/>
</aop:aspect>
</aop:config>
异常通知
异常通知:在访问目标对象方法之后,必须目标对象方法抛出异常,在执行通知中定义的方法
* 案例:监控信息,如果程序操作正常,不需要给工程师发送一个短信
* 特点:可以在通知的方法中添加目标对象方法抛出的异常!
public class Security {
public void checkSecurity(JoinPoint point,Throwable ex){
System.out.println("进行安全性检查");
//获取异常
System.out.println("ex "+ex);
}
}
配置文件如下
<bean id="security" class="com.itcast.service.Security" /><!-- 定义切面对象 -->
<bean id="userManager" class="com.itcast.service.UserManagerImpl" /><!--创建接口实现类对象-->
<aop:config> <!--所有的切面和通知都必须定义在aop:config元素内部 -->
<aop:aspect ref="security"> <!-- 声明切面 -->
<!-- 声明切入点 -->
<aop:pointcut id="userManagerPointcut"
expression="execution(* com.itcast.service..*.save*(..))"/>
<!--声明异常通知,在匹配方法抛出异常退出时执行-->
<aop:after-throwing method="checkSecurity" pointcut-ref="userManagerPointcut"
throwing="ex"/>
</aop:aspect>
</aop:config>
最终通知
最终通知:在访问目标对象方法之后,不管目标对象是否抛出异常,都会执行通知(后置通知+异常通知)
* 案例:后置通知+异常通知的特点
配置文件如下
<bean id="security" class="com.itcast.service.Security" /><!-- 定义切面对象 -->
<bean id="userManager" class="com.itcast.service.UserManagerImpl" /><!--创建接口实现类对象-->
<aop:config> <!--所有的切面和通知都必须定义在aop:config元素内部 -->
<aop:aspect ref="security"> <!-- 声明切面 -->
<!-- 声明切入点 -->
<aop:pointcut id="userManagerPointcut"
expression="execution(* com.itcast.service..*.save*(..))"/>
<!--声明最终通知,在匹配方法退出后执行,不论一个方法是如何结束的,最终通知都会运行-->
<aop:after method="checkSecurity" pointcut-ref="userManagerPointcut"/> </aop:aspect>
</aop:config>
环绕通知
环绕通知。环绕通知在一个方法执行之前和之后执行。
* 它使得通知有机会 在一个方法执行之前和执行之后运行。而且它可以决定这个方法在什么时候执行,如何执行,甚至是否执行。
* 环绕通知经常在某线程安全的环境下,你需要在一个方法执行之前和之后共享某种状态的时候使用。
* 请尽量使用最简单的满足你需求的通知。(比如如果简单的前置通知也可以适用的情况下不要使用环绕通知)。
public class Security {
public Object checkSecurity(ProceedingJoinPoint point){
System.out.println("进行安全性检查");
//pg自己控制目标方法的调用
Object returnvalue=null;
try {returnvalue=point.proceed();} catch (Throwable e) {e.printStackTrace();}
System.out.println("returnvalue "+returnvalue);
return returnvalue;
}
}
配置文件如下
<bean id="security" class="com.itcast.service.Security" /><!-- 定义切面对象 -->
<bean id="userManager" class="com.itcast.service.UserManagerImpl" /><!--创建接口实现类对象-->
<aop:config> <!--所有的切面和通知都必须定义在aop:config元素内部 -->
<aop:aspect ref="security"> <!-- 声明切面 -->
<!-- 声明切入点 -->
<aop:pointcut id="userManagerPointcut"
expression="execution(* com.itcast.service..*.save*(..))"/>
<!--声明环绕通知,可以控制方法的执行-->
<aop:around method=“checkSecurity” pointcut-ref=“userManagerPointcut” />
</aop:aspect>
</aop:config>
(2)基于注解方式的配置通知
为了在Spring配置中使用@AspectJ切面,你首先必须启用Spring对@AspectJ切面配置的支持,并确保自动代理 (蓝色部分):
<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 http://www.springframework.org/schema/aop/spring-aop.xsd”>
<!--启用Spring对@AspectJ的支持 -->
<aop:aspectj-autoproxy/>
<!-- 声明切面对象 -->
<bean id="security" class="com.itcast.service.Security" />
<!-- 创建接口实现类对象 -->
<bean id="userManager" class="com.itcast.service.UserManagerImpl" />
</beans>
前置通知
@Aspect //声明切面
public class Security {
/**
* @Pointcut 用于声明切入点
* 在@AspectJ注解风格的AOP中 一个切入点签名通过一个普通的方法来定义
* 1 作为切入点签名的方法必须返回void类型
* 2 方法没有参数 用private修饰
* 3 方法体为空
* 4 方法没有参数
* 切入点表达式的写法
* execution(主要)表示匹配方法执行的连接点
* 例如: * com.itcast.service..*.save*(..))
* 1 "*" 表示方法的返回类型任意
* 2 com.itcast.service..* 表示service包及其子包中所有的类
* 3 .save、delete* 表示类中所有以save或者delete开头的方法
* 4 (..) 表示参数是任意数量
*/
@Pointcut(“execution(* cn.itcast.service..*.save*(..))")
private void perform(){}
@Pointcut(“execution(* cn.itcast.service..*.delete*(..))")
private void perform2(){}
/**
* Before 前置通知 在方法调用前执行
* perform()|| perform2() 表示前面定义的切入点
*/
@Before("perform()||perform2()")
public void checkSecurity(JoinPoint point){
System.out.println("进行安全性检查");
System.out.println("point.getTarget() "+point.getTarget());
//获取方法调用方法的名称
System.out.println("point.getSignature().getName() "+point.getSignature().getName());
//获取方法的参数
if(point.getArgs()!=null&&point.getArgs().length>0){
for(int i=0;i<point.getArgs().length;i++){
System.out.println("point.getArgs() "+point.getArgs()[i]);
}
}
}
}
后置通知
@Aspect //声明切面
public class Security {
/**
* @Pointcut 用于声明切入点
* 在@AspectJ注解风格的AOP中 一个切入点签名通过一个普通的方法来定义
* 1 作为切入点签名的方法必须返回void类型
* 2 方法没有参数 用private修饰
* 3 方法体为空
* 4 方法没有参数
* 切入点表达式的写法
* execution(主要)表示匹配方法执行的连接点
* 例如: * com.itcast.service..*.save*(..))
* 1 "*" 表示方法的返回类型任意
* 2 com.itcast.service..* 表示service包及其子包中所有的类
* 3 .save* 表示类中所有以save开头的方法
* 4 (..) 表示参数是任意数量
*/
@Pointcut(“execution(* cn.itcast.service..*.save*(..))")
private void perform(){}
@Pointcut(“execution(* cn.itcast.service..*.delete*(..))")
private void perform2(){}
/**
* AfterReturning 后置通知 在一个匹配的方法返回的时候执行
* * value 使用的切入点
* * returning 表示方法的返回值.方法无返回值时,返回值为空
*
* * perform()||perform2() 表示前面定义的切入点
*/
@AfterReturning(value = "perform()||perform2()", returning = "retval")
public void checkSecurity(JoinPoint point, Object retval) {
System.out.println("进行安全性检查");
System.out.println("point.getTarget() " + point.getTarget());
// 获取方法调用方法的名称
System.out.println("point.getSignature().getName() "+ point.getSignature().getName());
// 获取方法的参数
if (point.getArgs() != null && point.getArgs().length > 0) {
for (int i = 0; i < point.getArgs().length; i++) {
System.out.println("point.getArgs() " + point.getArgs()[i]);
}
}
System.out.println("retval " + retval);//获取返回值的目的,在切入点后可以再执行业务逻辑
}
}
异常通知
@Aspect //声明切面
public class Security {
/**
* @Pointcut 用于声明切入点
* 在@AspectJ注解风格的AOP中 一个切入点签名通过一个普通的方法来定义
* 1 作为切入点签名的方法必须返回void类型
* 2 方法没有参数 用private修饰
* 3 方法体为空
* 4 方法没有参数
* 切入点表达式的写法
* execution(主要)表示匹配方法执行的连接点
* 例如: * com.itcast.service..*.save*(..))
* 1 "*" 表示方法的返回类型任意
* 2 com.itcast.service..* 表示service包及其子包中所有的类
* 3 .save* 表示类中所有以save开头的方法
* 4 (..) 表示参数是任意数量
*/
@Pointcut(“execution(* cn.itcast.service..*.save*(..))")
private void perform(){}
@Pointcut(“execution(* cn.itcast.service..*.delete*(..))")
private void perform2(){}
/**
* AfterThrowing 异常通知,在一个方法抛出异常后执行
* * value 使用的切入点
* * throwing 表示抛出的异常
* perform()||perform2() 表示前面定义的切入点
*/
@AfterThrowing(value = "perform()||perform2()", throwing = "ex")
public void checkSecurity(JoinPoint point, Throwable ex) {
System.out.println("进行安全性检查");
System.out.println("point.getTarget() " + point.getTarget());
// 获取方法调用方法的名称
System.out.println("point.getSignature().getName() "+ point.getSignature().getName());
// 获取方法的参数
if (point.getArgs() != null && point.getArgs().length > 0) {
for (int i = 0; i < point.getArgs().length; i++) {
System.out.println("point.getArgs() " + point.getArgs()[i]);
}
}
System.out.println("ex " + ex);
}
}
最终通知
@Aspect //声明切面
public class Security {
/**
* @Pointcut 用于声明切入点
* 在@AspectJ注解风格的AOP中 一个切入点签名通过一个普通的方法来定义
* 1 作为切入点签名的方法必须返回void类型
* 2 方法没有参数 用private修饰
* 3 方法体为空
* 4 方法没有参数
* 切入点表达式的写法
* execution(主要)表示匹配方法执行的连接点
* 例如: * com.itcast.service..*.save*(..))
* 1 "*" 表示方法的返回类型任意
* 2 com.itcast.service..* 表示service包及其子包中所有的类
* 3 .save* 表示类中所有以save开头的方法
* 4 (..) 表示参数是任意数量
*/
@Pointcut(“execution(* cn.itcast.service..*.save*(..))")
private void perform(){}
@Pointcut(“execution(* cn.itcast.service..*.delete*(..))")
private void perform2(){}
/**
* @After 最终通知 不论一个方法如何结束,最终通知都会执行
* 最终通知必须准备处理正常和异常两种情况,通常用它来释放资源
* perform()||perform2() 表示前面定义的切入点
*/
@After(value = "perform()||perform2()")
public void checkSecurity(JoinPoint point) {
System.out.println("进行安全性检查");
System.out.println("point.getTarget() " + point.getTarget());
// 获取方法调用方法的名称
System.out.println("point.getSignature().getName() "+ point.getSignature().getName());
// 获取方法的参数
if (point.getArgs() != null && point.getArgs().length > 0) {
for (int i = 0; i < point.getArgs().length; i++) {
System.out.println("point.getArgs() " + point.getArgs()[i]);
}
}
}
}
环绕通知
@Aspect //声明切面
public class Security {
/**
* @Pointcut 用于声明切入点
* 在@AspectJ注解风格的AOP中 一个切入点签名通过一个普通的方法来定义
* 1 作为切入点签名的方法必须返回void类型
* 2 方法没有参数 用private修饰
* 3 方法体为空
* 4 方法没有参数
* 切入点表达式的写法
* execution(主要)表示匹配方法执行的连接点
* 例如: * com.itcast.service..*.save*(..))
* 1 "*" 表示方法的返回类型任意
* 2 com.itcast.service..* 表示service包及其子包中所有的类
* 3 .save* 表示类中所有以save开头的方法
* 4 (..) 表示参数是任意数量
*/
@Pointcut(“execution(* cn.itcast.service..*.save*(..))")
private void perform(){}
@Pointcut(“execution(* cn.itcast.service..*.delete*(..))")
private void perform2(){}
/** @Around 环绕通知 可以控制方法的执行
* * 通知的第一个参数必须是 ProceedingJoinPoint类型
* * 调用ProceedingJoinPoint的proceed()方法会导致 后台的连接点方法执行
* perform()||perform2() 表示前面定义的切入点
* @throws Throwable */
@Around(value = "perform()||perform2()")
public Object checkSecurity(ProceedingJoinPoint point) {
System.out.println("进行安全性检查");
System.out.println("point.getTarget() " + point.getTarget());
// 获取方法调用方法的名称
System.out.println("point.getSignature().getName() "+ point.getSignature().getName());
// 获取方法的参数
if (point.getArgs() != null && point.getArgs().length > 0) {
for (int i = 0; i < point.getArgs().length; i++) {
System.out.println("point.getArgs() " + point.getArgs()[i]);
}
}
//切入点在通知之后执行
Object returnvalue=null;
try {returnvalue = point.proceed();}
catch (Throwable e) {
e.printStackTrace();
}
return returnvalue;
}
}
整合JDBC
使用Spring+JDBC集成需要的jar包
com.springsource.org.apache.commons.dbcp-1.2.2.osgi.jar
com.springsource.org.apache.commons.pool-1.5.3.jar
DBCP连接池需要的jar包
org.springframework.jdbc-3.2.0.RELEASE.jar
org.springframework.tx-3.2.0.RELEASE.jar
Spring的jdbc编程需要的jar包
连接数据库的驱动包
junit测试(org.junitcom.springsource.org.junit4.7.0com.springsource.org.junit-4.7.0.jar)
使用Spring+JDBC集成步骤如下:
<!-- 1 配置dbcp数据源 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="org.gjt.mm.mysql.Driver"/>
<!-- & 转义字符 其实就是代表一个& -->
<property name="url" value="jdbc:mysql://localhost:3306/itcastdb?useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!--2 配置JDBC模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置dao层 -->
<bean id="accountDao" class="com.itcast.dao.impl.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate" />
</bean>
配置数据源(详细)
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="org.gjt.mm.mysql.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/itcastdb?useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
<!-- 连接池启动时的初始值 -->
<property name="initialSize" value="1"/>
<!-- 连接池的最大值 -->
<property name="maxActive" value="500"/>
<!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 -->
<property name="maxIdle" value="2"/>
<!-- 最小空闲值.当空闲的连接数少于该值时,连接池就会预申请一些连接,以避免洪峰来时再申请而造成的性能开销 -->
<property name="minIdle" value="1"/>
</bean>
上面使用了Apache组织的dbcp数据源,我们需要把libjakarta-commons下的commons-dbcp.jar和commons-pool.jar加入类路下。
实现增删改查
public class AccountDaoImpl implements AccountDao {
private JdbcTemplate jdbcTemplate; //声明JDBC模板
//添加
public void saveAccount(Account account) throws Exception {
jdbcTemplate.update("insert into account(accountid,balance) values(?,?)“,
new Object[]{account.getAccountid(),account.getBalance()},
new int[]{java.sql.Types.VARCHAR,java.sql.Types.DOUBLE});
}
//更新
public void updateAccount(Account account) throws Exception {
String sql="update account set balance=? where accountid=?";
jdbcTemplate.update(sql,
new Object[]{account.getBalance(),account.getAccountid()},
new int[]{java.sql.Types.DOUBLE,java.sql.Types.VARCHAR});
}
//删除
public void deleteAccount(String accountid) throws Exception {
String sql="delete from account where accountid=?";
jdbcTemplate.update(sql,
new Object[]{accountid},
new int[]{java.sql.Types.VARCHAR}
);
}
}
//获取多条记录
public List<Account> findAll() {
RowMapper rowMapper=new RowMapper(){
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
Account account=new Account();
account.setAccountid(rs.getString(1));
account.setBalance(rs.getDouble(2));
return account;
}
};
List<Account> list=null;
String sql="SELECT accountid,balance FROM account ORDER BY accountid";
list=this.getJdbcTemplate().query(sql, rowMapper);
return list;
}
//获取单条记录,使用id
public Account findAccountById(String id) {
RowMapper rowMapper=new RowMapper(){
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
Account account=new Account();
account.setAccountid(rs.getString(1));
account.setBalance(rs.getDouble(2));
return account;
}
};
Account account=null;
String sql="SELECT accountid,balance FROM account WHERE accountid=?";
Object[] args={id};
account=(Account)this.getJdbcTemplate().queryForObject(sql, rowMapper, args);
return account;
}