动态代理


前言:最近学习到Spring的AOP的前置知识动态代理时有点晕,所以打算整理一些,本内容示例仅用JDK自带的类和接口实现动态代理

首先明了要构建哪些类和接口,跟别做什么:

1、切面类---要额外添加的功能实现
2、接口---主要逻辑实现的类的接口
3、实现类---对应上面的接口
4、测试类---这里用的是main方法,未用测试单元

1、切面类

 public class LogAspect {
    public void log(String msg) {
        System.out.println("log->" + msg);
    }
}

2、需要被实现的接口

public interface MyService {
    void delete(int id);
}

3、实现类

/**
 * 实现MyService接口
 * 此类是需要被代理的,即目标类 
 * 需要他的类加载器,   MyServiceImpl.getClass().getClassLoder()
 * 和
 * 所有的实现的接口  MyServiceImpl.getClass().getInterfaces()
 * @author user
 */
public class MyServiceImpl implements MyService{

    @Override
    public void delete(int id) {
        //这里应该有日志信息
        System.out.println("删除操作执行。。。"+id);
    }
}

4、测试类*

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * 测试类,这个动态代理的实现使用的是JDK自带的类和接口
 * 
 * 主要调用   Proxy的    三参的    静态方法Proxy.newProxyInstance  (
 *     目标对象的类加载器对象 , 
 *     目标对象所实现的所有的接口 , 
 *     InvocationHanler接口的实现类对象----->这个实现类的重要的有三个参数的方法
          public Object invoke(
 *                 Object proxy,         ----产生的代理对象
 *                 Method method,         ----需要被代理的方法的镜像
 *                 Object[] args)         ----方法的参数
 *                     throws Throwable {return proxy ;})
 */
public class DynamicProxyTest {
    
    public static void main(String[] args) {
        
        //日志类
        LogAspect log = new LogAspect();
        
        /**
        参数1   目标对象    的类加载器对象  .getClass().getClassLoader()
        参数2   目标对象  (类名.getClass())所实现的所有接口(.getInterfaces())
        参数3 InvocationHandler接口的  实现类对象  
         */
        
        //创建代理类对象
        MyService myServiceProxy = 
                (MyService) Proxy.newProxyInstance(        
                new MyServiceImpl().getClass().getClassLoader(), 
                new MyServiceImpl().getClass().getInterfaces(),
                new InvocationHandler() {
            
            //目标对象,下面用invoke调用这个对象的对应参数的方法
            private Object target = new MyServiceImpl(); 
            
            /**
             参数1 将来所产生的  代理对象
             参数2 将来需要调用到的目标对象里面真正的那个方法的镜像
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) 
             throws Throwable {
                // 获得将来所调用方法的名字
                String methodName = method.getName();
                // 用日志记录输出一下
                log.log(methodName + " 日志执行...");
                // 用反射的方式去调用将来需要真正调用的方法.
                proxy = method.invoke(target, args);

                return proxy ;
            }
        });
        //调用代理类的delete方法,这是会调用日志方法
        myServiceProxy.delete(3);
    }
}
  • ------另: 后面看到用Method的invoke方法时,注意到传的参数分别是要执行的对象,以及参数列表,想到是通过参数列表进行识别,而这样会造成方法不能完全正确识别???

所以特查了下invoke的源码和注释:

/**
 * 附:Method的invoke方法

中文
使用指定的参数在指定的对象上调用这个{@code method}对象表示的底层方法。
各个参数被自动展开以匹配原语形式参数,并且原语和引用参数都受到必要的方法调用转换的约束。
如果底层方法是静态的,则忽略指定的{@code obj}参数。它可能是空的。
 
如果底层方法所需的形式参数数量为0,则提供的{@code args}数组的长度可能为0或null。
如果底层方法是实例方法,则使用Java语言规范第二版第15.12.4.4节中记录的动态方法查找来调用它;特别是,将根据目标对象的运行时类型进行覆盖。
如果底层方法是静态的,则在方法尚未初始化的情况下初始化声明该方法的类。
如果该方法正常完成,则将其返回的值返回给invoke的调用者;如果值具有原语类型,则首先将其适当地包装在对象中。但是,如果值具有基元类型数组的类型,则数组的元素不会包装在对象中;换句话说,返回一个基元类型的数组。如果底层方法返回类型为void,则调用返回null。
调用底层方法的对象
@param指定用于方法调用的参数
@ @返回这个对象在{@code obj}上用参数{@code args}表示的调度方法的结果
@exception IllegalAccessException如果这个{@code方法}对象正在执行Java语言访问控制,并且底层方法不可访问。
如果方法是实例方法,而指定的对象参数不是声明底层方法的类或接口的实例(或子类或实现类的实例);实际参数与形式参数数量不一致的;如果原语参数的展开转换失败;或者,在可能的展开之后,无法通过方法调用转换将参数值转换为相应的forma参数类型。
@exception InvocationTargetException如果底层方法抛出异常。
@exception NullPointerException如果指定的对象为空且方法为实例方法。
@exception ExceptionInInitializerError如果这个方法引发的初始化失败。


原文
Invokes the underlying method represented by this {@code Method} object, on the specified object with the specified parameters.

Individual parameters are automatically unwrapped to match primitive formal parameters, 
and both primitive and reference parameters are subject to method invocation conversions as necessary.

If the underlying method is static, then the specified {@code obj} argument is ignored. It may be null.
 
If the number of formal parameters required by the underlying method is  0, the supplied {@code args} array may be of length 0 or null.

If the underlying method is an instance method, it is invoked using dynamic method lookup as documented in The Java Language Specification, 
Second Edition, section 15.12.4.4; in particular, overriding based on the runtime type of the target object will occur.

If the underlying method is static, the class that declared the method is initialized if it has not already been initialized.

If the method completes normally, the value it returns is  returned to the caller of invoke; if the value has a primitive  type, 
it is first appropriately wrapped in an object. However, if the value has the type of an array of a primitive type, the elements of the array are not wrapped in objects; 
in other words, an array of primitive type is returned.  If the underlying method return type is void, the invocation returns null.
 *  @param obj  the object the underlying method is invoked from
 *  @param args the arguments used for the method call
 *  @return the result of dispatching the method represented by this object on {@code obj} with parameters {@code args}
 *  @exception IllegalAccessException   if this {@code Method} object  is enforcing Java language access control and the underlying method is inaccessible.
 *  @exception IllegalArgumentException  if the method is an instance method and the specified object argument 
 is not an instance of the class or interface declaring the underlying method (or of a subclass or implementor thereof); 
 if the number of actual and formal parameters differ;  if an unwrapping conversion for primitive arguments fails; 
 or if,   after possible unwrapping, a parameter value  cannot be converted to the corresponding forma  parameter type by a method invocation conversion.
 *  @exception InvocationTargetException if the underlying method   throws an exception.
 *  @exception NullPointerException      if the specified object is null   and the method is an instance method.
 *  @exception ExceptionInInitializerError if the initialization provoked by this method fails.
 */

欸丶瓜子
0 声望0 粉丝