代理模式優勢及意義:1.安全上,能夠屏蔽客戶端訪問真是對象。2.可以在目標對象實現的基礎上,增強額外的功能操作,即功能擴展(相當於明星和經紀人)。3.系統性能上,可以對真是對象進行延時加載,從而達到按需分配的設計思想。(@lazy對真正需要加載數據時才加載。這樣節省資源分配;ioc管理bean對象時,都是通過代理模式來管理對象)
定義:給指定的目標對象提供了一種通過代理對象訪問的方式。
代理模式是一個結構化的設計模式。
結構圖:
靜態代理實例:
1.
package propxy;
//接口
public interface ByCar {
void buyCar();
}
2.
package propxy;
//目標對象
public class Person implements ByCar{
@Override
@ask(askTr="i have 15000$")
public void buyCar() {
System.out.println("i want by care;");
}
}
3.
package propxy;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
//對目標對象增強 採用註解方式
public @interface ask {
String askTr();
}
4.
package propxy;
//代理對象
public class ProxyBy implements ByCar {
// 目標對象的引用
private ByCar bc;
public ProxyBy(ByCar bc) {
super();
this.bc = bc;
}
@Override
public void buyCar() {
// 對目標對象增強
String askTr = null;
try {
askTr=bc.getClass().getMethod("buyCar")
.getAnnotatio(ask.class).askTr();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(askTr);
bc.buyCar();
}
}
5.
package propxy;
public class test {
public static void main(String[] args) {
ByCar byCaryCar=new ProxyBy(new Person());
byCaryCar.buyCar();
}
}
靜態代理常用環境:項目中的三層架構就是靜態代理;controller層調用service層;service層調用dao層;就是典型的靜態代理;
- JDK動態代理 2.cglib代理
1、如果目標對象實現了接口,默認情況下會採用JDK的動態代理實現AOP
2、如果目標對象實現了接口,可以強制使用CGLIB實現AOP
3、如果目標對象沒有實現了接口,必須採用CGLIB庫,spring會自動在JDK動態代理和CGLIB之間轉換
動態代理:通過字節碼重組的方式動態的創建proxy類實例和proxy實例;生成的class文件就是所謂的字節碼;如xx.getclass();
Jdk動態代理:
- 聲明subject接口,且realsubject必須實現該接口
- 必須實現invocationhandler接口,且覆蓋invoke方法
- 可以通過jdk提供的proxy.newproxyinstance靜態來構造代理對象
實例:
package propxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//實現invocationhandler 重寫invoke()方法
public class DynamicProxy implements InvocationHandler{
private ByCar bc;
@SuppressWarnings("unchecked")
public <T> T getProxy(ByCar bc) {
this.bc=bc;
//通過jdk自帶靜態的方法 proxy.newproxyinstance() 來生成代理對象
//通過字節碼重組的方式動態的生成一個class字節碼,在通過class字節碼反射的方式 創建一個對象
return (T) Proxy.newProxyInstance(DynamicProxy.class.getClassLoader(), bc.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//目標對象增強
String askTr = bc.getClass().getMethod("buyCar").getAnnotation(ask.class).askTr();
System.out.println(askTr);
//反射機制
return method.invoke(bc, args);
}
}
調用:
public static void main(String[] args) {
ByCar byCar =new DynamicProxy().getProxy(new Person());
byCar.buyCar();
}
Cglib動態代理:通過jvm(java虛擬機)中,通過動態的組裝當前類的一個子類來實現的動態代理;
需引入cglib的依賴插件;
三大步驟:
- 引入cglib插件;
- 實現methodinterceptor 用於攔截 重寫intercept方法
- 通過Enhancer enhancer=new Enhancer(); enhancer.create() 創建代理對象
- 實例:
1.
package propxy;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class DynamicCglib implements MethodInterceptor{
@SuppressWarnings("unchecked")
public <T> T GetInstances(Class<?> clazz) {
//通過傳入的字節碼文件(clazz) 從新生成一個字節碼文件繼承於它
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return (T) enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] objs, MethodProxy methodProxy) throws Throwable {
System.out.println("方法前--------------操作");
Object invokeSuper = methodProxy.invokeSuper(obj, objs);
System.out.println("方法後--------------操作");
return invokeSuper;
}
}
2.
package propxy;
public class User {
public void eat() {
System.out.println("user:eat............");
}
}
調用:
package propxy;
public class test {
public static void main(String[] args) {
//靜態
ByCar byCaryCar=new ProxyBy(new Person());
byCaryCar.buyCar();
//動態jdk
ByCar byCar =new DynamicProxy().getProxy(new Person());
byCar.buyCar();
//動態cglib
User obj = new DynamicCglib().GetInstances(User.class);
obj.eat();
}
}
Jdk動態代理與cglib的區別:1.如果bean實例沒有實現某一接口 就採用cglib代理模式;如果bean實例實現了某一接口,spring就會採用jdk動態代理模式;
2.cglib採用的是繼承於原來的一個類;調用目標對象的方法,不是採用的反射機制,而是採用的緩存fast機制;而jdk代理是採用實現接口的方式,調用目標對象的方法是採用的是反射機制;
Jdk動態代理與cglib的優劣:1.cglib的動態代理不需要目標對象實現任何接口,但是如果目標對象裏的方法是final時,就不能實現動態代理;jdk動態代理需要目標對象繼承某一接口,沒有方法final的限制;
2.cglib動態代理採用的緩存fast機制,它爲每個方法體編了一個記號,像用索引的方式去快速調用,而jdk採用的反射機制的調用該方法體;所以cglib動態代理在jvm編譯生成class文件的過程中較於jdk動態代理要慢一些;但是調用方法體要快一些;jdk採用反射機制的去調用方法體 是比較耗費資源的;
在項目中:service接口對象往往採用的是cglib的動態代理 而mybatis裏的mapper接口對象是採用的jdk動態代理的方式;
Spring的aop採用面向切面編程,代理模式在其中表現的淋淋盡致,比如我們放在service層方法上的註解如@transition 我們都知道是事物註解;那spring是怎麼來處理它的呢? 在我們代理模式中爲目標對象增強其功能,我們是不是會定義一個註解,放在目標對象方法上;在我們通過代理模式調用目標對象方法時通過方法上定義的註解 我們可以在方法前後做一些操作 就是對目標對象方法的一些增強;
比如 @transition,我們都是放在service層的方法上,而service層採用的是cglib動態代理的方式。我們在調用目標對象的方法時通過攔截器transitioninterceptor捕獲到方法上有@transition的註解(transitioninterceptor肯定實現了methodinterceptor方法攔截器),那麼,spring就會在目標方法前做事物開啓操作,然後動態代理的方式調用目標對象方法(方法裏肯定就是些mapper裏相關的事物操作啦 我這裏用的是mybatis框架 注意:mapper採用的是jdk的動態代理方式),方法後
如果有異常就做事物回滾操作;這些都是通過代理模式來實現的;
service層調用採用的cglib代理方式圖解:代理名字都含有cglib 通過上面實例 debug模式可驗證
mybatis裏mapper代理模式圖解:代理名字都是$proxy開頭 通過上面實例 debug模式可驗證