Spring的MethodBeforeAdvice实现原理探究&&实现一个简单的Advice

本文深入探讨了Spring中的MethodBeforeAdvice实现原理,涉及动态代理的InvocationHandler和Proxy。通过分析$Proxy0类的生成及调用过程,揭示了Spring如何在方法调用前执行通知。此外,还介绍了如何实现一个简单的MethodBeforeAdvice示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、类结构

MethodBeforeAdvice整个过程大致就是使用的jdk提供的动态代理的接口InvocationHandler和Proxy

所以首先在这讨论下动态代理在这里是如何使用的

1、获得动态代理动态生成的类($Proxy0.class)

参考博文:http://sin90lzc.iteye.com/blog/1037448

Field field = System.class.getDeclaredField("props");    
field.setAccessible(true);    
Properties props = (Properties) field.get(null);    
props.put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");  


2、反编译$Proxy0.class

Frontend 2.0 plus(cracked)下载(download)

反编译得到的java文件,查看生成的源代码,


//该类为动态代理Proxy生成的,最后getBean也是获取的该类的对象,
//但是无法在编译阶段访问,所以只能够转成接口类型或父类型访问相关的方法
//这里实现Seller接口是Spring配置文件中指明的,实现的SpringProxy接口是Marker Interface,主要就是标志这是一个Spring生成的代理类
//然后实现Advised接口,可以通过Advised接口获取代理配置信息
public final class $Proxy0 extends Proxy 

implements Seller, SpringProxy, Advised

{  

public $Proxy0(InvocationHandler invocationhandler)//构造函数,构建的时候传递一个invocationHandler处理后续的方法调用   

{    

    super(invocationhandler);    //此处的invocationHandler实际为JdkDynamicAopProxy的实例

}

.....
//拿两个比较典型的看看该类是如何工作的
 //该方法使用invocationHandler调用m2

//m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);

public final String toString() 

    {

        try

        {

            return (String)super.h.invoke(this, m2, null);

        }

        catch(Error _ex) { }

        catch(Throwable throwable)

        {

            throw new UndeclaredThrowableException(throwable);

        }

     }



//添加通知的方法



        //    m18 = Class.forName("org.springframework.aop.framework.Advised").getMethod("addAdvice", new Class[] {

        //        Integer.TYPE, Class.forName("org.aopalliance.aop.Advice")

         //   });

    public final void addAdvice(int i, Advice advice)

        throws AopConfigException

    {

        try

        {

            super.h.invoke(this, m18, new Object[] {

                Integer.valueOf(i), advice

            });

            return;

        }

        catch(Error _ex) { }

        catch(Throwable throwable)

        {

            throw new UndeclaredThrowableException(throwable);

        }

    }



//需要代理的目标类的方法

//m3 = Class.forName("com.iss.spring.advice.Seller").getMethod("sell", new Class[] {

    //            Class.forName("java.lang.String")

    //        });

    public final void sell(String s)

    {

        try

        {

            super.h.invoke(this, m3, new Object[] {

                s

            });

            return;

        }

        catch(Error _ex) { }

        catch(Throwable throwable)

        {

            throw new UndeclaredThrowableException(throwable);

        }

    }





......}

3.调用过程

通过上述源代码以及类图可以发现生成的$Proxy0这个类,也就是最后返回给用户的类对象,

给用户提供封装好的使用接口中这些方法直接调用,而不必自己去调用InvocationHandler

可以将该对象的引用强转为target接口类型,调用目标方法

这里的过程是调用了$Proxy0的sell方法,$Proxy0通过查找Seller接口找到方法,获取Method对象

然后传递给invocationHandler进行调用,

Seller seller = (Seller)context.getBean("proxy");
seller.sell("Genke");


也可以把该对象的引用强转为Advised来获取代理配置信息

这里的过程是调用了$Proxy0的getTargetClass方法,$Proxy0通过查找Advised接口找到方法,获取Method对象

然后传递给invocationHandler进行调用,


Advised advised = (Advised) context.getBean("proxy");
System.out.println(advised.getTargetClass());


InvocationHandler判断是否是Advised接口的,如果是就直接执行而不去管有通知

如果不是Advised接口的方法,则先获取通知链或者拦截器,执行完所有拦截器后将会执行目标对象的该方法,

注明:这里讨论的是MethodBeforeAdvice,几种Advice的执行顺序与目标对象的方法执行顺序见Spring的四种Advice源码探究


二、实现一个简单的通知(Advice)

Advice.java
package com.iss.spring.myadvice;

import java.lang.reflect.Method;
/**
 * 提供一个通知的接口
 * @author Administrator
 *
 */
public interface Advice
{
	public void before(Object target,Method method,Object[] args);
}


Bean.java
package com.iss.spring.myadvice;
/**
 * 维护bean信息
 * @author Administrator
 *
 */
public class Bean
{
	private String id;
	private String clazz;
        /*..........getter and setter..........*/
}

Configuration.java
package com.iss.spring.myadvice;

import java.util.List;

/**
 * 该接口用来维护相关的配置文件信息
 * @author Administrator
 *
 */
public interface Configuration
{
	public List<String> getProxyInterfaces();
	public List<String> getInterceptorNames();
	public String getTarget();
}

ConfigurationImpl.java
package com.iss.spring.myadvice;

import java.util.ArrayList;
import java.util.List;

/**
 * 实现Configuration,维护配置信息
 * 继承Bean,维护bean本身信息
 * @author Administrator
 * 
 */
public class ConfigurationImpl extends Bean implements Configuration
{
	private List<String> proxyInterfaces;
	private List<String> interceptorNames;
	private String target;

	private List<Class> Interfaces;
	private List<Object> Interceptor;
	private Object targetSource;
	
	public ConfigurationImpl()
	{
		proxyInterfaces = new ArrayList<String>();
		interceptorNames = new ArrayList<String>();
		Interfaces = new ArrayList<Class>();
		Interceptor = new ArrayList<Object>();
	}
	/*...getter and setter...*/
}

MyHandler.java
package com.iss.spring.myadvice;

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

/**
 * 代理类,执行$Proxy0传递过来的方法
 * @author Administrator
 *
 */
public class MyHandler implements InvocationHandler
{
	private Configuration config;

	public MyHandler(Configuration config)
	{
		this.config = config;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable
	{
		if (config == null)
			return null;

		if(method.getDeclaringClass()==Configuration.class) //如果是调用获取配置信息则直接执行,忽略拦截器
		{
			return method.invoke(config,args);
		}
		
		if (config instanceof ConfigurationImpl)
		{
			for (Object interceptor : ((ConfigurationImpl) config)  //需要执行所有的拦截器或通知
					.getInterceptor())
			{
				if (interceptor instanceof Advice) //如果该拦截器类实现了Advice接口则调用该拦截器的before方法
				{
					((Advice) interceptor).before(
							((ConfigurationImpl) config).getTargetSource(),
							method, args);
				}
			}
		}

		return method.invoke(((ConfigurationImpl)config).getTargetSource(), args);//执行目标对象的目标方法

	}

}

XmlContext.java

package com.iss.spring.myadvice;

import java.io.IOException;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.core.io.ClassPathResource;

/**
 * 使用Dom4j解析Xml,保存至Configuration中
 * 
 * @author Administrator
 * 
 */
public class XmlContext
{
	private static final String SPRING_CONFIGURATION_IMPL_CLASS = "com.iss.spring.myadvice.ConfigurationImpl";
	private String path;
	private Document doc;
	private List<Bean> beans;

	public XmlContext(String path) throws DocumentException, IOException
	{
		this.path = path;
		try
		{
			doc = new SAXReader().read(path);
		}
		catch (Exception e)
		{
			doc = new SAXReader().read(new ClassPathResource(path).getFile());// 在path上未找到则在classpath上找
		}
		beans = new ArrayList<Bean>();
		parse();
	}

	/**
	 * 在xml中配置的id属性,通过该id找到对应的Bean
	 * @param name bean id
	 * @return
	 */
	private Bean getBeanById(String name)
	{
		for (Bean bean : beans)
		{
			if (bean.getId().equals(name))
			{
				return bean;
			}
		}
		return null;
	}
	/**
	 * 提供给用户调用的getBean,如果是配置class为代理类则返回$Proxy0代理对象
	 * 否则返回bean对象本身
	 * @param name bean id
	 * @return
	 * @throws ClassNotFoundException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 */
	public Object getBean(String name) throws ClassNotFoundException,
			InstantiationException, IllegalAccessException
	{
		Bean bean = getBeanById(name);
		if (bean == null)
			System.out.println(name);
		if (bean.getClazz().equals(SPRING_CONFIGURATION_IMPL_CLASS))
		{
			ConfigurationImpl config = (ConfigurationImpl) bean;
			List<Class> interfaces = new ArrayList<Class>();
			for (String i : config.getProxyInterfaces())
			{
				interfaces.add(Class.forName(i));
			}
			interfaces.add(Configuration.class);
			config.setInterfaces(interfaces); // 设置所有接口

			List<Object> interceptors = new ArrayList<Object>();
			for (String i : config.getInterceptorNames())
			{
				interceptors.add(getBean(i));
			}
			config.setInterceptor(interceptors); // 设置所有拦截器

			config.setTargetSource(getBean(config.getTarget())); // 设置目标对象

			return Proxy.newProxyInstance(config.getClass().getClassLoader(),
					interfaces.toArray(new Class[0]), new MyHandler(config)); // 返回生成的中间类$Proxy0对象,调用函数会经MyHandler处理
		}
		else
		{
			// 假设就是一个简单的没有属性的bean
			return Class.forName(getBeanById(name).getClazz()).newInstance();
		}

	}

	/**
	 * 开始解析,获取xml所有配置信息
	 */
	private void parse()
	{
		if (doc == null)
			return;
		Element root = doc.getRootElement();
		for (Object obj : root.elements())
		{
			Element element = (Element) obj;
			if (element.getName().equals("bean"))
				parseBean(element);
		}
	}
	/**
	 * 解析单个bean节点
	 * @param bean
	 */
	private void parseBean(Element bean)
	{
		Bean obj = null;
		if (bean.attributeValue("class")
				.equals(SPRING_CONFIGURATION_IMPL_CLASS))
		{
			obj = new ConfigurationImpl();
			ConfigurationImpl temp = (ConfigurationImpl) obj;

			for (Object property : bean.elements("property"))
			{
				parseProperty((Element) property, temp);
			}

		}
		else
		{
			obj = new Bean();
		}
		obj.setId(bean.attributeValue("id"));
		obj.setClazz(bean.attributeValue("class"));
		beans.add(obj);
	}
	/**
	 * 解析单个property节点
	 * @param property
	 * @param config
	 */
	private void parseProperty(Element property, ConfigurationImpl config)
	{
		if (property.attributeValue("name").equals("proxyInterfaces"))
		{
			for (Object value : property.element("list").elements("value"))
			{
				Element v = (Element) value;
				config.getProxyInterfaces().add(v.getText());
			}
		}
		else if (property.attributeValue("name").equals("interceptorNames"))
		{
			for (Object value : property.element("list").elements("value"))
			{
				Element v = (Element) value;
				config.getInterceptorNames().add(v.getText());
			}
		}
		else if (property.attributeValue("name").equals("target"))
		{
			/*
			 * 先找local,如果local为null,则找bean
			 */
			String bean = property.element("ref").attributeValue("local") == null ? property
					.element("ref").attributeValue("bean") : property.element(
					"ref").attributeValue("local");
			config.setTarget(bean);
		}
	}

}

测试实例:

beans.xml
	<bean id="BreadSeller" class="com.iss.spring.advice.BreadSeller"></bean>
	<bean id="MyAdvice" class="com.iss.spring.advice.MyAdvice"></bean>
	<bean id="proxy" class="com.iss.spring.myadvice.ConfigurationImpl">
		<property name="proxyInterfaces">
			<list>
				<value>com.iss.spring.advice.Seller</value>
			</list>
		</property>
		<property name="interceptorNames">
			<list>
				<value>MyAdvice</value>
			</list>
		</property>
		<property name="target">
			<ref local="BreadSeller" />
		</property>
	</bean>

MyAdvice.java

public class MyAdvice implements Advice
{

	@Override
	public void before(Object target, Method method, Object[] args)
	{
		System.out.println(target+" is say hello to "+args[0] + " and then "+method);
	}

}


main
	public static void main(String[] args)
	{
		try
		{
			XmlContext context = new XmlContext("beans.xml");
			Seller seller = (Seller)context.getBean("proxy");//Seller接口就定义个sell(String)方法
			seller.sell("Genke");
			
			Configuration config = (Configuration)context.getBean("proxy");
			System.out.println(config.getTarget());
			

		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}


 
 

                
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值