文档章节

Spring AOP源码分析(七)ProxyFactoryBean介绍

乒乓狂魔
 乒乓狂魔
发布于 2015/02/07 10:22
字数 1272
阅读 377
收藏 0
这篇文章里面就要说说Spring自己的AOP,搞清楚哪种方式是Spring自己实现的AOP,哪种方式是Spring引入aspectj的AOP。
Spring自己的AOP实现在于ProxyFactoryBean。先看下使用案例(仍和之前的案例是一样的):接口AService、实现类AServiceImpl、通知MyBeforeAdvice

public interface AService {

	public void fooA(String _msg);  
	  
    public void barA(); 
}

public class AServiceImpl implements AService{

	@Override
	public void fooA(String _msg) {
		 System.out.println("AServiceImpl.fooA(msg:"+_msg+")");
	}

	@Override
	public void barA() {
		 System.out.println("AServiceImpl.barA()");  
	}

}

public class MyBeforeAdvice implements MethodBeforeAdvice{

	@Override
	public void before(Method method, Object[] args, Object target)
			throws Throwable {
		System.out.println("run my before advice");
	}

}

然后就是xml的配置:
<bean id="aServiceImpl" class="com.lg.aop.service.impl.AServiceImpl"/>
	<bean id="myBeforAdvice" class="com.lg.aop.MyBeforeAdvice"/>
	
	<bean class="org.springframework.aop.framework.ProxyFactoryBean">
		<property name="interfaces" value="com.lg.aop.service.AService"/>
		<property name="target">
			<ref bean="aServiceImpl"/>
		</property>
		 <property name="interceptorNames">  
            <list>  
                <value>myBeforAdvice</value>  
            </list>  
        </property>  
	</bean>

然后就可以使用了:
@Autowired
	private AService aService;
	
	@Test
	public void testAOP(){
		aService.barA();
	}

运行这个单元测试,然后你就会看到报如下错误:
No qualifying bean of type [com.lg.aop.service.AService] is defined: expected single matching bean but found 2: aServiceImpl,org.springframework.aop.framework.ProxyFactoryBean#0

原因就是对于接口AService,有两个实现类aServiceImpl和ProxyFactoryBean所生产的代理类。所以我们不能使用@Autowired(它是按类型注入),所以要使用按名称注入,我们怎么获取ProxyFactoryBean所产生的代理类的名称呢?其实就是ProxyFactoryBean配置的名称。因为ProxyFactoryBean实现了FactoryBean接口,对于这种接口从容器中获取该bean,不是获取的本身而是获取他的getObject方法所返回的值,看FactoryBean的文档:
/**
 * Interface to be implemented by objects used within a {@link BeanFactory}
 * which are themselves factories. If a bean implements this interface,
 * it is used as a factory for an object to expose, not directly as a bean
 * instance that will be exposed itself.
 *
 * <p><b>NB: A bean that implements this interface cannot be used as a
 * normal bean.</b> A FactoryBean is defined in a bean style, but the
 * object exposed for bean references ({@link #getObject()} is always
 * the object that it creates.

所以通过beanName找到了ProxyFactoryBean,然而不是返回该对象,而是返回他的getObject方法的返回值,所以我们通过ProxyFactoryBean的id就可以获取到它所产生的代理对象,所以更改如下:
<bean  id="aServiceImplProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
其他略

在使用注入的时候按名称注入
@Resource(name="aServiceImplProxy")
	private AService aService;

然后就可以正常运行了如下:
run my before advice
AServiceImpl.barA()

然后我们就要源码分析下这一过程,先看下是如何产生代理对象的,在ProxyFactoryBean的getObject方法中:
public Object getObject() throws BeansException {
//重点1
		initializeAdvisorChain();
		if (isSingleton()) {
//重点2
			return getSingletonInstance();
		}
		else {
			if (this.targetName == null) {
				logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
						"Enable prototype proxies by setting the 'targetName' property.");
			}
			return newPrototypeInstance();
		}
	}

重点1:就是根据我们配置的interceptorNames来获取对应的bean,并却转化成Advisor。如下:
private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
		if (this.advisorChainInitialized) {
			return;
		}

		if (!ObjectUtils.isEmpty(this.interceptorNames)) {
			if (this.beanFactory == null) {
				throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
						"- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));
			}

			// Globals can't be last unless we specified a targetSource using the property...
			if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX) &&
					this.targetName == null && this.targetSource == EMPTY_TARGET_SOURCE) {
				throw new AopConfigException("Target required after globals");
			}

			// Materialize interceptor chain from bean names.
			for (String name : this.interceptorNames) {
				if (logger.isTraceEnabled()) {
					logger.trace("Configuring advisor or advice '" + name + "'");
				}

				if (name.endsWith(GLOBAL_SUFFIX)) {
					if (!(this.beanFactory instanceof ListableBeanFactory)) {
						throw new AopConfigException(
								"Can only use global advisors or interceptors with a ListableBeanFactory");
					}
					addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
							name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
				}

				else {
					// If we get here, we need to add a named interceptor.
					// We must check if it's a singleton or prototype.
					Object advice;
					if (this.singleton || this.beanFactory.isSingleton(name)) {
						// Add the real Advisor/Advice to the chain.
						advice = this.beanFactory.getBean(name);
					}
					else {
						// It's a prototype Advice or Advisor: replace with a prototype.
						// Avoid unnecessary creation of prototype bean just for advisor chain initialization.
						advice = new PrototypePlaceholderAdvisor(name);
					}
					addAdvisorOnChainCreation(advice, name);
				}
			}
		}

		this.advisorChainInitialized = true;
	}

this.advisorChainInitialized:标示是否已进行过初始化,若以初始化则不再进行初始化。然后就是将interceptorNames转化成Advisor。根据interceptorNames所包含的字符串到容器中进行查找,如果含有*则,则表示进行一定的匹配,符合的都会纳入。如官方文档中说的:
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target" ref="service"/>
    <property name="interceptorNames">
        <list>
            <value>global*</value>
        </list>
    </property>
</bean>

<bean id="global_debug" class="org.springframework.aop.interceptor.DebugInterceptor"/>
<bean id="global_performance" class="org.springframework.aop.interceptor.PerformanceMonitorInterceptor"/>

这中间页经过了Advice到Advisor的转换,如下:
private void addAdvisorOnChainCreation(Object next, String name) {
		// We need to convert to an Advisor if necessary so that our source reference
		// matches what we find from superclass interceptors.
		Advisor advisor = namedBeanToAdvisor(next);
		if (logger.isTraceEnabled()) {
			logger.trace("Adding advisor with name '" + name + "'");
		}
		addAdvisor(advisor);
	}

private Advisor namedBeanToAdvisor(Object next) {
		try {
			return this.advisorAdapterRegistry.wrap(next);
		}
		}
	}

public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
		if (adviceObject instanceof Advisor) {
			return (Advisor) adviceObject;
		}
		if (!(adviceObject instanceof Advice)) {
			throw new UnknownAdviceTypeException(adviceObject);
		}
		Advice advice = (Advice) adviceObject;
		if (advice instanceof MethodInterceptor) {
			// So well-known it doesn't even need an adapter.
			return new DefaultPointcutAdvisor(advice);
		}
		for (AdvisorAdapter adapter : this.adapters) {
			// Check that it is supported.
			if (adapter.supportsAdvice(advice)) {
				return new DefaultPointcutAdvisor(advice);
			}
		}
		throw new UnknownAdviceTypeException(advice);
	}

这个包裹过程已经见过很多遍了,采用了适配器的模式。
之后又是和其他的AOP方式接轨了,设置一些列要实现的接口和参数,使用DefaultAopProxyFactory先创建出AopProxy,要么是JdkDynamicAopProxy,要么是CglibAopProxy,然后就可以调用AopProxy的getProxy方法来获取代理对象了。这个过程详见上一篇博客http://lgbolgger.iteye.com/blog/2119810

这种方式实现的AOP还是比较麻烦的,同时配置一个ProxyFactoryBean仅能实现对一个目标对象的拦截,要想拦截多个目标对象,需要配置多个ProxyFactoryBean。所以大部分还是使用Spring引进的aspectj的AOP方式来进行AOP编程。

若想转载请注明出处
作者:乒乓狂魔

© 著作权归作者所有

共有 人打赏支持
乒乓狂魔
粉丝 994
博文 105
码字总数 271356
作品 0
长宁
程序员
Spring AOP介绍及源码分析

一、AOP介绍 举个例子来说明一下吧!现在系统中有很多的业务方法,如上传产品信息、修改产品信息、发布公司库等;现在需要对这些方法的执行做性能监控,看每个业务方法的执行时间;在不改变原...

阿扬丶
2013/11/07
0
3
深入解析Spring架构与设计原理-AOP

关于AOP的个人理解 AOP联盟定义的AOP体系结构把与AOP相关的概念大致分为了由高到低、从使用到实现的三个层次。关于这个体系结构,个人的理解是这样的,从上往下,最高层是语言和开发环境,在...

Java小铺
08/27
0
0
Spring3.2 AOP 分析

Spring3.2 AOP个人分析: AOP, 即Aspect-Oriented-Programming, 面向切面编程, 又一Spring的核心,被广泛应用,如在Spring-tx中都有用到,其好处是实现松耦合的外围处理程序,先说些理论吧。...

ihaolin
2014/01/17
0
0
聊一聊 AOP:Advice 源码解析

在第一篇中的例子和概念介绍中我们对 Advice 有了一个初步的认知。在Spring AOP中,Advice的作用就是用来描述 Spring AOP 围绕方法调用而注入的切面行为。 本篇文章将从源码的角度来看一看 ...

glmapper
06/23
0
0
用ProxyFactoryBean创建AOP代理

用ProxyFactoryBean创建AOP代理 使用Spring提供的类org.springframework.aop.framework.ProxyFactoryBean是创建AOP的最基本的方式 。 使用 ProxyFactoryBean 来创建 AOP 代理的最重要的优点之...

嘻哈开发者
2015/07/02
0
0

没有更多内容

加载失败,请刷新页面

加载更多

如何通过 J2Cache 实现分布式 session 存储

做 Java Web 开发的人多数都会需要使用到 session (会话),我们使用 session 来保存一些需要在两个不同的请求之间共享数据。一般 Java 的 Web 容器像 Tomcat、Resin、Jetty 等等,它们会在...

红薯
36分钟前
1
0
C++ std::thread

C++11提供了std::thread类来表示一个多线程对象。 1,首先介绍一下std::this_thread命名空间: (1)std::this_thread::get_id():返回当前线程id (2)std::this_thread::yield():用户接口...

yepanl
今天
2
0
Nignx缓存文件与动态文件自动均衡的配置

下面这段nginx的配置脚本的作用是,自动判断是否存在缓存文件,如果有优先输出缓存文件,不经过php,如果没有,则回到php去处理,同时生成缓存文件。 PHP框架是ThinkPHP,最后一个rewrite有关...

swingcoder
今天
1
0
20180920 usermod命令与用户密码管理

命令 usermod usermod 命令的选项和 useradd 差不多。 一个用户可以属于多个组,但是gid只有一个;除了gid,其他的组(groups)叫做扩展组。 usermod -u 1010 username # 更改用户idusermod ...

野雪球
今天
1
0
Java网络编程基础

1. 简单了解网络通信协议TCP/IP网络模型相关名词 应用层(HTTP,FTP,DNS等) 传输层(TCP,UDP) 网络层(IP,ICMP等) 链路层(驱动程序,接口等) 链路层:用于定义物理传输通道,通常是对...

江左煤郎
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部