您的位置:首页 > 编程语言 > Java开发

SpringAOP功能自我调用的解决方案

2015-05-26 09:28 316 查看
使用AOP 代理后的方法调用执行流程,如图所示



也就是说我们首先调用的是AOP代理对象而不是目标对象,首先执行事务切面,事务切面内部通过TransactionInterceptor环绕增强进行事务的增强,即进入目标方法之前开启事务,退出目标方法时提交/回滚事务。

问题

目标对象内部的自我调用将无法实施切面中的增强,如图所示



此处的this指向目标对象,因此调用this.b()将不会执行b事务切面,即不会执行事务增强

解决方案一

1 此处a方法中调用b方法时,只要通过AOP代理调用b方法即可,即可以进行方法拦截,如下所示

判断一个Bean是否是AOP代理对象可以使用如下三种方法:

AopUtils.isAopProxy(bean) : 是否是代理对象;

AopUtils.isCglibProxy(bean) : 是否是CGLIB方式的代理对象;

AopUtils.isJdkDynamicProxy(bean) : 是否是JDK动态代理方式的代理对象;

定义一个接口类AService,该接口类有两个方法 A()和AA()

package com.spring.service;

public interface AService {

public void A();

public void AA();

}

定义一个AServiceImpl实现类,该实现类实现了AService接口,在该接口里面 A() 方法会调用自身的AA()方法,会使用AopContext.currentProxy()得到代理对象,注意是代理对象,而不是自身对象。如果是自身对象再调用方法就不会被拦截了。因为拦截的逻辑只是织入到代理对象中。

package cn.spring.service.impl;

import org.springframework.aop.framework.AopContext;

import org.springframework.beans.BeansException;

import org.springframework.context.ApplicationContext;

import org.springframework.context.ApplicationContextAware;

import com.spring.service.AService;

import com.spring.service.BeanSelfAware;

public class AServiceImpl implements AService{

public void A() {

System.out.println("A()");

((AService) AopContext.currentProxy()).AA();

}

@Override

public void AA() {

System.out.println("AA()");

}

}

定义一个advice,该advice会拦截service包或者子包里面的任何方法。

package cn.spring.advice;

public class Advice {

public void check(){

System.out.println("check()");

}

}

定义一个xml
<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"

xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"

xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

<bean id="aServiceImpl" class="cn.spring.service.impl.AServiceImpl" ></bean>

<bean id="advice" class="cn.spring.advice.Advice"></bean>

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

<aop:aspect ref="advice">

<aop:pointcut expression="execution( * cn.spring.service.impl..*.*(..))" id="checkPoint"/>

<aop:before method="check" pointcut-ref="checkPoint"/>

</aop:aspect>

</aop:config>

</beans>

测试:

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.*;

import com.spring.service.AService;

public class Test {

public static void main(String[] args) {

ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");

AService aService=(AService)context.getBean("aServiceImpl");

aService.A();

}

}

结果:



解决方案二

AServiceImpl 类实现ApplicationContextAware接口。

实现public void setApplicationContext(ApplicationContext applicationContext)throws BeansException
方法。该方法会自动回调,传入上下文对象。得到上下文对象,即可得到该类的代理对象。

public class AServiceImpl implements ApplicationContextAware, AService{

//上下文对象
private ApplicationContext context;
//代理对象

private AService proxyObject;

@Override

public void A() {
System.out.println("A()");

proxyObject.AA();

}

@Override

public void AA() {

System.out.println("AA()");

}

@Override

public void setApplicationContext(ApplicationContext applicationContext)throws BeansException {

System.out.println("setApplicationContext "+applicationContext.hashCode());

context=applicationContext;

self( );

}

public void self( ){

System.out.println("self()");

proxyObject=(AService)context.getBean("aServiceImpl");

}

}

测试代码:
public class Test {

public static void main(String[] args) {

ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");

System.out.println("context "+context.hashCode());

AService aService=(AService)context.getBean("aServiceImpl");

aService.A();

}

测试结果:



解决方案三:

通过BeanPostProcessor 在目标对象中注入代理对象,定义InjectBeanSelfProcessor类,实现BeanPostProcessor , ApplicationContextAware接口,实现public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException 方法和public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException方法,public void setApplicationContext(ApplicationContext applicationContext)throws BeansException 这三个方法会在某个时候回调。

public class InjectBeanSelfProcessor implements BeanPostProcessor , ApplicationContextAware {

private ApplicationContext context;

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

return bean;

}

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

if(!(bean instanceof BeanSelfAware)) { //② 如果Bean没有实现BeanSelfAware标识接口 跳过

return bean;

}

if(AopUtils.isAopProxy(bean)) { //③ 如果当前对象是AOP代理对象,直接注入

((BeanSelfAware) bean).setSelf(bean);

} else {

//④ 如果当前对象不是AOP代理,则通过context.getBean(beanName)获取代理对象并注入

//此种方式不适合解决prototype Bean的代理对象注入

((BeanSelfAware)bean).setSelf(context.getBean(beanName));

}

return bean;

}

@Override

public void setApplicationContext(ApplicationContext applicationContext)throws BeansException {

this.context = applicationContext;

}

}

public class AServiceImpl implements BeanSelfAware, AService{

private ApplicationContext context;

private AService proxyObject;

public void A() {

System.out.println("A()");

proxyObject.AA();

}

@Override

public void AA() {

System.out.println("AA()");

}

public void setSelf(Object proxyBean) {
this.proxyObject = (AService) proxyBean;

}

}

定义xml,要把InjectBeanSelfProcessor 类定义了。

<bean id="aServiceImpl" class="cn.spring.service.impl.AServiceImpl" ></bean>

<bean id="advice" class="cn.spring.advice.Advice"></bean>

<bean id="injectBeanSelfProcessor" class="com.spring.service.InjectBeanSelfProcessor"></bean>

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

<aop:aspect ref="advice">

<aop:pointcut expression="execution( * cn.spring.service.impl..*.*(..))" id="checkPoint"/>

<aop:before method="check" pointcut-ref="checkPoint"/>

</aop:aspect>

</aop:config>

测试代码

public class Test {

public static void main(String[] args) {

ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");

AService aService=(AService)context.getBean("aServiceImpl");

aService.A();

}

}

测试结果



注意测试结果,发现会有两个check()方法,第一个check(),并不是调用A()方法而引起的拦截方法,是代理对象在调用InjectBeanSelfProcessor类的postProcessAfterInitialization方法中调用(BeanSelfAware) bean).setSelf(bean);
这行代码引起的对代理对象的拦截,如果要使其不拦截,修改xml文件,pointcut就可以了. 如下:

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

<aop:aspect ref="advice">

<aop:pointcut expression="execution( * cn.spring.service.impl..*.A*(..))" id="checkPoint"/>

<aop:before method="check" pointcut-ref="checkPoint"/>

</aop:aspect>

</aop:config>

测试结果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐