`
conkeyn
  • 浏览: 1504503 次
  • 性别: Icon_minigender_1
  • 来自: 厦门
社区版块
存档分类
最新评论

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 <T>
 * @since 2014年8月21日 下午1:47:38
 */
public class RedisTransactionalJdkProxy<T> implements RedisTransactionalProxy<T>, 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 <T> 被代理的原始类型
 * @since 2014年8月22日 上午9:33:27
 */
public interface RedisTransactionalProxy<T>
{

	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.
	 * <p>
	 * 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());
	}

}

 

<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-2.5.xsd  
            http://www.springframework.org/schema/aop  
            http://www.springframework.org/schema/aop/spring-aop-2.5.xsd  
            ">
		<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor" />

		<bean id="someServiceTarget" class="spring.nest.aop.SomeServiceImpl" />

		<bean id="someService" class="org.springframework.aop.framework.ProxyFactoryBean">
			<property name="proxyInterfaces">
				<value>spring.nest.aop.SomeService</value>
			</property>
			<property name="target">
				<ref local="someServiceTarget" />
			</property>
			<property name="interceptorNames">
				<list>
					<value>someAdvisor</value>
				</list>
			</property>
		</bean>

		<bean id="someAdvisor"
			class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
			<property name="advice">
				<ref local="debugInterceptor" />
			</property>
			<property name="patterns">
				<list>
					<value>spring\.nest\.aop\.SomeService\.someMethod</value>
					<value>spring\.nest\.aop\.SomeService\.someInnerMethod</value>
				</list>
			</property>
		</bean>

	</beans>  

 

 

 

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

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p" xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.2.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
    
    <bean id="someService" class="spring.nest.aop.SomeServiceImpl" />
    
</beans>

 

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

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:p="http://www.springframework.org/schema/p" xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.2.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
    <context:annotation-config/>
    <!-- <context:spring-configured/> -->
        <!-- 使这个ApplicationContext作为一个父ApplicationContext存在,以便web.xml中的子ApplicationContext复制父ApplicationContext的属性值。 -->
	<bean id="businessBeanFactory"
		class="spring.nest.aop.test.ClassPathXmlApplicationContextRedisTransactional">
		<constructor-arg>
		  <list>
		      <value>classpath:config/service.xml</value>
		  </list>
		</constructor-arg>
	</bean>
</beans>

 

 

web.xml

 

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	version="3.0">
	<context-param>
		<param-name>locatorFactorySelector</param-name>
		<param-value>classpath:config/beanRefContext.xml</param-value>
	</context-param>
	<context-param>
		<param-name>parentContextKey</param-name>
		<param-value>businessBeanFactory</param-value>
	</context-param>
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:config/applicationContext.xml</param-value>
	</context-param>

	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

 

 

 

 

 

 

分享到:
评论

相关推荐

    高级开发spring面试题和答案.pdf

    某一个事务嵌套另一个事务的时候怎么办? REQUIRED_NEW和REQUIRED区别 Spring的事务是如何回滚的,实现原理; 抽象类和接口的区别,什么时候用抽象类什么时候用接口; StringBuilder和StringBuffer的区别 java值传递...

    Spring 2.0 开发参考手册

    6.1.3. Spring的AOP代理 6.2. @AspectJ支持 6.2.1. 启用@AspectJ支持 6.2.2. 声明一个切面 6.2.3. 声明一个切入点(pointcut) 6.2.4. 声明通知 6.2.5. 引入(Introductions) 6.2.6. 切面实例化模型 6.2.7....

    Spring-Reference_zh_CN(Spring中文参考手册)

    6.1.3. Spring的AOP代理 6.2. @AspectJ支持 6.2.1. 启用@AspectJ支持 6.2.2. 声明一个切面 6.2.3. 声明一个切入点(pointcut) 6.2.3.1. 切入点指定者的支持 6.2.3.2. 合并切入点表达式 6.2.3.3. 共享常见的切入点...

    spring.net中文手册在线版

    Spring.NET是一个应用程序框架,其目的是协助开发人员创建企业级的.NET应用程序。它提供了很多方面的功能,比如依赖注入、面向方面编程(AOP)、数据访问抽象及ASP.NET扩展等等。Spring.NET以Java版的Spring框架为...

    Spring中文帮助文档

    9.9.1. 对一个特定的 DataSource 使用了错误的事务管理器 9.10. 更多的资源 10. DAO支持 10.1. 简介 10.2. 一致的异常层次 10.3. 一致的DAO支持抽象类 11. 使用JDBC进行数据访问 11.1. 简介 11.1.1. 选择一...

    spring chm文档

    6.1.3. Spring的AOP代理 6.2. @AspectJ支持 6.2.1. 启用@AspectJ支持 6.2.2. 声明一个切面 6.2.3. 声明一个切入点(pointcut) 6.2.4. 声明通知 6.2.5. 引入(Introductions) 6.2.6. 切面实例化模型 6.2.7....

    Spring API

    9.9.1. 对一个特定的 DataSource 使用了错误的事务管理器 9.10. 更多的资源 10. DAO支持 10.1. 简介 10.2. 一致的异常层次 10.3. 一致的DAO支持抽象类 11. 使用JDBC进行数据访问 11.1. 简介 11.1.1. 选择一种...

    Spring.3.x企业应用开发实战(完整版).part2

    Spring3.0是Spring在积蓄了3年之久后,隆重推出的一个重大升级版本,进一步加强了Spring作为Java领域第一开源平台的翘楚地位。  Spring3.0引入了众多Java开发者翘首以盼的新功能和新特性,如OXM、校验及格式化框架...

    通用的报表缓存设计(Spring AOP + Redis)

    通用的报表缓存设计项目背景现状态方案代码实现Redis配置注解AOP缓存(关键)业务调用(部分)测试结果:第一次第二次测试结果总结 项目背景 1:用户群体大, 2:业务计算量大,计算逻辑复杂。 现状态 1:主面页面,...

    Spring3.x企业应用开发实战(完整版) part1

    Spring3.0是Spring在积蓄了3年之久后,隆重推出的一个重大升级版本,进一步加强了Spring作为Java领域第一开源平台的翘楚地位。  Spring3.0引入了众多Java开发者翘首以盼的新功能和新特性,如OXM、校验及格式化框架...

    Spring基础.pdf

    spring项⽬在启动的时候就会去初始化⼀个IoC容 器,通过读取XML⽂件中的配置去实例化所有项⽬所需要的对象并放⼊IoC容器中去。这样做的 好处: 不需要在代码中通过new的⽅式去在逻辑运⾏中再去创建对象,可以直接从...

    CloudStack二次开发帮助文档

    如何声明一个CLOUDSTACK组件 19 6.3. AUTO-WIRING 19 6.4. CLOUDSTACK SPRING组件的编码约定 20 6.4.1. 注意自动注入的时间 20 6.4.2. 公用构造器 20 6.4.3. 组件自主初始化 20 6.4.4. 运行时注入 21 6.4.5. ...

    积分管理系统java源码-knowledge:这是我的知识,包括我所有已知的

    积分管理系统java源码 基础知识 java基础 基本类型(占用的内存)...Spring集成下的SqlSession和Mapper Mybatis的事务 分析Mybatis动态代理的真正实现 手动实现Mini版本的Mybatis 分布式 分布式原理 分布式架构的演进过

    java面试宝典

    72、当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法? 18 73、线程的基本概念、线程的基本状态以及状态之间的关系 18 74、sleep() 和 wait() 有什么区别? 18 75、socket通信...

    千方百计笔试题大全

    72、当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法? 18 73、线程的基本概念、线程的基本状态以及状态之间的关系 18 74、sleep() 和 wait() 有什么区别? 18 75、socket...

    ff4j:Java功能标志变得简单

    AOP驱动的切换:保持代码的清洁和可读性:避免嵌套if语句,但使用批注。 借助Spring AOP,目标实现是在运行时选择的,因此受功能状态驱动。 功能监视:对于每个功能执行,ff4j都会评估谓词,因此可以收集和记录事件...

    Java面试宝典2010版

    2、编写一个程序,将d:\java目录下的所有.java文件复制到d:\jad目录下,并将原来文件的扩展名从.java改为.jad。 62 3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证...

    最新Java面试宝典pdf版

    2、编写一个程序,将d:\java目录下的所有.java文件复制到d:\jad目录下,并将原来文件的扩展名从.java改为.jad。 62 3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证...

    Java面试笔试资料大全

    2、编写一个程序,将d:\java目录下的所有.java文件复制到d:\jad目录下,并将原来文件的扩展名从.java改为.jad。 62 3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证...

    JAVA面试宝典2010

    2、编写一个程序,将d:\java目录下的所有.java文件复制到d:\jad目录下,并将原来文件的扩展名从.java改为.jad。 62 3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证...

Global site tag (gtag.js) - Google Analytics