Spring AOP下嵌套一个代理

参考http://fyting.iteye.com/blog/109236总结整理。

Spring AOP的基本原理是使用代理实现切面,但是由于当前的Spring AOP(使用的Spring 3.2.9)不支持嵌套AOP的情况。话不多说,直接进入主题。

把代码贴上:

 

package spring.nest.aop;
public interface SomeService {  
    void someMethod();  
    void someInnerMethod();  
} 

 

package spring.nest.aop;

import org.springframework.context.ApplicationContext;

public class SomeServiceImpl implements SomeService, BeanSelfAware
{
	
	private SomeService someService;

	protected static ApplicationContext ctx;

	@RedisTransactional
	public void someMethod()
	{
		//使同一个代理对象可以支持嵌套的方法代理。
		someService.someInnerMethod();
		System.out.println("someMethod");
	}

	public void someInnerMethod()
	{
		System.out.println("someInnerMethod");
	}

	public void setSelf(Object proxyBean)
	{
		this.someService = (SomeService) proxyBean;
	}

}

 

 

 

package com.aa.redis.transaction.proxy;

/**
 * 使同一个bean在同一个代理对象下,可以切面两次
 * 
 * @author Zhongqing.Lin
 * @version 1.0.0
 * @since 2014年8月22日 上午9:28:51
 */
public interface BeanSelfAware
{

	void setSelf(Object proxyBean);
}

 

package com.aa.redis.transaction.proxy;

import java.lang.reflect.Method;

import com.zk.redis.transaction.annotation.RedisTransactional;

/**
 * 用于检查是否添加了@RedisTransactional标识
 * 
 * @author Zhongqing.Lin
 * @version 1.0.0
 * @since 2014年8月22日 上午9:32:36
 */
public class RedisTransactionalBeanUtils
{
	private RedisTransactionalBeanUtils()
	{

	}

	/**
	 * 检查是否添加了@RedisTransactional标识
	 * 
	 * @since 2014年8月21日 下午2:23:06
	 * @param bean bean
	 * @return true表示添加@RedisTransactional标识; false表示未添加@RedisTransactional标识;
	 */
	public static boolean checkRedisTransactionalBean(Object bean)
	{
		boolean bool = false;
		Method[] methods = bean.getClass().getDeclaredMethods();
		if (null != methods)
		{
			for (Method method : methods)
			{
				if (method.isAnnotationPresent(RedisTransactional.class))
				{
					bool = true;
					break;
				}
			}
		}
		return bool;
	}

}

 

package com.aa.redis.transaction.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 使用Jdk的proxy实现代理
 * 
 * @author Zhongqing.Lin
 * @version 1.0.0
 * @param 
 * @since 2014年8月21日 下午1:47:38
 */
public class RedisTransactionalJdkProxy implements RedisTransactionalProxy, InvocationHandler
{
	// 目标对象   
	private T target;

	public T getTarget()
	{
		return this.target;
	}

	/**
	 * 构造方法
	 * 
	 * @param target 目标对象
	 */
	public RedisTransactionalJdkProxy(T target)
	{
		super();
		this.target = target;
	}

	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
	{
		// 在目标对象的方法执行之前简单的打印一下  
		System.out.println("------------------before  "+method.getName()+" ------------------");

		// 执行目标对象的方法  
		Object result = method.invoke(target, args);

		// 在目标对象的方法执行之后简单的打印一下  
		System.out.println("-------------------after  "+method.getName()+" ------------------");

		return result;
	}

	@SuppressWarnings("unchecked")
	public T getProxy()
	{
		return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
	}

}

 

package com.aa.redis.transaction.proxy;

/**
 * 代理对象接口
 * 
 * @author Zhongqing.Lin
 * @version 1.0.0
 * @param  被代理的原始类型
 * @since 2014年8月22日 上午9:33:27
 */
public interface RedisTransactionalProxy
{

	T getTarget();

}

 

package com.aa.redis.transaction.springframework.context.support;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

import com.zk.redis.transaction.proxy.BeanSelfAware;
import com.zk.redis.transaction.proxy.RedisTransactionalBeanUtils;
import com.zk.redis.transaction.proxy.RedisTransactionalJdkProxy;

/**
 * 添加Redis事务代理对象的bean处理器
 * 
 * @author Zhongqing.Lin
 * @version 1.0.0
 * @since 2014年8月22日 上午9:29:51
 */
public class RedisTransactionalBeanProcessor implements BeanPostProcessor
{

	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException
	{
		//如果实现
		if (bean instanceof BeanSelfAware)
		{
			((BeanSelfAware) bean).setSelf(bean);
		}
		if (RedisTransactionalBeanUtils.checkRedisTransactionalBean(bean))
		{
			System.out.println("inject proxy:" + bean.getClass());
			RedisTransactionalJdkProxy proxy = new RedisTransactionalJdkProxy(bean);
			return proxy.getProxy();
		}
		return bean;
	}

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

}

 

package com.aa.redis.transaction.springframework.context.support;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * 为了扩展支持redis事务,又不让Redis事物不受到其他AOP影响,必须使用此增强类
 * 
 * @author Zhongqing.Lin
 * @version 1.0.0
 * @since 2014年8月22日 上午9:26:35
 */
public class ClassPathXmlApplicationContextRedisTransactional extends ClassPathXmlApplicationContext
{

	public ClassPathXmlApplicationContextRedisTransactional()
	{
	}

	/**
	 * Create a new ClassPathXmlApplicationContext for bean-style configuration.
	 * 
	 * @param parent the parent context
	 * @see #setConfigLocation
	 * @see #setConfigLocations
	 * @see #afterPropertiesSet()
	 */
	public ClassPathXmlApplicationContextRedisTransactional(ApplicationContext parent)
	{
		super(parent);
	}

	/**
	 * Create a new ClassPathXmlApplicationContext, loading the definitions
	 * from the given XML file and automatically refreshing the context.
	 * 
	 * @param configLocation resource location
	 * @throws BeansException if context creation failed
	 */
	public ClassPathXmlApplicationContextRedisTransactional(String configLocation) throws BeansException
	{
		this(new String[] {configLocation}, true, null);
	}

	/**
	 * Create a new ClassPathXmlApplicationContext, loading the definitions
	 * from the given XML files and automatically refreshing the context.
	 * 
	 * @param configLocations array of resource locations
	 * @throws BeansException if context creation failed
	 */
	public ClassPathXmlApplicationContextRedisTransactional(String... configLocations) throws BeansException
	{
		this(configLocations, true, null);
	}

	/**
	 * Create a new ClassPathXmlApplicationContext with the given parent,
	 * loading the definitions from the given XML files and automatically
	 * refreshing the context.
	 * 
	 * @param configLocations array of resource locations
	 * @param parent the parent context
	 * @throws BeansException if context creation failed
	 */
	public ClassPathXmlApplicationContextRedisTransactional(String[] configLocations, ApplicationContext parent) throws BeansException
	{
		this(configLocations, true, parent);
	}

	/**
	 * Create a new ClassPathXmlApplicationContext, loading the definitions
	 * from the given XML files.
	 * 
	 * @param configLocations array of resource locations
	 * @param refresh whether to automatically refresh the context,
	 * loading all bean definitions and creating all singletons.
	 * Alternatively, call refresh manually after further configuring the context.
	 * @throws BeansException if context creation failed
	 * @see #refresh()
	 */
	public ClassPathXmlApplicationContextRedisTransactional(String[] configLocations, boolean refresh) throws BeansException
	{
		this(configLocations, refresh, null);
	}

	/**
	 * Create a new ClassPathXmlApplicationContext with the given parent,
	 * loading the definitions from the given XML files.
	 * 
	 * @param configLocations array of resource locations
	 * @param refresh whether to automatically refresh the context,
	 * loading all bean definitions and creating all singletons.
	 * Alternatively, call refresh manually after further configuring the context.
	 * @param parent the parent context
	 * @throws BeansException if context creation failed
	 * @see #refresh()
	 */
	public ClassPathXmlApplicationContextRedisTransactional(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException
	{

		super(parent);
		setConfigLocations(configLocations);
		if (refresh)
		{
			refresh();
		}
	}

	/**
	 * Create a new ClassPathXmlApplicationContext, loading the definitions
	 * from the given XML file and automatically refreshing the context.
	 * 

* This is a convenience method to load class path resources relative to a given Class. For full flexibility, consider using a GenericApplicationContext with an XmlBeanDefinitionReader and a ClassPathResource argument. * * @param path relative (or absolute) path within the class path * @param clazz the class to load resources with (basis for the given paths) * @throws BeansException if context creation failed * @see org.springframework.core.io.ClassPathResource#ClassPathResource(String, Class) * @see org.springframework.context.support.GenericApplicationContext * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader */ public ClassPathXmlApplicationContextRedisTransactional(String path, Class clazz) throws BeansException { this(new String[] {path}, clazz); } /** * Create a new ClassPathXmlApplicationContext, loading the definitions * from the given XML files and automatically refreshing the context. * * @param paths array of relative (or absolute) paths within the class path * @param clazz the class to load resources with (basis for the given paths) * @throws BeansException if context creation failed * @see org.springframework.core.io.ClassPathResource#ClassPathResource(String, Class) * @see org.springframework.context.support.GenericApplicationContext * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader */ public ClassPathXmlApplicationContextRedisTransactional(String[] paths, Class clazz) throws BeansException { this(paths, clazz, null); } /** * Create a new ClassPathXmlApplicationContext with the given parent, * loading the definitions from the given XML files and automatically * refreshing the context. * * @param paths array of relative (or absolute) paths within the class path * @param clazz the class to load resources with (basis for the given paths) * @param parent the parent context * @throws BeansException if context creation failed * @see org.springframework.core.io.ClassPathResource#ClassPathResource(String, Class) * @see org.springframework.context.support.GenericApplicationContext * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader */ public ClassPathXmlApplicationContextRedisTransactional(String[] paths, Class clazz, ApplicationContext parent) throws BeansException { super(paths, clazz, parent); } @Override //继承函数,并添加自定义代码。 protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { super.postProcessBeanFactory(beanFactory); //添加代理的bean处理器 beanFactory.addBeanPostProcessor(new RedisTransactionalBeanProcessor()); } }

 


		

		

		
			
				spring.nest.aop.SomeService
			
			
				
			
			
				
					someAdvisor
				
			
		

		
			
				
			
			
				
					spring\.nest\.aop\.SomeService\.someMethod
					spring\.nest\.aop\.SomeService\.someInnerMethod
				
			
		

	  

 

 

 

package com.aa.redis.transaction.test;

import org.springframework.context.ApplicationContext;

import spring.nest.aop.SomeService;

public class SomeServiceTest
{
	public static void main(String[] args)
	{
		String[] paths = {"classpath:spring/nest/aop/test/spring.xml"};
		ApplicationContext ctx = new ClassPathXmlApplicationContextRedisTransactional(paths);
		SomeService someService = (SomeService) ctx.getBean("someService");
		someService.someMethod();
		someService.someInnerMethod();
	}
}

 =====================================

 

在web环境中的配置方式:

service.xml



    
    
    

 

beanRefContext.xml,配置的自定义ClassPathXmlApplicationContext主要是为了让web.xml中的org.springframework.web.context.ContextLoaderListener在初始化是能够复制bean处理器(BeanPostProcessor)

 



    
    
        
	
		
		  
		      classpath:config/service.xml
		  
		
	

 

 

web.xml

 



	
		locatorFactorySelector
		classpath:config/beanRefContext.xml
	
	
		parentContextKey
		businessBeanFactory
	
	
		contextConfigLocation
		classpath:config/applicationContext.xml
	

	
		org.springframework.web.context.ContextLoaderListener
	

 

 

 

 

 

 

你可能感兴趣的:(Java/Spring,Spring,MVC)