spring AOP开发和JDBC
今天主要学习AOP的概念,简单地说,AOP将业务逻辑的各个部分进行隔离,采用横向抽取的机制,对模块间进行解耦,能有效提高开发效率。然后学习如何进行手动及spring进行AOP开发,利用spring进行AOP开发时,可以选择基于xml配置的方式或基于注释的方式实现AOP。基于注释的方式实现 AOP 的效果是最方便的方式,所以实际开发中推荐使用注解的方式。最后,基于昨天学习的配置数据源的基础上,学习了spring提供的JDBC Template的配置和基本CRUD操作的使用,JDBC模板可以对不同的数据源进行封装,并为用户提供了统一的大量的查询和更新数据库的方法,如 query()、update() 等,方便开发。
AOP简介
AOP相关概念
- AOP 的全称是“Aspect Oriented Programming”,即面向切面编程,AOP和面向对象编程(OOP)类似,也是一种编程模
- Spring AOP 是基于 AOP 编程模式的一个框架,它的使用有效减少了系统间的重复代码,达到了模块间的松耦合目的。它将业务逻辑的各个部分进行隔离,使开发人员在编写业务逻辑时可以专心于核心业务,从而提高了开发效率
- AOP 采取横向抽取机制,取代了传统纵向继承体系的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面
- 目前最流行的 AOP 框架有两个,分别为 Spring AOP 和 AspectJ。Spring AOP 使用纯 Java 实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式向目标类植入增强的代码。AspectJ 是一个基于 Java 语言的 AOP 框架,从 Spring 2.0 开始,Spring AOP 引入了对 AspectJ 的支持。AspectJ 扩展了 Java 语言,提供了一个专门的编译器,在编译时提供横向代码的植入
- AOP作用及优势
- 在程序运行期间,在不修改代码的情况下,对方法进行功能增强
- 减少重复代码,提高开发效率,并且便于维护
- AOP底层实现
- AOP的底层是通过spring提供的动态代理技术实现的。在运行期间,spring通过动态代理技术动态地生成代理对象,代理对象方法执行时进行功能增强的介入,去调用目标对象的代方法,从而实现功能增强
AOP术语
为了更好地理解 AOP,就需要对 AOP 的相关术语有一些了解,这些专业术语主要包含 Joinpoint(spring中就是指方法)、Pointcut、Advice、Target、Weaving、Proxy 和 Aspect,它们的含义如下表所示:
AOP常用动态代理技术
- JDK代理:基于接口的动态代理技术
- cglib代理:基于父类的动态代理技术
手写JDK动态代理示例
- 创建目标接口并定义实现类
package com.example.proxy.jdk; public interface TargetInterface { public void save(); } // -------------------------------------- package com.example.proxy.jdk; public class TargetInterfaceImpl implements TargetInterface{ @Override public void save() { System.out.println("save running"); } }
- 定义增强类,类的方法为增强目标对象的方法
package com.example.proxy.jdk; public class Advice { public void before(){ System.out.println("前置增强"); } public void after(){ System.out.println("后置增强"); } }
- 定义测试类,调用jdk代理方法,代理目标对象,并进行方法增强
package com.example.proxy.jdk; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class JdkTest { public static void main(String[] args) { // 创建目标对象 TargetInterfaceImpl target = new TargetInterfaceImpl(); // 增强对象 Advice advice = new Advice(); // 生成动态代理对象 TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance( target.getClass().getClassLoader(), // 目标对象加载器类 target.getClass().getInterfaces(), // 与目标对象相同的接口字节码对象数组 new InvocationHandler() { // 调用代理对象的任何方法,实质执行都是invoke方法 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 前置增强 advice.before(); Object ret = method.invoke(target, args);// 执行目标方法 // 后置增强 advice.after(); return ret; } } ); // 调用代理对象的方法,实质调用的是invoke,所以具有前置、后置增强效果 proxy.save(); } }
手写cglib动态代理示例
- 直接定义目标类,cglib是基于父类的,不需要像Jdk代理一样定义接口
package com.example.proxy.cglib; public class Target { public void save() { System.out.println("save running"); } }
- 增强类与Jdk代理一致
- 定义测试类,使用cglib代理方法,对目标进行增强
package com.example.proxy.cglib; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CglibTest { public static void main(String[] args) { // 创建目标对象 Target target = new Target(); // 增强对象 Advice advice = new Advice(); // 基于cglib,生成动态代理对象 // 1. 创建增强器 Enhancer enhancer = new Enhancer(); // 2. 设置父类(目标) enhancer.setSuperclass(Target.class); // 3. 设置回调 enhancer.setCallback(new MethodInterceptor() { // 与jdk代理中的invoke类似 @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { // 执行前置增强 advice.before(); // 执行目标 Object invoke = method.invoke(target, args); // 执行后置增强 advice.after(); return invoke; } }); // 4. 创建代理对象,cglib基于父类的,代理是Target的子类对象 Target proxy = (Target) enhancer.create(); // 调用增强后的方法 proxy.save(); } }
spring进行AOP开发事项
- 需要编写的内容
- 编写核心业务代码(目标类的目标方法)
- 编写切面类,切面类中有通知(增强功能方法)
- 在配置文件中,配置织入关系,即将哪些通知与哪些连接点进行结合
- AOP技术实现的内容
- spring框架监控切入点方法的执行,一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能切入,完成完整的代码逻辑运行。
- AOP底层使用哪种代理方式
- spring框架会根据目标类是否实现了接口来决定采用哪种动态代理方式
基于XML的AOP开发
切点表达式
- 切点表达式语法:
execution([修饰符] 返回类型 包名.类名.方法名(参数))
- 访问修饰符可以省略
- 返回值类型、包名、类名、方法名可以使用*代表任意
- 包名与类名之间一个点.代表当前包下的类,两个点… 代表当前包及其子包下的类
- 参数列表可以使用两个点… 表示任意个数、任意类型的参数列表
通知配置语法及类型
开发步骤
- 导入AOP相关坐标
- 创建目标接口和目标类(内部须有切点)
- 创建切面类(内部有增强方法)
- 将目标类和切面类的对象创建权交给spring
- 在applicationContext.xml文件中配置织入关系
- 测试代码
开发示例
- pom.xml导入AOP相关坐标,使用aspectj开发AOP
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.6</version> </dependency>
- 创建目标接口和目标类
package com.example.aop; // 目标接口 public interface TargetInterface { public void save(); } // ---------------------------------- package com.example.aop; // 目标类 public class Target{ public void save() { System.out.println("save running"); } }
- 创建切面类(内部有增强方法)
package com.example.aop; // 创建切面类 public class MyAspect { public void before(){ System.out.println("前置增强"); } public void after(){ System.out.println("后置增强"); } }
- 将目标类和切面类的对象创建权交给spring,需要导