AOP讲解


代理模式 (Proxy Pattren)  也称为委托模式,是属于结构型设计模式,作用是为为其它对象提供一种代理以控制对这个对象的访问。简单来说这就是给目标对象生成一个代理对象,并由代理对象控制对目标对象的引用。

组成

  • Subject:抽象角色,是一个接口。该接口是真实对象和它的代理共用的接口;
  • RealSubject:真实角色,被代理对象。
  • Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实对象。

 

静态代理

优点:可以做到在不修改目标对象的功能前提下,对目标功能扩展。
缺点 : 因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类。

Subject 抽象角色接口

public interface Subject{
    void hello();
}

RealSubject 真实角色

public class RealSubject implements Subject{
    @Override
    public void hello(){
        System.out.println("RealSubject");
    }
}

Proxy 代理类

public class Proxy implements Subject {
    private Subject subject = null;
    @Override
    public void hello(){
        if(subject == null)
        subject = new RealSubject();
        System.out.print("Hello, I'm A Proxy, I'm invoking...");
        this.subject.hello();
        System.out.print("Hello, I'm A Proxy, I'm Done...");
    }
}

 

动态代理

动态代理即代理类是程序在运行时创建的,使用动态代理,可以编写通用的代理逻辑,用于各种类型的被代理对象,而不需要为每个被代理的类型都创建一个静态代理类。

jdk动态代理

使用示例

1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法
2.创建被代理的类以及接口
3.通过Proxy的静态方法
newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理
4.通过代理调用方法

//接口
publicinterfaceHello{
    voidsayHello();
}
//实现类
public class Amy implements Hello{
    private String name;
    public Amy(String name) {
        this.name = name;
    }
    public void sayHello() {
        System.out.println(name + " say hello");
    }
}
//动态代理类,必须实现InvocationHandler接口
public class ProxyHello implements InvocationHandler {
    private Hello hello;
    public ProxyHello(Hello hello) {
        this.hello = hello;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before say hello");
        method.invoke(hello, args);
        System.out.println("after say hello");
        return null;
    }
}
//测试
public class Test {
    public static void main(String[] args) {
        Amy amy = new Amy("Amy");
        ProxyHello proxyHello = new ProxyHello(amy);
        Hello helloProxy = (Hello) Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(),
                new Class[]{Hello.class},
                proxyHello);
        helloProxy.sayHello();
    }
}
创建代理类

JDK提供的生成代理类的方法是调用Proxy的静态方法newProxyInstance 生成代理类,它需要三个参数。
1)loader表示类加载器。
2)interfaces表示代理类要实现的接口列表,是一个数组,元素的类型只能是接口,不能是普通的类。
3)实现了InvocationHandler 的代理对象。

InvocationHandler

JDK 动态代理类必须实现反射包中的 java.lang.reflect.InvocationHandler 接口,在此接口中只有一个 invoke() 方法
invoke()方法:所有执行代理对象的方法都会被替换成执行invoke方法。

// Object proxy 代理
// Method method 方法
// Object[] args 参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before say hello");
        method.invoke(hello, args);
        System.out.println("after say hello");
}
newProxyInstance()
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h) throws IllegalArgumentException {
    //验证传入的InvocationHandler不能为空
    Objects.requireNonNull(h);
    //复制代理类实现的所有接口
    final Class<?>[] intfs = interfaces.clone();
    //获取安全管理器
    final SecurityManager sm = System.getSecurityManager();
    //进行一些权限检验
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }
    // 该方法先从缓存获取代理类, 如果没有再去生成一个代理类
    // 如果缓存中已经存在相应接口的代理类,直接返回;否则,使用ProxyClassFactory创建代理类
    // getProxyClass0方法生成代理类的字节码文件。
    Class<?> cl = getProxyClass0(loader, intfs);
    try {
        //进行一些权限检验
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }
        //获取参数类型是InvocationHandler.class的代理类构造器
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        //如果代理类是不可访问的, 就使用特权将它的构造器设置为可访问
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
        }
        //传入InvocationHandler实例去构造一个代理类的实例
        //所有代理类都继承自Proxy, 因此这里会调用Proxy的构造器将InvocationHandler引用传入
        return cons.newInstance(new Object[]{h});
    } catch (Exception e) {
        throw new InternalError(e.toString(), e);
    }
}
getProxyClass0()
private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        // 限定代理的接口不能超过65535个
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
        // 从缓冲中获取代理类,如果缓存中不存在再通过ProxyClassFactory创建
        // 缓存是通过弱引用来缓存,弱引用很适合用来做缓存
        // proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory())
        return proxyClassCache.get(loader, interfaces);
    }
ProxyClassFactory
private static final class ProxyClassFactory
            implements BiFunction[], Class<?>>
    {
        // 代理类前缀
        private static final String proxyClassNamePrefix = "$Proxy";
        // 生成代理类名称的计数器
        private static final AtomicLong nextUniqueNumber = new AtomicLong();
        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
            Map, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                /*
                 * 校验类加载器是否能通过接口名称加载该类
                 */
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                            intf + " is not visible from class loader");
                }
                /*
                 * 校验该类是否是接口类型
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                            interfaceClass.getName() + " is not an interface");
                }
                /*
                 * 校验接口是否重复
                 */
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                            "repeated interface: " + interfaceClass.getName());
                }
            }
            String proxyPkg = null;     // 代理类包名
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
            /*
             * 非public接口,代理类的包名与接口的包名相同
             */
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                                "non-public interfaces from different packages");
                    }
                }
            }
            if (proxyPkg == null) {
                // public代理接口,使用com.sun.proxy包名
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }
            /*
             * 为代理类生成名字
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;
            /*
             * 真正生成代理类的字节码文件的地方,生成表示代理类class对象的二进制
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                    proxyName, interfaces, accessFlags);
            try {
                // 使用类加载器将代理类的字节码文件加载到JVM中
                return defineClass0(loader, proxyName,
                        proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                throw new IllegalArgumentException(e.toString());
            }
        }
    }
最终生成的代理类
已经继承了proxy类 所有不能多继承
public final class $Proxy0 extends Proxy implements Hello {
  private static Method m1;
  private static Method m3;
  private static Method m2;
  private static Method m0;
  public $Proxy0(InvocationHandler paramInvocationHandler) {
    super(paramInvocationHandler);
  }
  public final boolean equals(Object paramObject) {
    try {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    } catch (Error|RuntimeException error) {
      throw null;
    } catch (Throwable throwable) {
      throw new UndeclaredThrowableException(throwable);
    } 
  }
  public final void sayHello() {
    try {
      this.h.invoke(this, m3, null);
      return;
    } catch (Error|RuntimeException error) {
      throw null;
    } catch (Throwable throwable) {
      throw new UndeclaredThrowableException(throwable);
    } 
  }
  public final String toString() {
    try {
      return (String)this.h.invoke(this, m2, null);
    } catch (Error|RuntimeException error) {
      throw null;
    } catch (Throwable throwable) {
      throw new UndeclaredThrowableException(throwable);
    } 
  }
  public final int hashCode() {
    try {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    } catch (Error|RuntimeException error) {
      throw null;
    } catch (Throwable throwable) {
      throw new UndeclaredThrowableException(throwable);
    } 
  }
  static {
    try {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m3 = Class.forName("com.lhd.proxy.Hello").getMethod("sayHello", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      return;
    } catch (NoSuchMethodException noSuchMethodException) {
      throw new NoSuchMethodError(noSuchMethodException.getMessage());
    } catch (ClassNotFoundException classNotFoundException) {
      throw new NoClassDefFoundError(classNotFoundException.getMessage());
    } 
  }
}
通过这个代理类我们可以知道JDK动态代理为什么只能基于接口的代理,因为该代理类继承了Proxy类,而java是不支持多继承的,所有只能通过实现接口来实现代理。
jdk动态代理是通过反射来实现的。

cglib 动态代理

什么是CGLIB?

CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。

原理

CGLIB原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。
CGLIB底层:使用字节码处理框架ASM,来转换字节码并生成新的类

使用
public class Student {
    private String name = "zhangsan";
    public String getStuName() {
        return name;
    }
}
public class CglibMethodInterceptTest {
 public static void main(String[] args) {
  //创建一个Enhancer对象
  Enhancer enchaner = new Enhancer();
  //设置被代理的类
  enchaner.setSuperclass(Student.class);
  //创建一个回调接口
  Callback interceptor = new MethodInterceptor() {
   @Override
   public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
     throws Throwable {
    System.err.println("原方法名是 : " + method.getName());
    System.err.println("原方法声明的类为 " + method.getDeclaringClass());
    System.err.println("我是 " + (String) proxy.invokeSuper(obj, args));
    System.err.println("我调用结束了");
    return null;
   }
  };
  enchaner.setCallback(interceptor);
  Student student = (Student) enchaner.create();
  student.getStuName();
 }
}
原方法名是 : getStuName
原方法声明的类为 class com.iminer.Student
我是 zhangsan
我调用结束了

Spring中的应用

AOP介绍

AOP(Aspect Orient Programming),作为面向对象编程的一种补充,广泛应用于处理一些具有横切性质的系统级服务,如日志收集、事务管理、安全检查、缓存、对象池管理等。AOP实现的关键就在于AOP框架自动创建的AOP代理,AOP代理则可分为静态代理和动态代理两大类,其中静态代理是指使用AOP框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强;而动态代理则在运行时借助于JDK动态代理、CGLIB等在内存中“临时”生成AOP动态代理类,因此也被称为运行时增强。
面向切面的编程(AOP) 是一种编程范式,旨在通过允许横切关注点的分离,提高模块化。AOP提供切面来将跨越对象关注点模块化。虽然现在可以获得许多AOP框架,但在这里我们要区分的只有两个流行的框架:Spring AOP和AspectJ。

AspectJ

AspectJ是一个面向切面的框架,他定义了AOP的一些语法,有一个专门的字节码生成器来生成遵守java规范的 class文件。
AspectJ的通知类型不仅包括我们之前了解过的三种通知:前置通知、后置通知、环绕通知,在Aspect中还有异常通知以及一种最终通知即无论程序是否正常执行,最终通知的代码会得到执行。

Spring AOP

1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

Spring AOP 与 AspectJ的关系

其实两者没有关系,当你用到Spring AOP提供的注入@Before、@After等注解时,Spring AOP和AspectJ就有了关系。Spring AOP支持注解,在使用@Aspect注解创建和配置切面时将更加方便。而使用AspectJ,需要通过.aj文件来创建切面,并且需要使用ajc(Aspect编译器)来编译代码。Spring AOP其实现原理是JDK动态代理,在运行时生成代理类。为了启用Spring对@AspectJ切面配置的支持,并保证Spring容器中的目标Bean被一个或多个切面自动增强,必须在Spring配置文件中添加如下配置


如需强制开启cglib代理则需要更改配置

Filter、Interceptor、Aspect?

filter -> interceptor -> ControllerAdvice -> aspect -> controller

Filter过滤器
过滤器可以拦截到方法的请求和响应(ServletRequest request, ServletResponse response),并对请求响应做出过滤操作。过滤器依赖于servlet容器。在实现上,基于函数回调,它可以对几乎所有请求进行过滤,一个过滤器实例只能在容器初始化时调用一次。
1.启动服务器时加载过滤器的实例,并调用init()方法来初始化实例;
2.每一次请求时都只调用方法doFilter()进行处理;
3.停止服务器时调用destroy()方法,销毁实例。

Interceptor拦截器
依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。在实现上,基于Java的反射机制,属于面向切面编程(AOP)的一种运用,就是在一个方法前,调用一个方法,或者在方法后,调用一个方法。

Aspect切片
AOP操作可以对操作进行横向的拦截,最大的优势在于他可以获取执行方法的参数,对方法进行统一的处理。常见使用日志,事务,请求参数安全验证等。