什麼是動態代理?Spring中有哪幾種實現?有什麼區別?

什麼是動態代理?Spring中有哪幾種實現?有什麼區別?

答: 1. 可以任意的控制任意對象的執行過程,這個對象的執行過程可以由客戶端靈活的指定
    2. 兩種
    3. jdk和CGLib

JDK
	1. JDK version <= 1.6 的時候實現InvocationHandler,重寫invoke,自定義初始化對象(method.invoke(Object,args))
	2.利用攔截器(必須要實現InvocationHandler接口)加上反射機制生成一個代理接口的匿名類,在具體方法前調用InvocationHandler來處理

CGlib
	1.實現MethodInterceptor 重寫intercept (methodProxy.invokeSuper(Object,args))
	2.CGLib利用ASM框架,對代理對象生成的class文件加載進來,通過修改其字節碼文件生成子類來處理
	2.CGLib不能對聲明final的類和方法進行代理,因爲CGlib原理是動態生成被代理類的子類
	3.完全不受代理類必須實現接口類的限制,採用的是接口繼承的方式

JDK默認提供的代理例子:

接口(inteface)

public interface JdkHello {

    public String hello();

    public String world();
}

正常實現類(class)

/**
 * @author youshang
 */
public class JdkHelloImpl implements JdkHello {
    
    @Override
    public String hello() {
        return "hello";
    }
    @Override
    public String world() {
        return "world";
    }
}

代理實現類(Proxy Class)

@Log4j2
public class JdkHelloProxy implements JdkHello {
    
    private JdkHello jdkHello = new JdkHelloImpl();
    
    @Override
    public String hello() {
        log.info("通過動態代理執行hello");
        return jdkHello.hello();
    }

    @Override
    public final String world() {
        log.info("通過動態代理執行world");
        return jdkHello.world();
    }
}

代理類(Proxy)

/**
 * JDK1.6+ 底層提供的動態代理
 */
@Log4j2
public class JdkProxy {

    private Object object;

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

    /**
     * 封裝代理方法,取代實現InvocationHandler
     * @return
     */
    public Object getProxyInstance() {
        long startTime = System.currentTimeMillis();
        return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), (proxy, method, args) -> {
            DateTime startDate = DateUtil.date(startTime);
            log.info(String.format("開始操作你的需求: %s ",startDate));
            //進行代理
            Object invoke = method.invoke(object, args);

            long endTime = System.currentTimeMillis();
            log.info(String.format("操作此需求總共耗時: %s 毫秒",(endTime- startTime)));

            return invoke;
        });
    }

//    (proxy, method ,args) -> method.invoke(proxy,args)
//    =
//    new InvocationHandler() {
//        @Override
//        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//            return method.invoke(object,args);
//        }
}

代理類(Proxy)

/**
 * JDK1.6+ 底層提供的動態代理
 */
@Log4j2
public class JdkProxy {

    private Object object;

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

    /**
     * 封裝代理方法,取代實現InvocationHandler
     * @return
     */
    public Object getProxyInstance() {
        long startTime = System.currentTimeMillis();
        return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), (proxy, method, args) -> {
            DateTime startDate = DateUtil.date(startTime);
            log.info(String.format("開始操作你的需求: %s ",startDate));
            //進行代理
            Object invoke = method.invoke(object, args);

            long endTime = System.currentTimeMillis();
            log.info(String.format("操作此需求總共耗時: %s 毫秒",(endTime- startTime)));

            return invoke;
        });
    }

//    (proxy, method ,args) -> method.invoke(proxy,args)
//    =
//    new InvocationHandler() {
//        @Override
//        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//            return method.invoke(object,args);
//        }
}

測試代理類(Test)

/**
 * 測試jdk動態代理
 */
public class JdkProxyTest {

    @Test
    public void test(){
        //通過代理類實現調用JdkHelloImpl實現類
        JdkHello jdkHello = new JdkHelloProxy();
        //執行代理操作
        JdkHello jdkHelloProxy = (JdkHello)new JdkProxy(jdkHello).getProxyInstance();
        System.out.println(jdkHelloProxy.hello());

        //經過final修飾的方法
        System.out.println(jdkHelloProxy.world());
    }
}

CGLib代理測試例子

代理目標類(class)

public class CglibServiceImpl{
    public String say() {
        return "say";
    }

    public String useCase() {
        return "useCase";
    }

    /**
     * final修飾的方法,CGLib不能代理
     * @return
     */
    public final String example(){
        return "勞資的東西你別碰。。。";
    }
}

代理類(Proxy Class)

/**
 * 實現CGLib核心接口 MethodInterceptor
 */
@Log4j2
public class CglibProxy implements MethodInterceptor {

    private Long startTime;

    public CglibProxy(){
        startTime = System.currentTimeMillis();
    }

    /**
     * 回調接口的方法
     * 回調接口的方法執行的條件是:代理對象執行目標方法時會調用回調接口的方法
     * @param obj
     * @param method
     * @param args
     * @param proxy
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        log.info(String.format("方法名: %s",method.getName()));
        log.info(String.format("開始執行日期: %s ", DateUtil.date(startTime)));

        Object o = proxy.invokeSuper(obj, args);

        long endTime = System.currentTimeMillis();
        log.info(String.format("方法總共執行時間: %s 毫秒",endTime - startTime));
        return o;
    }
}

測試CGlib代理方法

    @Test
    public void test(){
        Enhancer enhancer = new Enhancer();
        //將目標類設置爲父類,cglib動態代理增強的原理就是子類增強父類,cglib不能增強目標類爲final的類和方法
        enhancer.setSuperclass(CglibServiceImpl.class);
        //設置當前類的具體子類(允許爲null)
        enhancer.setClassLoader(CglibServiceImpl.class.getClassLoader());
        //設置回調接口,這裏的MethodInterceptor實現類回調接口,而我們又實現了MethodInterceptor
        enhancer.setCallback(new CglibProxy());
        //create()方法用於創建cglib動態代理對象
        CglibServiceImpl cglibService = (CglibServiceImpl)enhancer.create();
        //實現CGLib代理調用方法
        System.out.println(cglibService.say());
        System.out.println(cglibService.useCase());
        //通過final修飾的方法,CGlib沒辦法代理
        System.out.println(cglibService.example());

    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章