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

来源:互联网 发布:淘宝宝贝介绍图片尺寸 编辑:程序博客网 时间:2024/06/05 17:40

使用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>



测试结果:




1 0
原创粉丝点击