一、
spring Aop实际上也是通过动态代理机制进行面向切面编程的,在java里可以通过实现InvocationHandler接口建立自己的动态代理类,然后通过Proxy建立动态的代理类的对象。
动态代理其实就是java.lang.reflect.Proxy类动态的根据指定的所有接口生成一个class byte,该class会继承Proxy类,并实现所有你指定的接口(您在参数中传入的接口数组);然后再利用指定的classloader将 class byte加载进系统,最后生成这样一个类的对象,并初始化该对象的一些值,如invocationHandler,以即所有的接口对应的Method成员。 初始化之后将对象返回给调用的客户端。这样客户端拿到的就是一个实现你所有的接口的Proxy对象
首先InvocationHandler接口中只有一个方法
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
proxy: 指代我们所代理的那个真实对象
method: 指代的是我们所要调用真实对象的某个方法的Method对象
args: 指代的是调用真实对象某个方法时接受的参数
Proxy中动态创建实例的方法
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
这个方法的作用就是得到一个动态的代理对象,其接收三个参数,我们来看看这三个参数所代表的含义:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
loader: 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
interfaces: 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
h: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
二、
好了现在简单的模拟一下aop
首先新建一个接口
public interface Subject {
public void rent();
public void hello(String str);
}
对应的实现类
public class SubjectImpl implements Subject{
@Override
public void rent()
{
System.out.println("I want to rent my house");
}
@Override
public void hello(String str)
{
System.out.println("hello: " + str);
}
}
public class DynamicProxy implements InvocationHandler
{
// 要代理的对象
private Object target;
// 将构造方法禁用掉,不让外部通过new来得到DynamicProxy对象
private DynamicProxy()
{
};
/**
* 返回一个动态的代理对象
*
* @param object
* @return
*/
public static Object newInstance(Object object)
{
DynamicProxy proxy = new DynamicProxy();
proxy.target = object;
// 通过Proxy的newProxyInstance方法来得到一个代理对象
Object result = Proxy.newProxyInstance(object.getClass()
.getClassLoader(), object.getClass().getInterfaces(), proxy);
return result;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
{
// // 只有方法名为add和delete时候才引入日志
// if(method.getName().equals("add") || method.getName().equals("delete"))
// {
// Logger.logInfo("动态代理类");
// }
// 根据LogAnnotation来判断,如果被标注了注解,则输出日志
if(method.isAnnotationPresent(LogAnnotation.class))
{
LogAnnotation log = method.getAnnotation(LogAnnotation.class);
Logger.logInfo(log.value());
}
Object object = method.invoke(target, args);
return object;
}
}
然后是spring的配置
<!-- 如果要对static方法进行注入,可以通过factory-method属性来制定方法名字,并通过构造函数的方式传入参数 -->
<bean id="userDAOProxy" class="com.xiaoluo.proxy.DynamicProxy" factory-method="newInstance">
<constructor-arg ref="userDAO"/>
</bean>
因为我们的DynamicProxy类的对象以及代理对象是通过static方法来进行注入的,因此我们如果要对其进行注入的话,需要通过 factory-method 这个属性来给我们的静态方法进行属性注入,通过 来讲参数传递进去,这样我们的userDAOProxy就是一个代理对象了
三、
关于InvocationHandler接口的invoke方法,其实这个类就是最终Proxy调用的固定接口方法。Proxy不管客户端的业务方法是怎么实现的。当客户端调用Proxy时,它只
会调用InvocationHandler的invoke接口,所以我们的真正实现的方法就必须在invoke方法中去调用。关系如下:
BusinessProcessorImpl bpimpl = new BusinessProcessorImpl();
BusinessProcessorHandler handler = new BusinessProcessorHandler(bpimpl);
BusinessProcessor bp = (BusinessProcessor)Proxy.newProxyInstance(....);
bp.processBusiness()-->invocationHandler.invoke()-->bpimpl.processBusiness();
Proxy.newProxyInstance方法会做如下几件事:
1,根据传入的第二个参数interfaces动态生成一个类,实现interfaces中的接口,该例中即BusinessProcessor接口的processBusiness方法。并且继承了Proxy类,重写了hashcode,toString,equals等三个方法。具体实现可参看 ProxyGenerator.generateProxyClass(…); 该例中生成了$Proxy0类
2,通过传入的第一个参数classloder将刚生成的类加载到jvm中。即将$Proxy0类load
3,利用第三个参数,调用 Proxy0的 Proxy0(InvocationHandler)构造函数 创建$Proxy0的对象,并且用interfaces参数遍历其所有接口的方法,并生成Method对象初始化对象的几个Method成员变量
4,将$Proxy0的实例返回给客户端。
现在好了。我们再看客户端怎么调就清楚了。
1,客户端拿到的是 Proxy0的实例对象,由于 Proxy0继承了BusinessProcessor,因此转化为BusinessProcessor没任何问题。
BusinessProcessor bp = (BusinessProcessor)Proxy.newProxyInstance(….);
2,bp.processBusiness();
实际上调用的是 Proxy0.processBusiness();那么 Proxy0.processBusiness()的实现就是通过InvocationHandler去调用invoke方法啦!
注:文章以自己记录为主,详细的可以去下面地方看