1 AOP
1.1 什么是AOP
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
* AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码
* 经典应用:性能监视、事务管理、安全检查、缓存等
* Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码
* AspectJ是一个基于Java语言的AOP框架,Spring2.0开始,Spring AOP引入对Aspect的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入
1.2 aop实现原理
* aop 底层采用是代理机制。
* 接口 + 实现类:有接口,采用JDK动态代理。
* 实现类:只有实现类,没有接口,采用CGLIB 字节码增强
1.3 aop相关术语(掌握)
* Target : 目标类,需要被增强的类。
* JoinPoint:连接点,目标类上需要被增强的方法。(这些方法可以被增强,也可能不增强)
* PointCut:切入点,被增强的连接点。(已经增强了)
切入点可以理解为是连接点一个子集。
* Advice :增强/通知,增强的方法。
* weaving:织入,将切入点和通知结合,生成代理类过程。
* Proxy:代理类
* Aspect:切面,切入点和通知结合(切入点和 通知点 多点形成面)
特殊情况:一个切入点和 一个通知
* Introduction(引介):特殊的通知,可以对类增强,添加方法或字段。(知道)
2 代理模式-手动方式:(了解)
2.1 动态代理:接口+实现类
* 步骤一:目标类,接口+实现类
public interface UserService { public void addUser(); public void updateUser(); }
public class UserServiceImpl implements UserService { @Override public void addUser() { System.out.println("jdk add user"); } @Override public void updateUser() { System.out.println("jdk update user"); } }
* 步骤二:编写切面类
public class MyAspect { public void before(){ System.out.println("前"); } public void after(){ System.out.println("后"); } }
* 步骤三:编写工厂,生成代理类,将目标类与切面类结合。使用JDK的动态代理
public class MyFactory { /** * 工厂生产代理类,目的:将目标类(切入点)和切面类(通知) 结合。 * @return */ public static UserService createService() { //1 创建目标类 final UserService userService = new UserServiceImpl(); //2 创建切面类 final MyAspect myAspect = new MyAspect(); //3 使用jdk 动态代理生产代理类 UserService proxyService = (UserService)Proxy.newProxyInstance( MyFactory.class.getClassLoader(), userService.getClass().getInterfaces(), new InvocationHandler(){ // 代理类每一个方法执行时,都将调用处理类的invoke方法 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 执行前通知 myAspect.before(); //执行目标类的相应方法 Object obj = method.invoke(userService, args); // 执行后通知 myAspect.after(); return obj; //返回方法返回值 } }); return proxyService; //返回代理类 } }
* 步骤四:测试
@Test public void demo02(){ UserService userService = MyFactory.createService(); userService.addUser(); userService.updateUser(); }
2.2 CGLIB代理:实现类
* cglib:字节码增强工具,一般框架都使用。只要有类就可以增强。
* 使用需要到导入jar包:
核心:cglib-2.2.jar
依赖:asm...jar
spring已经将cglib 和 asm 整合 spring.core..jar中。
* 步骤一:提供目标类,没有接口
public class UserServiceImpl{ public void addUser() { System.out.println("cglib add user"); } public void updateUser() { System.out.println("cglib update user"); } }
* 步骤二:提供切面类
public class MyAspect { public void before(){ System.out.println("前"); } public void after(){ System.out.println("后"); } }
* 步骤三:编写工厂,使用cglib生成代理类
/** * 工厂生产代理类,目的:将目标类(切入点)和切面类(通知) 结合。 * @return */ public static UserServiceImpl createService() { //1 创建目标类 final UserServiceImpl userService = new UserServiceImpl(); //2 创建切面类 final MyAspect myAspect = new MyAspect(); //3 使用cglib 创建 代理类 , cglib 运行时,动态创建目标类子类(代理类) , 所以目标类不能使final的 public final class ... // 3.1 创建核心类 Enhancer enhancer = new Enhancer(); // 3.2 设置父类 enhancer.setSuperclass(userService.getClass()); // 3.3 代理类方法将调用回调函数,等效JDK InvocationHandler // ** 接口:Callback,子接口 MethodInterceptor 对方法进行增强的。 enhancer.setCallback(new MethodInterceptor(){ @Override // 前三个参数:与jdk 动态代理 invoke相同的 // 参数4:methodProxy , 方法代理 public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { //1 切面的类 前通知 myAspect.before(); //2 目标类的方法 Object obj = method.invoke(userService, args); //执行目标类的方法 methodProxy.invokeSuper(proxy, args); //执行代理类(子类)的父类方法 (父类就是目标类) //3 切面类 后通知 myAspect.after(); return obj; } }); // 3.4 创建代理类 return (UserServiceImpl)enhancer.create(); }
2.3 代理知识总结
* Spring在运行期,生成动态代理对象,不需要特殊的编译器
* Spring AOP的底层就是通过JDK动态代理或CGLib动态代理技术 为目标Bean执行横向织入
1.若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。
2.若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。
* 程序中应优先对接口创建代理,便于程序解耦维护
* 标记为final的方法,不能被代理,因为无法进行覆盖
? JDK动态代理,是针对接口生成子类,接口中方法不能使用final修饰
? CGLib 是针对目标类生产子类,因此类或方法 不能使final的
* Spring只支持方法连接点,不提供属性连接
3 spring工厂bean,半自动(了解)
3.1 aop联盟通知类型
* 使用spring提供 FactoryBean创建代理对象,手动的获取代理对象。生成代理需要应用增强(通知),通知需要确定方法名称。spring规范规定通知类型,从而确定方法名称。
* aop联盟确定5中通知类型,spring对aop联盟进行支持。
前置通知org.springframework.aop.MethodBeforeAdvice
在目标方法执行前实施增强
后置通知org.springframework.aop.AfterReturningAdvice
在目标方法执行后实施增强
环绕通知org.aopalliance.intercept.MethodInterceptor【】
在目标方法执行前后实施增强
异常抛出通知org.springframework.aop.ThrowsAdvice
在方法抛出异常后实施增强
引介通知org.springframework.aop.IntroductionInterceptor(了解)
在目标类中添加一些新的方法和属性
环绕
try{
前置
//必须手动执行目标方法
后置
} catch(){
//异常抛出
}
3.2 使用代理工厂bean
3.2.1 导入jar包
spring: 4个核心 + 1个依赖
springaop联盟:com.springsource.org.aopalliance-1.0.0.jar
springaop 实现:spring-aop-3.2.2.RELEASE.jar
3.2.2 目标类和接口
public class UserServiceImpl implements UserService { @Override public void addUser() { System.out.println("factorybean add user"); } @Override public void updateUser() { System.out.println("factorybean update user"); } }
3.2.3 编写切面类
* 必须遵循 aop联盟规范,通知需要实现相应接口
/** * 切面类,用于存放通知,使用的aop联盟规范,必须实现接口,从而确定方法名称(及spring如果执行通知) * */ public class MyAspect implements MethodInterceptor{ @Override public Object invoke(MethodInvocation mi) throws Throwable { System.out.println("前"); //环绕通知,必须手动的执行目标方法 Object obj = mi.proceed(); System.out.println("后"); return obj; } }
3.2.4 编写 spring配置
* 确定目标类 和切面类,通过spring提供ProxyFactoryBean生成代理类,将目标类和切面类进行结合。
通过属性注入的方式进行。
<!-- 1 创建目标类 --> <bean id="userServiceId" class="com.itheima.b_factorybean.UserServiceImpl"></bean> <!-- 2创建切面类(通知) --> <bean id="myAspectId" class="com.itheima.b_factorybean.MyAspect"></bean> <!-- 3 生成代理对象,目的:将目标类与切面类结合 * ProxyFactoryBean :生成一个特殊代理bean。 * interfaces 确定接口,需要使用value * target 确定目标类,需要使用ref(对目标类的引用) * interceptorNames 确定通知所在类中名称,只需要名称不需要对象。需要使用value * optimize 确定是否使用cglib生成代理,true是,默认是false。 --> <bean id="proxyServiceId" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 3.1 确定接口 --> <property name="interfaces" value="com.itheima.b_factorybean.UserService"></property> <!-- 3.2 确定目标类 --> <property name="target" ref="userServiceId"></property> <!-- 3.3 确定通知 ,使用切面类名称--> <property name="interceptorNames" value="myAspectId"></property> <!-- 3.4 强制使用cglib --> <property name="optimize" value="true"></property> </bean>
3.2.5 测试
@Test public void demo02(){ //从工厂(spring)获得代理对象 String xmlPath = "com/itheima/b_factorybean/beans.xml"; ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath); UserService userService = (UserService) applicationContext.getBean("proxyServiceId"); userService.addUser(); userService.updateUser(); }
4 spring传统aop开发(掌握)
* 从spring容器获得目标类,进行aop配置从而让spring创建代理类。全自动过程。
* 需添加aop命名空间
* 使用aspectj 切入点表达式,需要导入jar包
* 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" 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"> <!-- 1 创建目标类 --> <bean id="userServiceId" class="com.itheima.c_aop.UserServiceImpl"></bean> <!-- 2创建切面类(通知) --> <bean id="myAspectId" class="com.itheima.c_aop.MyAspect"></bean> <!-- 3 spring 传统aop开发,使 通知 引用到 目标类切入点上 * 添加aop命名空间 * aop 编程 <aop:config> * <aop:pointcut> 用于声明切入点,确定目标类上的那些方法将被增强。 id : 切入点名称 expression : 用于编写切入点表达式 (aspectj 切入点表达式) execution(* com.itheima.c_aop.*.*(..)) 固定 返回值类型 包 类名 方法名 参数列表 * <aop:advisor> 特殊的切面,只有一个切入点和一个通知 advice-ref:一个通知引用 pointcut-ref:一个切入点引用 --> <aop:config> <aop:pointcut expression="execution(* com.itheima.c_aop.*.*(..))" id="myPointCut"/> <aop:advisor advice-ref="myAspectId" pointcut-ref="myPointCut"/> </aop:config> </beans>
5 AspectJaop框架(掌握)
5.1 AspectJ介绍
* AspectJ是一个基于Java语言的AOP框架
* Spring2.0以后新增了对AspectJ切入点表达式支持
* @Aspect是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面
新版本Spring框架,建议使用AspectJ方式来开发AOP
* 导入jar包:
aop联盟:com.springsource.org.aopalliance-1.0.0.jar
springaop支持:spring-aop-3.2.0.RELEASE.jar
aspect规范:com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
springaspect 支持:spring-aspects-3.2.0.RELEASE.jar
5.2 切入点表达式(掌握)
1.execution:匹配方法的执行(常用)
格式:execution(修饰符 返回值类型 包.类名.方法(参数) throws 异常)
1.1修饰符,表示方法的修饰符,一般省略。
1.2返回值类型
String 表示返回String
void 表示没有返回值
* 表示返回任意类型【】
1.3包
com.itheima.service 表示指定的包
com.itheima.crm.*.service 表示crm的子模块,名称任意。
(例如:com.itheima.crm.user.service)
com.itheima.service.. 表示service目录,及子目录
综合:com.itheima.crm.*.service.. --> service / service.impl
1.4类名
UserService 表示指定的类
*Service 表示以Service结尾
Test* 表示以Test开头
* 表示任意类名
1.5方法名(与类名类似)
addUser 表示指定方法
add* 表示以add开头
*Do 表示以Do结尾
* 表示任意
1.6参数列表
() 表示没有参数
(int) 表示一个参数int类型
(int,int) 表示两个参数int类型
(如果是java.lang包下的可以省略,其他都必须是全限定类名)
(..) 表示参数任意
1.7throws 异常, 一般省略。
综合:execution(*com.itheima.crm.*.service..*.*(..)) ↓↓↓ 匹配↓↓↓
com.itheima.crm.user.service.impl.UserService.addUser(Useruser)
2.within:匹配包或子包中的方法(了解)
within(cn.itcast.aop..*)
3.this:匹配实现接口的代理对象中的方法(了解)
this(cn.itcast.aop.user.UserDAO)
4.target:匹配实现接口的目标对象中的方法(了解)
target(cn.itcast.aop.user.UserDAO)
5.args:匹配参数格式符合标准的方法(了解)
args(int,int)
6.bean(名称) ,匹配指定的bean(了解)
bean("userServiceId")
5.3 AspectJ规定通知类型
* 共6个,知道5个,掌握1个。
1.before:前置通知(应用:各种校验)
在方法执行前执行,如果通知抛出异常,阻止方法运行
2.afterReturning:后置通知(应用:常规数据处理)
方法正常返回后执行,如果方法中抛出异常,通知无法执行
必须在方法执行后才执行,所以可以获得方法的返回值。
3.around:环绕通知(应用:十分强大,可以做任何事情) 【】
方法执行前后分别执行,可以阻止方法的执行。要求必须手动的执行目标方法。
4.afterThrowing:抛出异常通知(应用:包装异常信息)
方法抛出异常后执行,如果方法没有抛出异常,无法执行
5.after:最终通知(应用:清理现场)
方法执行完毕后执行,无论方法中是否出现异常
环绕(around)
try{
//前置(before)
//手动执行目标类方法
//后置(afterReturning) --可以获得返回值
} catch(){
//抛出异常(afterThrowing) --可以获得具体的异常信息
} finally{
//最终(after)
}
5.4 基于xml aspectj
* aspectj通知类型 通过配置文件确定,没有具体接口。通知方法任意。
* xml配置
>常用属性
? pointcut:配置切入点表达式
? pointcut-ref:配置切入点引用对象
? method:配置切入点执行的通知方法
>JoinPoint连接点的信息
接口:org.aspectj.lang.JoinPoint
? 目标对象:getTarget()
? 获得方法签名:getSignature()
? 获得方法名称:getSignature().getName()
? 获得实际参数:getArgs()
? 获得当前指定方法的类型:getKind()
? method-execution 方法执行、
? constructor-execution构造方法执行
? field-get get方法
<?xml version="1.0" encoding="UTF-8"?> <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"> <!-- 1 创建目标类 --> <bean id="userServiceId" class="com.itheima.d_aspect.a_xml.UserServiceImpl"></bean> <!-- 2创建切面类(通知) --> <bean id="myAspectId" class="com.itheima.d_aspect.a_xml.MyAspect"></bean> <!-- 3 aspect 编程 <aop:config> aop 编程 proxy-target-class 如果设置true表示强转使用cglib代理 <aop:aspect> aspect编程 ref 用于确定切面类,从而确定通知 --> <aop:config> <aop:aspect ref="myAspectId"> <!-- 声明切入点,确定目标类上哪些方法将成为切入点,需要被增强 --> <aop:pointcut expression="execution(* com.itheima.d_aspect.a_xml.*.*(..))" id="myPointcut"/> <!-- 声明通知方式 1.前置通知 <aop:before method=""/> method 切面类中具体的方法名称 方法可以有一个参数,类型: org.aspectj.lang.JoinPoint 用于获得当前执行方法的具体详情 pointcut-ref 切入点的引用(大家共享) pointcut 给当前前置通知编写切入点表达式(自己使用) <aop:before method="myBefore" pointcut-ref="myPointcut"/> 2.后置通知 目标方法执行之后才执行,可以获得返回值。 returning 用于设置通知第二个参数的名称,类型:Object public void myAfterReturning(JoinPoint joinPoint,Object ret){ <aop:after-returning returning="ret"/> <aop:after-returning method="myAfterReturning" pointcut-ref="myPointcut" returning="ret"/> 3.环绕通知 环绕通知方法要求 1.返回类型Object 2.必须抛出异常 throws Throwable 3.必须接受一个参数,类型 ProceedingJoinPoint 4.方法体中手动执行目标方法,Object obj = joinPoint.proceed(); <aop:around method="myAround" pointcut-ref="myPointcut"/> 4.抛出异常 目标方法出现异常时执行,如果没有异常忽略。 throwing 设置第二个参数名称,获得具体的异常信息的。类型:Throwable public void myAfterThrowing(JoinPoint joinPoint,Throwable e){ <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointcut" throwing="e"/> 5.最终 无论是否有异常,都将执行 --> <aop:after method="myAfter" pointcut-ref="myPointcut" /> </aop:aspect> </aop:config> </beans>
* 切面类
/** * 切面类,存放所有通知,aspectj 没有提供接口,所有的通知都是通过配置文件确定。 */ public class MyAspect { public void myBefore(JoinPoint joinPoint){ System.out.println("前置通知 : " + joinPoint.getSignature().getName()); } public void myAfterReturning(JoinPoint joinPoint,Object ret){ System.out.println("后置通知 : " + ret ); } public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("前"); //手动执行目标类 Object obj = joinPoint.proceed(); System.out.println("后"); return obj; } public void myAfterThrowing(JoinPoint joinPoint,Throwable e){ System.out.println("抛出异常 : " + e.getMessage()); } public void myAfter(JoinPoint joinPoint){ System.out.println("最终"); } }
5.5 基于注解 aspectJ
* 步骤一:将切面类配置给spring
@Component
public class MyAspect {
* 步骤二:将切面类声明成切面
@Component
@Aspect
public class MyAspect {
* 步骤三:声明共有切入点
1.方法必须private,没有返回值,没有参数
2.之后使用将其当成方法调用。例如:@After("myPointcut()")
@Pointcut("execution(*com.itheima.d_aspect.b_annotation.*.*(..))")
privatevoid myPointcut(){}
* 步骤四:编写相应的通知
@Before 前置
@AfterReturning 后置,可以获得返回值,必须在注解中确定返回值参数名称。
@AfterThrowing 抛出异常,可以获得具体异常信息,必须在注解确定第二个参数名称
@Around 环绕
@After 最终
/** * 切面类,存放所有通知,aspectj 没有提供接口,所有的通知都是通过配置文件确定。 */ @Component //2.<bean id="myAspectId" class="com.itheima.d_aspect.b_annotation.MyAspect"></bean> @Aspect //3.1 <aop:aspect ref="myAspectId"> 让切面类形成切面 (通知和切入点结合) public class MyAspect { //@Before("execution(* com.itheima.d_aspect.b_annotation.*.*(..))") // 取代 <aop:before method="myBefore" pointcut="execution(* com.itheima.d_aspect.b_annotation.*.*(..))"/> public void myBefore(JoinPoint joinPoint){ System.out.println("前置通知 : " + joinPoint.getSignature().getName()); } //@AfterReturning(value="execution(* com.itheima.d_aspect.b_annotation.*.*(..))",returning="ret") public void myAfterReturning(JoinPoint joinPoint,Object ret){ System.out.println("后置通知 : " + ret ); } //编写共有的切入点表达式 @Pointcut("execution(* com.itheima.d_aspect.b_annotation.*.*(..))") private void myPointcut(){} //@Around("myPointcut()") public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{ System.out.println("前"); //手动执行目标类 Object obj = joinPoint.proceed(); System.out.println("后"); return obj; } //@AfterThrowing(value="myPointcut()",throwing="e") public void myAfterThrowing(JoinPoint joinPoint,Throwable e){ System.out.println("抛出异常 : " + e.getMessage()); } @After("myPointcut()") public void myAfter(JoinPoint joinPoint){ System.out.println("最终"); } }
* 步骤五:必须在xml中扫描注解和启用aop自动代理
<?xml version="1.0" encoding="UTF-8"?> <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/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 1 扫描 --> <context:component-scan base-package="com.itheima.d_aspect.b_annotation"></context:component-scan> <!-- 2 使aop注解生效 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
6 JdbcTemplate
Spring支持持久模板
* spring提供 用于操作数据库模板。类似:DbUtils。使用时必须设置数据源(DataSource)
* 数据源:DBCP、C3P0等
* 导入jar包:
dbcp:
com.springsource.org.apache.commons.dbcp-1.2.2.osgi.jar
com.springsource.org.apache.commons.pool-1.5.3.jar
c3p0: com.springsource.com.mchange.v2.c3p0-0.9.1.2.jar
spring-jdbc spring-jdbc-3.2.0.RELEASE.jar
spring-tx(transaction) spring-tx-3.2.0.RELEASE.jar
数据库驱动:mysql-connector-java-5.1.22-bin.jar
6.1 创建数据库及表
create database spring_day02_db; use spring_day02_db; create table t_user( id int primary key auto_increment, username varchar(50), password varchar(32) ); insert into t_user(username,password) values('jack','1234');
6.2 使用DBCP
回顾使用API连接
@Test public void demo01(){ //1 创建数据库 BasicDataSource dataSource = new BasicDataSource(); // 1.1 驱动 dataSource.setDriverClassName("com.mysql.jdbc.Driver"); // 1.2 url dataSource.setUrl("jdbc:mysql://localhost:3306/spring_day02_db"); // 1.3 user dataSource.setUsername("root"); // 1.4 password dataSource.setPassword("1234"); //2 创建模板 JdbcTemplate jdbcTemplate = new JdbcTemplate(); // 2.1 设置数据源 jdbcTemplate.setDataSource(dataSource); //3 录入数据 jdbcTemplate.update("insert into t_user(username,password) values(?,?)", "rose","1234"); } }
* 配置数据源,配置模板,dao直接使用模板。
<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"> <!--1 配置数据源 --> <bean id="dataSourceId" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/spring_day02_db"></property> <property name="username" value="root"></property> <property name="password" value="1234"></property> </bean> <!-- 2 配置模板,需要数据源 --> <bean id="jdbcTemplateId" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSourceId"></property> </bean> <!--3 配置dao,需要模板 --> <bean id="userDaoId" class="com.itheima.e_jdbc.b_dbcp.UserDao"> <property name="jdbcTemplate" ref="jdbcTemplateId"></property> </bean>
6.3 使用C3P0
* dao之后继承 JdbcDaoSupport,spring直接给dao注入数据源DataSource即可,JdbcDaoSupport底层自动进行模板创建。
c3p0配置
<!--1 配置数据源 c3p0 --> <bean id="dataSourceId" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring_day02_db"></property> <property name="user" value="root"></property> <property name="password" value="1234"></property> </bean> <!-- 2 配置模板,需要数据源 <bean id="jdbcTemplateId" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSourceId"></property> </bean> --> <!--3 配置dao,需要模板 * 将数据源 datasource注入给dao,继承JdbcDaoSupport 提供setDataSource,且此方法中将自动的创建模板。 --> <bean id="userDaoId" class="com.itheima.e_jdbc.c_c3p0.UserDao"> <property name="dataSource" ref="dataSourceId"></property> </bean>
dao使用(继承JdbcDaoSupport)
public class UserDao extends JdbcDaoSupport{ public void save(String username,String password){ this.getJdbcTemplate().update("insert into t_user(username,password) values(?,?)", username,password); } public User find(int id) { // queryForObject 查询一个结果,如果没有结果抛异常。 String sql = "select * from t_user where id = ?"; return this.getJdbcTemplate().queryForObject(sql, ParameterizedBeanPropertyRowMapper.newInstance(User.class), id); } }
6.4 使用properties
* 将连接数据的具体参数配合到properties文件,由spring加载properties,并在spring配置文件中使用。
jdbcinfo.properties
jdbc.driverClass=com.mysql.jdbc.Driver jdbc.jdbcUrl=jdbc:mysql://localhost:3306/spring_day02_db jdbc.user=root jdbc.password=1234
配置
<?xml version="1.0" encoding="UTF-8"?> <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/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 0 加载properties文件 location 用于指定配置文件位置 classpath: 固定的字符串表示从 src下获取 在spring 配置文件中,就可以通过${key} 方式获取 --> <context:property-placeholder location="classpath:com/itheima/e_jdbc/d_properties/jdbcinfo.properties"/> <!--1 配置数据源 c3p0 --> <bean id="dataSourceId" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="${jdbc.driverClass}"></property> <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <property name="user" value="${jdbc.user}"></property> <property name="password" value="${jdbc.password}"></property> </bean> <!-- 2 配置模板,需要数据源 <bean id="jdbcTemplateId" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSourceId"></property> </bean> --> <!--3 配置dao,需要模板 * 将数据源 datasource注入给dao,继承JdbcDaoSupport 提供setDataSource,且此方法中将自动的创建模板。 --> <bean id="userDaoId" class="com.itheima.e_jdbc.d_properties.UserDao"> <property name="dataSource" ref="dataSourceId"></property> </bean> </beans>
6.5 JdbcTemplate API
* update进行 增删改操作
* queryForObject 查询一个
* query 查询所有
* queryForInt查询一个整形(分页)
public class UserDao extends JdbcDaoSupport{ public void save(String username,String password){ this.getJdbcTemplate() .update("insert into t_user(username,password) values(?,?)" , username,password); } public User find(int id) { // queryForObject 查询一个结果,如果没有结果抛异常。 String sql = "select * from t_user where id = ?"; return this.getJdbcTemplate() .queryForObject(sql, ParameterizedBeanPropertyRowMapper .newInstance(User.class), id); } public List<User> findAll() { String sql = "select * from t_user"; return this.getJdbcTemplate() .query(sql, ParameterizedBeanPropertyRowMapper .newInstance(User.class)); } }