• 基础篇——代理模式之SpringAOP


    一、AOP概述
      AOP思想的实现一般都是基于代理模式,JDK动态代理模式只能代理接口,而不能代理类。但是SpringAOP同时支持CGLIB、JDK、ASPECTJ这三种代理模式,因此,如果目标的对象实现了接口,SpringAOP将会采用JDK动态代理,如果目标对象没有实现接口,SpringAOP切换成CGLIB动态代理。
    在实际情况下,我们通常需要对一个接口实现多个切面,比如日志打印、分布式锁、权限校验等等。这时候我们就会面临一个优先级的问题,这么多的切面如何告知Spring执行顺序呢?这就需要我们定义每个切面的优先级,我们可以使用注解@Order(i)来表示切面的优先级,i的值越小,优先级越高。假设我们现在一共有两个切面,一个日志处理切面,设置其@Order(100);一个分布式锁,设置其@Order(99),那么分布式锁的优先级是高于日志处理的,对于通知@Before来说,先执行分布式锁,再执行日志处理;对于通知@After和@AfterReturning来说,先执行日志处理,再执行分布式锁,这样就可以完成一个首尾的对应,也可以理解为先进后出原则。
    二、静态代理
      接口类:
    interface Person { void speak();}

      实体类:

    class Actor implements Person {
         private String content;
         public Actor(String content) { this.content = content;}
         public void speak(){
            System.out.println(this.content);
          }
         }

      代理类:

    class Agent implements Person {
            private Actor actor;
            private String before;
            private String after;
            public Agent(Actor actor, String before, String after){
                this.actor = actor;
                this.before = before;
                this.after = after;
             } 
              public void speak(){
                System.out.println("前置通知:" + this.before);
                this.actor.speak();
                System.out.println("后置通知:" + this.after);
             }
         }

      测试类:

    public class StaticProxyTest{
                public static void main(String[] args){
                    Actor actor = new Actor("实体类的真正内容");
                    Agent agent = new Agent(actor, "前置内容", "后置内容");
                    agent.speak();
                 }
             }

    二、动态代理

      1、JDK自带动态代理

              最核心的一个接口和方法,java.lang.reflect包下的InvocationHandler接口:
    public interface InvocationHandler{
                  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
             }
              proxy:被代理的类的实例
              method:调用被代理的类的方法
              args:该方法需要的参数
              我们实现该接口,在invoke方法中调用实体类的方法,并在其前后进行动态操作,从而实现动态代理。另外一个很重要的静态方法是java.lang.reflect包中的Proxy类的newProxyInstance方法:
    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException;
        loader:被代理类的类加载器
             interfaces:被代理类的接口数组
             h:调用处理器类的对象实例
             该方法会返回一个被修改过的类的实例,从而可以自由的调用该实例的方法。
      Person接口:
    public interface Person{
                     public void show();
                 }

      Actor实体类:

    public class Actor implements Person{
                  public void show(){
                       System.out.println("真正实现的方法");
                 }
             }

      Agent代理类:

    public class DynamicAgent{
                  static class Agent implements InvocationHandler{
                    private Object proxy;
                    public Agent(Object proxy){
                         this.proxy = proxy;   
                    }
    
                    public Object invoke(Object object, Method method, Object[] args) throws Throwable {
                        System.out.println("自己添加的前置通知");
                       //真正调用的方法
                       Object res = method.invoke(this.proxy, args);
                       System.out.println("自己添加的后置通知");
                       return res;
                     }
                  public static Object agent(Class interfaceClazz, Object proxy){
                       return Proxy.newProxyInstance(interfaceClazz.getClassLoader(), new Class[]{interfaceClazz}, new Agent(proxy));
                 }
                }
             }

      测试类ReflectTest:

    public class ReflectTest{
                  public static void main(String[] args) throws InvocationTargetException, IllegralAccessException {
                     Person person = (Person)DynamicAgent.agent(Person.class, new Actor());
                     person.show();
                 }
             }

      这样以后对于不同的实现类来说,可以用同一个动态代理类来进行代理,但是这种方法有个缺点,就是被代理的类一定要是实现了某个接口的。

      2、CGLIB动态代理
           同样的CGLIB也有两个重要的方法,在MethodInterceptor接口中的getInstance(Object proxy)和intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)方法 。
      CGLIB代理类CglibAgent:
    public class CglibAgent implements MethodInterceptor {
                private Object proxy;
                 public Object getInstance(Object proxy){
                    this.proxy = proxy;
                    Enhancer enhancer = new Enhancer();
                    enhancer.setSuperclass(this.proxy.getClass);
                    enhancer.setCallback(this);
                    return enhancer.create();     
                 }
                 public Object intercept(Object o, Method method, Object[] object, MethodProxy methodProxy) throws Throwable{
                    System.out.println("自己添加的前置通知");
                    Object res = methodProxy.invokeSuper(this.proxy, args);
                    System.out.println("自己添加的后置通知");
                    return res;
                 }
                  //测试
                  public static void main(String[] args){
                         CglibAgent cglibAgent = new CglibAgent();
                         Actor actor = (Actor)cglibAgent.getInstance(new Actor());
                         actor.show();
                 }
             }

      使用CGLIB处理之后,可以直接对实体类进行动态代理。

  • 相关阅读:
    vim编辑器
    Linux实用命令
    EhCache缓存页面、局部页面和对象缓存
    微信公众号开发之获取微信用户的openID
    23种设计模式概要及易懂的例子
    CSS Hack
    BOM之navigator对象和用户代理检测
    动态脚本
    DOM遍历
    DOM范围
  • 原文地址:https://www.cnblogs.com/zzb-yp/p/12625277.html
Copyright © 2020-2023  润新知