Spring:AOP, 面向切面编程,JDK的动态代理,CGLIB代理,Spring的AOP技术(底层就是JDK动态代理和CGLIB代理技术) AOP概述 2. 什么是动态代理技术? 3. 使用JDK动态代理 使用CGLIB 第三方代理 代理小结 动态代理模式的缺陷是什么 Spring的AOP 基于XML配置AOP 环绕通知 基于注解配置AOP 小结
什么是AOP, 面向切面编程
AOP为Aspect Oriented Programming的缩写, 意为:面向切面编程, 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. AOP是OOP的延续, 是函数式编程的一种衍生范型. 利用AOP可以对业务逻辑的各个部分进行隔离, 从而使得业务逻辑各部分之间的耦合度降低, 提高程序的可重用性, 同时提高了开发的效率. -
传统开发模型: 纵向的编程.
面向切面编程: 纵横配合的编程.
AOP的作用及优势
作用:
在程序运行期间,不修改任何相关源码对已有方法进行增强。
优势:
减少重复代码、提高开发效率、维护方便
AOP的实现方式
使用动态代理模式来实现
可能通过上面的介绍,我们还是没有一个清晰的认识。没关系,我们看看下面的具体应用。
//.模拟事务管理器 public class TransactionManagerHandler { public void begin() { System.out.println("开启事务"); } public void commit() { System.out.println("提交事务"); } public void rollback() { System.out.println("回滚事务"); } public void close() { System.out.println("关闭session"); } }
//Service层代码 public class UserServiceImpl implements UserService { // 引入事务管理器 private TransactionManagerHandler txManager = new TransactionManagerHandler(); @Override public void insert(String username) { try { txManager.begin(); System.out.println("调用dao层插入方法"); txManager.commit(); } catch (Exception e) { txManager.rollback(); } finally { txManager.close(); } } @Override public void update(String username) { try { txManager.begin(); System.out.println("调用dao层修改方法"); txManager.commit(); } catch (Exception e) { txManager.rollback(); } finally { txManager.close(); } } }
存在的问题
上面代码的问题就是:我们的事务控制的代码是重复性的。这还只是一个业务类,如果有多个业务了,每个业务类中都会有这些重复性的代码。是不是重复代码太多了?
思考:我们有什么办法解决这个问题吗?
解决上述问题的方案
1. JDK的动态代理
2. CGLIB代理
3. Spring的AOP技术(底层就是JDK动态代理和CGLIB代理技术)
2. 什么是动态代理技术?
Java中的动态代理,就是使用者使用的不是真实的对象,而是使用的一个代理对象,而这个代理对象中包含的就是真实的对象,代理对象就是不改变原有对象的功能方法的基础之上封装新的功能
3. 使用JDK动态代理
JDK动态代理是Java官方的代理
使用JDK官方的Proxy类创建代理对象
1. 需要通过Proxy类创建代理对象
2. 创建代理对象必须要一个代理处理类(实现了接口InvocationHandler的类)
3.1. JDK动态代理API分析
1、java.lang.reflect.Proxy 类: Java 动态代理机制生成的所有动态代理类的父类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。 主要方法: public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler hanlder) 方法职责:为指定类加载器、一组接口及调用处理器生成动态代理类实例 参数: loader:类加载器 interfaces: 模拟的接口 hanlder:代理执行处理器
返回:动态生成的代理对象 |
2、java.lang.reflect.InvocationHandler接口: public Object invoke(Object proxy, Method method, Object[] args) 方法职责:负责集中处理动态代理类上的所有方法调用 参数: proxy:生成的代理对象 method:当前调用的真实方法对象 args:当前调用方法的实参
返回: 真实方法的返回结果 ------------------------------------------------------------------------------------ njdk动态代理操作步骤 ① 实现InvocationHandler接口,创建自己增强代码的处理器。 ② 给Proxy类提供ClassLoader对象和代理接口类型数组,创建动态代理对象。 ③ 在处理器中实现增强操作。 |
接口
package cn.zj.spring.service; public interface Service01 { void insert(String name); void Update(String name); }
接口实现类(被代理对象)
package cn.zj.spring.service.impl; import cn.zj.spring.service.Service01; public class ServiceImpl implements Service01 { @Override public void insert(String name) { System.out.println(name + "insert"); } @Override public void Update(String name) { System.out.println(name + "Udpate"); } }
事务插入
package cn.zj.spring; public class TransactionManagerHandler { public void begin() { System.out.println("开启事务"); } public void commit() { System.out.println("提交事务"); } public void rollback() { System.out.println("回滚事务"); } public void close() { System.out.println("关闭session"); } }
jdk代理
//JDK的动态代理原理: package cn.zj.spring.proxy; /* * 此类是最终返回代理对象类 * 此类必须提供一个方法,返回最终返回代理对象 * * 返回代理对象必须需要的资源 * 1,被代理的对象 * 2,要增强内容 * 事务管理器 * */ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import cn.zj.spring.TransactionManagerHandler; public class Proxy01 { //被代理对象,实际开发应该用Object,通用 //目前场景 注入 cn.zj.spring.service.impl.UserServiceImpl private Object object; //需要增强的内容,此处事务管理器 private TransactionManagerHandler tr; /** * 返回动态代理对象(此处就是ServiceImpl代理对象) * @param clz 被代理的数据类型 * @return 代理对象 * * 方法内部我们使用JDK的动态代理类 Proxy类创建代理对象 */ public <T> T getProxyObject(Class<T> clz) { /*创建的代理对象方法 * Proxy.newProxyInstance(loader, interfaces, h) * loader :类加载器 ClassLoader * 类加载器在程序运行jvm自动创建了,我们开发者只需要获取即可 * 获取类加载器的方式 * 方式一:任何一个类的字节码实例可以直接获取 * 如 :this.class.getClassLoader() * 方式二:通过当前线程获取 * ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); * interfaces : 被代理对象的接口 * h:InvocationHandler 执行处理器 * 我们开发需要根据自己的业务去自己创建匿名内部内,自己去在内部实现具体代理要做什么事情 */ //类加载器 ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); //被代理对象的接口 Class<?>[] interfaces = object.getClass().getInterfaces(); //Proxy代理创建的代理对象 Object pr=Proxy.newProxyInstance(contextClassLoader, interfaces, new InvocationHandler() { /** * 处理器内部方法,开发者具体做代理增强的地方 * @param obj 代理对象,JDK底层帮忙创建的 * @param method 被代理对象的方法 * @param arg2 被代理对象方法的参数 * @return 返回被代理对象执行方法后的结果 * @throws Throwable */ public Object invoke(Object obj, Method method, Object[] arg2) throws Throwable { /* * 方法增强思路 * 1,通过反射执行被代理对象的方法 * 2,在被代理对象方法执行之前,之后,异常,最终做相关增强(此处是增强模拟事务管理器方法) */ Object invoke=null; try { //开启事务 tr.begin(); //执行被代理对象的方法 invoke = method.invoke(object,arg2); //提交事务 tr.commit(); } catch (Exception e) { e.printStackTrace(); //回滚事务 tr.rollback(); }finally { //关闭session tr.close(); } return invoke; } }); return (T) pr; } public void setObject(Object object) { this.object = object; } public void setTr(TransactionManagerHandler tr) { this.tr = tr; } }
配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd " > <!-- 配置事务管理器(模拟) --> <bean id="transactionManagerHandler" class="cn.zj.spring.TransactionManagerHandler"/> <!-- 配置 proxy01 自定义的代理类 --> <bean id="proxy" class="cn.zj.spring.proxy.Proxy01"> <!-- 注入被代理的对象 --> <property name="object"> <bean class="cn.zj.spring.service.impl.ServiceImpl"/> </property> <!-- 注入要增强的内容(模拟事务管理器) --> <property name="tr" ref="transactionManagerHandler"></property> </bean> </beans>
测试
package cn.zj.spring.test; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import cn.zj.spring.proxy.Proxy01; import cn.zj.spring.service.Service01; import cn.zj.spring.service.impl.ServiceImpl; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class Test01 { /* * 注入自定义代理类的对象 这个对象会返回一个代理对象方法 */ @Autowired private Proxy01 proxy; @Test public void testName() throws Exception { // 调用 代理类 getProxyObject方法返回代理对象 Service01 proxyObject = proxy.getProxyObject(ServiceImpl.class); System.out.println(proxyObject.getClass().getName()); // 执行代理对象的方法 proxyObject.Update("我爱你"); } @Test public void testName1() throws Exception { // 调用 代理类 getProxyObject方法返回代理对象 Service01 proxyObject = proxy.getProxyObject(ServiceImpl.class); System.out.println(proxyObject.getClass().getName()); proxyObject.insert("hello"); } @Test public void testName2() throws Exception { // 调用 代理类 getProxyObject方法返回代理对象 Service01 proxyObject = proxy.getProxyObject(ServiceImpl.class); proxyObject.toString(); proxyObject.hashCode(); } }
JDK动态代理的不足
1,JDK动态代理的对象必须要实现一个接口;
2,需要为每个对象创建代理对象;
3,动态代理的最小单位是类(所有类中的方法都会被处理),查询方法不需要事务,可能不需要被代理
使用CGLIB 第三方代理
CGLIB(Code Generation Library)是一个开源项目
CGLIB和JDK动态代理一样都是动态代理,但是CGLIB代理没有接口可以进行代理
Spring默认已经集成CGLIB代理,直接可以使用即可,不用拷贝任何jar包
在我们的ServiceImpl的实现类中,使用CGLIB的动态代理,进行方法的增强,增强事物的功能
package cn.zj.spring.service.impl; //public class ServiceImpl implements Service01 { public class ServiceImpl { public void insert(String name) { System.out.println(name + "insert"); } public void Update(String name) { System.out.println(name + "Udpate"); } }
//CGLIB的动态代理原理: package cn.zj.spring.proxy; /* * 此类是最终返回代理对象类 * 此类必须提供一个方法,返回最终返回代理对象 * * 返回代理对象必须需要的资源 * 1,被代理的对象 * 2,要增强内容 * 事务管理器 * */ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import org.springframework.cglib.proxy.Enhancer; import org.springframework.jmx.access.InvocationFailureException; import cn.zj.spring.TransactionManagerHandler; public class Proxy2 { //被代理对象,实际开发应该用Object,通用 //目前场景 注入 cn.zj.spring.service.impl.UserServiceImpl private Object object; //需要增强的内容,此处事务管理器 private TransactionManagerHandler tr; /** * 返回动态代理对象(此处就是ServiceImpl代理对象) * @param clz 被代理的数据类型 * @return 代理对象 * * 方法内部我们使用JDK的动态代理类 Proxy类创建代理对象 */ public <T> T getProxyObject(Class<T> clz) { /* * CGLIB 代理使用的 * Enhancer 类创建代理对象 */ //1.创建 Enhancer对象 Enhancer enhancer = new Enhancer(); //2.设置类加载器 enhancer.setClassLoader(this.getClass().getClassLoader()); //3.设置被代理对象的类型 enhancer.setSuperclass(object.getClass()); //4.设置处理器 enhancer.setCallback(new org.springframework.cglib.proxy.InvocationHandler() { /** * 处理器内部方法,开发者具体做代理增强的地方 * @param obj 代理对象,JDK底层帮忙创建的 * @param method 被代理对象的方法 * @param arg2 被代理对象方法的参数 * @return 返回被代理对象执行方法后的结果 * @throws Throwable */ public Object invoke(Object obj, Method method, Object[] arg2) throws Throwable { /* * 方法增强思路 * 1,通过反射执行被代理对象的方法 * 2,在被代理对象方法执行之前,之后,异常,最终做相关增强(此处是增强模拟事务管理器方法) */ Object invoke=null; try { //开启事务 tr.begin(); //执行被代理对象的方法 invoke = method.invoke(object,arg2); //提交事务 tr.commit(); } catch (Exception e) { e.printStackTrace(); //回滚事务 tr.rollback(); }finally { //关闭session tr.close(); } return invoke; } }); Object create = enhancer.create(); return (T) create; } public void setObject(Object object) { this.object = object; } public void setTr(TransactionManagerHandler tr) { this.tr = tr; } }
package cn.zj.spring.test; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import cn.zj.spring.proxy.Proxy2; import cn.zj.spring.service.Service01; import cn.zj.spring.service.impl.ServiceImpl; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class Test01 { /* * 注入自定义代理类的对象 这个对象会返回一个代理对象方法 */ @Autowired private Proxy2 proxy; @Test public void testName() throws Exception { // 调用 代理类 getProxyObject方法返回代理对象 ServiceImpl proxyObject = proxy.getProxyObject(ServiceImpl.class); System.out.println(proxyObject.getClass().getName()); // 执行代理对象的方法 proxyObject.Update("我爱你"); } @Test public void testName1() throws Exception { // 调用 代理类 getProxyObject方法返回代理对象 ServiceImpl proxyObject = proxy.getProxyObject(ServiceImpl.class); System.out.println(proxyObject.getClass().getName()); proxyObject.insert("hello"); } @Test public void testName2() throws Exception { // 调用 代理类 getProxyObject方法返回代理对象 ServiceImpl proxyObject = proxy.getProxyObject(ServiceImpl.class); proxyObject.toString(); proxyObject.hashCode(); } }
CGLIB代理总结
1,CGLIB可以生成目标类的子类,并重写父类非final修饰符的方法。
2,要求类不能是final的,要代理的方法要是非final、非static、非private的。
3,动态代理的最小单位是类(所有类中的方法都会被处理);
代理小结
解决代码重复的方案
在Spring中:
若目标对象实现了若干接口,Spring就会使用JDK动态代理。
若目标对象没有实现任何接口,Spring就使用CGLIB库生成目标对象的子类。
直接使用代理的缺陷
- 如果直接使用代理解决代码重复问题,我们会发现,我们每一个类都要配置代理类,非常的麻烦
动态代理模式的缺陷是什么
动态代理模式的缺陷是:
- 必须需要实现类必须要有一个接口
- 无法通过规则制定拦截的方法
如何解决这个问题:Spring提供了AOP的实现。
Spring的AOP
Spring通过动态代理模式的实现后,我们可以定义AOP其实就是用于通过规则设置来拦截方法,加入可以统一处理的代码。
关于代理的选择
在spring中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。
AOP相关术语
Joinpoint(连接点):
所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。
---就是根据规则,可以指定拦截的方法,我们将每一个被拦截的方法称为连接点。
Pointcut(切入点):
--所谓的切入点,就是拦截方法设置的规则
所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。
Advice(通知/增强):
--就是可以设置在方法之前拦截或者方法执行之后拦截或者方法出异常后拦截,或者方法之前和之后都拦截。我们将这些拦截场景称为通知
所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。
通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
Aspect(切面):
--所谓的切面就是我们的拦截处理类。
是切入点和通知的结合。
Weaving(织入):
-把切面加入到对象,并创建出代理对象的过程。(该过程由Spring来完成)
基于XML配置AOP
通过XML的方式配置AOP
第一步:加入AOP的支持包
AOP 思想必须使用AspectJ语法,而AOP思想不是Spring框架设计出来的,而是叫一个AOP联盟组织提出这种思想,所以开发者需要导入 AOP联盟提供的 aspectjweaver.jar(aspectweaver织入包)
接口
package cn.zj.spring.service; public interface Service01 { void insert(String name); void Update(String name); }
实现类(被代理)
package cn.zj.spring.service.impl; import cn.zj.spring.service.Service01; //public class ServiceImpl implements Service01 { public class ServiceImpl { public void insert(String name) { System.out.println(name + "insert"); } public void Update(String name) { System.out.println(1/0); System.out.println(name + "Udpate"); } }
配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd "> <!-- 配置事务管理器(模拟) --> <bean id="transactionManagerHandler" class="cn.zj.spring.TransactionManagerHandler" /> <!-- 配置Service --> <bean id="serviceImpl" class="cn.zj.spring.service.impl.ServiceImpl" /> <!-- 配置spring 的aop 有一个www原则 w: what ?干什么 w:where ?地点 w:when ?时机 --> <!-- 开始aop的配置 --> <aop:config> <!-- 切入点 : where expreession : 配置切入点的具体位置,一般都切入到service层 内部有切入点语法 id :唯一标识,以供其他地方使用 * cn.zj.spring.service..*.*(..) --> <aop:pointcut expression="execution(* cn.zj.spring.service..*.*(..))" id="pt"/> <!-- 配置 切面 = 切入点+通知 ref :要增强的具体内容(模拟事务管理器) --> <aop:aspect ref="transactionManagerHandler"> <!-- advice : 通知/增强 前置,后置,异常,最终 --> <!-- 前置增强: method:增强功能方法名 pointcut-ref :切入点引用 --> <aop:before method="begin" pointcut-ref="pt"/> <!-- 后置增强 --> <aop:after-returning method="commit" pointcut-ref="pt"/> <!-- 异常增强 throwing : 抛出的异常,抛出的Throwable 对应的方法必须有Throwable 参数,并且参数的名称必须和 throwing属性的值一样 --> <aop:after-throwing method="rollback" throwing="e" pointcut-ref="pt"/> <!-- 最终增强 --> <aop:after method="close" pointcut-ref="pt"/> <!-- 织入,由spring框架完成 --> </aop:aspect> </aop:config> </beans>
测试
package cn.zj.spring.test; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import cn.zj.spring.service.Service01; import cn.zj.spring.service.impl.ServiceImpl; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class Test01 { /* * 注入自定义代理类的对象 这个对象会返回一个代理对象方法 */ @Autowired private ServiceImpl service01; @Test public void testName() throws Exception { service01.Update("我爱你"); } @Test public void testName1() throws Exception { service01.insert("hello"); } @Test public void testName2() throws Exception { service01.hashCode(); } }
切入点表达式说明
execution:
匹配方法的执行(常用) execution(表达式) 表达式语法:execution([修饰符] 返回值类型 包名.类名.方法名(参数)) 写法说明: 全匹配方式: public void cn.zj.service.impl.CustomerServiceImpl.saveCustomer() 访问修饰符可以省略 void com.zj.service.impl.CustomerServiceImpl.saveCustomer() 返回值可以使用*号,表示任意返回值 * com.zj.service.impl.CustomerServiceImpl.saveCustomer() 包名可以使用*号,表示任意包,但是有几级包,需要写几个* * *.*.*.*.CustomerServiceImpl.saveCustomer() 使用..来表示当前包,及其子包 * com..CustomerServiceImpl.saveCustomer() 类名可以使用*号,表示任意类 * com..*.saveCustomer() 方法名可以使用*号,表示任意方法 * com..*.*() 参数列表可以使用*,表示参数可以是任意数据类型,但是必须有参数 * com..*.*(*) 参数列表可以使用..表示有无参数均可,有参数可以是任意类型 * com..*.*(..) 全通配方式: * *..*.*(..) |
1.1. 常用标签
1.1.1. <aop:config>
作用:
用于声明开始aop的配置
1.1.2. <aop:aspect>
作用:
用于配置切面。
属性:
id:给切面提供一个唯一标识。
ref:引用配置好的通知类bean的id。
1.1.3. <aop:pointcut>
作用:
用于配置切入点表达式
属性:
expression:用于定义切入点表达式。
id:用于给切入点表达式提供一个唯一标识。
1.1.4. <aop:before>
作用:
用于配置前置通知
属性:
method:指定通知中方法的名称。
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的引用
1.1.5. <aop:after-returning>
作用:
用于配置后置通知,如果出了异常就一定不会调用切面的方法
属性:
method:指定通知中方法的名称。
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的引用
1.1.6. <aop:after-throwing>
作用:
用于配置异常通知,只有出了异常才会调用切面对应的方法
属性:
method:指定通知中方法的名称。
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的引用
1.1.7. <aop:after>
作用:
用于配置最终通知,不管出不出异常,调用的切面的方法
属性:
method:指定通知中方法的名称。
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的引用
1.1.8. <aop:around>
作用:
用于配置环绕通知
属性:
method:指定通知中方法的名称。
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的引用
环绕通知
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd "> <!-- 配置事务管理器(模拟) --> <bean id="transactionManagerHandler" class="cn.zj.spring.TransactionManagerHandler" /> <!-- 配置Service --> <bean id="serviceImpl" class="cn.zj.spring.service.impl.ServiceImpl" /> <!-- 配置spring 的aop 有一个www原则 w: what ?干什么 w:where ?地点 w:when ?时机 --> <!-- 开始aop的配置 --> <aop:config> <!-- 切入点 : where expreession : 配置切入点的具体位置,一般都切入到service层 内部有切入点语法 id :唯一标识,以供其他地方使用 * cn.zj.spring.service..*.*(..) --> <aop:pointcut expression="execution(* cn.zj.spring.service..*.*(..))" id="pt"/> <!-- 配置 切面 = 切入点+通知 ref :要增强的具体内容(模拟事务管理器) --> <aop:aspect ref="transactionManagerHandler"> <!-- 环绕通知,一个方法处理全部增强内容 --> <aop:around method="allInOne" pointcut-ref="pt"/> </aop:aspect> </aop:config> </beans>
package cn.zj.spring; import org.aspectj.lang.ProceedingJoinPoint; public class TransactionManagerHandler { public void allInOne(ProceedingJoinPoint pjp) {//连接点的子接口 try { System.out.println("开启事务"); //连接点(JoinPonit)方法执行 pjp.proceed(); System.out.println("提交事务"); } catch (Throwable e) { e.printStackTrace(); System.out.println("回滚事务"); }finally { System.out.println("关闭sesion"); } } }
基于注解配置AOP
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd "> <!-- 配置组件的包扫描位置 --> <context:component-scan base-package="cn.zj.spring"/> <!-- 开启aop注解配置 --> <aop:aspectj-autoproxy/> </beans>
package cn.zj.spring; import java.util.Arrays; 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.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; @Component @Aspect // 声明当前类为 aop 配置类 等价于 <aop:aspect> public class TransactionManagerHandler { //<aop:pointcut expression="execution(* cn.zj.spring.service..*.*(..))" /> @Pointcut("execution(* cn.zj.spring.service..*.*(..))") public void pointcut() {} //<aop:before method="begin" pointcut-ref="pt"/> @Before("pointcut()") public void begin(JoinPoint jp) { // 被代理类型 System.out.println(jp.getTarget()); // 方法参数 System.out.println(Arrays.toString(jp.getArgs())); System.out.println("开启事务"); } // <aop:after-returning method="commit" pointcut-ref="pt"/> @After("pointcut()") public void commit() { System.out.println("提交事务"); } //<aop:after-throwing method="rollback" throwing="e" pointcut-ref="pt"/> @AfterThrowing(value="pointcut()",throwing="e") public void rollback(Throwable e) { // 异常 e.printStackTrace(); System.out.println("异常消息:" + e.getMessage()); System.out.println("回滚事务"); } //<aop:after method="close" pointcut-ref="pt"/> @AfterReturning("pointcut()") public void close() { System.out.println("关闭session"); } public void allInOne(ProceedingJoinPoint pjp) { try { System.out.println("开启事务"); // 连接点(JoinPonit)方法执行 pjp.proceed(); System.out.println("提交事务"); } catch (Throwable e) { e.printStackTrace(); System.out.println("回滚事务"); } finally { System.out.println("关闭sesion"); } } }
环绕增强
package cn.zj.spring; import java.util.Arrays; 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 // 声明当前类为 aop 配置类 等价于 <aop:aspect> public class TransactionManagerHandler { //<aop:pointcut expression="execution(* cn.zj.spring.service..*.*(..))" /> @Pointcut("execution(* cn.zj.spring.service..*.*(..))") public void pointcut() {} //<aop:around method="allInOne" pointcut-ref="pt"/> @Around("pointcut()") public void allInOne(ProceedingJoinPoint pjp) { try { System.out.println("开启事务"); // 连接点(JoinPonit)方法执行 pjp.proceed(); System.out.println("提交事务"); } catch (Throwable e) { e.printStackTrace(); System.out.println("回滚事务"); } finally { System.out.println("关闭sesion"); } } }
小结
今日内容回顾
AOP 面向切面切面编程
为什么要有AOP 编程?
问题:service层在实际开发中要处理事务,日志等操作,这些操作是和业务无关代码,但是又不能少,而且每个方法都有,大量重复,如何把这些代码抽取出去
解决方案:此种场景只能使用Java的动态代理技术解决。为Service层的类创建代理对象。在代理对象中为 各个方法进行增强,使用的时候直接面向代理对象。调用代理对象方法。代理对象底层还是会调用真实对象的方法,但是在 方法执行之前,之后,异常,最终都可以做相应增强
代理模式
Java中有一种设计模式,就是代理模式,讲解就上面问题解决方案,是一种思想
动态代理技术 :实际上就是对代理模式思想的一种具体实现
- JDK动态代理-Java官方
(1) 使用方法
① Object proxy = Proxy.newProxuInstacne(classLoader,interfaces,h)
1) classLoader: 类加载
2) 被代理对象的接口
3) 处理器,开发者具体做增的地方
- 开发需要自己创建匿名内部内
(2) 缺点:只能代理有接口的类
- CGLIB 第三方代理
(1) 使用方法
① Enhancer
(2) 优点:既可以代理有接口的,又可以代理没有接口的类
① 但是不能代理 final 修饰类或者方法
动态代理能解决主要问题:代码重复问题,但是引来新的小问题(所有方法都代理,不能按照指定规则配置代理方法等等)
SpringAOP ,Spring的AOP在代理的基础之上,增加可以 指定规则 配置对应的代理
SpringAOP 开发者不需要写过多的程序主要编写 Spring配置代码
- SpringAOPxml配置
(1) 在xml配置文件中引入新的 aop 命名空间
(2) Springaop xml配置标签
① <aop:config> 开启aop配置,aop所有配置都在此标签内部配置
1) <aop:ponitcut exepression=”切入点语法” id=””> 配置切入点
2) <aop:apect ref=“引入要增强的bean(模拟事务管理器)”> 配置切面
- <aop:before>前置通知
- <aop:aftereturning>后置通知
- <aop:afterthrowing>异常通知
- <aop:after>最终通知
- <aop:around>环绕通知
- SpringAOP注解配置
(1) 在配置文件汇总开启注解配置
① <aop:aspectj-autoproxy/>
(2) @Aspect 在类贴上这个注解,把对应的类当前切面配置类
(3) @Pointcut 切入点注解
(4) @Before 前置通知注解
(5) @After 后置通知注解
(6) @Aftertrhowing 异常通知注解
(7) @Afterreturing 最终通知注解
(8) @Around 环绕通知注解