基础篇——代理模式之SpringAOP

Posted 不浪小生

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基础篇——代理模式之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处理之后,可以直接对实体类进行动态代理。

以上是关于基础篇——代理模式之SpringAOP的主要内容,如果未能解决你的问题,请参考以下文章

Spring AOP深入理解之拦截器调用

#yyds干货盘点# 设计模式之代理模式:动态代理

设计模式之动态代理

0.SpringAop之基础

SpringAOP之静态代理

SpringAOP的代理默认是Jdk还是Cglib?