Spring AOP技术--动态代理

概述:

Spring AOP是一种面向切面的编程方式,使用这种方式,我们可以在处理业务逻辑代码的前后织入某些公共的处理,比如:事务管理,在业务逻辑处理前后添加性能监视代码等,最常用到AOP的地方就是事务处理。spring的AOP使用动态代理技术在代码运行期间织入前置或者后置通知(advice)代码。spring使用到了java的动态代理和cglib的动态代理来实现AOP的功能。

关于java的动态代理可以参看一个简单实例:点击打开链接
关于cglib的动态代理可以参看这个简单示例:点击打开链接

使用spring的ProxyFactory来创建代理对象:

前置通知、后置通知

下面通过一个示例使用ProxyFactory来创建代理对象,在业务处理的方法前后使用前置通知(MethodBeforeAdvice)和后置通知(AfterReturningAdvice)来围绕业务处理方法添加额外的处理。示例的结构如下:
Spring AOP技术--动态代理_第1张图片
其中StudentServiceImpl类为业务操作类,MyMethodBeforAdvice和MyAfterReturningAdvice类分别是前置通知和后置通知类,最后一个是测试类。
StudentServiceImpl类代码如下:
package cn.qing.spring.aop;
/**
 * 业务处理类
 * @author ding
 *
 */
public class StudentServiceImpl {

	public String findStudent(String stuName) {
		System.out.println("execute findStudent method....");
		return "hello "+stuName;
	}

	public int findStudentNum() {
		System.out.println("findStudentNum method ...");
		return 20;
	}

}

MyMethodBeforAdvice类的代码:
package cn.qing.spring.aop;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

public class MyMethodBeforAdvice implements MethodBeforeAdvice {

	/**
	 * method		目标实例的方法
	 * args			目标方法的参数
	 * obj			目标实例
	 */
	public void before(Method method, Object[] args, Object obj)
			throws Throwable {
		System.out.println("访问的目标对象:"+obj.getClass());
		System.out.println("访问的方法名称:"+method.getName());
		if(args!=null && args.length>0)	
		{
			for(Object o : args)
				System.out.println("方法的参数:"+o.toString());
		}
		System.out.println("这个是spring的前置通知,可以在方法执行前执行这里的代码....");
		System.out.println("**************************************");
	}

}
MyAfterReturningAdvice类代码:
package cn.qing.spring.aop;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

public class MyAfterReturningAdvice implements AfterReturningAdvice {

	/**
	 * returnObj	目标实例方法返回的结果
	 * method		目标类的方法
	 * args			目标实例的方法入参
	 * obj			目标实例
	 */
	public void afterReturning(Object returnObj, Method method, Object[] args,
			Object obj) throws Throwable {
		System.out.println("****************************************");
		System.out.println("访问的目标对象为:"+obj.getClass());
		
		System.out.println("访问的方法为:"+method.getName());
		
		if(returnObj!=null)
			System.out.println("目标实例方法返回的结果为:"+returnObj.toString());
		
		System.out.println("可以在这里进行一些后置处理...");
	}

}

测试类:
package cn.qing.spring.aop;

import org.springframework.aop.AfterAdvice;
import org.springframework.aop.BeforeAdvice;
import org.springframework.aop.framework.ProxyFactory;

public class TestAdvice {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		StudentServiceImpl ssi = new StudentServiceImpl();
		//实例化自定义前置通知
		BeforeAdvice beforeAdvice = new MyMethodBeforAdvice();
		//实例化自定义后置通知
		AfterAdvice afterAdvice = new MyAfterReturningAdvice();
		//使用spring的ProxyFactory来创建代理对象
		ProxyFactory proxyFactory = new ProxyFactory();
		//设置目标对象
		proxyFactory.setTarget(ssi);
		//为代理目标添加前置通知
		proxyFactory.addAdvice(beforeAdvice);
		//为代理目标添加后置通知
		proxyFactory.addAdvice(afterAdvice);
		
		StudentServiceImpl impl = (StudentServiceImpl)proxyFactory.getProxy();
		
		impl.findStudent("xiaoming");
		//打印出从ProxyFactory中获取的实例的class,可以看到返回的是cglib创建的代理类
		System.out.println("impl class:"+impl.getClass());
	}

}

输出结果:
访问的目标对象:class cn.qing.spring.aop.StudentServiceImpl
访问的方法名称:findStudent
方法的参数:xiaoming
这个是spring的前置通知,可以在方法执行前执行这里的代码....
**************************************
execute findStudent method....
****************************************
访问的目标对象为:class cn.qing.spring.aop.StudentServiceImpl
访问的方法为:findStudent
目标实例方法返回的结果为:hello xiaoming
可以在这里进行一些后置处理...
impl class:class cn.qing.spring.aop.StudentServiceImpl$$EnhancerByCGLIB$$d304c998

从输出结果可以看出,在调用StudentServiceImpl类的findStudent("xiaoming")方法前后都输出了我们在前置通知和后置通知中添加的代码。需要注意的是,在spring 3.x之后的版本都是将spring按各个模块分开打的包,所以在使用aop时需要再导入一个aop的标准包aopalliance-1.0.jar,只有加入这个包才能正常使用。在spring 2.x的版本中所有的jar包都是打在了spring.jar中,所以不存在这个问题。

在上面的TestAdvice类中是通过显示声明ProxyFactory类,然后通过代码添加前置后置通知,最后再获取代理对象,这种通过代码的操作可以在spring的配置文件中通过配置bean来替换,下面是在spring的配置文件中进行的配置:
	
	
	
	
	
	
	
	
		
		
		
	

然后得到代理bean,并执行对应的方法:
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
StudentServiceImpl impl = (StudentServiceImpl)context.getBean("serviceProxy");
impl.findStudent("xiaoming");
System.out.println("impl name:"+impl.getClass());

最终的输出结果和上面通过代码的输出是一样。

环绕通知:

环绕通知使用的是AOP联盟所定义的MethodInterceptor接口来实现的,这个接口其实和cglib中的MethodInterceptor很类似,都是可以在业务方法前后添加相应的处理,所以称之为环绕通知,它实现的效果其实和同时使用前置通知和后置通知没太大区别。使用的具体代码如下:
package cn.qing.spring.aop;

import java.lang.reflect.Method;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
 * 自定义环绕通知
 * @author ding
 *
 */
public class MyMethodInterceptor implements MethodInterceptor {

	public Object invoke(MethodInvocation invocation) throws Throwable {
		
		//得到目标对象
		Object obj = invocation.getThis();		
		//获取目标对象方法
		Method method = invocation.getMethod();
		//获取参数列表信息
		Object[] args = invocation.getArguments();
		
		System.out.println("访问的目标对象:"+obj.getClass());
		System.out.println("访问的方法名称:"+method.getName());
		if(args!=null && args.length>0)	
		{
			for(Object o : args)
				System.out.println("方法的参数:"+o.toString());
		}
		System.out.println("这个是spring的前置通知,可以在方法执行前执行这里的代码....");
		System.out.println("**************************************");
		
		//通过反射调用目标方法
		Object object = invocation.proceed();
		
		System.out.println("****************************************");
		System.out.println("访问的目标对象为:"+obj.getClass());
		
		System.out.println("访问的方法为:"+method.getName());
		System.out.println("可以在这里进行一些后置处理...");
		return object;
	}

}

这个通知在使用时和前面的一样,可以使用ProxyFactory.addAdvice()方法直接添加通知,也可以在spring 配置文件中配置使用,在获取代理对象以后访问某个具体方法时也会在前后输出上面的打印信息。


你可能感兴趣的:(spring学习)