AOP动态代理--基本原理

 AOP是目前Spring框架中的核心之一,在应用中具有非常重要的作用,也是Spring其他组件的基础。它是一种面向切面编程的思想。

今天来讲解下AOP的核心功能的底层实现机制:如何用动态代理来实现切面拦截。

        AOP的拦截功能是由java中的动态代理来实现的。说白了,就是在目标类的基础上增加切面逻辑,生成增强的目标类(该切面逻辑或者在目标类函数执行之前,或者目标类函数执行之后,或者在目标类函数抛出异常时候执行。不同的切入时机对应不同的Interceptor的种类,如BeforeAdviseInterceptor,AfterAdviseInterceptor以及ThrowsAdviseInterceptor等)。         那么动态代理是如何实现将切面逻辑(advise)织入到目标类方法中去的呢?下面我们就来详细介绍并实现AOP中用到的两种动态代理。         AOP的源码中用到了两种动态代理来实现拦截切入功能:jdk动态代理和cglib动态代理。两种方法同时存在,各有优劣。jdk动态代理是由java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。 还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。由此可以看出,jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。

一、jdk动态代理实现AOP拦截(代码中的关键地方都添加了注释

package com.service;
/**
 * 该类是所有被代理类的接口类,jdk实现的代理要求被代理类基于统一的接口 
 * @author xx
 * 2017年3月24日 下午4:17:53
 */
public interface StudentInfoService {
	void findInfo(String studentName);
}

2、实现类,我们真正的业务代码,需要做切面

package com.service.impl;

import com.service.StudentInfoService;

public class StudentInfoServiceImpl implements StudentInfoService{

	@Override
	public void findInfo(String studentName) {
		//
		System.out.println("#####你目前输入的名字是:"+studentName);
		System.out.println("#####业务代码xxx1");
		System.out.println("#####业务代码xxx2");
	}

}

3,实现动态动态代理类   3.1先写一个工厂类AOPFactory
,连接需要代理的接口(可以不写)

package com.handler;

public class AOPFactory {	
	public static <T> Object getAOPProxyedObject(Class<T> cls){
		Object proxy = null;
		MyHandler handler = new MyHandler();
		try {
			Object obj = cls.newInstance();
			if (obj != null) {
				proxy = handler.bind(obj);
			} else {
				System.out.println("Can't get the proxyobj");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	
		return proxy;
	}
}

 代理类MyHandler

package com.handler;

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

import org.apache.log4j.Logger;

public class MyHandler implements InvocationHandler {
	private Object proxyObj;
	private static Logger log = Logger.getLogger(MyHandler.class);

	public Object bind(Object obj) {
		this.proxyObj = obj;
		return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
				.getClass().getInterfaces(), this);
	}
	@Override
	public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
		Object result=null;
		try{
		//请在这里插入代码,在方法前调用
		log.debug("调用log日志方法 before"+method.getName());
		result=method.invoke(proxyObj,args); //原方法
		//请在这里插入代码,方法后调用
		log.debug("调用log日志方法 after"+method.getName());
		}catch(Exception e){
		e.printStackTrace();
		}
		return result;
	}

}

4.运行结果

03/24/2017,16:35:26:com.handler.MyHandler[main]-DEBUG:调用log日志方法 beforefindInfo
#####你目前输入的名字是:阿飞
#####业务代码xxx1
#####业务代码xxx2
03/24/2017,16:35:26:com.handler.MyHandler[main]-DEBUG:调用log日志方法 afterfindInfo

5.分析一下。这个是最基本的JDK动态代理,通过java反射原理,将需要代理的method前后绑定了逻辑,不影响其方法内的运行! 缺陷是,该方法必须是通过实现接口的。

Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
				.getClass().getInterfaces(), this)

二、cglib动态代理实现AOP拦截


还是拿刚才的代理类做示范。 一,
cglib代理处理类(实现了MethodInterceptor类,用将目标类传进
Enhancer调用
intercept方法,intercept和jdk invoke类似

package com.handler.AOPInstrumenter;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import org.apache.log4j.Logger;

public class AOPInstrumenter implements MethodInterceptor {
	private Logger log = Logger.getLogger(AOPInstrumenter.class);
	private Enhancer enhancer = new Enhancer();

	public Object getInstrumentedClass(Class<?> clz) {
		enhancer.setSuperclass(clz);
		//this便是传进来的clz,最后增强目标类调用的是代理类对象CglibProxy中的intercept方法  
		enhancer.setCallback(this);
		return enhancer.create();
	}

	public Object intercept(Object o, Method method, Object[] args,
			MethodProxy proxy) throws Throwable {
		// 请在这里插入代码,在方法前调用
		log.debug("调用log日志方法 before" + method.getName());
		Object result = proxy.invokeSuper(o, args);
		// 请在这里插入代码,方法后调用
		log.debug("调用log日志方法 after" + method.getName());
		return result;
	}
}

二 测试代码

	AOPInstrumenter instrumenter=new AOPInstrumenter();
		StudentInfoServiceImpl studentInfo=(StudentInfoServiceImpl)instrumenter.getInstrumentedClass(StudentInfoServiceImpl.class);
		studentInfo.findInfo("阿飞");

三 ,结果

03/24/2017,17:03:11:com.handler.AOPInstrumenter.AOPInstrumenter[main]-DEBUG:调用log日志方法 beforefindInfo
#####你目前输入的名字是:阿飞
#####业务代码xxx1
#####业务代码xxx2
03/24/2017,17:03:11:com.handler.AOPInstrumenter.AOPInstrumenter[main]-DEBUG:调用log日志方法 afterfindInfo

完成,基本的原来就是这些,spring 源码使用它的AOP当然远远比这个复杂,后续再使用spring aop分析

    原文作者:AOP
    原文地址: https://blog.csdn.net/afzaici/article/details/65632599
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞