SSH与SSM学习之Spring12——动态代理之实现

  • SSH与SSM学习之Spring12动态代理之实现
    • 一说明
    • 二动态代理主要涉及到的类
      • 1 Proxy
      • 2 InvocationHandler
    • 三获取代理类并且打印构造方法和方法
    • 四通过构造函数来创建代理实例
    • 五通过 ProxynewProxyInstance 实现动态代理
    • 六源码下载

SSH与SSM学习之Spring12——动态代理之实现

一、说明

为了增强某个类的方法,我们可以使用代理模式来处理。例如静态代理和动态代理

静态代理查看 静态代理

要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,

将是一件非常麻烦的事情!所以我们就得使用动态代理。


二、动态代理主要涉及到的类

2.1 Proxy

动态代理,使用的 Proxy 类,在 java.lang.reflect 包下。

Proxy主要有以下方法

//返回指定代理实例的调用处理程序。
public static InvocationHandler getInvocationHandler(Object proxy)

//返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。
public static Class getProxyClass(ClassLoader loader,Class... interfaces)

//返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
public static Object newProxyInstance(ClassLoader loader,
                                          Class[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException

上面三个方法中,我们使用动态代理,其实使用到的只有 newProxyInstance。其他方法可以不用关心。

2.2 InvocationHandler

这是一个接口,在 java.lang.reflect 包下。动态代理时候,代理方法的处理就是通过实现这个接口中的 invoke 方法来实现的。

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

三、获取代理类并且打印构造方法和方法

上面的方法说明中,我们知道了,想要获取代理类,我们可以通过如下的方法来获取。需要说明的是,动态代理的前提是被代理类一点要有接口。

//返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。
public static Class getProxyClass(ClassLoader loader,Class... interfaces)

下面的代码我们只是获取代理类和打印它的方法,这里我们代理的是 Collection 接口

    /**
     * 获取代理类
     */
    @Test
    public void test1(){
        //1.获取代理类
        Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);
        System.out.println(clazzProxy.getName());

        //2.获取构造方法,并且打印出来
        Constructor[] constructors = clazzProxy.getConstructors();
        System.out.println("--------constructors list--------");
        for (Constructor constructor : constructors) {
            StringBuilder sb = new StringBuilder( constructor.getName() );
            sb.append('(');
            Class[] clazzParams = constructor.getParameterTypes();
            for (Class clazzParam : clazzParams) {
                sb.append(clazzParam.getName()).append(",");
            }
            //删除最后一个逗号
            if(clazzParams!=null && clazzParams.length !=0){
                sb.deleteCharAt(sb.length()-1);
            }
            sb.append(')');
            System.out.println(sb.toString());
        }

        //3.获取方法,并且打印
        Method[] methods = clazzProxy.getMethods();
        System.out.println("--------methods list--------");
        for (Method method : methods) {
            StringBuilder sb = new StringBuilder( method.getName() );
            sb.append('(');
            Class[] clazzParams = method.getParameterTypes();
            for (Class clazzParam : clazzParams) {
                sb.append(clazzParam.getName()).append(",");
            }
            //删除最后一个逗号
            if(clazzParams!=null && clazzParams.length !=0){
                sb.deleteCharAt(sb.length()-1);
            }
            sb.append(')');
            System.out.println(sb.toString());
        }
    }

结果

com.sun.proxy.$Proxy4
--------constructors list--------
com.sun.proxy.$Proxy4(java.lang.reflect.InvocationHandler)
--------methods list--------
add(java.lang.Object)
remove(java.lang.Object)
equals(java.lang.Object)
toString()
hashCode()
clear()
.....

四、通过构造函数来创建代理实例

其实这里我们这个处理中,我们就实现了动态代理。

这里我们通过获取代理类,然后获取使用它的构造方法,来创建一个代理实例。目标对象是 ArrayList。

实现的结果是,再调用ArrayList的方法时候,调用前和调用后都打印数据。

下面是实现代码

    /**
     * 通过构造函数来创建代理实例
     */
    @Test
    public void test2() throws Exception {
        //1.创建一个代理类
        Class clazzProxy = Proxy.getProxyClass(ArrayList.class.getClassLoader(),ArrayList.class.getInterfaces());

        //2.获取构造方法
        Constructor constructor = clazzProxy.getConstructor(InvocationHandler.class);

        //3.通过构造方法创建实例
        Collection arrayListProxy = (Collection) constructor.newInstance(new MyInvocationHandler(new ArrayList()));

//       4.添加数据
        arrayListProxy.add("1");
        arrayListProxy.add("2");

        arrayListProxy.isEmpty();
        arrayListProxy.remove("2");
    }

     public class MyInvocationHandler implements InvocationHandler{

            private Object target;

            public MyInvocationHandler(Object target){
                this.target = target;
            }

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("----------------------------");
                System.out.println(method.getName() + " : 方法调用之前----");
                Object obj = method.invoke(target,args);
                System.out.println(method.getName() + " : 方法掉用之后");
                System.out.println("----------------------------");
                return obj;
            }
        }

结果

----------------------------
add : 方法调用之前----
add : 方法掉用之后
----------------------------
----------------------------
add : 方法调用之前----
add : 方法掉用之后
----------------------------
----------------------------
isEmpty : 方法调用之前----
isEmpty : 方法掉用之后
----------------------------
----------------------------
remove : 方法调用之前----
remove : 方法掉用之后
----------------------------

五、通过 Proxy.newProxyInstance 实现动态代理

上面我们实现了动态代理,但是如果每次都那么弄,麻烦得不要不要的。有没有简单的呢?肯定是有的,而且真是使用中,我们也只会使用这种方式来实现动态代理。

//返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
public static Object newProxyInstance(ClassLoader loader,
                                          Class[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException

这里我们还是使用上面的 MyInvocationHandler 处理类,来动态代理 HashSet 类

    @Test
    public void test3(){
        Collection arrayListProxy = (Collection)Proxy.newProxyInstance(HashSet.class.getClassLoader(),
                                                                       HashSet.class.getInterfaces(),
                                                                       new MyInvocationHandler(new HashSet()));
        arrayListProxy.add("jjj");
    }

结果

----------------------------
add : 方法调用之前----
add : 方法掉用之后
----------------------------

或者我们代码封装一下,实现传入我们目标实例,生成代理对象。代码如下

    @Test
    public void test5(){
         Map map = (Map)getProxyObj(new HashMap());
         map.put("name","xiaoming");
         map.remove("name");
    }

    public Object getProxyObj(Object target){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("----------------------------");
                System.out.println(method.getName() + " : 方法调用之前----");
                Object obj = method.invoke(target,args);
                System.out.println(method.getName() + " : 方法掉用之后");
                System.out.println("----------------------------");
                return obj;
            }
        });
    }

结果

----------------------------
put : 方法调用之前----
put : 方法掉用之后
----------------------------
----------------------------
remove : 方法调用之前----
remove : 方法掉用之后
----------------------------

六、源码下载

https://github.com/wimingxxx/spring01/blob/master/src/com/qwm/spring2/c_proxy/Demo.java

你可能感兴趣的:(SSH与SSM学习)