5

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的概念我就不多说了,大家都知道是基于动态代理实现的,就是上面我们说的这些,那么具体是怎么实现的呢?

在代理模式中有三种核心的角色:委托类、代理类、接口,而 cglib 动态代理中“接口”是非必须的,因此我们关注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对比

动态代理cglibjdk
是否提供子类代理
是否提供接口代理
区别必须依赖于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关键字修饰的类不可变只能被引用不能被修改

总结来说:

  • 在1.6和1.7的时候,JDK动态代理的速度要比CGLib动态代理的速度要慢。
  • 在JDK1.8的时候,JDK动态代理的速度已经比CGLib动态代理的速度快很多了。

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

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


KerryWu
641 声望159 粉丝

保持饥饿