Java 本身的動態代理,必須要基於介面定義,若類別並沒有實作特定介面,就無法使用 Java 動態代理機制,這時可以透過 cglib(Code Generation Library),它的底層基於 ASM,可於執行時期修改位元組碼、動態生成代理物件。
若要將〈動態代理〉的專案範例改用 cglib,可以在 gradle.build 中加入相依:
compile 'cglib:cglib-nodep:'3.2.9'
接著實作一個攔截器,它實作了 MethodInterceptor
:
package cc.openhome.interceptor;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.logging.Logger;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class LoggingInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object target, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
Logger.getLogger(target.getClass().getName())
.info(String.format("%s.%s(%s)",
target.getClass().getName(), method.getName(), Arrays.toString(args)));
return proxy.invokeSuper(target, args);
}
}
然後 AppConfig
基於 cglib 來建立代理物件:
package cc.openhome;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import cc.openhome.interceptor.LoggingInterceptor;
import cc.openhome.model.AccountDAO;
import net.sf.cglib.proxy.Enhancer;
@Configuration
@ComponentScan
public class AppConfig {
@Autowired
@Qualifier("accountDAOJdbcImpl")
private AccountDAO accountDAO;
@Autowired
private DataSource dataSource;
@Bean(destroyMethod="shutdown")
public DataSource dataSource(){
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:schema.sql")
.addScript("classpath:testData.sql")
.build();
}
@Bean
public AccountDAO accountDAO() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(accountDAO.getClass());
enhancer.setCallback(new LoggingInterceptor());
return (AccountDAO) enhancer.create(
new Class[] {DataSource.class},
new Object[] {dataSource}
);
}
}
在進行動態代理時,Spring 底層預設會採用 Java 動態代理,若目標對象沒有實作介面則改用 cglib,實際上,AOP 是個概念,各廠商會有各自的實現,為此,AOP Alliance 定義了一套介面標準,MethodInterceptor
在 AOP Alliance 的定義則是:
package org.aopalliance.intercept;
public interface MethodInterceptor {
Object invoke(MethodInvocation methodInvocation) throws Throwable;
}
若使用 Spring AOP,你也可以實作這個介面,這部份可參考我舊版文件中的 AroundAdvice 中的範例。