Java
Java Web框架

Spring(2)

简介:Spring是一个开源框架,Spring是于2003年兴起的一个轻量级的Java开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。

AOP

什么是AOP

在软件业,AOP为Aspect Oriented Programming的缩写,意为面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

  • AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码
  • 经典应用:事务管理、性能监视、安全检查、缓存 、日志等
  • Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码
  • AspectJ是一个基于Java语言的AOP框架,Spring2.0开始,Spring AOP引入对Aspect的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入

AOP实现原理

  • AOP底层将采用代理机制进行实现。
  • 接口+实现类 :Spring采用 JDK 的动态代理Proxy
  • 实现类:Spring 采用cglib字节码增强

AOP术语【掌握】

  1. Target(目标类):需要被代理的类。例如:UserService
  2. Joinpoint(连接点):所谓连接点是指那些可能被拦截到的方法。例如:UserService的方法
  3. PointCut(切入点):已经被增强的连接点。例如:addUser()
  4. Advice(通知/增强):增强代码。例如:afterbefore
  5. Weaving(织入):是指把增强(Advice)应用到目标对象(Target)来创建新的代理对象Proxy的过程.
  6. Proxy(代理类)
  7. Aspect(切面): 是切入点Pointcut和通知Advice的结合。一个线是一个特殊的面。一个切入点和一个通知,组成成一个特殊的面。

2.1-1.AOP术语.png

手动方式实现AOP

我们可以通过手动方式实现AOP的编程,有JDK动态代理和cglib字节码增强两种方式。

JDK动态代理

JDK动态代理是对“装饰者”设计模式的简化。使用前提是必须要有接口。下面是编写步骤

  1. 目标类:接口 + 实现类
  • package com.coderap.a_proxy.a_jdk;
  • public interface UserService {
  • public void addUser();
  • public void updateUser();
  • public void deleteUser();
  • }
  • package com.coderap.a_proxy.a_jdk;
  • public class UserServiceImpl implements UserService {
  • @Override
  • public void addUser() {
  • System.out.println("a_proxy.a_jdk addUser");
  • }
  • @Override
  • public void updateUser() {
  • System.out.println("a_proxy.a_jdk updateUser");
  • }
  • @Override
  • public void deleteUser() {
  • System.out.println("a_proxy.a_jdk deleteUser");
  • }
  • }
  1. 切面类:用于存通知MyAspect
  • package com.coderap.a_proxy.a_jdk;
  • public class MyAspect {
  • public void before() {
  • System.out.println("执行前方法");
  • }
  • public void after() {
  • System.out.println("执行后方法");
  • }
  • }
  1. 工厂类:编写工厂生成代理

有了目标类和切面类,我们需要做的就是将切面类中的方法和目标类中的方法结合在一起,因此需要使用工厂类来生成结合后的代理对象:

  • package com.coderap.a_proxy.a_jdk;
  • import java.lang.reflect.InvocationHandler;
  • import java.lang.reflect.Method;
  • import java.lang.reflect.Proxy;
  • public class MyBeanFactory {
  • public static UserService createService(){
  • // 1 目标类
  • final UserService userService = new UserServiceImpl();
  • // 2 切面类
  • final MyAspect myAspect = new MyAspect();
  • /* 3 代理类:将目标类(切入点)和 切面类(通知) 结合,即切面,使用Proxy.newProxyInstance生成
  • * 参数1:loader,类加载器,动态代理类运行时创建,任何类都需要类加载器将其加载到内存。
  • * 一般情况使用“当前类.class.getClassLoader();”
  • * 也可以使用“目标类实例.getClass().getClassLoader();”
  • * 参数2:Class[] interfaces,代理类需要实现的所有接口
  • * 方式1:目标类实例.getClass().getInterfaces();,注意:只能获得自己的接口,不能获得父类的接口
  • * 方式2:new Class[]{UserService.class}
  • * 参数3:InvocationHandler,处理类,接口,必须进行实现类,一般采用匿名内部类
  • * 该匿名内部类提供 invoke方法,代理类的每一个方法执行时,都将调用一次invoke
  • * 参数31:Object proxy,代理对象
  • * 参数32:Method method,代理对象当前执行的方法的描述对象(反射)
  • * 执行方法名:method.getName()
  • * 执行方法:method.invoke(对象,实际参数)
  • * 参数33:Object[] args,方法实际参数
  • *
  • */
  • UserService proxyService = (UserService) Proxy.newProxyInstance(
  • MyBeanFactory.class.getClassLoader(),
  • userService.getClass().getInterfaces(),
  • new InvocationHandler() {
  • @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;
  • }
  • }
  1. 测试
  • package com.coderap.a_proxy.a_jdk;
  • import org.junit.Test;
  • public class TestJDK {
  • @Test
  • public void demo01() {
  • // JDK动态代理返回的是$Proxy4代理对象
  • UserService userService = MyBeanFactory.createService();
  • userService.addUser();
  • userService.updateUser();
  • userService.deleteUser();
  • }
  • }

通过测试可以得到测试打印信息:

  • 执行前方法
  • a_proxy.a_jdk addUser
  • 执行后方法
  • 执行前方法
  • a_proxy.a_jdk updateUser
  • 执行后方法
  • 执行前方法
  • a_proxy.a_jdk deleteUser
  • 执行后方法

cglib字节码增强

cglib字节码增强适用于没有接口,只有实现类的情况,采用字节码增强框架cglib,在运行时创建目标类的子类,从而对目标类进行增强。使用cglib字节码需要导入jar包:

  • 核心:hibernate-distribution-3.6.10.Final\lib\bytecode\cglib\cglib-2.2.jar
  • 依赖:struts-2.3.15.3\apps\struts2-blank\WEB-INF\lib\asm-3.3.jar

spring-core-3.2.0.RELEASE.jar已经整合以上两个内容。编写步骤如下:

  1. 目标类:由于不需要接口,因此只需要提供目标类即可。
  • package com.coderap.a_proxy.b_cglib;
  • public class UserServiceImpl {
  • public void addUser() {
  • System.out.println("a_proxy.b_cglib addUser");
  • }
  • public void updateUser() {
  • System.out.println("a_proxy.b_cglib updateUser");
  • }
  • public void deleteUser() {
  • System.out.println("a_proxy.b_cglib deleteUser");
  • }
  • }
  1. 切面类:用于存通知MyAspect,与JDK动态代理的方式一样。
  • package com.coderap.a_proxy.b_cglib;
  • public class MyAspect {
  • public void before() {
  • System.out.println("执行前方法");
  • }
  • public void after() {
  • System.out.println("执行后方法");
  • }
  • }
  1. 工厂类:编写工厂生成代理

与JDK动态代理一样,需要使用工厂类来生成结合后的代理对象,但此时使用的是Enhancer对象:

  • package com.coderap.a_proxy.b_cglib;
  • import java.lang.reflect.Method;
  • import org.springframework.cglib.proxy.Enhancer;
  • import org.springframework.cglib.proxy.MethodInterceptor;
  • import org.springframework.cglib.proxy.MethodProxy;
  • public class MyBeanFactory {
  • public static UserServiceImpl createService(){
  • // 1 目标类
  • final UserServiceImpl userService = new UserServiceImpl();
  • // 2 切面类
  • final MyAspect myAspect = new MyAspect();
  • // 3 代理类 ,采用cglib,底层创建目标类的子类
  • // 3.1 核心类
  • Enhancer enhancer = new Enhancer();
  • // 3.2 确定父类
  • enhancer.setSuperclass(userService.getClass());
  • /* 3.3 设置回调函数 ,MethodInterceptor接口等效于JDK动态代理中的InvocationHandler接口
  • * intercept()等效JDK动态代理中的invoke()
  • * 参数1、参数2、参数3,与invoke一样
  • * 参数4:methodProxy,方法的代理
  • *
  • */
  • enhancer.setCallback(new MethodInterceptor() {
  • @Override
  • public Object intercept(Object proxy, Method method, Object[] args,
  • MethodProxy methodProxy) throws Throwable {
  • // 前
  • myAspect.before();
  • // 执行目标类的方法
  • Object obj = method.invoke(userService, args);
  • // * 执行代理类的父类 ,执行目标类 (目标类和代理类 父子关系)
  • methodProxy.invokeSuper(proxy, args);
  • // 后
  • myAspect.after();
  • return obj;
  • }
  • });
  • // 3.4 创建代理
  • UserServiceImpl proxyService = (UserServiceImpl) enhancer.create();
  • return proxyService;
  • }
  • }
  1. 测试
  • package com.coderap.a_proxy.b_cglib;
  • import org.junit.Test;
  • public class TestCglib {
  • @Test
  • public void demo01() {
  • // cglib动态代理返回的是UserServiceImpl$$EnhancerByCGLIB$$3cd26195代理对象
  • UserService userService = MyBeanFactory.createService();
  • userService.addUser();
  • userService.updateUser();
  • userService.deleteUser();
  • }
  • }

通过测试可以得到测试打印信息:

  • 执行前方法
  • a_proxy.b_cglib addUser
  • a_proxy.b_cglib addUser
  • 执行后方法
  • 执行前方法
  • a_proxy.b_cglib updateUser
  • a_proxy.b_cglib updateUser
  • 执行后方法
  • 执行前方法
  • a_proxy.b_cglib deleteUser
  • a_proxy.b_cglib deleteUser
  • 执行后方法

AOP联盟通知类型

  • AOP联盟为通知Advice定义了org.aopalliance.aop.Advice
  • Spring按照通知Advice在目标类方法的连接点位置,可以分为5类:
  1. 前置通知org.springframework.aop.MethodBeforeAdvice,在目标方法执行前实施增强;
  2. 后置通知org.springframework.aop.AfterReturningAdvice,在目标方法执行后实施增强;
  3. 环绕通知org.aopalliance.intercept.MethodInterceptor,在目标方法执行前后实施增强;
  4. 异常抛出通知org.springframework.aop.ThrowsAdvice,在方法抛出异常后实施增强;
  5. 引介通知org.springframework.aop.IntroductionInterceptor,在目标类中添加一些新的方法和属性。

Spring编写代理:半自动

让Spring创建代理对象,从Spring容器中手动的获取代理对象。除Spring的基础包之外,还需要导入AOP相关的jar包,包括:

AOP联盟(规范):com.springsource.org.aopalliance-1.0.0.jar
Spring-aop (实现):spring-aop-3.2.0.RELEASE.jar

编写步骤如下:

  1. 目标类及接口:
  • package com.coderap.b_factory_bean;
  • public interface UserService {
  • public void addUser();
  • public void updateUser();
  • public void deleteUser();
  • }
  • package com.coderap.b_factory_bean;
  • public class UserServiceImpl implements UserService {
  • @Override
  • public void addUser() {
  • System.out.println("b_factory_bean addUser");
  • }
  • @Override
  • public void updateUser() {
  • System.out.println("b_factory_bean updateUser");
  • }
  • @Override
  • public void deleteUser() {
  • System.out.println("b_factory_bean deleteUser");
  • }
  • }
  1. 切面类:用于通知MyAspect。
  • package com.coderap.b_factory_bean;
  • import org.aopalliance.intercept.MethodInterceptor;
  • import org.aopalliance.intercept.MethodInvocation;
  • /**
  • * 切面类中确定通知,需要实现不同接口,接口就是规范,从而就确定方法名称。
  • * * 采用“环绕通知” MethodInterceptor
  • */
  • 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;
  • }
  • }
  1. 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">
  • <!-- 1 创建目标类 -->
  • <bean id="userServiceId" class="com.coderap.b_factory_bean.UserServiceImpl"></bean>
  • <!-- 2 创建切面类 -->
  • <bean id="myAspectId" class="com.coderap.b_factory_bean.MyAspect"></bean>
  • <!-- 3 创建代理类
  • * 使用工厂bean,FactoryBean底层调用 getObject()返回特殊bean
  • * ProxyFactoryBean用于创建代理工厂bean,生成特殊代理对象
  • interfaces:确定多个接口,通过<array>可以设置多个值,只有一个值时,可以使用value=""
  • target:确定目标对象
  • interceptorNames:通知(即切面类)的名称,类型String[],如果设置一个值,可以使用value=""
  • optimize:强制使用cglib
  • <property name="optimize" value="true"></property>
  • 底层机制
  • 如果目标类有接口,采用jdk动态代理
  • 如果没有接口,采用cglib字节码增强
  • 如果声明 optimize = true ,无论是否有接口,都采用cglib
  • -->
  • <bean id="proxyServiceId" class="org.springframework.aop.framework.ProxyFactoryBean">
  • <property name="interfaces" value="com.coderap.b_factory_bean.UserService"></property>
  • <property name="target" ref="userServiceId"></property>
  • <property name="interceptorNames" value="myAspectId"></property>
  • </bean>
  • </beans>
  1. 测试代码:
  • @Test
  • public void demo01() {
  • String xmlPath = "com/coderap/b_factory_bean/beans.xml";
  • ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
  • // 获得代理类
  • UserService userService = (UserService) applicationContext.getBean("proxyServiceId");
  • userService.addUser();
  • userService.updateUser();
  • userService.deleteUser();
  • }

需要注意的是,这里使用UserService userService = (UserService) applicationContext.getBean("proxyServiceId");获取的对象其实是代理类对象(与目标类同接口)。

通过测试可以得到测试打印信息:

  • 执行前方法
  • b_factory_bean addUser
  • 执行后方法
  • 执行前方法
  • b_factory_bean updateUser
  • 执行后方法
  • 执行前方法
  • b_factory_bean deleteUser
  • 执行后方法

Spring AOP编程:全自动

从Spring容器获得目标类,如果配置AOP,Spring将自动生成代理。使用这种方式,需要确定目标类,AspectJ切入点表达式。首先导入jar包spring-framework-3.0.2.RELEASE-dependencies\org.aspectj\com.springsource.org.aspectj.weaver\1.6.8.RELEASE。

同时目标类、目标类接口、切面类不用做任何修改,但切面类还是需要继承MethodInterceptor

  1. 目标类及接口:
  • package com.coderap.c_spring_aop;
  • public interface UserService {
  • public void addUser();
  • public void updateUser();
  • public void deleteUser();
  • }
  • package com.coderap.c_spring_aop;
  • public class UserServiceImpl implements UserService {
  • @Override
  • public void addUser() {
  • System.out.println("b_factory_bean addUser");
  • }
  • @Override
  • public void updateUser() {
  • System.out.println("b_factory_bean updateUser");
  • }
  • @Override
  • public void deleteUser() {
  • System.out.println("b_factory_bean deleteUser");
  • }
  • }
  1. 切面类:
  • package com.coderap.c_spring_aop;
  • import org.aopalliance.intercept.MethodInterceptor;
  • import org.aopalliance.intercept.MethodInvocation;
  • /**
  • * 切面类中确定通知,需要实现不同接口,接口就是规范,从而就确定方法名称。
  • * 采用“环绕通知” MethodInterceptor
  • */
  • 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;
  • }
  • }
  1. Spring配置,需要首先配置命名空间:
  • xmlns:aop="http://www.springframework.org/schema/aop"
  • http://www.springframework.org/schema/aop
  • http://www.springframework.org/schema/aop/spring-aop.xsd

然后编写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.xsd
  • http://www.springframework.org/schema/aop
  • http://www.springframework.org/schema/aop/spring-aop.xsd">
  • <!-- 1 创建目标类 -->
  • <bean id="userServiceId" class="com.coderap.c_spring_aop.UserServiceImpl"></bean>
  • <!-- 2 创建切面类(通知) -->
  • <bean id="myAspectId" class="com.coderap.c_spring_aop.MyAspect"></bean>
  • <!-- 3 aop编程
  • 3.1 导入命名空间
  • 3.2 使用 <aop:config>进行配置,proxy-target-class="true"设置声明时使用cglib代理
  • <aop:pointcut>:切入点 ,从目标对象获得具体方法
  • <aop:advisor>:特殊的切面,只有一个通知和 一个切入点
  • advice-ref:通知引用
  • pointcut-ref:切入点引用
  • 3.3 切入点表达式
  • execution(* com.coderap.c_spring_aop.*.*(..))
  • -->
  • <aop:config proxy-target-class="true">
  • <aop:pointcut expression="execution(* com.coderap.c_spring_aop.*.*(..))" id="myPointCut"/>
  • <aop:advisor advice-ref="myAspectId" pointcut-ref="myPointCut"/>
  • </aop:config>
  • </beans>
  1. 测试代码:
  • package com.coderap.c_spring_aop;
  • import org.junit.Test;
  • import org.springframework.context.ApplicationContext;
  • import org.springframework.context.support.ClassPathXmlApplicationContext;
  • public class TestSpringAOP {
  • @Test
  • public void demo01(){
  • String xmlPath = "com/coderap/c_spring_aop/beans.xml";
  • ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
  • // 获得目标类
  • UserService userService = (UserService) applicationContext.getBean("userServiceId");
  • userService.addUser();
  • userService.updateUser();
  • userService.deleteUser();
  • }
  • }

使用Spring AOP实现AOP时,可以直接通过UserService userService = (UserService) applicationContext.getBean("userServiceId");获取目标类对象。

以上代码打印内容如下:

  • 执行前方法
  • c_spring_aop addUser
  • 执行后方法
  • 执行前方法
  • c_spring_aop updateUser
  • 执行后方法
  • 执行前方法
  • c_spring_aop deleteUser
  • 执行后方法

AspectJ

AspectJ是一个基于Java语言的AOP框架,Spring2.0以后新增了对AspectJ切点表达式支持,@AspectJ是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面
新版本Spring框架,建议使用AspectJ方式来开发AOP。AspectJ主要用于自定义开发。

切入点表达式

execution()用于描述方法,语法:execution(修饰符 返回值 包.类.方法名(参数) throws异常)

  1. 修饰符,一般省略
  • public:公共方法
  • *:任意
  1. 返回值,不能省略
  • void:返回没有值
  • String:返回值字符串
  • *:任意
  1. 包,可省略
  • com.coderap.crm:固定包
  • com.coderap.crm.*.service:crm包下面子包任意 (例如:com.coderap.crm.staff.service
  • com.coderap.crm..:crm包下面的所有子包(含自己)
  • com.coderap.crm.*.service..:crm包下面任意子包,固定目录service,service目录任意包
  1. 类,可省略
  • UserServiceImpl:指定类
  • *Impl:以Impl结尾
  • User*:以User开头
  • *:任意
  1. 方法名,不能省略
  • addUser:固定方法
  • add*:以add开头
  • *Do:以Do结尾
  • *:任意
  1. (参数)
  • ():无参
  • (int):一个整型
  • (int ,int):两个
  • (..):参数任意
  1. throws:可省略,一般不写。

例子:

execution(* com.coderap.crm.*.service..*.*(..))

<aop:pointcut expression="execution(* com.coderap.*WithCommit.*(..)) || execution(* com.coderap.*Service.*(..))" id="myPointCut"/>

within:匹配包或子包中的方法(了解)

within(com.coderap.aop..*)

this:匹配实现接口的代理对象中的方法(了解)

this(com.coderap.aop.user.UserDAO)

target:匹配实现接口的目标对象中的方法(了解)

target(com.coderap.aop.user.UserDAO)

args:匹配参数格式符合标准的方法(了解)

args(int,int)

bean(id):对指定的bean所有的方法(了解)

bean('userServiceId')

AspectJ 通知类型

AOP联盟定义通知类型,具有特性接口,必须实现,从而确定方法名称。AspectJ通知类型,只定义类型名称。已经方法格式。一共有6种:

  • before:前置通知(应用:各种校验),在方法执行前执行,如果通知抛出异常,阻止方法运行。(org.springframework.aop.aspectj.AspectJMethodBeforeAdvice.class
  • // AspectJMethodBeforeAdvice
  • public void before(Method method, Object[] args, Object target) throws Throwable {
  • invokeAdviceMethod(getJoinPointMatch(), null, null);
  • }
  • afterReturning:后置通知(应用:常规数据处理),方法正常返回后执行,如果方法中抛出异常,通知无法执行;必须在方法执行后才执行,所以可以获得方法的返回值。(org.springframework.aop.aspectj.AspectJAfterReturningAdvice.class
  • // AspectJAfterReturningAdvice
  • public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
  • if (shouldInvokeOnReturnValueOf(method, returnValue)) {
  • invokeAdviceMethod(getJoinPointMatch(), returnValue, null);
  • }
  • }
  • around:环绕通知(应用:十分强大,可以做任何事情),方法执行前后分别执行,可以阻止方法的执行;必须手动执行目标方法。(org.springframework.aop.aspectj.AspectJAroundAdvice.class
  • // AspectJAroundAdvice
  • public Object invoke(MethodInvocation mi) throws Throwable {
  • if (!(mi instanceof ProxyMethodInvocation)) {
  • throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
  • }
  • ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
  • ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
  • JoinPointMatch jpm = getJoinPointMatch(pmi);
  • return invokeAdviceMethod(pjp, jpm, null, null);
  • }
  • afterThrowing:抛出异常通知(应用:包装异常信息),方法抛出异常后执行,如果方法没有抛出异常,无法执行。(org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.class
  • // AspectJAfterThrowingAdvice
  • public Object invoke(MethodInvocation mi) throws Throwable {
  • try {
  • return mi.proceed();
  • }
  • catch (Throwable t) {
  • if (shouldInvokeOnThrowing(t)) {
  • invokeAdviceMethod(getJoinPointMatch(), null, t);
  • }
  • throw t;
  • }
  • }
  • after:最终通知(应用:清理现场),方法执行完毕后执行,无论方法中是否出现异常。(org.springframework.aop.aspectj.AspectJAfterAdvice.class
  • // AspectJAfterAdvice
  • public Object invoke(MethodInvocation mi) throws Throwable {
  • try {
  • return mi.proceed();
  • }
  • finally {
  • invokeAdviceMethod(getJoinPointMatch(), null, null);
  • }
  • }
  • try {
  • //前置:before
  • //手动执行目标方法
  • //后置:afterRetruning
  • } catch(){
  • //抛出异常 afterThrowing
  • } finally{
  • //最终 after
  • }

导入jar包

  • com.springsource.org.aopalliance-1.0.0.jar,AOP联盟规范
  • com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar,AspectJ规范
  • spring-aop-3.2.0.RELEASE.jar,Spring AOP实现
  • spring-aspects-3.2.0.RELEASE.jar,Spring AspectJ实现

基于XML的AOP实现

  1. 目标类:接口与实现
  • package com.coderap.d_aspect.a_xml;
  • public interface UserService {
  • public void addUser();
  • public String updateUser();
  • public void deleteUser();
  • }
  • package com.coderap.d_aspect.a_xml;
  • public class UserServiceImpl implements UserService {
  • @Override
  • public void addUser() {
  • System.out.println("d_aspect.a_xml addUser");
  • }
  • @Override
  • public String updateUser() {
  • System.out.println("d_aspect.a_xml updateUser");
  • int i = 1 / 0;
  • return "updateUser Return";
  • }
  • @Override
  • public void deleteUser() {
  • System.out.println("d_aspect.a_xml deleteUser");
  • }
  • }
  1. 切面类:编写多个通知,采用AspectJ通知名称任意(方法名任意)
  • package com.coderap.d_aspect.a_xml;
  • import org.aspectj.lang.JoinPoint;
  • import org.aspectj.lang.ProceedingJoinPoint;
  • /**
  • * 切面类,含有多个通知
  • */
  • public class MyAspect {
  • public void myBefore(JoinPoint joinPoint) {
  • System.out.println("前置通知 : " + joinPoint.getSignature().getName());
  • }
  • public void myAfterReturning(JoinPoint joinPoint, Object ret) {
  • System.out.println("后置通知 : " + joinPoint.getSignature().getName()
  • + " , -->" + 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("最终通知");
  • }
  • }
  1. AOP编程,即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.coderap.d_aspect.a_xml.UserServiceImpl"></bean>
  • <!-- 2 创建切面类(通知) -->
  • <bean id="myAspectId" class="com.coderap.d_aspect.a_xml.MyAspect"></bean>
  • <aop:config>
  • <!-- 3 aop编程
  • <aop:aspect> 将切面类 声明“切面”,从而获得通知(方法)
  • ref:切面类引用
  • -->
  • <aop:aspect ref="myAspectId">
  • <!--
  • <aop:pointcut> 声明一个切入点,所有的通知都可以使用。
  • expression:切入点表达式
  • id:名称,用于其它通知引用
  • -->
  • <aop:pointcut expression="execution(* com.coderap.d_aspect.a_xml.UserServiceImpl.*(..))" id="myPointCut"/>
  • <!-- 3.1 前置通知
  • <aop:before method="" pointcut="" pointcut-ref=""/>
  • method:通知,及方法名
  • pointcut:切入点表达式,此表达式只能当前通知使用。
  • pointcut-ref:切入点引用,可以与其他通知共享切入点。
  • 通知方法格式:public void myBefore(JoinPoint joinPoint){...}
  • 参数1:org.aspectj.lang.JoinPoint 用于描述连接点(目标方法),获得目标方法名等
  • 例如:
  • <aop:before method="myBefore" pointcut-ref="myPointCut"/>
  • -->
  • <!-- 3.2后置通知 ,目标方法后执行,获得返回值
  • <aop:after-returning method="" pointcut-ref="" returning=""/>
  • returning:通知方法第二个参数的名称
  • 通知方法格式:public void myAfterReturning(JoinPoint joinPoint,Object ret){...}
  • 参数1:连接点描述
  • 参数2:类型Object,参数名 returning="ret" 配置的
  • 例如:
  • <aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret" />
  • -->
  • <!-- 3.3 环绕通知
  • <aop:around method="" pointcut-ref=""/>
  • 通知方法格式:public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{...}
  • 返回值类型:Object
  • 方法名:任意
  • 参数:org.aspectj.lang.ProceedingJoinPoint
  • 抛出异常
  • 执行目标方法:Object obj = joinPoint.proceed();
  • 例如:
  • <aop:around method="myAround" pointcut-ref="myPointCut"/>
  • -->
  • <!-- 3.4 抛出异常
  • <aop:after-throwing method="" pointcut-ref="" throwing=""/>
  • throwing :通知方法的第二个参数名称
  • 通知方法格式:public void myAfterThrowing(JoinPoint joinPoint,Throwable e){...}
  • 参数1:连接点描述对象
  • 参数2:获得异常信息,类型Throwable ,参数名由throwing="e" 配置
  • 例如:
  • <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/>
  • -->
  • <!-- 3.5 最终通知 -->
  • <aop:after method="myAfter" pointcut-ref="myPointCut"/>
  • </aop:aspect>
  • </aop:config>
  • </beans>
  1. 测试
  • package com.coderap.d_aspect.a_xml;
  • import org.junit.Test;
  • import org.springframework.context.ApplicationContext;
  • import org.springframework.context.support.ClassPathXmlApplicationContext;
  • public class TestAspectXml {
  • @Test
  • public void demo01(){
  • String xmlPath = "com/coderap/d_aspect/a_xml/beans.xml";
  • ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
  • //获得目标类
  • UserService userService = (UserService) applicationContext.getBean("userServiceId");
  • userService.addUser();
  • userService.updateUser();
  • userService.deleteUser();
  • }
  • }

基于注解的AOP开发

接下来,我们将使用注解的方式替换上面的XML配置。

替换Bean

原配置:

  • <!-- 1 创建目标类 -->
  • <bean id="userServiceId" class="com.coderap.d_aspect.b_anno.UserServiceImpl"></bean>
  • <!-- 2 创建切面类(通知) -->
  • <bean id="myAspectId" class="com.coderap.d_aspect.b_anno.MyAspect"></bean>

注解方式:

  1. 配置扫描
  • <?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.coderap.d_aspect.b_anno"></context:component-scan>
  • </beans>
  1. 替换userServiceId
  • package com.coderap.d_aspect.b_anno;
  • import org.springframework.stereotype.Service;
  • @Service("userServiceId")
  • public class UserServiceImpl implements UserService {
  • @Override
  • public void addUser() {
  • System.out.println("d_aspect.b_anno addUser");
  • }
  • @Override
  • public String updateUser() {
  • System.out.println("d_aspect.b_anno updateUser");
  • int i = 1 / 0;
  • return "updateUser Return";
  • }
  • @Override
  • public void deleteUser() {
  • System.out.println("d_aspect.b_anno deleteUser");
  • }
  • }
  1. 替换myAspectId,由于myAspectId是组件,所以使用@Component
  • package com.coderap.d_aspect.b_anno;
  • import org.aspectj.lang.JoinPoint;
  • import org.aspectj.lang.ProceedingJoinPoint;
  • /**
  • * 切面类,含有多个通知
  • */
  • @Component
  • public class MyAspect {...}
  1. 替换AOP
  • 必须进行AspectJ自动代理
  • <!-- 2.确定AOP注解生效 -->
  • <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
  • 声明切面

原配置:

  • <aop:aspect ref="myAspectId">

替换注解:

  • package com.coderap.d_aspect.b_anno;
  • import org.aspectj.lang.JoinPoint;
  • import org.aspectj.lang.ProceedingJoinPoint;
  • /**
  • * 切面类,含有多个通知
  • */
  • @Component
  • @Aspect
  • public class MyAspect {...}
  • 替换前置通知

原配置:

  • <aop:before method="myBefore" pointcut="execution(* com.coderap.d_aspect.b_anno.UserServiceImpl.*(..))"/>

替换注解:

  • //切入点当前有效
  • @Before("execution(* com.coderap.d_aspect.b_anno.UserServiceImpl.*(..))")
  • public void myBefore(JoinPoint joinPoint){
  • System.out.println("前置通知:" + joinPoint.getSignature().getName());
  • }

因为后置通知的切入点是一个引用对象,即引用的<aop:pointcut />,所以我们对切入点并进行注解替换:

  • 替换公共切入点

原配置:

  • <aop:pointcut expression="execution(* com.coderap.d_aspect.b_anno.UserServiceImpl.*(..))" id="myPointCut"/>

替换注解:

  • // 声明公共切入点
  • @Pointcut("execution(* com.coderap.d_aspect.b_anno.UserServiceImpl.*(..))")
  • private void myPointCut(){
  • }
  • 替换后置通知

原配置:

  • <aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="ret" />

替换注解:

  • // 替换后置通知
  • @AfterReturning(value="myPointCut()" ,returning="ret")
  • public void myAfterReturning(JoinPoint joinPoint,Object ret){
  • System.out.println("后置通知 : " + joinPoint.getSignature().getName() + " , -->" + ret);
  • }
  • 替换环绕通知

原配置:

  • <aop:around method="myAround" pointcut-ref="myPointCut"/>

替换注解:

  • // 替换环绕通知
  • @Around(value = "myPointCut()")
  • public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
  • System.out.println("前");
  • // 手动执行目标方法
  • Object obj = joinPoint.proceed();
  • System.out.println("后");
  • return obj;
  • }
  • 替换抛出异常

原配置:

  • <aop:after-throwing method="myAfterThrowing" pointcut="execution(* com.coderap.d_aspect.b_anno.UserServiceImpl.*(..))" throwing="e"/>

替换注解:

  • // 替换抛出异常
  • @AfterThrowing(value="execution(* com.coderap.d_aspect.b_anno.UserServiceImpl.*(..))" ,throwing="e")
  • public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
  • System.out.println("抛出异常通知 : " + e.getMessage());
  • }
  • 替换最终通知

原配置:

  • <aop:after method="myAfter" pointcut-ref="myPointCut"/>

替换注解:

  • // 替换最终通知
  • @After("myPointCut()")
  • public void myAfter(JoinPoint joinPoint) {
  • System.out.println("最终通知");
  • }

最终切面类如下:

  • package com.coderap.d_aspect.b_anno;
  • import org.aspectj.lang.JoinPoint;
  • import org.aspectj.lang.ProceedingJoinPoint;
  • import org.aspectj.lang.annotation.After;
  • import org.aspectj.lang.annotation.AfterReturning;
  • import org.aspectj.lang.annotation.AfterThrowing;
  • import org.aspectj.lang.annotation.Around;
  • import org.aspectj.lang.annotation.Aspect;
  • import org.aspectj.lang.annotation.Before;
  • import org.aspectj.lang.annotation.Pointcut;
  • import org.springframework.stereotype.Component;
  • /**
  • * 切面类,含有多个通知
  • */
  • @Component
  • @Aspect
  • public class MyAspect {
  • // 切入点当前有效
  • // @Before("execution(* com.coderap.d_aspect.b_anno.UserServiceImpl.*(..))")
  • public void myBefore(JoinPoint joinPoint) {
  • System.out.println("前置通知 : " + joinPoint.getSignature().getName());
  • }
  • // 声明公共切入点
  • @Pointcut("execution(* com.coderap.d_aspect.b_anno.UserServiceImpl.*(..))")
  • private void myPointCut() {
  • }
  • // @AfterReturning(value="myPointCut()" ,returning="ret")
  • public void myAfterReturning(JoinPoint joinPoint, Object ret) {
  • System.out.println("后置通知 : " + joinPoint.getSignature().getName()
  • + " , -->" + ret);
  • }
  • // @Around(value = "myPointCut()")
  • public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable {
  • System.out.println("前");
  • // 手动执行目标方法
  • Object obj = joinPoint.proceed();
  • System.out.println("后");
  • return obj;
  • }
  • // @AfterThrowing(value="execution(* com.coderap.d_aspect.b_anno.UserServiceImpl.*(..))"
  • // ,throwing="e")
  • public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
  • System.out.println("抛出异常通知 : " + e.getMessage());
  • }
  • @After("myPointCut()")
  • public void myAfter(JoinPoint joinPoint) {
  • System.out.println("最终通知");
  • }
  • }

JDBCTemplate

Spring 提供用于操作JDBC工具类,类似DBUtils。依赖连接池DataSource(数据源)

环境搭建

  • 创建表
  • create database ee19_spring_day02;
  • use ee19_spring_day02;
  • 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');
  • insert into t_user(username,password) values('rose','5678');

导入jar包

  • com.springsource.com.mchange.v2.c3p0-0.9.1.2.jar,C3P0连接池
  • com.springsource.org.apache.commons.dbcp-1.2.2.osgi.jarcom.springsource.org.apache.commons.pool-1.5.3.jar,DBCP连接池
  • mysql-connector-java-5.1.28-bin.jar,MySQL驱动
  • spring-jdbc-3.2.0.RELEASE.jar,Spring JDBC开发
  • spring-tx-3.2.0.RELEASE.jar,事务

Javabean

  • package com.coderap.a_domain;
  • public class User {
  • private Integer id;
  • private String username;
  • private String password;
  • public Integer getId() {
  • return id;
  • }
  • public void setId(Integer id) {
  • this.id = id;
  • }
  • public String getUsername() {
  • return username;
  • }
  • public void setUsername(String username) {
  • this.username = username;
  • }
  • public String getPassword() {
  • return password;
  • }
  • public void setPassword(String password) {
  • this.password = password;
  • }
  • @Override
  • public String toString() {
  • return "User [id=" + id + ", username=" + username + ", password="
  • + password + "]";
  • }
  • }

使用api(了解)

  • package com.coderap.b_api;
  • import org.apache.commons.dbcp.BasicDataSource;
  • import org.springframework.jdbc.core.JdbcTemplate;
  • public class TestApi {
  • public static void main(String[] args) {
  • // 1 创建数据源(连接池) dbcp
  • BasicDataSource dataSource = new BasicDataSource();
  • // * 基本4项
  • dataSource.setDriverClassName("com.mysql.jdbc.Driver");
  • dataSource.setUrl("jdbc:mysql://localhost:3306/ee19_spring_day02");
  • dataSource.setUsername("root");
  • dataSource.setPassword("1234");
  • // 2 创建模板
  • JdbcTemplate jdbcTemplate = new JdbcTemplate();
  • jdbcTemplate.setDataSource(dataSource);
  • // 3 通过api操作
  • jdbcTemplate.update(
  • "insert into t_user(username,password) values(?,?);", "tom",
  • "998");
  • }
  • }

配置DBCP

  • <?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">
  • <!-- 创建数据源 -->
  • <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/ee19_spring_day02"></property>
  • <property name="username" value="root"></property>
  • <property name="password" value="1234"></property>
  • </bean>
  • <!-- 创建模板,需要注入数据源 -->
  • <bean id="jdbcTemplateId" class="org.springframework.jdbc.core.JdbcTemplate">
  • <property name="dataSource" ref="dataSourceId"></property>
  • </bean>
  • <!-- 配置dao -->
  • <bean id="userDaoId" class="com.coderap.c_dbcp.UserDao">
  • <property name="jdbcTemplate" ref="jdbcTemplateId"></property>
  • </bean>
  • </beans>

UserDao:

  • package com.coderap.c_dbcp;
  • import org.springframework.jdbc.core.JdbcTemplate;
  • import com.coderap.a_domain.User;
  • public class UserDao {
  • // jdbc模板将由spring注入
  • private JdbcTemplate jdbcTemplate;
  • public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
  • this.jdbcTemplate = jdbcTemplate;
  • }
  • public void update(User user) {
  • String sql = "update t_user set username=?,password=? where id =?";
  • Object[] args = { user.getUsername(), user.getPassword(), user.getId() };
  • jdbcTemplate.update(sql, args);
  • }
  • }

测试:

  • package com.coderap.c_dbcp;
  • import org.junit.Test;
  • import org.springframework.context.ApplicationContext;
  • import org.springframework.context.support.ClassPathXmlApplicationContext;
  • import com.coderap.a_domain.User;
  • public class TestDBCP {
  • @Test
  • public void demo01() {
  • User user = new User();
  • user.setId(1);
  • user.setUsername("杰克");
  • user.setPassword("4321");
  • String xmlPath = "com/coderap/c_dbcp/beans.xml";
  • ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
  • xmlPath);
  • // 获得目标类
  • UserDao userDao = (UserDao) applicationContext.getBean("userDaoId");
  • userDao.update(user);
  • }
  • }

配置C3P0

  • <?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">
  • <!-- 创建数据源 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/ee19_spring_day02"></property>
  • <property name="user" value="root"></property>
  • <property name="password" value="1234"></property>
  • </bean>
  • <!-- 创建模板 ,需要注入数据源 -->
  • <bean id="jdbcTemplateId" class="org.springframework.jdbc.core.JdbcTemplate">
  • <property name="dataSource" ref="dataSourceId"></property>
  • </bean>
  • <!-- 配置dao -->
  • <bean id="userDaoId" class="com.coderap.d_c3p0.UserDao">
  • <property name="jdbcTemplate" ref="jdbcTemplateId"></property>
  • </bean>
  • </beans>

UserDao:

  • package com.coderap.d_c3p0;
  • import java.util.List;
  • import org.springframework.jdbc.core.JdbcTemplate;
  • import org.springframework.jdbc.core.RowMapper;
  • import org.springframework.jdbc.core.simple.ParameterizedBeanPropertyRowMapper;
  • import com.coderap.a_domain.User;
  • public class UserDao {
  • // jdbc模板将由spring注入
  • private JdbcTemplate jdbcTemplate;
  • public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
  • this.jdbcTemplate = jdbcTemplate;
  • }
  • public void update(User user) {
  • String sql = "update t_user set username=?,password=? where id =?";
  • Object[] args = { user.getUsername(), user.getPassword(), user.getId() };
  • jdbcTemplate.update(sql, args);
  • }
  • /**
  • * 查询所有
  • *
  • * @return
  • */
  • public List<User> findAll() {
  • return jdbcTemplate.query("select * from t_user",
  • ParameterizedBeanPropertyRowMapper.newInstance(User.class));
  • }
  • }

注: ParameterizedBeanPropertyRowMapper.newInstance(User.class)用于包装JavaBean。

测试:

  • package com.coderap.d_c3p0;
  • import java.util.List;
  • import org.junit.Test;
  • import org.springframework.context.ApplicationContext;
  • import org.springframework.context.support.ClassPathXmlApplicationContext;
  • import com.coderap.a_domain.User;
  • public class TestC3P0 {
  • @Test
  • public void demo01() {
  • String xmlPath = "com/coderap/d_c3p0/beans.xml";
  • ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
  • xmlPath);
  • // 获得目标类
  • UserDao userDao = (UserDao) applicationContext.getBean("userDaoId");
  • List<User> allUser = userDao.findAll();
  • for (User user : allUser) {
  • System.out.println(user);
  • }
  • }
  • }

使用JdbcDaoSupport

我们让Dao继承org.springframework.jdbc.core.support.JdbcDaoSupport接口,可以省略JdbcTemplate的创建或注入工作,让其用接口的默认实现来完成:

  • package com.coderap.e_jdbcdaosupport;
  • import java.util.List;
  • import org.springframework.jdbc.core.JdbcTemplate;
  • import org.springframework.jdbc.core.RowMapper;
  • import org.springframework.jdbc.core.simple.ParameterizedBeanPropertyRowMapper;
  • import org.springframework.jdbc.core.support.JdbcDaoSupport;
  • import com.coderap.a_domain.User;
  • public class UserDao extends JdbcDaoSupport {
  • public void update(User user) {
  • String sql = "update t_user set username=?,password=? where id =?";
  • Object[] args = { user.getUsername(), user.getPassword(), user.getId() };
  • this.getJdbcTemplate().update(sql, args);
  • }
  • /**
  • * 查询所有
  • *
  • * @return
  • */
  • public List<User> findAll() {
  • return this.getJdbcTemplate().query("select * from t_user",
  • ParameterizedBeanPropertyRowMapper.newInstance(User.class));
  • }
  • }

配置文件:

  • <?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">
  • <!-- 创建数据源 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/ee19_spring_day02"></property>
  • <property name="user" value="root"></property>
  • <property name="password" value="1234"></property>
  • </bean>
  • <!-- 配置dao
  • * dao 继承 JdbcDaoSupport,之后只需要注入数据源,底层将自动创建模板
  • -->
  • <bean id="userDaoId" class="com.coderap.e_jdbcdaosupport.UserDao">
  • <property name="dataSource" ref="dataSourceId"></property>
  • </bean>
  • </beans>

源码分析:

  • public abstract class JdbcDaoSupport extends DaoSupport {
  • private JdbcTemplate jdbcTemplate;
  • public final void setDataSource(DataSource dataSource) {
  • if (this.jdbcTemplate == null || dataSource != this.jdbcTemplate.getDataSource()) {
  • this.jdbcTemplate = createJdbcTemplate(dataSource);
  • initTemplateConfig();
  • }
  • }
  • protected JdbcTemplate createJdbcTemplate(DataSource dataSource) {
  • return new JdbcTemplate(dataSource);
  • }
  • ...
  • }

配置properties

properties文件

  • jdbc.driverClass=com.mysql.jdbc.Driver
  • jdbc.jdbcUrl=jdbc:mysql://localhost:3306/ee19_spring_day02
  • jdbc.user=root
  • jdbc.password=1234

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: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">
  • <!-- 加载配置文件
  • "classpath:"前缀表示src目录下
  • 在配置文件之后通过 ${key} 获得内容
  • -->
  • <context:property-placeholder location="classpath:com/coderap/f_properties/jdbcInfo.properties"/>
  • <!-- 创建数据源 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>
  • <!-- 配置dao
  • * dao 继承 JdbcDaoSupport,之后只需要注入数据源,底层将自动创建模板
  • -->
  • <bean id="userDaoId" class="com.coderap.f_properties.UserDao">
  • <property name="dataSource" ref="dataSourceId"></property>
  • </bean>
  • </beans>