qiuxuhui

导航

Spring中的JDK黑客,Spring黑客

JDK动态代理和CGlib动态代理

JDK动态代理: 利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
CGlib动态代理: 利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
区别: JDK代理只能对实现接口的类生成代理;CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中的方法,这种通过继承类的实现方式,不能代理final修饰的类。

 

一、JDK动态代理:

我们举个简单的例子:
有一个接口,定义了一个sum()方法:

package change;

public interface Computer {
    int sum(int a,int b);
}

  

当然还有它的实现类:

package change.Impls;

import change.Computer;

public class ComputerImpl implements Computer {
    @Override
    public int sum(int a, int b) {
        return a+b;
    }
}

  

接着我们定义一个代理类,用来在执行的时候加入一些操作(逻辑验证或者业务需求相关的操作等)

package aspect;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class ComputerProxy implements InvocationHandler {
    Object target;//目标
    public ComputerProxy(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        check();
        Object result = method.invoke(target, args);
        return result;
    }
    private void check(Object... args) {
        // 模拟其他操作   
        System.out.println("我在检查");    
    }
}

  

那么代理有了,我们要怎么使用呢?
下面我们在测试类中去执行:

import aspect.ComputerProxy;
import change.Computer;
import change.Impls.ComputerImpl;

import java.lang.reflect.Proxy;

public class Test {
    public static void main(String[] args) {
        //正常调用
        Computer computer=new ComputerImpl();
        int sum = computer.sum(1, 2);
        System.out.println(sum);
        //利用JDK动态代理调用
        //目标对象
       	Computer target = new ComputerImpl();
        //代理对象
        ComputerProxy proxy=new ComputerProxy(target);
        //生成动态代理
       Computer jdkProxy = (Computer) Proxy.newProxyInstance(Computer.class.getClassLoader(),
                new Class[]{Computer.class}, proxy);
        int result = jdkProxy.sum(1, 2);
        System.out.println(result);
    }
}

  

执行结果

3
我在检查
3

  

二、CGlib动态代理:

CGlib方式生成动态代理的步骤与JDK生成大致相同,只是在配置动态代理的时候需要调用其他的接口,而JDK是原生的不需要导入Jar包
所以如果要使用Spring动态代理,必须导入spring-aop 这个jar包
下载地址: https://repo.spring.io/release/org/springframework/spring/
具体配置如下:
Spring代理实例

package aspect;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class SpringProxy implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        //获取目标方法参数
        Object[] arguments = methodInvocation.getArguments();
        //模拟其他操作
        check(arguments);
        //调用目标方法
        Object result = methodInvocation.proceed();
        return result;
    }
    private void check(Object... args) {
        // 模拟其他操作
        System.out.println("检查权限:checkPopedom()!");
    }
}

  

xml文件配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 1、目标对象 -->
<bean id="target" class="com.cc.dao.impl.ComputerImpl"/>
<!-- 2、Spring代理实例 -->
<bean id="springProxy" class="com.cc.proxy.SpringProxy"/>
<!--3、代理对象:是目标对象与Spring代理融合体(cglib) -->
<bean  id="ComputerProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    <!-- 1)注入目标对象  -->
    <property name="targetName" value="target"/>
    <!-- 2)Spring代理实例  -->
    <property name="interceptorNames">
        <array>
            <value>springProxy</value>
        </array>
    </property>
</bean>
</beans>

  

测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:beans2.xml")
public class SpringHkTest1 {
    @Autowired
    @Qualifier("ComputerProxy")
    private  Computer computer;
    @Test
    public void testSpringHk() {
       System.out.println( computer.sum(30, 5));;
    }
}

  

注意:在编写测试类时不能使用main方法运行,因为main方法不会开启注解扫描,会报错,需要在Test单元测试中开启(另外junit4.10以上的版本是没有集成hamcrest-core-1.3.jar包的,需要额外下载这个包,或者将junit版本降级,否则也会报错)

 

总结:

  1. JDK代理使用的是反射机制实现aop的动态代理,CGLIB代理使用字节码处理框架asm,通过修改字节码生成子类。所以jdk动态代理的方式创建代理对象效率较高,执行效率较低,cglib创建效率较低,执行效率高;

  2. JDK动态代理机制是委托机制,具体说动态实现接口类,在动态生成的实现类里面委托hanlder去调用原始实现类方法,CGLIB则使用的继承机制,具体说被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,如果被代理类有接口,那么代理类也可以赋值给接口。

 

posted on 2020-12-20 22:16  qiuxuhui  阅读(145)  评论(0编辑  收藏  举报