代理模式
代理模式就是隱藏真實對象,而暴露代理對象,而由代理對象去調用真實對象的行爲。
靜態代理
public interface Subject {
/**
* 處理方法
*/
void process();
}
代理類
Subject realSubject;
public Proxy(Subject realSubject){
this.realSubject=realSubject;
}
/**
* 處理方法
*/
@Override
public void process() {
System.out.println("before");
System.out.println("-------------------------------------------");
try {
realSubject.process();
}catch (Exception e){
System.out.println("after Throwing");
System.out.println(e.getMessage());
}finally {
System.out.println("-------------------------------------------");
System.out.println("after");
}
}
真實類
public class RealSubject implements Subject {
/**
* 處理方法
*/
@Override
public void process() {
System.out.println("hello");
}
}
Main
public class app {
public static void main(String[] args) {
Subject subject=new Proxy(new RealSubject());
subject.process();
}
}
結果
可以看見在執行打印Hello之前和執行之後多了兩行打印文字,這就是代理帶來的功能,在執行業務操作之前和之後能夠額外的添加其他操作。
那麼在實際中有什麼用途呢?
在web項目中,一般需要對請求和返回進行記錄,同時需要對異常進行記錄,這時如果把Logger放在業務類中,則每個方法都需要寫一大段重複代碼,於是代理類的出現解決了這個問題。
但是想一想,靜態代理如果有100個類需要進行代理也就意味着要給100個類寫上
implement Subject
,這是不現實的。於是動態代理應運而生。
動態代理
相信大家在學習Spring的過程中都使用過AOP,其中各種Advice註解能夠方便的讓我們做切面編程,
回到剛纔了例子,如果使用AOP來操作會是怎樣的呢?
@Around(value = "log()")
public Object log(ProceedingJoinPoint joinPoint){
for (Object arg:joinPoint.getArgs()){
logger.info("請求值:"+arg.toString());
}
Object result=null;
try {
result = joinPoint.proceed();
}catch (Throwable e){
logger.error("error",e);
}
logger.info("返回值"+result.toString());
return result;
}
可以看見已經非常簡潔了。那麼它底層是如何實現的呢?這裏就要引出主角了,JDKProxy和CGLIB
JDKProxy
使用java.lang.reflec.Proxy.newInstance進行生成代理類
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
裏面的Class<> interfaces就是真實類實現的接口
public class app {
public static void main(String[] args) {
//將生成的代理類保存
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
Subject proxySubject= (Subject) Proxy.newProxyInstance(app.class.getClassLoader(),RealSubject.class.getInterfaces(),new SubjectInvocationHandler(new RealSubject()));
proxySubject.process();
}
}
需要實現InvocationHandler
public class SubjectInvocationHandler implements InvocationHandler {
Subject subject;
public SubjectInvocationHandler(Subject realSubject){
subject=realSubject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
System.out.println("----------------------------");
Object result=null;
try {
System.out.println("開啓事務");
result=method.invoke(subject,args);
System.out.println("事務提交");
}catch (Exception e){
System.out.println("事務發現異常進行回滾");
System.out.println(e.getMessage());
}finally {
System.out.println("----------------------------");
System.out.println("after");
}
return result;
}
}
生成的代理類代碼
public final class $Proxy0 extends Proxy implements Subject {
private static Method m1;
private static Method m2;
private static Method m0;
private static Method m3;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final void process() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m3 = Class.forName("staticproxy.Subject").getMethod("process");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
運行結果
可以看到生成的代理類實現了Subject接口,同時持有實現的SubjectInvocationHandler,那麼執行process的時候就是執行SubjectInvocationHandler的invoke方法。
CGLIB
cglib採用asm動態生成字節碼技術
實際上CGLIB是使用攔截器,把方法的執行過程攔截,給方法做代理
public class SubjectInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before");
System.out.println("------------------------------");
Object result=null;
try {
result=methodProxy.invokeSuper(o,objects);
}catch (Exception e){
System.out.println(e.getMessage());
}finally {
System.out.println("----------------------------------");
System.out.println("after");
}
return result;
}
}
public class App {
public static void main(String[] args) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\class");
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(RealSubject.class);
enhancer.setCallback(new SubjectInterceptor());
Subject proxySubject= (Subject) enhancer.create();
proxySubject.process();
}
}
查看CGLIB生成的代理類,可以看見MethodInterceptor對象var10000調用了intercept方法。
public class RealSubject$$EnhancerByCGLIB$$13c51f84 extends RealSubject implements Factory {
public final void process() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$process$0$Method, CGLIB$emptyArgs, CGLIB$process$0$Proxy);
} else {
super.process();
}
}
}
JDKProxy和CGLIB的區別
JDKProxy裏面只能對實現interface的類進行代理,而CGLIB是基於繼承,從而沒有接口限制。
Spring中
DefaultAopProxyFactory源碼
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
return new JdkDynamicAopProxy(config);
} else {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
} else {
return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
}
}
}
對於Spring來說,@EnableAspectJAutoProxy中proxyTargetClass默認爲false,
如果proxyTargetClass爲false則很可能使用JDKProxy。