乐趣区

关于springboot:Spring-Aop-动态代理

1. 代理模式

代理是设计模式的一种,代理类为委托类提供音讯预处理,音讯转发,预先音讯解决等性能。Java 中的代理分为三种角色:代理类、委托类、接口

为了放弃行为的一致性,代理类和委托类通常会实现雷同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这两头一层,能无效管制对委托类对象的间接拜访,也能够很好地暗藏和爱护委托类对象,同时也为施行不同控制策略预留了空间,从而在设计上取得了更大的灵活性。Java 动静代理机制以奇妙的形式近乎完满地实际了代理模式的设计理念。

Java 中的代理依照代理类生成机会不同又分为 动态代理 动静代理

  • 动态代理:动态代理的特点是, 为每一个业务加强都提供一个代理类, 由代理类来创立代理对象. 上面咱们通过动态代理来实现对转账业务进行身份验证.
  • 动静代理:动态代理会为每一个业务加强都提供一个代理类, 由代理类来创立代理对象, 而动静代理并不存在代理类, 代理对象间接由代理生成工具动静生成.

1.2. 动态代理

Java 中的动态代理要求代理类 (ProxySubject) 和委托类 (RealSubject) 都实现同一个接口(Subject)。如下示例:

接口 Subject.java

public interface Subject {public void sayHello();
    
}

委托类 RealSubject.java

public class RealSubject implements Subject {

    @Override
    public void sayHello() {System.out.println("hello!");
    }
    
}

代理类 ProxySubject.java

class ProxySubject implements Subject {
    private Subject subject;

    public ProxySubject(Subject subject) {this.subject = subject;}
    
    @Override
    public void sayHello() {System.out.println("Before say hello...");
        subject.sayHello();
        System.out.println("After say hello...");
    }
    
}

测试方法 main

    public static void main(String[] args) {Subject subject = new RealSubject();
        ProxySubject proxySubject = new ProxySubject(subject);
        proxySubject.sayHello();}

依照示例中,代理模式实现的性能,能够了解成一个简易版的 Spring AOP 实现,那咱们就拿代理模式和 Spring AOP 做比照。

代理模式的组成包含:接口、委托类和代理类。咱们在 Spring 中应用 AOP,通常针对的“切面”,也就是委托类会有很多。接口和委托类是业务代码,必不可少,但代理类这是为了代理模式而创立的。如果每个委托类对应代理类的逻辑都不一样还好,可 如果多个委托类复用同一个代理类办法,就显得很冗余了

1.2. jdk 动静代理

为了解决这类问题,jdk 有提供动静代理的实现,即提供可复用的代理类。动静代理就是要生成一个包装类对象,因为代理的对象是动静的,所以叫动静代理。

JDK 动静代理是应用 java.lang.reflect 包下的代理类来实现. JDK 动静代理动静代理必须要有接口.

因为咱们须要加强,这个加强是须要留给开发人员开发代码的,因而代理类不能间接蕴含被代理对象,而是一个 InvocationHandler,该 InvocationHandler 蕴含被代理对象,并负责散发申请给被代理对象,散发前后均能够做加强。从原理能够看出,JDK 动静代理是“对象”的代理。

下面的代码实现,能够批改成上面这种形式,同样能实现性能。

接口 Subject.java

public interface Subject {public void sayHello();
    
}

委托类 RealSubject.java

public class RealSubject implements Subject {

    @Override
    public void sayHello() {System.out.println("hello!");
    }
    
}

代理类 InvocationHandlerImpl.java

public class InvocationHandlerImpl implements InvocationHandler {

    private Object object;

    public InvocationHandlerImpl(Object object) {this.object = object;}

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before say hello...");
        Object returnValue = method.invoke(subject, args);
        System.out.println("After say hello...");
        return returnValue;
    }
}

测试方法 main

    public static void main(String[] args) {Subject realSubject = new RealSubject();
        InvocationHandler handler = new InvocationHandlerImpl(realSubject);
        ClassLoader loader = realSubject.getClass().getClassLoader();
        Class[] interfaces = realSubject.getClass().getInterfaces();
        Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);
        subject.sayHello();}

1.3. cglib 动静代理

JDK 动静代理必须要有接口, 但如果要代理一个没有接口的类该怎么办呢? 这时咱们能够应用 CGLIB 动静代理. CGLIB 动静代理的原理是生成指标类的子类, 这个子类对象就是代理对象, 代理对象是被加强过的.

留神: 不论有没有接口都能够应用 CGLIB 动静代理, 而不是只有在无接口的状况下能力应用.

委托类 RealSubject.java

public class RealSubject {public void sayHello() {System.out.println("hello!");
    }
}

代理类 MethodInterceptorImpl.java

public class MethodInterceptorImpl implements MethodInterceptor {

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println("Before say hello...");
        Object returnValue= methodProxy.invokeSuper(obj, args);
        System.out.println("After say hello...");
        return returnValue;
    }

}

测试方法 main

    public static void main(String[] args) {RealSubject target = new RealSubject();
        RealSubject proxy = (RealSubject) Enhancer.create(target.getClass(),
                new MethodInterceptorImpl());
        proxy.sayHello();}

2. Spring AOP 实现原理

对于 Spring AOP 的概念我就不多说了,大家都晓得是基于动静代理实现的,就是下面咱们说的这些,那么具体是怎么实现的呢?

在代理模式中有三种外围的角色:委托类、代理类、接口 ,而 gclib 动静代理中“接口”是非必须的,因而咱们关注 Spring AOP 中 委托类 代理类 的实现。

委托类

回顾一下 Aop 的实现代码:须要在实现类上加上 @Aspect 的注解,还须要通过 @Pointcut 注解来申明“切点”,即委托类和委托办法的门路。

有了这些信息就足够获取委托类了。这里充沛用到 Java 反射,先找到蕴含 @Aspect 注解的类,而后找到该类下的 @Pointcut 注解,读取所定义的委托类和委托办法门路,就齐全能拿到委托类对象。

代理类

因为咱们应用的是动静代理,这里的代理类能够被替换成 代理办法 。同样,咱们在@Aspect 注解的类中,用 @Around、@Before、@After 润饰的办法,就是咱们想要的代理办法。

总结
咱们能够通过 BeanFactoryPostProcessor 的实现类,实现对所有 BeanDefinition 的扫描,找出咱们定义的所有的切面类,而后循环外面的办法,找到切点、以及所有的告诉办法,而后依据注解判断告诉类型(也就是前置,后置还是盘绕),最初解析切点的内容,扫描出所有的指标类。这样就获取了 委托类 代理办法

当初 委托类 代理办法 都有了,咱们晓得在动静代理模式中,最终的目标是将委托类的办法执行,替换成代理类的办法执行。然而在 Spring 中,咱们是感知不到 代理类 的,咱们在代码中还是调用原 委托类 的办法,那么 Spring 框架是如何神不知鬼不觉地将 委托类 替换成 代理类 的呢?

这就波及到咱们之前无关 Ioc 文章的内容了,在 Bean 的生命周期中,Bean 在初始化前后会执行 BeanPostProcessor 的办法。能够把它了解成一个加强办法,能够将原始的 Bean 通过“加强”解决后加载到 Ioc 容器中。这就是一个人造的代理办法,原始的 Bean 就是 委托类,在此处实现代理办法生成代理类,再将代理类加载进 Ioc 容器。

3. jdk 动静代理和 cglib 比照

动静代理 cglib jdk
是否提供子类代理
是否提供接口代理
区别 必须依赖于 CGLib 的类库,然而它须要类来实现任何接口代理的是指定的类生成一个子类,笼罩其中的办法 实现 InvocationHandler,应用 Proxy.newProxyInstance 产生代理对象,被代理的对象必须要实现接口

Cglib 和 jdk 动静代理的区别?

1、Jdk 动静代理:利用拦截器(必须实现 InvocationHandler)加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用 InvokeHandler 来解决
2、Cglib 动静代理:利用 ASM 框架,对代理对象类生成的 class 文件加载进来,通过批改其字节码生成子类来解决

什么时候用 cglib 什么时候用 jdk 动静代理?

1、指标对象生成了接口 默认用 JDK 动静代理
2、如果指标对象应用了接口,能够强制应用 cglib
3、如果指标对象没有实现接口,必须采纳 cglib 库,Spring 会主动在 JDK 动静代理和 cglib 之间转换

JDK 动静代理和 cglib 字节码生成的区别?

1、JDK 动静代理只能对实现了接口的类生成代理,而不能针对类
2、Cglib 是针对类实现代理,次要是对指定的类生成一个子类,笼罩其中的办法,并笼罩其中办法的加强,然而因为采纳的是继承,所以该类或办法最好不要生成 final,对于 final 类或办法,是无奈继承的

Cglib 比 JDK 快?

1、cglib 底层是 ASM 字节码生成框架,然而字节码技术生成代理类,在 JDL1.6 之前比应用 java 反射的效率要高
2、在 jdk6 之后逐渐对 JDK 动静代理进行了优化,在调用次数比拟少时效率高于 cglib 代理效率
3、只有在大量调用的时候 cglib 的效率高,然而在 1.8 的时候 JDK 的效率已高于 cglib
4、Cglib 不能对申明 final 的办法进行代理,因为 cglib 是动静生成代理对象,final 关键字润饰的类不可变只能被援用不能被批改

Spring 如何抉择是用 JDK 还是 cglib?

1、当 bean 实现接口时,会用 JDK 代理模式
2、当 bean 没有实现接口,用 cglib 实现
3、能够强制应用 cglib(在 spring 配置中退出 <aop:aspectj-autoproxy proxyt-target-class=”true”/>)

退出移动版