Spring AOP自调用

1.问题描述

目标对象内部的函数自我调用时被调函数将忽略切面。

1)实例

SelfInvokable.java

public interface SelfInvokable {
	
	void invoke();
	
	void method();
	
}

SelfInvoker.java

public class SelfInvoker implements SelfInvokable {

	public void invoke() {
		System.out.println("in SelfInvoker.invoke...");
	}

	public void method() {
		System.out.println("in SelfInvoker.method...");
		this.invoke();
	}

}

SelfInvoker2.java

public class SelfInvoker2 {

	public void invoke() {
		System.out.println("in SelfInvoker2.invoke...");
	}

	public void method() {
		System.out.println("in SelfInvoker2.method...");
		this.invoke();
	}
	
}

spring-aop.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-3.0.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    
    <bean id="selfInvoker" class="com.siyuan.study.spring.aop.SelfInvoker"/>
    
    <bean id="selfInvoker2" class="com.siyuan.study.spring.aop.SelfInvoker2"/>
    
    <bean id="beforeAdvisor" class="com.siyuan.study.spring.aop.advisor.BeforeAdvisor"/>
    
    <aop:config>
        <aop:pointcut id="selfInvokerPointcut" expression="execution(* com.siyuan.study.spring.aop.*.*(..))" />
        <aop:advisor advice-ref="beforeAdvisor" pointcut-ref="selfInvokerPointcut"/>
    </aop:config>
    
</beans>

SelfInvokerAOPTest.java

public class SelfInvokerAOPTest {
	
	private ApplicationContext ctxt;
	
	@Before
	public void setup() {
		ctxt = new ClassPathXmlApplicationContext("spring-aop.xml");
	}
	
	@Test
	public void testSelfInvokable() {
		SelfInvokable selfInvoker = (SelfInvokable) ctxt.getBean("selfInvoker");
		System.out.println("JDK proxy : " + AopUtils.isJdkDynamicProxy(selfInvoker));
		selfInvoker.invoke();
		selfInvoker.method();
	}
	
	@Test
	public void testSelfInvokable2() {
		SelfInvoker2 selfInvoker2 = (SelfInvoker2) ctxt.getBean("selfInvoker2");
		System.out.println("CGLIB proxy : " + AopUtils.isCglibProxy(selfInvoker2));
		selfInvoker2.invoke();
		selfInvoker2.method();
	}
	
}

执行结果:

JDK proxy : true
before invoke
in SelfInvoker.invoke...
before method
in SelfInvoker.method...
in SelfInvoker.invoke...
CGLIB proxy : true
before invoke
in SelfInvoker2.invoke...
before method
in SelfInvoker2.method...
in SelfInvoker2.invoke...

2)分析:

Spring AOP实现代理有两种方式

--JDK:目标对象有实现接口时,仅对目标对象实现接口中的方法进行拦截,接口中未包含的方法将不进行拦截。

--CGLIB:目标对象未实现任何接口时。

从执行结果来可看出,两种不同方式获得的代理在函数自我调用时被调用函数均忽略了切面。

2.修复

1)AopContext获取代理对象,实现原理ThreadLocal

SelfInvokable.java

public interface SelfInvokable {
	
	...
	
	void methodFix();
	
}

SelfInvoker.java

public class SelfInvoker implements SelfInvokable {

	...

	public void methodFix() {
		System.out.println("in SelfInvoker.methodFix...");
		((SelfInvokable) AopContext.currentProxy()).invoke();
	}
	
}

SelfInvoker2.java

public class SelfInvoker2 {

	...
	
	public void methodFix() {
		System.out.println("in SelfInvoker2.methodFix...");
		((SelfInvoker2) AopContext.currentProxy()).invoke();
	}
	
}

spring-aop.xml

...
    <aop:config expose-proxy="true">
...

SelfInvokerAOPTest.java

@Test
public void testSelfInvokable() {
	...
	selfInvoker.methodFix();
}

@Test
public void testSelfInvokable2() {
	...
	selfInvoker2.methodFix();
}

执行结果

JDK proxy : true
before invoke
in SelfInvoker.invoke...
before method
in SelfInvoker.method...
in SelfInvoker.invoke...
before methodFix
in SelfInvoker.methodFix...
before invoke
in SelfInvoker.invoke...
CGLIB proxy : true
before invoke
in SelfInvoker2.invoke...
before method
in SelfInvoker2.method...
in SelfInvoker2.invoke...
before methodFix
in SelfInvoker2.methodFix...
before invoke
in SelfInvoker2.invoke...

2)BeanPostProcessor+BeanSelfAware(标记需要自调用的接口)

BeanSelfAware.java

public interface BeanSelfAware {
	
	void setSelf(Object self);
	
}

SelfInvoker.java

...
	public void methodFix() {
		System.out.println("in SelfInvoker.methodFix...");
		//((SelfInvokable) AopContext.currentProxy()).invoke();
		self.invoke();
	}
	
	private SelfInvokable self;
	
	public void setSelf(Object self) {
		this.self = (SelfInvokable) self;
	}
...

InjectBeanSelfProcessor.java

public class InjectBeanSelfProcessor implements BeanPostProcessor {

	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {
		if(bean instanceof BeanSelfAware) {
			BeanSelfAware myBean = (BeanSelfAware)bean;
			myBean.setSelf(bean);
			return myBean;
		}
		return bean;
	}

	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		return bean;
	}

}

spring-aop.xml

...
<bean class="com.siyuan.study.spring.extension.InjectBeanSelfProcessor"/>
...

SelfInvokerAOPTest.java

...
	@Test
	public void testSelfInvokable() {
		SelfInvokable selfInvoker = (SelfInvokable) ctxt.getBean("selfInvoker");
		//System.out.println("JDK proxy : " + AopUtils.isJdkDynamicProxy(selfInvoker));
		//selfInvoker.invoke();
		//selfInvoker.method();
		selfInvoker.methodFix();
	}
...

运行结果

before setSelf
before methodFix
in SelfInvoker.methodFix...
before invoke
in SelfInvoker.invoke...
before setSelf

3.参考资料

http://blog.163.com/yf_198407/blog/static/513854112012621105114276/

猜你喜欢

转载自siyuan-zhu.iteye.com/blog/2059422