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:workspaceWireless-Router	est-pilling	arget	est-classesmethods	est==>0
ENTRY_MODIFY
[2018-11-20 12:53:18] DEBUG FileListenServiceImpl:69 - update file : E:workspaceWireless-Router	est-pilling	arget	est-classesmethods	est==>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:workspaceWireless-Router	est-pilling	arget	est-classesmethods	est
ENTRY_CREATE
[2018-11-20 13:05:13] DEBUG FileListenServiceImpl:62 - create file : E:workspaceWireless-Router	est-pilling	arget	est-classesmethods	est==>0
原文地址:https://www.cnblogs.com/wuxun1997/p/6379720.html