代理模式

什么是代理模式

代理模式主要对我们方法执行之前与之后实现增强

代理模式应用场景

日志的采集、权限控制、实现aop、Mybatis mapper、Spring的事务、全局捕获异常、Rpc远程调用、接口分布式事务原理代理数据源

代理模式实现的原理

 

代理模式主要包含三个角色,即抽象主题角色(Subject)、委托类角色(被代理角色,Proxied)以及代理类角色(Proxy),如图所示:

 

 

 

抽象主题角色:可以是接口,也可以是抽象类;

委托类角色:真实主题角色,业务逻辑的具体执行者;

代理类角色:内部含有对真实对象RealSubject的引用,负责对真实主题角色的调用,并在真实主题角色处理前后做预处理和后处理。

静态代理

静态代理需要开发人员手动编写代理类代码

public interface SampleService {

    void hello();
}

 

public class SampleServiceImpl implements SampleService {
    public void hello() {
        System.out.println("hello");
    }
}
@Data
@AllArgsConstructor
public class SampleServiceProxy implements SampleService {

    private SampleService sampleService;


    public void hello() {
        System.out.println("前置增强");
        sampleService.hello();
        System.out.println("后置增强");
    }
}
public class SampleServiceProxyTest {
    public static void main(String[] args) {
        SampleService sampleServiceProxy = new SampleServiceProxy(new SampleServiceImpl());
        sampleServiceProxy.hello();

    }
}

动态代理

动态代理是在实现阶段不用关心代理类,在运行阶段才指定哪一个对象。

动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成。

Jdk动态代理

1.创建被代理的接口和类;

2.实现InvocationHandler接口,对目标接口中声明的所有方法进行统一处理;

3.调用Proxy的静态方法,创建代理类并生成相应的代理对象;

@Data
@AllArgsConstructor
public class JdkInvocationHandler implements InvocationHandler {
    /**
     * 目标对象
     */
    private Object target;


    /**
     *
     * @param proxy 使用jdk程序生成的代理类
     * @param method 目标方法
     * @param args 方法需要传递的参数
     * @return
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("使用Jdk动态代理前置增强" );
        Object result = method.invoke(target, args);
        System.out.println("使用Jdk动态代理后置增强");
        return result;
    }

    /**
     * 生成代理类
     * @param <T>
     * @return
     */
    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }

}
public class JdkInvocationHandlerTest {
    public static void main(String[] args) {
        JdkInvocationHandler jdkInvocationHandler = new JdkInvocationHandler(new SampleServiceImpl());
        SampleService sampleServiceJdkProxy = jdkInvocationHandler.getProxy();
        sampleServiceJdkProxy.hello();
    }

获取代理的生成的class文件

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
public final class $Proxy0 extends Proxy implements SampleService {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void hello() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.hanke.SampleService").getMethod("hello");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

方法m3通过反射获得sampleService的hello方法

注意:继承了Proxy类,实现了代理的接口,由于java不能多继承,这里已经继承了Proxy类了,不能再继承其他的类,所以JDK的动态代理不支持对实现类的代理,只支持接口的代理。

CGLIB动态代理

<dependencies>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.2.12</version>
    </dependency>
</dependencies>
public class CglibSampleServiceProxy implements MethodInterceptor {

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("cglib前置增强");
        Object reuslt = methodProxy.invokeSuper(o, objects);
        System.out.println("cglib后置增强");
        return reuslt;
    }

}
public class CglibSampleServiceProxyTest {
    public static void main(String[] args) {
        CglibSampleServiceProxy cglibSampleServiceProxy = new CglibSampleServiceProxy();
        Enhancer enhancer = new Enhancer();
        // 设置代理类的付类
        enhancer.setSuperclass(SampleServiceImpl.class);
        // 设置回调对象
        enhancer.setCallback(cglibSampleServiceProxy);
        // 创建代理对象
        SampleServiceImpl sampleService = (SampleServiceImpl) enhancer.create();
        sampleService.hello();
    }
}

JDK和CGLIB动态代理区别

jdk:

利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类,
在调用具体方法前调用InvokeHandler来处理。

只能对实现了接口的类生成代理,而不能针对类。

cglib:

利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

GLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,
并覆盖其中方法实现增强,但是因为采用的是继承,所以该类或方法最好不要声明成final,对于final类或方法,是无法继承的。

 

CGLib所创建的动态代理对象在实际运行时候的性能要比JDK动态代理高不少,有研究表明,大概要高10倍(运行快)

CGLib在创建对象的时候所花费的时间却比JDK动态代理要多很多,有研究表明,大概有8倍的差距;(创建对象慢)

spring何时使用JDK还是CGLiB?

1)如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP。

2)如果目标对象实现了接口,可以强制使用CGLIB实现AOP。

3)如果目标对象没有实现了接口,必须采用CGLIB库,Spring会自动在JDK动态代理和CGLIB之间转换。

如何强制使用CGLIB实现AOP?

1)添加CGLIB库(aspectjrt-xxx.jar、aspectjweaver-xxx.jar、cglib-nodep-xxx.jar)

2)在Spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值