spring切面配置,代理用jdk和cglib的區別


  jdk的動態代理大家應該都聽說過,條件是必須要有接口;cglib不要求接口,那么它是怎么實現切面的呢?很簡單,通過繼承,它動態的創建出一個目標類的子類,復寫父類的方法,由此實現對方法的增強。看例子:

  spring-core.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans     
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.0.xsd ">
    <context:annotation-config />
    <context:component-scan base-package="com.wulinfeng.test.testpilling" />
    <bean class="com.wulinfeng.test.testpilling.util.PropertiesConfigUtil">
        <property name="ignoreUnresolvablePlaceholders" value="true" />
        <property name="locations">
            <list>
                <value>classpath:global.properties</value>
            </list>
        </property>
        <property name="fileEncoding">
            <value>UTF-8</value>
        </property>
    </bean>

    <bean
        class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="staticMethod"
            value="com.wulinfeng.test.testpilling.service.TestPillingService.init" />
    </bean>

    <bean id="advice" class="com.wulinfeng.test.testpilling.util.TimeCostUtil" />

    <aop:config>
        <aop:pointcut
            expression="execution(* com.wulinfeng.*.testpilling.service..*Service.*(..))"
            id="pointCut" />
        <aop:advisor advice-ref="advice" pointcut-ref="pointCut" />
    </aop:config>
</beans>

  通知類:

package com.wulinfeng.test.testpilling.util;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 * 統計接口時延
 *
 * @author wulinfeng
 * @version C10 2018年11月19日
 * @since SDP V300R003C10
 */
public class TimeCostUtil implements MethodInterceptor
{
    private static Logger LOGGER = LogManager.getLogger(TimeCostUtil.class);
    
    @Override
    public Object invoke(MethodInvocation invocation)
        throws Throwable
    {
        // 獲取服務開始時間
        long beginTime = System.currentTimeMillis();
        
        // 獲取類名和方法名
        String srcClassName = "";
        String methodName = "";
        if (invocation != null)
        {
            String className = invocation.getClass() != null ? invocation.getClass().getName() : "";
            LOGGER.debug("The proxy class name is : " + className);
            if (invocation.getMethod() != null)
            {
                methodName = invocation.getMethod().getName();
            }
            if (invocation.getThis() != null && invocation.getThis().getClass() != null)
            {
                srcClassName = invocation.getThis().getClass().getName();
                
            }
        }
        
        // 調用原來的方法
        Object result = invocation.proceed();
        
        // 打印耗時
        LOGGER.debug(srcClassName + "." + methodName + " cost time: " + (System.currentTimeMillis() - beginTime));
        
        return result;
    }
    
}

  目標類:

package com.wulinfeng.test.testpilling.service;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.concurrent.Executors;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Service;

/**
 * 監聽文件修改,打印到日志里
 *
 * @author wulinfeng
 * @version C10 2018年11月20日
 * @since SDP V300R003C10
 */
@Service
public class FileListenServiceImpl implements FileListenService
{
    private static Logger LOGGER = LogManager.getLogger(FileListenServiceImpl.class);
    
    @Override
    public void updateOnListen(String filePath)
        throws IOException
    {
        LOGGER.debug("The file path is : " + filePath);
        
        // 監聽文件所在路徑
        Path path = Paths.get(filePath);
        final WatchService ws = FileSystems.getDefault().newWatchService();
        path.register(ws,
            StandardWatchEventKinds.ENTRY_MODIFY,
            StandardWatchEventKinds.ENTRY_DELETE,
            StandardWatchEventKinds.ENTRY_CREATE);
        Executors.newCachedThreadPool().execute(new Runnable()
        {
            
            @Override
            public void run()
            {
                while (true)
                {
                    try
                    {
                        WatchKey key = ws.take();
                        for (WatchEvent<?> event : key.pollEvents())
                        {
                            System.out.println(event.kind().toString());
                            if (event.kind().equals(StandardWatchEventKinds.ENTRY_CREATE))
                            {
                                Path createdPath = (Path)event.context();
                                createdPath = path.resolve(createdPath);
                                long size = Files.size(createdPath);
                                LOGGER.debug("create file : " + createdPath + "==>" + size);
                            }
                            else if (event.kind().equals(StandardWatchEventKinds.ENTRY_MODIFY))
                            {
                                Path createdPath = (Path)event.context();
                                createdPath = path.resolve(createdPath);
                                long size = Files.size(createdPath);
                                LOGGER.debug("update file : " + createdPath + "==>" + size);
                            }
                            else if (event.kind().equals(StandardWatchEventKinds.ENTRY_DELETE))
                            {
                                Path createdPath = (Path)event.context();
                                createdPath = path.resolve(createdPath);
                                LOGGER.debug("delete file : " + createdPath);
                            }
                        }
                        key.reset();
                    }
                    catch (Exception e)
                    {
                        e.printStackTrace();
                    }
                }
            }
        });
    }
    
}

  另一個TestPillingService沒有實現接口,不貼了,看下單測:

package com.wulinfeng.test.testpilling;

import java.io.IOException;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.wulinfeng.test.testpilling.service.FileListenService;
import com.wulinfeng.test.testpilling.service.TestPillingService;
import com.wulinfeng.test.testpilling.util.PropertiesConfigUtil;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring-core.xml"})
public class TimeCostUtilTest
{
    @Autowired
    TestPillingService tps;
    
    @Autowired
    FileListenService fls;
    @Test
    public void timeCostTest()
        throws IOException
    {
        String CLASS_PATH = TestPillingService.class.getResource("/").getPath().startsWith("/")
            ? TestPillingService.class.getResource("/").getPath().substring(1)
            : TestPillingService.class.getResource("/").getPath();
        String filePath = CLASS_PATH + PropertiesConfigUtil.getProperty("filepath", "methods");
        fls.updateOnListen(filePath);
        tps.editMethodContent("test", "hello world!");
    }
    
    
}

  運行結果:

log4j:WARN No appenders could be found for logger (org.springframework.test.context.junit4.SpringJUnit4ClassRunner).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
ERROR StatusLogger Unable to locate appender "httpClient-log" for logger config "org.asynchttpclient"
[2018-11-20 12:53:18] DEBUG TestPillingService:71 - Enter TestPillingService.init, filePath : methods, loginPath : login
[2018-11-20 12:53:18] DEBUG TimeCostUtil:32 - The proxy class name is : org.springframework.aop.framework.ReflectiveMethodInvocation
[2018-11-20 12:53:18] DEBUG FileListenServiceImpl:34 - The file path is : E:/workspace/Wireless-Router/test-pilling/target/test-classes/methods
[2018-11-20 12:53:18] DEBUG TimeCostUtil:48 - com.wulinfeng.test.testpilling.service.FileListenServiceImpl.updateOnListen cost time: 6
[2018-11-20 12:53:18] DEBUG TimeCostUtil:32 - The proxy class name is : org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation
[2018-11-20 12:53:18] DEBUG TimeCostUtil:48 - com.wulinfeng.test.testpilling.service.TestPillingService.editMethodContent cost time: 43
ENTRY_CREATE
[2018-11-20 12:53:18] DEBUG FileListenServiceImpl:62 - create file : E:\workspace\Wireless-Router\test-pilling\target\test-classes\methods\test==>0
ENTRY_MODIFY
[2018-11-20 12:53:18] DEBUG FileListenServiceImpl:69 - update file : E:\workspace\Wireless-Router\test-pilling\target\test-classes\methods\test==>14

  我們看到jdk動態代理的實際實現類是ReflectiveMethodInvocation,它最終實現了MethodInterceptor接口的invoke方法和MethodInvocation接口的getMethod方法;而cglib動態代理實際實現類為CglibAopProxy的內部類CglibMethodInvocation(它繼承自ReflectiveMethodInvocation,復寫了invokeJoinpoint方法)。他們倆執行目標類的實際方法時都是通過ReflectiveMethodInvocation的proceed來進行的。

  如果我們把<aop:config>改成這樣:

<aop:config proxy-target-class="true">

   測試結果:

log4j:WARN No appenders could be found for logger (org.springframework.test.context.junit4.SpringJUnit4ClassRunner).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
ERROR StatusLogger Unable to locate appender "httpClient-log" for logger config "org.asynchttpclient"
[2018-11-20 13:05:12] DEBUG TestPillingService:71 - Enter TestPillingService.init, filePath : methods, loginPath : login
[2018-11-20 13:05:13] DEBUG TimeCostUtil:32 - The proxy class name is : org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation
[2018-11-20 13:05:13] DEBUG FileListenServiceImpl:34 - The file path is : E:/workspace/Wireless-Router/test-pilling/target/test-classes/methods
[2018-11-20 13:05:13] DEBUG TimeCostUtil:48 - com.wulinfeng.test.testpilling.service.FileListenServiceImpl.updateOnListen cost time: 50
[2018-11-20 13:05:13] DEBUG TimeCostUtil:32 - The proxy class name is : org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation
[2018-11-20 13:05:13] DEBUG TimeCostUtil:48 - com.wulinfeng.test.testpilling.service.TestPillingService.editMethodContent cost time: 42
ENTRY_DELETE
[2018-11-20 13:05:13] DEBUG FileListenServiceImpl:75 - delete file : E:\workspace\Wireless-Router\test-pilling\target\test-classes\methods\test
ENTRY_CREATE
[2018-11-20 13:05:13] DEBUG FileListenServiceImpl:62 - create file : E:\workspace\Wireless-Router\test-pilling\target\test-classes\methods\test==>0

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM