SpringAOP描述及实现_AspectJ详解_基于注解的AOP实现_SpringJdbcTemplate详解

AOP

AOP特点:

  • 面向切面编程, 利用AOP对业务逻辑的各个部分进行抽取公共代码, 降低耦合度, 提高代码重用性, 同时提高开发效率.
  • 采取横向抽取, 取代传统纵向继承体系重复性代码
  • 解决事务管理, 性能监视, 安全检查, 缓存, 日志等问题
  • Spring AOP在运行期, 通过反向代理的方式解决类加载, 属性注入
  • AspectJ是基于Java的AOP框架, 在Spring使用AspectJ实现AOP

AOP实现机制:
底层采用代理机制实现AOP.
2 种代理机制: 1.采用JDK的的动态代理Proxy; 2.采用CGLIB字节码增强

AOP专业术语:
Target: 目标类 ( 需要被代理的类 )
Joinpoint: 连接点 ( 可能需要使用的目标类方法 )
Advice: 增强代码 ( 对连接点增强的代码 )
PointCut: 切入点 ( 可能需要 Advice 增强的连接点 )
Weaving: 织入 ( 创建代理对象 proxy 执行切入点的方法 )
Aspect: 切面 ( Advice 与 PointCust的结合 )

SpringAOP术语

JDK/CGLIB的AOP实现

下面通过JDK动态代理和CGLIB字节码增强两种方式实现AOP操作
当目标类没有实现接口或者需要更好的性能的时候就需要考虑使用CGLIB实现动态Proxy

JDK动态代理:
1.目标类: Service层
2.切面类: 使用JDK动态代理对Service层代码增强
3.工厂类: 获得proxy对象

//目标类
public interface  UserService {void addUser();void updateUser();void deleteUser();
}
public class UserServiceImpl implements UserService {public void addUser() {System.out.println("add User");}public void updateUser() {System.out.println("update User");}public void deleteUser() {System.out.println("delete User");}
}切面, 增强连接点:
public class MyAspect {public void before(){System.out.println("before");}public void after(){System.out.println("after");}
}静态代理对象工厂:
public class MyProxyBeanFactory {public static UserService createService(){final UserService userService = new UserServiceImpl();final MyAspect myAspect = new MyAspect();//通过userService获得代理对象UserService proxyService = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),userService.getClass().getInterfaces(),new InvocationHandler(){//Proxy代理对象, method代理类的目标方法, args目标方法参数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;}
}applicationContext.xml:
<bean id="userService" class="com.f_aop.jdkproxy.MyProxyBeanFactory" factory-method="createService"></bean>测试方法:@Testpublic void f1(){String XMLPATH="com/f_aop/jdkproxy/applicationContext.xml";ApplicationContext applicationContext = new ClassPathXmlApplicationContext(XMLPATH);UserService userService = (UserService) applicationContext.getBean("userService");userService.addUser();userService.updateUser();userService.deleteUser();}

CGLIB字节码增强动态代理:
原理: cglib动态生成一个代理类的子类, 子类重写代理类的所有不是final的方法, 在子类中采用方法拦截技术拦截所有父类的方法调用, 顺势织入切面逻辑, 实现AOP, 它比JDK动态代理要快.
其操作流程与JDK动态代理一致.
下面只给出静态代理工厂的代码:

public class MyProxyBeanFactory {public static UserService createService(){final UserService userService = new UserServiceImpl();final MyAspect myAspect = new MyAspect();//创建代理Enhancer enhancer = new Enhancer();//确定父类enhancer.setSuperclass(userService.getClass());//向代理对象的方法中织入切面代码enhancer.setCallback(new MethodInterceptor() {public Object intercept(Object proxy, Method method, Object[] args,MethodProxy methodProxy) throws Throwable {myAspect.before();//执行目标方法//Object obj = method.invoke(userService, args);//执行目标方法, 效果与method.invoke(userService, args);//一般执行这个方法, 速度要快一些Object obj = methodProxy.invoke(proxy, args);myAspect.after();//返回目标方法返回值return obj;}});//使用enhancer创建代理对象return (UserService) enhancer.create();}
}

cglib的整个流程与JDK的动态代理都是一样的, 就在底层处理接口和加载字节码文件有区别

AOP联盟通知类型

AOP联盟定义Advice规范, 编写Advice代码需实现Advice接口.
Spring按照Advice在目标类中方法的连接点的位置, 分为5类

前置通知: 实现接口: org.springframework.aop.MethodBeforeAdvice只在目标方法前进行代码增强;
后置通知: 实现接口: org.springframework.aop.AfterReturningAdvice只在目标方法后进行代码增强;
环绕通知( 必须手动执行目标方法 ): 实现接口: org.springframework.aop.MethodInterceptor只在目标方法前后进行代码增强; 效果等同于JDK的Proxy/cglib
异常通知: 实现接口: org.springframework.aop.ThrowsAdvice在抛出异常的时候进行代码增强;
引介通知:实现接口: org.springframework.aop.IntroductionInterceptor只在目标类中增添一些新的方法和属性;

AOP联盟的代理实现

  • 使用Spring提供的ProxyFactoryBean模拟代理过程, 实现Spring的AOP:

使用环绕型通知进行演示(目标类与前面的一样):

1.导入aop, aopalliance jar包
2.切面类(MyAspect)实现MethodInterceptor接口
3.实现MethodInterceptor中invoke方法, 手动织入横向代码
4.在applicationContext.xml中配置, 使用Spring提供的ProxyFactoryBean对目标类实现代理

演示代码:

切面类:
public class MyAspect implements MethodInterceptor{@Overridepublic Object invoke(MethodInvocation mi) throws Throwable {System.out.println("前");//手动执行目标方法Object obj = mi.proceed();System.out.println("后");//返回目标方法执行的返回值return obj;}
}配置applicationContext.xml:<!-- 获得目标类对象 --><bean id="userService" class="com.f_aop.methodInterceptor.UserServiceImpl"></bean><!-- 创建切面类 --><bean id="myAspect" class="com.f_aop.methodInterceptor.MyAspect"></bean><!-- 创建代理类, 使用Spring配备的代理工厂 --><bean id="proxyService" class="org.springframework.aop.framework.ProxyFactoryBean"><!-- 指定接口 --><property name="interfaces" value="com.f_aop.methodInterceptor.UserService"></property><!-- 确定目标类对象 --><property name="target" ref="userService"></property>
<!-- 确定Aspect, 由于interceptorNames的形参值是String[], 所以使用value, 而非ref --><property name="interceptorNames" value="myAspect"></property><property name="optimize" value="true"></property></bean>测试方法:@Testpublic void f1(){String XMLPATH="com/f_aop/methodInterceptor/applicationContext.xml";ApplicationContext applicationContext = new ClassPathXmlApplicationContext(XMLPATH);//使用proxyService, 而非userService//通过代理对象执行AdviceUserService userService = (UserService) applicationContext.getBean("proxyService");userService.addUser();userService.updateUser();userService.deleteUser();}运行结果:
前
add User
后
前
update User
后
前
delete User
后

applicationContext.xml中创建代理类标签详解

ProxyFactoryBean: Spring的代理工厂,生成代理对象interfaces: 目标类实现的接口, 多个值使用<array><value>确定每个值单个值的时候直接使用valuetarget: 确定目标类interceptorNames: 确定切面类的名称, 类型为String[], 使用value, 切记不使用refoptimize: 强制底层使用cglib当没有设置optimize的值时:Spring自动判断, 没有接口使用cglib, 有接口使用jdk显式设置optimize, 如果声明optimize=true,无论是否有接口,都采用cglib

上面这种代理实现, 是在applicationContext.xml配置文件中模拟代理工厂产生代理对象, 在测试函数中获得是容器产生的代理对象proxyService.

  • 利用AspectJ简化Spring中ProxyFactoryBean的配置:
    使用环绕型通知进行演示, 编写流程:
1.导入aspectj.weaver jar包.
2.在applicationContext.xml配置文件中添加aop的xmlns和xsi限制
3.在配置文件中配置切面类(MyAspect)的切入点(PointCut), 特殊切面(包含advice与PointCut).首先使用expression表达式配置切入点(PointCut), 即目标类中哪些方法需要增强.然后配置特殊切面, 对配置好的切入点, 使用增强点advice进行增强.

下面使用代码演示, 因为只需修改配置文件与测试类, 只给出配置文件代码:

<?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.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><bean id="userService" class="com.f_aop.aspectJ.UserServiceImpl"></bean><!-- 创建切面类 --><bean id="myAspect" class="com.f_aop.aspectJ.MyAspect"></bean><!-- 配置特殊切面 --><!-- proxy-target-class配置是否使用cglib --><aop:config proxy-target-class="true"><aop:pointcut id="myPointCut" expression="execution(* com.f_aop.aspectJ.*.*(..))"/><aop:advisor advice-ref="myAspect" pointcut-ref="myPointCut"/></aop:config>
</beans><!--aop:config: 配置AOPproxy-target-class: 配置是否强行使用cglib, 效果与前面的optimize一样pointcut: 配置切入点.expression:  配置切入点表达式,用于获得目标类中需要增强的目标方法.advisor: 配置切入点与切面类, 指明哪些方法需要增强.advice-ref: 切入类对象引用.pointcut-ref: 切入点引用.
-->

相比于Spring提供的ProxyFactoryBean, AspectJ更加便捷.

AspectJ详解

AspectJ是基于Java的AOP框架, 用于自定义AOP开发.

  • 切入点表达式
    用于描述目标类中的目标方法, 指定哪些方法可作为切入点.
    下面说明切入点表达式写法:
语法: expression = " execution( 修饰符 返回值 包.类.方法名(参数) throws 异常 ) "切入表达式针对每个部分的编写规则如下
修饰符(一般省略):public	公共方法*		任意方法
返回值(不能省略):void	没有返回值String	返回值为字符串*		返回值任意
包(可省略):com.demo				固定包com.demo.*				demo下任意子包,例如:com.demo.aopcom.demo..				demo下所有包(包含自己,也包含多级子包)com.demo.*.service..	demo下任意子包, 子包中包含固定包service,service下所有包
类(可省略):UserServiceImpl	指定类*Impl			以Impl结尾的类User*			以User开头的类*				任意类
方法名(不能省略):addUser	指定方法add*	以add开头的方法*User	以User结尾的方法*		任意方法
参数:()			无参(int)		一个int型参数(int, int)	两个int型参数(..)		任意参数
throws(可省略, 一般不写)下面给出一个例子:
1.execution(* com.demo.*.service..*.*(..))指定com.demo下具有固定service包的任意子包中任意类中的任意方法,方法返回值任意.其他种类的expression表达式:
1.within: 匹配包或子包中的方法.within(com.demo..*) demo下所有包中任意类中任意方法
2.this: 匹配实现接口的类的代理对象中方法:this(com.demo.aop.user.UserDAO) 匹配UserDAO中实现类代理对象任意方法.
3.target: 匹配实现接口的类的目标对象中方法:target(com.demo.aop.user.UserDAO) 匹配UserDAO中实现类目标对象任意方法.
4.args: 匹配参数格式符合标准的方法args(int, int) 匹配形参值类型为int, int的任意方法.
5.bean(id): 匹配指定bean所有方法bean('userService') 匹配userService中所有方法
  • AspectJ通知类型
    与AOP联盟一样, AspectJ也定义了多种通知类型.

AspectJ总共6中通知类型:

1.before: 前置通知,用于校验数据在目标方法之前执行, 若抛出异常, 组织目标方法运行.
2.afterReturning: 后置通知,常规数据处理目标方法执行后执行, 可获得目标方法的返回值.目标方法出现异常, 方法不执行.
3.around: 环绕通知目标方法前后, 可阻止目标方法执行, 必须手动执行目标方法.
4.afterThrowing: 抛出异常通知目标方法出现异常后执行, 没有出现异常就不执行.
5.after: 最终通知, 资源回收, 类似finally方法方法执行完, 无论方法中是否出现异常, 都将执行.环绕通知与其他通知之间的联系:
try{//前置: before//手动执行目标方法//后置: afterReturning
} catch(){//捕获异常: afterThrowing
} finally{//最终通知: after
}

从上面看出, 完全可以使用环绕通知模拟前置通知, 后置通知, 环绕通知结合AfterThrowing, After实现AOP.

aop标签对应的通知类型种类
applicationContext.xml文件中aop标签种类

使用AOP联盟进行切面类编写, 需要定义通知类型, 切面类必须实现特定接口(MethodInterceptor), 然后为目标方法添加增强代码, 相比于AOP联盟, AspectJ只要定义切面类, 增强代码的使用完全交给配置文件, 避免代码污染, 简化操作.

使用AspectJ实现SpringAOP

基于xml配置通知类型的开发流程:
1.导入AOP联盟, AspectJ, AOP依赖, Aspect规范 jar包.
2.编写目标类: 接口与实现类.
3.编写切面类: 编写AspectJ的通知类型方法, 方法名任意, 无需实现什么接口.
4.配置xml: 配置通知类型.
5.测试.

下面给出演示代码, 代码中已经给出注释加以说明(若有不懂请在评论区留言):

目标类 ( 接口与实现类 ):
public interface UserService {void addUser();void updateUser();void deleteUser();
}
public class UserServiceImpl implements UserService {public void addUser() {System.out.println("add User");}public void updateUser() {System.out.println("update User");}public void deleteUser() {System.out.println("delete User");}
}切面类:
package com.f_aop.aspectJFinal;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;public class MyAspect{//	测试前置通知与后置通知	
//	public void myBefore(JoinPoint jPoint){
//		System.out.println("前置通知"+jPoint.getSignature().getName());
//	}
//	
//	public void myAfterReturning(JoinPoint jPoint, Object ret){
//		System.out.println("后置通知"+jPoint.getSignature().getName()+"--"+ret);
//	}public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{System.out.println("前置通知");//手动执行目标方法Object obj = joinPoint.proceed();
//		环绕通知与抛出异常通知的测试结果:		
//		int i = 1/0;
//		前置通知
//		add User
//		抛出异常通知/ by zero
//		最终通知System.out.println("后置通知");return obj;}public void myAfterThrowing(JoinPoint jPoint, Throwable e){System.out.println("抛出异常通知"+e.getMessage());}public void myAfter(JoinPoint jPoint){System.out.println("最终通知");}
}applicationContext.xml:
<?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.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"><!-- 创建目标类对象 --><bean id="userService" class="com.f_aop.aspectJFinal.UserServiceImpl"></bean><!-- 创建切面类 --><bean id="myAspect" class="com.f_aop.aspectJFinal.MyAspect"></bean><!-- 使用 config 配置AspectJ的AOP --><aop:config><!-- 声明切入面 --><aop:aspect ref="myAspect"><!-- 配置目标方法的切入点 --><aop:pointcut id="myPointCut" expression="execution(* com.f_aop.aspectJFinal.UserServiceImpl.*(..))"/><!-- 配置通知类型的时候, method代表切入类方法, pointcut-ref代表目标类切入点.二者结合的意思就是目标类中哪些切入点需要切入方法进行增强.--><!-- 前置通知 <aop:before method="myBefore" pointcut-ref="myPointCut"/>后置通知, returning用于接收目标方法执行完后的返回值<aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret"/>--><!-- 抛出异常通知要配合环绕通知使用, 环绕通知抛出的异常使用抛出异常通知接收 --><aop:around method="myAround" pointcut-ref="myPointCut"/><!-- 抛出异常, throwing="e" 代表执行目标方法后,可能会抛出的异常通过 e 进行接收 --><aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/><!-- 最终通知 --><aop:after method="myAfter" pointcut-ref="myPointCut"/></aop:aspect></aop:config>
</beans>测试方法:@Testpublic void f1(){String XMLPATH="com/f_aop/aspectJFinal/applicationContext.xml";ApplicationContext applicationContext = new ClassPathXmlApplicationContext(XMLPATH);UserService userService = (UserService) applicationContext.getBean("userService");//测试AOPuserService.addUser();userService.updateUser();userService.deleteUser();}

基于注解的通知类型开发流程:
1.在刚开始配置注解的时候, 可以按照 xml 中bean, aop的配置信息来给类/属性添加注解, 这样不容易把逻辑搞混.
2.测试, 其实整个开发过程与 xml 配置没什么区别, 都是一样的, 只是形式上有区别.

在给各种类添加注解之间, 一定要牢记:

1.在 xml 配置文件中添加扫描, 扫描注解类:<context:component-scan base-package="com.demo.aspectJAnnotation"></context:component-scan>
2.确定AOP注解生效:<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

AspectJ中通知类型的注解种类:

1.@Aspect	声明切面类, 不需要指定切面类名称.等同于<aop:aspect ref="myAspect">, 一般与 @Component 结合使用, Component代表myAspect对象2.@Pointcut("execution(* com.f_aop.aspectJFinalAnnotation.UserServiceImpl.*(..))")声明公共切入点, 通过"方法名"获得切入点引用.等同于<aop:pointcut id="myPointCut" expression="execution(* com.f_aop.aspectJFinalAnnotation.UserServiceImpl.*(..))"/>2.@Before(value="execution(* com.demo..service.*.*(..))")前置通知, 直接添加在切面类方法前.等同于<aop:before method="myBefore" pointcut-ref="myPointCut"/>或者上面 @Before 也可写做 @Before(value="myPointCut()") myPointCut是方法名此时要先在切面类中声明公共切入点方法: @Pointcut("execution(* com.f_aop.aspectJFinalAnnotation.UserServiceImpl.*(..))")private void myPointCut(){}这样写的作用就是为了少写代码, 避免在多个切面类通知方法前都要加execution=(...).并且如果切入点表达式写错了, 也很难排查问题.(不懂请看下面的演示代码)3.@AfterReturning(value="myPointCut()", returning="ret")后置通知, 直接添加在后置通知方法前.等同于<aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret"/>ret表示接收的返回值名称, 含有与标签中的ret一样.4.@Around("myPointCut()")环绕通知, 添加在环绕方法前面.等同于<aop:around method="myAround" pointcut-ref="myPointCut"/>5.@AfterThrowing(value="myPointCut()", throwing="e")抛出异常通知, 添加在抛出异常通知方法前.等同于<aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/>6.@After("myPointCut()")最终通知, 添加在最终通知之前.等同于<aop:after method="myAfter" pointcut-ref="myPointCut"/>

接下来给出演示代码:

目标类:
package com.f_aop.aspectJFinalAnnotation;
import org.springframework.stereotype.Service;//生成UserService的bean: userService
@Service("userService")
public class UserServiceImpl implements UserService {public void addUser() {System.out.println("add User");}public void updateUser() {System.out.println("update User");}public void deleteUser() {System.out.println("delete User");}
}xml 配置文件 applicationContext.xml:
<?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"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><!-- 扫描注解类 --><context:component-scan base-package="com.f_aop.aspectJFinalAnnotation"></context:component-scan><!-- 确定AOP注解生效 --><aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>切面类:
package com.f_aop.aspectJFinalAnnotation;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;//获得切面类Bean
@Component
//声明切面类
@Aspect
//由于二者都修饰同一个类, 所以不加idpublic class MyAspect{//直接设置切入点, 不使用自定义的公共切入点
//	@Before("execution(* com.f_aop.aspectJFinalAnnotation.UserServiceImpl.*(..))")	
//	public void myBefore(JoinPoint jPoint){
//		System.out.println("前置通知"+jPoint.getSignature().getName());
//	}//	设置切入点, 通过returning获得返回值	
//	@AfterReturning(value="myPointCut()", returning="ret)
//	public void myAfterReturning(JoinPoint jPoint, Object ret){
//		System.out.println("后置通知"+jPoint.getSignature().getName()+"--"+ret);
//	}@Pointcut("execution(* com.f_aop.aspectJFinalAnnotation.UserServiceImpl.*(..))")private void myPointCut(){//配置空方法,用于声明公共切入点}@Around("myPointCut()")public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{System.out.println("前置通知");//手动执行目标方法Object obj = joinPoint.proceed();int i = 1/0;
//		前置通知
//		add User
//		抛出异常通知/ by zero
//		最终通知System.out.println("后置通知");return obj;}@AfterThrowing(value="myPointCut()", throwing="e")public void myAfterThrowing(JoinPoint jPoint, Throwable e){System.out.println("抛出异常通知"+e.getMessage());}@After("myPointCut()")public void myAfter(JoinPoint jPoint){System.out.println("最终通知");}}

JdbcTemplate

主要是Spring提供操作Jdbc的工具类, 类似于DBUtils, 依赖于连接池DataSource.

开发流程:
1.创建数据库表.
2.导入 C3P0/DBCP连接池, mysql驱动, Spring-jdbc, spring-tx, 等Spring其余核心jar包
3.创建JavaBean, 配置数据源
4.编写DAO层
5.配置applicationContext.xml文件, 使用Spring管理DAO层对象
6.测试.

下面使用C3P0进行测试, DBCP与C3P0是一样的, 只是在配置数据库连接上有所不同.

数据库:create table t_user(id int primary key,name varchar(10);password varchar(10)	);User:
public class User implements Serializable{private Integer id;private String name;private String password;//构造方法, get/set方法
}UserDao:
public class UserDao{//使用Spring提供的JdbcTemplate, 在Spring配置文件中注入private JdbcTemplate jdbcTemplate;public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}//查询所有的Userpublic List<User> findAll(){return jdbcTemplate.query("select * from t_user", ParameterizedBeanPropertyRowMapper.newInstance(User.class));}//演示查询单个Userpublic void find(User user){String sql = "select * from t_user";
//		String sql = "select * from t_user where id=?";		
//		Object findUserId = user.getId();//查询条件信息就加Object参数
//		List<Map<String,Object>> queryForList = jdbcTemplate.queryForList(sql, findUserId);//查询所有直接不加参数List<Map<String, Object>> queryForList = jdbcTemplate.queryForList(sql);for (Map<String, Object> map : queryForList) {//一个map中储存着一个学生的信息for (Map.Entry<String, Object> m : map.entrySet()) {System.out.println(m.getKey()+"--"+m.getValue());}}//匿名内部类, 实现RowMapper接口, 自定义查询结果类型/*List<User> queryUsers = jdbcTemplate.query(sql, new RowMapper<User>(){@Overridepublic User mapRow(ResultSet resultSet, int i) throws SQLException {return new User(resultSet.getInt("id"), resultSet.getString("name"), resultSet.getString("password"));}});同理带条件查询, jdbcTemplate.query(sql, rowMapper, i)*/}
//	@Testpublic void insert(User user){String sql = "insert into t_user(id, name, password)values(?, ?, ?)";//Object数组中参数对应sql中"?"的顺序Object[] obj = new Object[]{user.getId(), user.getName(), user.getPassword()};//受影响行数int row = jdbcTemplate.update(sql, obj);System.out.println("受影响行数"+row);}public void update(User user){String sql = "update t_user set name=? where id=?";Object[] obj = new Object[]{user.getName(), user.getId()};//受影响行数int row = jdbcTemplate.update(sql, obj);System.out.println("受影响行数"+row);}public void delete(User user){String sql = "delete from t_user where id=?";Object[] obj = new Object[]{user.getId()};//受影响行数int row = jdbcTemplate.update(sql, obj);System.out.println("受影响行数"+row);}
}applicationContext.xml配置文件:
<?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.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><!-- 创建数据源Bean --><bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><property name="driverClass" value="com.mysql.jdbc.Driver"></property><property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property><property name="user" value="root"></property><property name="password" value="12345"></property></bean><!-- 创建模板Bean, 向JdbcTemplate中注入数据源 --><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="comboPooledDataSource"></property></bean><!-- 配置DAO, 向Dao注入JdbcTemplate Bean --><bean id="userDao" class="com.g_jdbc.c3p0.UserDao"><property name="jdbcTemplate" ref="jdbcTemplate"></property></bean></beans>测试方法:
@Testpublic void f1(){String xml = "com/g_jdbc/c3p0/applicationContext.xml";ApplicationContext application = new ClassPathXmlApplicationContext(xml);UserDao bean = (UserDao) application.getBean("userDao");//查询List<User> list = bean.findAll();for (User user : list) {System.out.println(user);}}

这里给出DBCP的配置文件写法, 用于区别C3P0:
二者只有创建数据源有所不同

<!-- 创建数据源 --><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver"></property><property name="url" value="jdbc:mysql://localhost:3306/test"></property><property name="username" value="root"></property><property name="password" value="12345"></property></bean>

使用JdbcTemplate还有这种简化写法:
Spring提供JdbcDaoSupport, 用于封装JdbcTemplate, 使得DAO不用关心JdbcTemplate模板的处理.

1.让Dao继承JdbcDaoSupport, 创建模板的过程交给父类处理.

public class UserDao extends JdbcDaoSupport{//使用JdbcDaoSupport通过get方法获得数据源,JdbcTemplate模板public List<User> findAll(){String sql = "select * from t_user";//使用getJdbcTemplate()获取父类创建好的模板, 然后再执行查询操作return this.getJdbcTemplate().query(sql, ParameterizedBeanPropertyRowMapper.newInstance(User.class));}}

2.修改配置文件.

	<bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"><property name="driverClass" value="com.mysql.jdbc.Driver"></property><property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property><property name="user" value="root"></property><property name="password" value="12345"></property></bean><!-- 配置DAO使用JdbcDaoSupport, 底层已经封装了JdbcTemplate, 只需配置DataSource即可--><bean id="userDao" class="com.g_jdbc.jdbcdaosupport.UserDao"><property name="dataSource" ref="comboPooledDataSource"></property></bean>

3.测试:

//测试方法和C3P0, DBCP的测试方法是一样的
@Testpublic void f1(){String xml = "com/g_jdbc/jdbcdaosupport/applicationContext.xml";ApplicationContext application = new ClassPathXmlApplicationContext(xml);UserDao bean = (UserDao) application.getBean("userDao");List<User> list = bean.findAll();for (User user : list) {System.out.println(user);}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/468674.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(十九)java多线程之ForkJoinPool

本人邮箱: kco1989qq.com 欢迎转载,转载请注明网址 http://blog.csdn.net/tianshi_kco github: https://github.com/kco1989/kco 代码已经全部托管github有需要的同学自行下载 引言 java 7提供了另外一个很有用的线程池框架,Fork/Join框架 理论 Fork/Join框架主要有以下两个类组…

串口,com口,ttl,max232你应该知道的事

&#xff11;今天&#xff0c;说几个比较基础的知识&#xff0c;大家在开发过程中经常会遇到但是又不是特别注意的知识点。TTL电平&#xff1a;TTL是Transistor-Transistor Logic&#xff0c;即晶体管-晶体管逻辑的简称&#xff0c;它是计算机处理器控制的设备内部各部分之间通…

Caffe2 的基本数据结构(Basics of Caffe2 - Workspaces, Operators, and Nets)[4]

这篇文章主要介绍Caffe2的基本数据结构&#xff1a; WorkspacesOperatorsNets在开始之前最好先阅读以下Intro Turorial首先&#xff0c;导入caffe2。其中core和worksapce模块&#xff0c;这是必须的两个模块。如果你要使用Caffe2生成的protocol buffers&#xff0c;那么你也需要…

Linux 开发者最应该知道的命令汇总

&#xff11;最近发现一个好东西&#xff0c;在 github 上发现的&#xff0c;我觉得非常适合大家研究 linux&#xff0c;说白了就是一些命令而已&#xff0c;只不过是作者帮忙总结好了&#xff0c;比较适合大家开发和学习 linux 系统&#xff0c;so , 推荐给大家学习下。https:…

华为任职资格_华为采购总部专业任职资格标准|

目 录 序 言 概述 .........................第一部分 级别定义.....................第二部分 资格标准 ....................1、采购工程师&#xff08;生产采购&#xff09;任职资格标准........2、采购员&#xff08;生产采购&#xff09;任职资格标准............3、采购员…

C 语言内存分配

&#xff11;昨天有一个群里的同学问我&#xff0c;他问我的问题是 c 语言函数是存在哪里的&#xff1f;是如何执行的&#xff1f;我下意识的觉得这位同学应该是个初学者&#xff0c;所以今天就写下一些基础方面的内容&#xff0c;「C语言的内存布局」。程序代码可以看做是一个…

没有梦想,你跟咸鱼有什么分别?

&#xff11;标题起的有点夸张&#xff0c;其实这个就是一个招聘贴&#xff0c;之前从来没有用发头条文章来招聘&#xff0c;实在不好意思&#xff0c;这个招聘对我非常重要&#xff0c;这是一个非常好的朋友公司的招聘信息&#xff0c;也希望大家帮忙扩散一下&#xff0c;因为…

一个很Low的通讯录管理系统(但是能用)C/C++单链表实现

通讯录管理系统的设计 问题需求分析 在计算机还未普及之前通讯管理都是由联系人采用名片&#xff0c;通讯录往往采用的是笔录手工记帐的方式来操作的。现在一般的通讯录管理都是采用计算机作为工具的实用的计算机通讯录管理程序来帮助人们进行更有效的通讯录信息管理。本通讯…

2017《面向对象程序设计》课程作业三

作业链接github链接 对于文件读写和多参数主函数学习过程中遇到的问题 这次文件读写改用了C的形式&#xff0c;然后总体还算顺利&#xff0c;借鉴了林燊的&#xff0c;因为他写的代码最容易看懂&#xff1b;还有就是借鉴了《C程序设计》&#xff0c;讲真&#xff0c;谭浩强的还…

华为不做黑寡妇,开源编译器,与友商共建安卓性能

&#xff11;今天我的一个老哥开了头条号&#xff0c;第一次发文章&#xff0c;我觉得不错&#xff0c;拿来用用&#xff0c;给大家看看华为技术总工的文采。这位总工潜伏在我的微信群里很少说话&#xff0c;大家一定要有这个想法&#xff0c;就是最低调的那个人&#xff0c;真…

点击链接如何直接跳转到相对应的聊天窗口

解决这个问题的步骤如下&#xff1a; <a target"_blank" href"http://wpa.qq.com/msgrd?v3&uin3237465337&siteqq&menuyes">一、登陆腾讯官方网站&#xff1a;http://wp.qq.com/ 二、登陆之后&#xff0c;点“设置”&#xff0c;按下图…

哈夫曼树编码与译码(完整C/C++实现代码)

哈夫曼编码的设计与应用 问题需求分析 用哈夫曼编码(Huffman Coding)&#xff0c;又称霍夫曼编码&#xff0c;是一种编码方式&#xff0c;哈夫曼编码是可变字长编码(VLC)的一种。Huffman于1952年提出一种编码方法&#xff0c;该方法完全依据字符出现概率来构造异字头的平均长…

移动应用开发实例_物联网改变移动应用开发的4种方式

图片来源&#xff1a;pixabay.com来源&#xff1a;物联之家网(iothome.com)转载请注明来源&#xff01;物联网改变了移动应用程序的开发格局。那么&#xff0c;为物联网开发移动应用程序有何不同&#xff1f;物联网与移动应用程序开发齐头并进。物联网改变了人类与机器的互动方…

谁都能看懂的网络模型知识

&#xff11;.网络是我们做嵌入式 避无可避的知识点&#xff0c;但是网络的层次很多&#xff0c;很多时候我们根本理解不了其中的层次和作用&#xff0c;今天跟我们公司的 X 总聊到这个&#xff0c;给我普及了一些知识&#xff0c;我觉得非常有用&#xff0c;分享给大家。最近事…

常用排序算法以及算法性能测试(完整C/C++代码实现)

排序算法性能的比较 注: 由于只是测试算法性能, 所以不会对排序算法做深入讲解, 在随后的时间将会推出排序的详细讲解 问题需求分析 排序算法经过了很长时间的演变&#xff0c;产生了很多种不同的方法。每种算法主要针对不同的数列进行排序&#xff0c;这些排序算法具有各自…

我就随便BB一下

&#xff11;.如果我开始写文章的时候&#xff0c;停顿了几分钟&#xff0c;那结果应该很明显&#xff0c;这一定是一篇比较垃圾的文章&#xff0c;没什么东西值得看&#xff0c;所以我把之前的删除了&#xff0c;重新开头来写&#xff0c;我认为&#xff0c;一个好的开头一定是…

和我一起探索嵌入式

&#xff11;.本文为微信群管理员小磊投稿作品&#xff0c;作者计划编写一系列文章&#xff0c;该篇为第一篇&#xff0c;如果有做STM32的同学这将是一个非常好的系列教程&#xff0c;欢迎关注。我15年刚建立了一个BLE的QQ群&#xff0c;很有幸认识了小磊同学&#xff0c;一个非…

Java NIO_I/O基本概念_Java中的缓冲区(Buffer)_通道(Channel)_网络I/O

I/O基本概念 缓冲区基础 缓冲区是I/O的基础, 进程使用read(), write()将数据读出/写入从缓冲区中; 当缓冲区写满, 内核向磁盘发出指令, 将缓冲区中数据写入磁盘中(这一步不需要CPU), 当磁盘控制器将缓冲区装满, 内核将缓冲区数据拷贝到进程中指定的缓冲区; 操作如下图: 当中…

跟一个大佬前辈交流了一下

&#xff11;.最近&#xff0c;跟我们公司的测试总监聊天&#xff0c;我随便问了下他几个问题&#xff0c;他也给出了答案&#xff0c;在这里随便聊下&#xff0c;希望给大家的职业生涯中有一些借鉴的作用。也能给新入职场的同学一些方向和指引。2.先介绍下这个技术总监&#x…

LINQ简记(1):基本语法

关于LINQ&#xff08;语言集成查询&#xff09;是.NET 3.5和Visual Studio 2008以上版本中引入的一种有趣的全新概念&#xff0c;语言版本有VB和C#&#xff0c;由于C#与.NET平台结合最为紧密&#xff0c;也是MS当初首推的语言&#xff0c;因此&#xff0c;本系列文章的示例代码…