Spring Aop(十一)——编程式的创建Aop代理之ProxyFactory
转发地址:https://www.iteye.com/blog/elim-2397388
编程式的创建Aop代理之ProxyFactory
Spring Aop是基于代理的,ProxyFactory是Spring Aop内部用来创建Proxy对象的一个工厂类。如果我们需要在程序运行时来动态的应用Spring Aop,则我们可以考虑使用ProxyFactory。使用ProxyFactory时,我们需要为它指定我们需要代理的目标对象、代理时我们需要使用的Advisor或Advice。如下示例就是一个简单的使用ProxyFactory创建MyService对象的代理,同时对其应用了一个MethodBeforeAdvice,即每次调用代理对象的方法时都将先调用MethodBeforeAdvice的before方法。
@Test
public void testProxyFactory() {
MyService myService = new MyService();
ProxyFactory proxyFactory = new ProxyFactory(myService);
proxyFactory.addAdvice(new MethodBeforeAdvice() { @Override
public void before(Method method, Object[] args,
Object target) throws Throwable {
System.out.println("执行目标方法调用之前的逻辑");
//不需要手动去调用目标方法,
//Spring内置逻辑里面会调用目标方法
} });;
MyService proxy = (MyService) proxyFactory.getProxy();
proxy.add();
}
指定被代理对象
ProxyFactory有多个重载的构造函数,上面示例中笔者用的是指定被代理对象的构造函数,如果我们应用的是其它构造函数,则可以通过ProxyFactory的setTarget(Object)方法来指定被代理对象。如果我们没有指定被代理对象的Class,那么默认创建出来的代理对象是我们传递的被代理对象的类型,即获取的是targetObject.getClass()类型。如果我们的被代理对象的类型是包含多个接口实现或父类型的,而我们只希望代理其中的某一个类型时,我们可以通过ProxyFactory的setTargetClass(Class)来指定创建的代理对象是基于哪个Class的。默认情况下,ProxyFactory会根据实际情况选择创建的代理对象是基于JDK代理的还是基于CBLIB代理的,即目标对象拥有接口实现且没有设置proxyTargetClass="true"或者指定的targetClass是一个接口的时候将采用JDK代理,否则将采用CGLIB代理。也就是说即算是你通过ProxyFactory.setProxyTargetClass(true)指定了将会建立基于Class的CGLIB代理,最终也不一定是CGLIB代理,因为这种情况下如果targetClass是一个接口也将建立JDK代理。这块的逻辑是由DefaultAopProxyFactory的createAopProxy()方法实现的,其源码如下。
public AopProxy createAopProxy(AdvisedSupport config)
throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass()
|| hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface()) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
ProxyFactory底层在创建代理对象的时候实际上是会委托给AopProxyFactory对象的,AopProxyFactory是一个接口,其只定义了一个createAopProxy()方法,Spring提供了一个默认实现,DefaultAopProxyFactory。ProxyFactory中使用的就是DefaultAopProxyFactory,有兴趣的朋友可以参考一下ProxyFactory的源代码。
指定Advisor
使用Aop时我们是需要对拦截的方法做一些处理的,对于Spring Aop来讲,需要对哪些方法调用做什么样的处理是通过Advisor来定义的,通常是一个PointcutAdvisor。PointcutAdvisor接口中包含主要有两个接口方法,一个用来获取Pointcut,一个用来获取Advice对象,它俩的组合就构成了需要在哪个Pointcut应用哪个Advice。所以有需要的时候我们也可以实现自己的Advisor实现。
/**
* 简单的实现自己的PointcutAdvisor
* @author Elim 2017年5月9日
*/
public class MyAdvisor implements PointcutAdvisor { @Override
public Advice getAdvice() {
return new MethodBeforeAdvice() { @Override
public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("BeforeAdvice实现,在目标方法被调用前调用,目标方法是:" + method.getDeclaringClass().getName() + "."
+ method.getName());
}
};
} @Override
public boolean isPerInstance() {
return true;
} @Override
public Pointcut getPointcut() {
//匹配所有的方法调用
return Pointcut.TRUE;
} }
@Test
public void testProxyFactory2() {
MyService myService = new MyService();
ProxyFactory proxyFactory = new ProxyFactory(myService);
proxyFactory.addAdvisor(new MyAdvisor());
MyService proxy = (MyService) proxyFactory.getProxy();
proxy.add();
}
上述示例就是一个指定代理对象对应的Advisor的示例。其实一个代理对象可以同时绑定多个Advisor对象,ProxyFactory的addAdvisor()方法可多次被调用,且该方法还有一些重载的方法定义,可以参数Spring的API文档。
指定Advice
我们的第一个示例指定的代理对象绑定的是一个Advice,而第二个示例指定的Advisor,对此你会不会有什么疑问呢?依据我们对Spring Aop的了解,Spring的Aop代理对象绑定的就一定是一个Advisor,而且通常是一个PointcutAdvisor,通过它我们可以知道我们的Advice究竟是要应用到哪个Pointcut(哪个方法调用)?当我们通过ProxyFactory在创建代理对象时绑定的是一个Advice对象时,实际上ProxyFactory内部还是为我们转换为了一个Advisor对象的,只是该Advisor对象对应的Pointcut是一个匹配所有方法调用的Pointcut实例。
指定是否需要发布代理对象
在调用Aop代理对象的方法时,默认情况下我们是不能访问到当前的代理对象的,如果我们指定了创建的代理对象需要对外发布代理对象,那么在调用代理对象的方法时Spring会把当前的代理对象存入AopContext中,我们就可以在目标对象的方法中通过AopContext中获取到当前的代理对象了。这是通过exposeProxy属性来指定的,如果我们希望对外发布代理对象,我们可以通过exposeProxy的set方法来指定该属性的值为true。如:
@Test
public void testProxyFactory2() {
MyService myService = new MyService();
ProxyFactory proxyFactory = new ProxyFactory(myService);
proxyFactory.setExposeProxy(true);//指定对外发布代理对象,即在目标对象方法中可以通过AopContext.currentProxy()访问当前代理对象。
proxyFactory.addAdvisor(new MyAdvisor());
proxyFactory.addAdvisor(new MyAdvisor());//多次指定Advisor将同时应用多个Advisor
MyService proxy = (MyService) proxyFactory.getProxy();
proxy.add();
}
除了上述配置信息以外,ProxyFactory其实还可以配置很多其它的信息,更多的配置信息项请参考ProxyFactory的源代码或参考Spring API文档。
参考文档
- Spring4.1.0官方文档
- Spring源代码
(本文是基于Spring4.1.0所写,Elim写于2017年5月9日)
Spring Aop(十一)——编程式的创建Aop代理之ProxyFactory的更多相关文章
- Spring Aop(十二)——编程式的创建Aop代理之AspectjProxyFactory
转发地址:https://www.iteye.com/blog/elim-2397922 编程式的创建Aop代理之AspectjProxyFactory 之前已经介绍了一款编程式的创建Aop代理的工厂 ...
- Spring通知类型及使用ProxyFactoryBean创建AOP代理
Spring 通知类型 通过前面的学习可以知道,通知(Advice)其实就是对目标切入点进行增强的内容,Spring AOP 为通知(Advice)提供了 org.aopalliance.aop.Ad ...
- 吴裕雄--天生自然JAVA SPRING框架开发学习笔记:Spring通知类型及使用ProxyFactoryBean创建AOP代理
通知(Advice)其实就是对目标切入点进行增强的内容,Spring AOP 为通知(Advice)提供了 org.aopalliance.aop.Advice 接口. Spring 通知按照在目标类 ...
- spring事务管理——编程式事务、声明式事务
本教程将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. 先决条件 本教程假定您已经掌握了 ...
- 使用docx4j编程式地创建复杂的Word(.docx)文档
原文链接:Create complex Word (.docx) documents programatically with docx4j 原文作者:jos.dirksen 发表日期:2012年2月 ...
- Spring Aop(十三)——ProxyFactoryBean创建代理对象
转发地址:https://www.iteye.com/blog/elim-2398673 ProxyFactoryBean创建代理对象 ProxyFactoryBean实现了Spring的Factor ...
- Spring框架——事务处理(编程式和声明式)
一. 事务概述 ●在JavaEE企业级开发的应用领域,为了保证数据的完整性和一致性,必须引入数据库事务的概念,所以事务管理是企业级应用程序开发中必不可少的技术. ●事务就是一组由于逻辑上紧密关联而合 ...
- 深入理解TransactionTemplate编程式事务
Spring可以支持编程式事务和声明式事务. Spring提供的最原始的事务管理方式是基于TransactionDefinition.PlatformTransactionManager.Transa ...
- Spring Aop(十六)——编程式的自定义Advisor
转发:https://www.iteye.com/blog/elim-2399437 https://www.iteye.com/blogs/subjects/springaop 编程式的自定义Adv ...
随机推荐
- socket 实现一个类似简单聊天室的功能(多客户端互相通信)
server端: #coding=utf-8 ''' 一个广播程序,linux运行 ''' import select,socket import traceback def broadcast(co ...
- 多域名解析到同一网站C的php重定向代码
在index.php最前面加上以下代码: <?php if(strpos($_SERVER['HTTP_HOST'],'afish.cnblogs.com')===false){ #header ...
- python_公共方法
1.计算长度 value = "wangdianchao" # 计算字符个数(长度) number = len(value) print(number) 2.索引取值 value ...
- [2019HDU多校第一场][HDU 6580][C. Milk]
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6580 题目大意:\(n\times m\)大小的方格上有\(k\)瓶水,喝完每瓶水都需要一定的时间.初 ...
- BZOJ 3275: Number (二分图最小割)
题意 有nnn个数,其中同时满足下面两个条件的数对不能同时选,求选出一些数让和最大. 若两个数aaa,bbb同时满足以下条件,则aaa,bbb不能同时被选 存在正整数ccc,使a∗a+b∗b=c∗ca ...
- SQL的左连接与右链接
1.准备工作 Oracle 外连接(OUTER JOIN)包括以下: 左外连接(左边的表不加限制) 右外连接(右边的表不加限制) 全外连接(左右两表都不加限制) 对应SQL:LEFT/RIGHT/F ...
- python--openCV--鼠标事件
import cv2 import numpy as np cv2.namedWindow("new") def drawxxx(event,x,y,flags,param): # ...
- click([[data],fn]) 触发每一个匹配元素的click事件。
click([[data],fn]) 概述 触发每一个匹配元素的click事件. 这个函数会调用执行绑定到click事件的所有函数.大理石平台精度等级 参数 fnFunctionV1.0 在每一个匹配 ...
- 001_UCOSIII引入原因
(一)在前年的学习中一直都是在裸机上跑程序的,根本没有用到操作系统,但是因为以前开发的东西很小,根本用不着,也没必要在操作系统上跑,所以就没有学习.在未来的几天中将进军UCOSIII.为什么选择UCO ...
- Codeforces.520B.Two Buttons(正难则反)
题目链接 \(Description\) 给定两个数\(n,m\),每次可以使\(n\)减一或使\(n\)乘2.求最少需要多少次可以使\(n\)等于\(m\). \(Solution\) 暴力连边BF ...