zoukankan      html  css  js  c++  java
  • 动态代理与AOP

    1. 代理的分类:

      静态代理:每个代理类只能为一个接口服务

      动态代理:可以通过一个代理类完成全部的代理功能(由JVM生成实现一系列接口的代理类,即:生成实现接口的类的代理)

    2. 动态代理:

    在Java中要想实现动态代理机制,需要 java.lang.reflect.InvocationHandler 接口和 java.lang.reflect.Proxy 类的支持

    java.lang.reflect.InvocationHandler 接口的定义如下:

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

    Object proxy:被代理的对象

    Method method:要调用的方法

    Object[] args:方法调用时传递的参数

    注意并不是对象的所有方法均通过代理来完成,对如继承自Object的方法,只将hashCode()、equals()、toString()转交给InvocationHandler来处理。

    可以通过Proxy中的getProxyClass()或newProxyInstance()方法获取代理

    其中newProxyInstance()方法定义如下:

    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

    ClassLoader loader:类加载器

    Class<?>[] interfaces:要实现的接口

    InvocationHandler h:InvocationHandler子类的实例对象

    下面来实现动态代理:

    首先定义接口:

    1 public interface Sourceable {  
    2     void function();
    3 }  

    然后定义实现类:

    1 public class Source implements Sourceable {
    2 
    3     @Override
    4     public void function() {
    5         System.out.println("function");
    6     }
    7 }

    定义工具类:

     1 import java.lang.reflect.Constructor;
     2 import java.lang.reflect.InvocationHandler;
     3 import java.lang.reflect.Method;
     4 import java.lang.reflect.Proxy;
     5 
     6 public class SourceableProxyUtil {
     7     private Sourceable source;
     8 
     9     public SourceableProxyUtil(Sourceable source) {
    10         super();
    11         this.source = source;
    12     }
    13 
    14     public Sourceable getProxy() throws Exception {
    15         Class<Sourceable> clazzSource = (Class<Sourceable>) Proxy
    16                 .getProxyClass(source.getClass().getClassLoader(), source
    17                         .getClass().getInterfaces());
    18         Constructor<Sourceable> constructor = clazzSource
    19                 .getConstructor(InvocationHandler.class);  // 没有无参构造函数
    20 
    21         return constructor.newInstance(new InvocationHandler() {
    22 
    23             @Override
    24             public Object invoke(Object proxy, Method method, Object[] args)
    25                     throws Throwable {
    26                 System.out.println("before");
    27                 Object retVal = method.invoke(source, args);
    28                 System.out.println("after");
    29                 return retVal;
    30             }
    31         });
    32     }
    33 }

    但是,使用getProxyClass()方式过于复杂,改用newProxyInstance():

     1 import java.lang.reflect.Constructor;
     2 import java.lang.reflect.InvocationHandler;
     3 import java.lang.reflect.Method;
     4 import java.lang.reflect.Proxy;
     5 
     6 public class SourceableProxyUtil {
     7     private Sourceable source;
     8 
     9     public SourceableProxyUtil(Sourceable source) {
    10         super();
    11         this.source = source;
    12     }
    13 
    14     public Sourceable getProxy() throws Exception {
    15         return (Sourceable)Proxy.newProxyInstance(source.getClass().getClassLoader(),
    16                 source.getClass().getInterfaces(), new InvocationHandler() {
    17 
    18                     @Override
    19                     public Object invoke(Object proxy, Method method,
    20                             Object[] args) throws Throwable {
    21                         System.out.println("before");
    22                         Object retVal = method.invoke(source, args);
    23                         System.out.println("after");
    24                         return retVal;
    25                     }
    26                 });
    27     }
    28 }

    测试代码:

    1 public class Main {
    2 
    3     public static void main(String[] args) throws Exception {
    4         Sourceable source = new Source();
    5         Sourceable proxy = new SourceableProxyUtil(source).getProxy();
    6         proxy.function();
    7     }
    8 }

    测试结果:

    before
    function
    after

    可以看出,代理类能够正常使用。

    但是上述方式只能用于生成Sourceable接口的代理类,可以进一步将其抽象得到通用的代理类生成工具:

     1 import java.lang.reflect.InvocationHandler;
     2 import java.lang.reflect.Method;
     3 import java.lang.reflect.Proxy;
     4 
     5 public final class ProxyUtil {
     6     public static Object getProxy(final Object target) throws Exception {
     7         return Proxy.newProxyInstance(target.getClass().getClassLoader(),
     8                 target.getClass().getInterfaces(), new InvocationHandler() {
     9 
    10                     @Override
    11                     public Object invoke(Object proxy, Method method,
    12                             Object[] args) throws Throwable {
    13                         System.out.println("before");
    14                         Object retVal = method.invoke(target, args);
    15                         System.out.println("after");
    16                         return retVal;
    17                     }
    18                 });
    19     }
    20 }

    这样,无论任何类型的对象都可以通过此种方法获得相应的代理对象。此时,测试代码如下:

    1 public class Main {
    2 
    3     public static void main(String[] args) throws Exception {
    4         // TODO Auto-generated method stub
    5         Sourceable proxy = (Sourceable)ProxyUtil.getProxy(new Source());
    6         proxy.function();
    7     }
    8 }

    3. 抽取切面:

    将上述代码中的目标方法执行前后的操作进一步地抽象,将其抽取为切面。将切面代码进行封装得到通告。

    定义通告接口:

    1 public interface Advice {
    2     void before();
    3     void after();
    4 }

    改写工具类:

     1 import java.lang.reflect.InvocationHandler;
     2 import java.lang.reflect.Method;
     3 import java.lang.reflect.Proxy;
     4 
     5 public final class ProxyUtil {
     6     public static Object getProxy(final Object target, final Advice advice) throws Exception {
     7         return Proxy.newProxyInstance(target.getClass().getClassLoader(),
     8                 target.getClass().getInterfaces(), new InvocationHandler() {
     9 
    10                     @Override
    11                     public Object invoke(Object proxy, Method method,
    12                             Object[] args) throws Throwable {
    13                         advice.before();
    14                         Object retVal = method.invoke(target, args);
    15                         advice.after();
    16                         return retVal;
    17                     }
    18                 });
    19     }
    20 }

    此时的测试代码为:

     1 public class Main {
     2 
     3     public static void main(String[] args) throws Exception {
     4         Sourceable proxy = (Sourceable)ProxyUtil.getProxy(new Source(), new Advice() {
     5                         
     6             @Override
     7             public void before() {
     8                 // TODO Auto-generated method stub
     9                 System.out.println("before");
    10             }
    11             
    12             @Override
    13             public void after() {
    14                 // TODO Auto-generated method stub
    15                 System.out.println("after");
    16             }
    17         });
    18         proxy.function();
    19     }
    20 }

    此种方式更具有普遍适用性,无论对于任意类型都可以产生相对应的代理对象,同时可以自定义所要进行的操作(通过通告)。

    在整个过程中需要手动写的只有通告Advice

    4. AOP编程:

    AOP(Aspect-Oriented Programming)可以通过代理方式完成诸如:安全控制、事务管理、性能统计、日志记录、错误处理...等功能。

    在实际过程中,更多是通过getter/setter方式传递target与advice的:

     1 import java.lang.reflect.InvocationHandler;
     2 import java.lang.reflect.Method;
     3 import java.lang.reflect.Proxy;
     4 
     5 public class ProxyFactoryBean {
     6 
     7     private Object target;
     8     private Advice advice;
     9 
    10 
    11     public Object getTarget() {
    12         return target;
    13     }
    14 
    15 
    16     public void setTarget(Object target) {
    17         this.target = target;
    18     }
    19 
    20 
    21     public Advice getAdvice() {
    22         return advice;
    23     }
    24 
    25 
    26     public void setAdvice(Advice advice) {
    27         this.advice = advice;
    28     }
    29 
    30 
    31     public Object getProxy() {
    32 
    33         return Proxy.newProxyInstance(target.getClass().getClassLoader(),
    34                 target.getClass().getInterfaces(), new InvocationHandler() {
    35 
    36                     @Override
    37                     public Object invoke(Object proxy, Method method,
    38                             Object[] args) throws Throwable {
    39                         advice.before();
    40                         Object retVal = method.invoke(target, args);
    41                         advice.after();
    42                         return retVal;
    43                     }
    44                 });
    45     };
    46 }

    通过AOP的方式可以在运行时动态地将代码切入到类的指定方法、指定位置上。

    Spring框架的核心即是IoC和AOP。

  • 相关阅读:
    表格标签
    图片标签
    超链接标签
    媒体标签
    实体标签
    html常用的标签
    头信息的作用
    【bzoj5017】[Snoi2017]炸弹 线段树优化建图+Tarjan+拓扑排序
    【bzoj3309】DZY Loves Math 莫比乌斯反演+线性筛
    【bzoj4010】[HNOI2015]菜肴制作 拓扑排序+堆
  • 原文地址:https://www.cnblogs.com/joshua-aw/p/6031748.html
Copyright © 2011-2022 走看看