Spring AOP之增强

本文介绍了Spring AOP的五种增强类型:前置、后置、异常、引介和环绕增强,并通过具体示例展示了如何使用Spring配置文件进行前置增强及引介增强的实现。

      继续Spring AOP的学习,总所周知,aop的魅力所在就是它能在方法前后起拦截作用,以便我们定义横切逻辑代码。增强大致分为5类:前置增强(就是在目标方法调用前拦截),后置增强(就是在目标方法调用之后拦截),异常增强(就是在目标方法抛出后拦截),引介增强(它是一种特殊的增强,可以在目标类添加属性和方法,通过拦截定义一个接口,让目标代理实现这个接口,它的连接点是类级别的),而前面三种增强则是方法级别的。以上四种增强接口是Spring定义的,还有只用环绕增强(目标方法前后拦截)是AOP联盟定义的接口。其实aop增强很简单,通过实现这些增强接口,在实现这些接口的方法中定义横切逻辑,然后通过配置spring配置文件,就可以完成将增强织入到目标方法中了。

 

首先,以前置增强为例,大家都知道如果我们到餐厅去,服务员首先会欢迎顾客,然后再为我们提供服务。这里我们一个

Waiter类:

package com.me.services;

public interface Waiter {
	void greetTo(String name);
	void serveTo(String name);
}

 现在我们来看一个训练不足的服务生的服务情况:

package com.me.services.imp;

import com.me.services.Waiter;

public class LuceyWaiter implements Waiter {

	public void greetTo(String name) {
		System.out.println("greet to "+name);
	}

	public void serveTo(String name) {
		System.out.println("server "+name);
	}

}

Lucey走到闷不做声走到顾客面前, 提供服务,那么,我们就需要对每个服务生进行规范,在服务之前要行礼貌用语。

package com.me.advice;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

public class GreetingBeforeAdvice implements MethodBeforeAdvice {

	public void before(Method method, Object[] args, Object obj)
			throws Throwable {
		String clientName = args[0].toString();
		System.out.println("How are you,"+clientName+"!");
	}

}

 这里我们就用到了前置增强,通过实现org.springframework.aop.MethodBeforeAdvice接口的before方法定义逻辑代码。before中的三个参数分别代表:目标方法,目标方法的参数列表,目标实例。

目标对象也有了,增强也有了,接下来如何把增强织入的目标对象生成代理呢?上一次博客写到spring的代理机制,我们知道可以通过JDK和CGLib两种方式动态创建代理。但是那样我们代码太过麻烦,要自定义一个类实现指定接口,那有没有简单的方法呢?当然,spring为我们提供了ProxyFactoryBean,它帮我们完成了上面通过JDK,CGLib创建代理那些复杂的工作,我们只需要在spring中配置一下即可完成织入工作,^-^

<beans
	xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
	<!-- 需要代理的业务类 -->
	<bean id="target" class="com.me.services.imp.LuceyWaiter"></bean>
	<!-- 指定增强,一个业务类可以有多个增强形成增强链,调用顺序与添加顺序一致 -->
	<bean id="greetAdvice" class="com.me.advice.GreetingBeforeAdvice"></bean>
	<!-- 利用ProxyFactoryBean创建代理 -->
	<bean id="waiter" class="org.springframework.aop.framework.ProxyFactoryBean">
	<!-- 代理要实现的接口,可以是多个接口 -->		
		<property name="proxyInterfaces" value="com.me.services.Waiter"/>
		<property name="target" ref="target"/>
		<!-- 需要织入目标对象的0增强数组 -->
		<property name="interceptorNames">
			<list>
				<value>greetAdvice</value>
			</list>
		</property>
	</bean>
</beans>

关于ProxyFactoryBean的工作原理将在以后写到,但需要进一步了解它的几个常用配置属性,除上面的属性外,还有singleton:返回代理是否为单实例,默认true.

optimize:当设置为true时,强制使用CGLib代理。对于单实例推荐配置true,但对于作用域类型的代理推荐false.

proxyTargetClass:是否对类代理(不是对接口进行代理),设置为true使用CGLib代理。当然如果设置为true就不必设置proxyInterfaces属性了,及时设置了也将被忽略。下面来一段测试代码吧:

package com.me.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.me.services.Waiter;

public class TestBeforeAdvice {

	public static void main(String[] args) {
		ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");
        Waiter waiter = (Waiter)cxt.getBean("waiter");
        waiter.greetTo("John");
	}

}

 后置增强,异常抛出增强和环绕增强用法差不多,只是实现的接口不一样罢了。下面就做说明了,我将测试代码上传供大家学习。接下来,来谈谈我们类级别的增强,即引介增强,我们还是根据上一篇博客代理机制的示例来学习引介增强。我们前面用到监测用户添加和删除性能,现在将起改为可控的。即维护人员可控制是否采用性能监控。

首先定义一个用于标志目标类是否支持性能监控的接口:

package com.me.advice;

public interface Monitorable {
	/** 
	 * @param IsActive 
	 * true:性能监控功能激活
	 * false: 性能监控功能关闭
	 */
	public void setMonitorActive(boolean IsActive);
}

  通过继承DelegatingIntroductionInterceptor为目标类引入性能监控功能,代码如下:

package com.me.util;

import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;

@SuppressWarnings("serial")
public class ControllablePerformanceMonitor extends
		DelegatingIntroductionInterceptor implements Monitorable {
	//为了解决线程安全问题
	private ThreadLocal<Boolean> statusMap = new ThreadLocal<Boolean>();
	
	public void setMonitorActive(boolean IsActive) {
		statusMap.set(new Boolean(IsActive));		
	}
	
	public Object invoke(MethodInvocation mi)throws Throwable{
		Object obj = null;
		if(statusMap.get()!=null && statusMap.get().booleanValue()){
			PerformanceMonitor pm = new PerformanceMonitor();
			pm.start(mi.getClass().getName()+"."+mi.getMethod().getName());
			obj = super.invoke(mi);
			pm.end();
		}else{
			obj = super.invoke(mi);
		}
		return obj;
	}

}

 

配置引介增强

<bean id="target" class="com.me.services.imp.UserServiceImp"/>
 	<bean id="pmonitor" class="com.me.util.ControllablePerformanceMonitor"/>
 	<!-- 创建代理 -->
 	<bean id="userService" class="org.springframework.aop.framework.ProxyFactoryBean">
 	<!-- 引介增强所实现的接口 -->
 	<property name="interfaces">
 		<value>com.me.util.Monitorable</value>
 	</property>
 	<property name="target" ref="target"/>
 	<property name="interceptorNames">
 		<idref local="pmonitor"/>
 	</property>
 	<!-- 由于引介增强通过子类创建代理,所以强制使用CGLib代理实现机制 -->
 	<property name="proxyTargetClass" value="true"/>
 	</bean>

 测试引介增强

package com.me.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.me.services.UserService;
import com.me.entity.*;
import com.me.util.Monitorable;
public class TestCotrollerPerformance {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		 ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");
		 UserService service = (UserService)cxt.getBean("userService");
		 service.deleteUser(1);
		 service.insertUser(new User());
		 //这里强制成果,说明返回的代理引入了Monitorable接口
		 Monitorable monitor = (Monitorable)service;
		 monitor.setMonitorActive(true);
		 
		 service.deleteUser(1);
		 service.insertUser(new User());
	}

}

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值