本文目錄一覽:
- 1、spring中aop的動態代理機制有哪些
- 2、Java實現AOP的幾種方式
- 3、如何使用Proxy模式及Java內建的動態代理機制
- 4、java編程,spring里ioc和aop用什麼原理實現的
- 5、如何用Java動態代理實現AOP
spring中aop的動態代理機制有哪些
1.事務管理: (1)資料庫事務:(2)編程事務(3)聲明事物:Spring AOP–聲明事物
2.日誌處理:
3.安全驗證: Spring AOP—OOP升級
靜態代理原理:目標對象:調用業務邏輯 代理對象:日誌管理
表示層調用—代理對象(日誌管理)–調用目標對象
動態代理原理:spring AOP採用動態代理來實現
(1)實現InvocationHandler介面
(2)創建代理類(通過java API)
Java實現AOP的幾種方式
(1)AOP的各種實現
在編譯器修改源代碼、在運行期位元組碼載入前修改位元組碼或位元組碼載入後動態創建代理類的位元組碼。以下是各種實現機制的比較:
類別分為靜態AOP(包括靜態織入)和動態AOP(包括動態代理、動態位元組碼生成、自定義類載入器、位元組碼轉換)。
靜態織入:
a、原理:在編譯期,切面直接以位元組碼形式編譯到目標位元組碼文件中 ;
b、優點:對系統性能無影響;
c、缺點:不夠靈活;
動態代理 :
a、原理:在運行期,目標類載入後,為介面動態生成代理類。將切面織入到代理類中;
b、優點:更靈活;
c、缺點:切入的關注點要實現介面;
如何使用Proxy模式及Java內建的動態代理機制
在學習Spring的時候,我們知道Spring主要有兩大思想,一個是IoC,另一個就是AOP,對於IoC,依賴注入就不用多說了,而對於Spring的核心AOP來說,我們不但要知道怎麼通過AOP來滿足的我們的功能,我們更需要學習的是其底層是怎麼樣的一個原理,而AOP的原理就是java的動態代理機制,所以本篇隨筆就是對java的動態機制進行一個回顧。
在java的動態代理機制中,有兩個重要的類或介面,一個是 InvocationHandler(Interface)、另一個則是 Proxy(Class),這一個類和介面是實現我們動態代理所必須用到的。首先我們先來看看java的API幫助文檔是怎麼樣對這兩個類進行描述的:
InvocationHandler:
InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.
每一個動態代理類都必須要實現InvocationHandler這個介面,並且每個代理類的實例都關聯到了一個handler,當我們通過代理對象調用一個方法的時候,這個方法的調用就會被轉發為由InvocationHandler這個介面的 invoke 方法來進行調用。我們來看看InvocationHandler這個介面的唯一一個方法 invoke 方法:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
我們看到這個方法一共接受三個參數,那麼這三個參數分別代表什麼呢?
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
proxy:指代我們所代理的那個真實對象
method:指代的是我們所要調用真實對象的某個方法的Method對象
args:指代的是調用真實對象某個方法時接受的參數
如果不是很明白,等下通過一個實例會對這幾個參數進行更深的講解。
接下來我們來看看Proxy這個類:
Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.
Proxy這個類的作用就是用來動態創建一個代理對象的類,它提供了許多的方法,但是我們用的最多的就是 newProxyInstance 這個方法:
public static Object newProxyInstance(ClassLoader loader, Class?[] interfaces, InvocationHandler h) throws IllegalArgumentException
Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler.
這個方法的作用就是得到一個動態的代理對象,其接收三個參數,我們來看看這三個參數所代表的含義:
public static Object newProxyInstance(ClassLoader loader, Class?[] interfaces, InvocationHandler h) throws IllegalArgumentException
loader:一個ClassLoader對象,定義了由哪個ClassLoader對象來對生成的代理對象進行載入
interfaces:一個Interface對象的數組,表示的是我將要給我需要代理的對象提供一組什麼介面,如果我提供了一組介面給它,那麼這個代理對象就宣稱實現了該介面(多態),這樣我就能調用這組介面中的方法了
h:一個InvocationHandler對象,表示的是當我這個動態代理對象在調用方法的時候,會關聯到哪一個InvocationHandler對象上
好了,在介紹完這兩個介面(類)以後,我們來通過一個實例來看看我們的動態代理模式是什麼樣的:
首先我們定義了一個Subject類型的介面,為其聲明了兩個方法:
public interface Subject
{
public void rent();
public void hello(String str);
}
接著,定義了一個類來實現這個介面,這個類就是我們的真實對象,RealSubject類:
public class RealSubject implements Subject
{
@Override
public void rent()
{
System.out.println(“I want to rent my house”);
}
@Override
public void hello(String str)
{
System.out.println(“hello: ” + str);
}
}
下一步,我們就要定義一個動態代理類了,前面說個,每一個動態代理類都必須要實現 InvocationHandler 這個介面,因此我們這個動態代理類也不例外:
public class DynamicProxy implements InvocationHandler
{
// 這個就是我們要代理的真實對象
private Object subject;
// 構造方法,給我們要代理的真實對象賦初值
public DynamicProxy(Object subject)
{
this.subject = subject;
}
@Override
public Object invoke(Object object, Method method, Object[] args)
throws Throwable
{
//在代理真實對象前我們可以添加一些自己的操作
System.out.println(“before rent house”);
System.out.println(“Method:” + method);
// 當代理對象調用真實對象的方法時,其會自動的跳轉到代理對象關聯的handler對象的invoke方法來進行調用
method.invoke(subject, args);
//在代理真實對象後我們也可以添加一些自己的操作
System.out.println(“after rent house”);
return null;
}
}
最後,來看看我們的Client類:
public class Client
{
public static void main(String[] args)
{
// 我們要代理的真實對象
Subject realSubject = new RealSubject();
// 我們要代理哪個真實對象,就將該對象傳進去,最後是通過該真實對象來調用其方法的
InvocationHandler handler = new DynamicProxy(realSubject);
/*
* 通過Proxy的newProxyInstance方法來創建我們的代理對象,我們來看看其三個參數
* 第一個參數 handler.getClass().getClassLoader() ,我們這裡使用handler這個類的ClassLoader對象來載入我們的代理對象
* 第二個參數realSubject.getClass().getInterfaces(),我們這裡為代理對象提供的介面是真實對象所實行的介面,表示我要代理的是該真實對象,這樣我就能調用這組介面中的方法了
* 第三個參數handler, 我們這裡將這個代理對象關聯到了上方的 InvocationHandler 這個對象上
*/
Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject
.getClass().getInterfaces(), handler);
System.out.println(subject.getClass().getName());
subject.rent();
subject.hello(“world”);
}
}
我們先來看看控制台的輸出:
$Proxy0
before rent house
Method:public abstract void com.xiaoluo.dynamicproxy.Subject.rent()
I want to rent my house
after rent house
before rent house
Method:public abstract void com.xiaoluo.dynamicproxy.Subject.hello(java.lang.String)
hello: world
after rent house
我們首先來看看 $Proxy0 這東西,我們看到,這個東西是由 System.out.println(subject.getClass().getName()); 這條語句列印出來的,那麼為什麼我們返回的這個代理對象的類名是這樣的呢?
Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject
.getClass().getInterfaces(), handler);
可能我以為返回的這個代理對象會是Subject類型的對象,或者是InvocationHandler的對象,結果卻不是,首先我們解釋一下為什麼我們這裡可以將其轉化為Subject類型的對象?原因就是在newProxyInstance這個方法的第二個參數上,我們給這個代理對象提供了一組什麼介面,那麼我這個代理對象就會實現了這組介面,這個時候我們當然可以將這個代理對象強制類型轉化為這組介面中的任意一個,因為這裡的介面是Subject類型,所以就可以將其轉化為Subject類型了。
同時我們一定要記住,通過 Proxy.newProxyInstance 創建的代理對象是在jvm運行時動態生成的一個對象,它並不是我們的InvocationHandler類型,也不是我們定義的那組介面的類型,而是在運行是動態生成的一個對象,並且命名方式都是這樣的形式,以$開頭,proxy為中,最後一個數字表示對象的標號。
接著我們來看看這兩句
subject.rent();
subject.hello(“world”);
這裡是通過代理對象來調用實現的那種介面中的方法,這個時候程序就會跳轉到由這個代理對象關聯到的 handler 中的invoke方法去執行,而我們的這個 handler 對象又接受了一個 RealSubject類型的參數,表示我要代理的就是這個真實對象,所以此時就會調用 handler 中的invoke方法去執行:
public Object invoke(Object object, Method method, Object[] args)
throws Throwable
{
//在代理真實對象前我們可以添加一些自己的操作
System.out.println(“before rent house”);
System.out.println(“Method:” + method);
// 當代理對象調用真實對象的方法時,其會自動的跳轉到代理對象關聯的handler對象的invoke方法來進行調用
method.invoke(subject, args);
//在代理真實對象後我們也可以添加一些自己的操作
System.out.println(“after rent house”);
return null;
}
我們看到,在真正通過代理對象來調用真實對象的方法的時候,我們可以在該方法前後添加自己的一些操作,同時我們看到我們的這個 method 對象是這樣的:
public abstract void com.xiaoluo.dynamicproxy.Subject.rent()
public abstract void com.xiaoluo.dynamicproxy.Subject.hello(java.lang.String)
正好就是我們的Subject介面中的兩個方法,這也就證明了當我通過代理對象來調用方法的時候,起實際就是委託由其關聯到的 handler 對象的invoke方法中來調用,並不是自己來真實調用,而是通過代理的方式來調用的。
這就是我們的java動態代理機制
java編程,spring里ioc和aop用什麼原理實現的
控制反轉(IOC)
(理解好Ioc的關鍵是要明確「誰控制誰,控制什麼,為何是反轉(有反轉就應該有正轉了),哪些方面反轉了」)
1、Ioc—Inversion of Control:即「控制反轉」,不是什麼技術,而是一種設計思想。在Java開發中,Ioc意味著將你設計好的對象交給容器控制,而不是傳統的在你的對象內部直接控制。
2、誰控制誰,控制什麼:傳統Java SE程序設計,我們直接在對象內部通過new進行創建對象,是程序主動去創建依賴對象;而IoC是有專門一個容器來創建這些對象即由Ioc容器來控制對象的創建。
誰控制誰?當然是IoC 容器控制了對象。
控制什麼?那就是主要控制了外部資源獲取(不只是對象包括比如文件等)。
3、為何是反轉,哪些方面反轉了: 有反轉就有正轉,傳統應用程序是由我們自己在對象中主動控制去直接獲取依賴對象,也就是正轉;而反轉則是由容器來幫忙創建及注入依賴對象。
為何是反轉?因為由容器幫我們查找及注入依賴對象,對象只是被動的接受依賴對象,所以是反轉。
哪些方面反轉了?依賴對象的獲取被反轉了。
還是不明白沒事,下面搞個簡單案例來說就懂啦 !!!
例子:當我們在任何一個有實際開發意義的程序項目中,我們會使用很多類來描述他們特有的功能,並且通過類與類之間的相互協作來完成特定的業務邏輯。這個時候,每個類都需要負責管理與自己有交互的類的引用和依賴,代碼將會變的異常難以維護和極高的高耦合。而IOC的出現正是用來解決這個問題,我們通過IOC將這些依賴對象的創建、協調工作交給spring容器去處理,每個對象值需要關注其自身的業務邏輯關係就可以了。在這樣的角度上來看,獲得依賴的對象的方式,進行了反轉,變成了由spring容器控制對象如何獲取外部資源(包括其他對象和文件資料等)。
總的來說:IOC就是通過在Xml配置文件里依賴注入來解決代碼問題。
IOC的注入類型有幾種?主要可以劃分為三種:構造函數注入、屬性注入和介面注入。Spring支持構造函數注入和屬性注入
面向切面(AOP)
(面向切面編程,AOP其實只是OOP的補充而已,AOP基本上是通過代理機制實現的。)
我們管切入到指定類指定方法的代碼片段稱為切面,而切入到哪些類、哪些方法則叫切入點。有了AOP,我們就可以把幾個類共有的代碼,抽取到一個切片中,等到需要時再切入對象中去,從而改變其原有的行為。
我們都知道 Java 是 OOP-面向對象編程的,它有自己的優勢,也有自己的不足。比如說:在我們開發中,都會有一條業務主線(即客戶的需求)。而我們要做的就是實現這個主線上的需求。我們在實現這些功能的時候,經常要干一些額外的不可避免的事情,比如事務的管理,日誌的記錄等,就很繁雜且代碼量增多,所以 Spring 提供了另一種角度來思考程序結構,也就是把這一些事情剝離出來,然後適時適地的把它們加入到我們的代碼中,比如說 聲明式事務管理的時候,我們在 service 層檢測到save*、update*這些方法要被調用的時候,我們先進行開啟事務什麼的,這就是AOP,面向編程的思想。
AOP的術語:
1、通知(Advice):就是你想要的功能,也就是上面說的 安全,事物,日誌等。你給先定義好把,然後在想用的地方用一下
2、連接點(JoinPoint):這個更好解釋了,就是spring允許你使用通知的地方,那可真就多了,基本每個方法的前,後(兩者都有也行),或拋出異常時都可以是連接點,spring只支持方法連接點.其他如aspectJ還可以讓你在構造器或屬性注入時都行,不過那不是咱關注的,只要記住,和方法有關的前前後後(拋出異常),都是連接點。
3、切入點(Pointcut):上面說的連接點的基礎上,來定義切入點,你的一個類里,有15個方法,那就有幾十個連接點了對把,但是你並不想在所有方法附近都使用通知(使用叫織入,以後再說),你只想讓其中的幾個,在調用這幾個方法之前,之後或者拋出異常時干點什麼,那麼就用切點來定義這幾個方法,讓切點來篩選連接點,選中那幾個你想要的方法。
4、切面(Aspect):切面是通知和切入點的結合。現在發現了吧,沒連接點什麼事情,連接點就是為了讓你好理解切點,搞出來的,明白這個概念就行了。通知說明了幹什麼和什麼時候干(什麼時候通過方法名中的before,after,around等就能知道),而切入點說明了在哪干(指定到底是哪個方法),這就是一個完整的切面定義。
5、引入(introduction):允許我們向現有的類添加新方法屬性。這不就是把切面(也就是新方法屬性:通知定義的)用到目標類中嗎
6、目標(target):引入中所提到的目標類,也就是要被通知的對象,也就是真正的業務邏輯,他可以在毫不知情的情況下,被咱們織入切面。而自己專註於業務本身的邏輯。
7、代理(proxy):怎麼實現整套aop機制的,都是通過代理,這個一會給細說。
8、織入(weaving):把切面應用到目標對象來創建新的代理對象的過程。有3種方式,spring採用的是運行時,為什麼是運行時,後面解釋。
如何用Java動態代理實現AOP
在目前的Java開發包中包含了對動態代理的支持,但是其實現只支持對介面的的實現。 其實現主要通過是java.lang.reflect.Proxy類和java.lang.reflect.InvocationHandler介面。 Proxy 類主要用來獲取動態代理對象,InvocationHandler介面用來約束調…
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/242300.html