詳解使用Java原生代理實現(xiàn)AOP實例
一說到AOP,大家一定會想到spring,因為這東西實在是太強大了.但是大家一定要清楚,AOP是一只編程思想,而Spring僅僅是AOP的一種實現(xiàn)罷了.
首先百度下:
在軟件業(yè),AOP為Aspect Oriented Programming的縮寫,意為:面向切面編程,通過預編譯方式和運行期動態(tài)代理實現(xiàn)程序功能的統(tǒng)一維護的一種技術。AOP是OOP的延續(xù),是軟件開發(fā)中的一個熱點,也是Spring框架中的一個重要內容,是函數(shù)式編程的一種衍生范型。利用AOP可以對業(yè)務邏輯的各個部分進行隔離,從而使得業(yè)務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發(fā)的效率。
今天呢,咱們就一起用Java原生代理實現(xiàn)簡單的AOP功能.
首先,你得需要了解基本的反射知識,否則可能會感到困惑.
不羅嗦了,直接開始擼碼
首先,咱們先寫一個簡單的接口.名字叫AnimalInterface,用來聲明規(guī)范動物的一些基本方法.
這些方法包括 設置名字,獲取名字,叫聲,屬性(原諒我沒文化,其實就是獲得是陸棲還是水棲或者水陸兩棲)
package proxy.imp;
public interface AnimalInterface {
//設置名字
void setName(String name);
//獲取名字
String getName();
//叫聲
void say();
//獲取棲性
void getProperty();
}
然后咱們實現(xiàn)這個接口,創(chuàng)建一個名叫小黑的Dog
package proxy;
import proxy.imp.AnimalInterface;
public class DogImp implements AnimalInterface {
private String name = "小黑";
public DogImp() {
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public String getName() {
return this.name;
}
@Override
public void say() {
System.out.println("小狗:汪汪汪汪.....");
}
@Override
public void getProperty() {
System.out.println("小狗是陸地動物,但是會游泳哦");
}
}
大家一定迫不及待了,怎么實現(xiàn)類似AOP的功能呢….
咱們先創(chuàng)建一個名為AOPHandle的類,讓其實現(xiàn)InvocationHandler接口,不能使用invoke時使用proxy作為反射參數(shù)時,因為代理對象的接口,不同于對象,這種代理機制是面向接口,而不是面向類的,如果使用proxy,會造成無限遞歸.然后就是棧溢出,但是依舊能反射成功一次,這說明代理對象和對象的代理是不一樣的,但是咱們可以通過proxy參數(shù)的proxy.getClass()獲得class對象,然后獲得被代理類的方法和參數(shù),這也為注解注入,特定方法注入,屬性注入提供了一種實現(xiàn)途徑吧,關于這個,咱們后面再說..
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class AOPHandle implements InvocationHandler{
//保存對象
private Object o;
public AOPHandle(Object o) {
this.o=o;
}
/**
* 這個方法會自動調用,Java動態(tài)代理機制
* 會傳入下面是個參數(shù)
* @param Object proxy 代理對象的接口,不同于對象
* @param Method method 被調用方法
* @param Object[] args 方法參數(shù)
* 不能使用invoke時使用proxy作為反射參數(shù)時,因為代理對象的接口,不同于對象
* 這種代理機制是面向接口,而不是面向類的
**/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法返回值
Object ret=null;
//打印方法名稱
System.err.println("執(zhí)行方法:"+method.getName()+"n參數(shù)類型為:");
//打印參數(shù)
for(Class type:method.getParameterTypes())
System.err.println(type.getName());
//打印返回類型
System.err.println("返回數(shù)據(jù)類型:"+method.getReturnType().getName());
//反射調用方法
ret=method.invoke(o, args);
//聲明結束
System.err.println("方法執(zhí)行結束");
//返回反射調用方法的返回值
return ret;
}
}
動態(tài)代理已經搞定..然后就是咱們的AnimalFactory了..咱們繼續(xù)
package proxy;
import java.lang.reflect.Proxy;
public class AnimalFactory {
/***
* 獲取對象方法
* @param obj
* @return
*/
private static Object getAnimalBase(Object obj){
//獲取代理對象
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(), new AOPHandle(obj));
}
/***
* 獲取對象方法
* @param obj
* @return
*/
@SuppressWarnings("unchecked")
public static T getAnimal(Object obj){
return (T) getAnimalBase(obj);
}
/***
* 獲取對象方法
* @param className
* @return
*/
@SuppressWarnings("unchecked")
public static T getAnimal(String className){
Object obj=null;
try {
obj= getAnimalBase(Class.forName(className).newInstance());
} catch (Exception e) {
e.printStackTrace();
}
return (T)obj;
}
/***
* 獲取對象方法
* @param clz
* @return
*/
@SuppressWarnings("unchecked")
public static T getAnimal(Class clz){
Object obj=null;
try {
obj= getAnimalBase(clz.newInstance());
} catch (Exception e) {
e.printStackTrace();
}
return (T)obj;
}
}
終于到最后了…還差什么呢,大家來這里看看效果吧…
哈哈…小二,上個菜..哦~不對,是個測試類..哈哈////
package proxy;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;
import proxy.AnimalFactory;
import proxy.imp.AnimalInterface;
@RunWith(BlockJUnit4ClassRunner.class)
public class AOPTest {
@Test
public void Test1() {
AnimalInterface dog=AnimalFactory.getAnimal(DogImp.class);
dog.say();
System.out.println("我的名字是"+dog.getName());
dog.setName("二狗子");
System.out.println("我的名字是"+dog.getName());
}
}
啥?什么,,到了最后說,,這又卵用,這不是坑爹么?就捕獲一個這個玩意,什么用啊…
什么AOP,我怎么一點AOP的影子都沒有看到,怎么切入自定義方法,就一個syso輸入,往這忽悠觀眾來了?…..
好吧,那咱們繼續(xù)…看看如何實現(xiàn)注入自定義方法…
首先增加一個接口,咱們就稱為AOP注入接口吧.取名AOPMethod哈
創(chuàng)建after和before方法,接收Object proxy, Method method, Object[] args參數(shù)
這樣就能做更多的事情叻…比如執(zhí)行方法前,記錄類狀態(tài),寫入log.監(jiān)控xx變量,,,
開啟你的腦洞吧.
package proxy.imp;
import java.lang.reflect.Method;
public interface AOPMethod{
//實例方法執(zhí)行前執(zhí)行的方法
void after(Object proxy, Method method, Object[] args);
//實例方法執(zhí)行后執(zhí)行的方法
void before(Object proxy, Method method, Object[] args);
}
然后修改AOPHandle類,增加AOPMethod屬性.
在修改構造方法,讓在類初始化時獲得AOPMethod實例.
最后修改invoke方法….直接上代碼哈
package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import proxy.imp.AOPMethod;
public class AOPHandle implements InvocationHandler{
//保存對象
private AOPMethod method;
private Object o;
public AOPHandle(Object o,AOPMethod method) {
this.o=o;
this.method=method;
}
/**
* 這個方法會自動調用,Java動態(tài)代理機制
* 會傳入下面是個參數(shù)
* @param Object proxy 代理對象的接口,不同于對象
* @param Method method 被調用方法
* @param Object[] args 方法參數(shù)
* 不能使用invoke時使用proxy作為反射參數(shù)時,因為代理對象的接口,不同于對象
* 這種代理機制是面向接口,而不是面向類的
**/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object ret=null;
//修改的地方在這里哦
this.method.before(proxy, method, args);
ret=method.invoke(o, args);
//修改的地方在這里哦
this.method.after(proxy, method, args);
return ret;
}
}
呼呼,大功告成,,看起來一切都么問題,萌萌噠..
趕緊更新下測試類…
package proxy;
import java.lang.reflect.Method;
import org.junit.runner.RunWith;
import org.junit.runners.BlockJUnit4ClassRunner;
import proxy.imp.AOPMethod;
import proxy.imp.AnimalInterface;
@RunWith(BlockJUnit4ClassRunner.class)
public class AOPTest {
public static void main(String[] args) {
AnimalInterface dog = AnimalFactory.getAnimal(DogImp.class, new AOPMethod() {
// 這里寫方法執(zhí)行前的AOP切入方法
public void before(Object proxy, Method method, Object[] args) {
System.err.println("我在" + method.getName() + "方法執(zhí)行前執(zhí)行");
}
// 這里系方法執(zhí)行后的AOP切入方法
public void after(Object proxy, Method method, Object[] args) {
System.err.println("我在 " + method.getName() + "方法執(zhí)行后執(zhí)行");
}
});
dog.say();
String name1="我的名字是" + dog.getName();
System.out.println(name1);
dog.setName("二狗子");
String name2="我的名字是"+dog.getName();
System.out.println(name2);
}
}
呼呼,親們,是不是有注入的感覺了?是不是感覺把自己的方法切進去了???哈哈….
看起來一切都已經完美了,但是總覺得差了點什么?哦,對,缺少了類似于Spring那么樣的配置文件..
其實那些已經很簡單了,交給你們去做吧,設計好XML格式化就妥了,等等,你說什么,還不能攔截自定義方法?
不能像Spring那樣攔截自定義方法?oh~~NO,其實已經可以了在before(Object proxy, Method method, Object[] args)中利用method和的給methodName就能做判斷了.
當然,本例的并沒有什么實用意義,更不能個各種完善的AOP框架相比,本文僅僅為您提供一種思路,但是一定要記住,再牛的東西也是一點點積累出來的
實例下載:http://xiazai.jb51.net/201701/yuanma/JavaAOP_jb51.rar
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
Spring?Validation接口入?yún)⑿r炇纠a
Spring?Validation是一種用于實現(xiàn)數(shù)據(jù)校驗的框架,它提供了一系列的校驗器,針對不同的數(shù)據(jù)類型可以使用不同的校驗器進行校驗,下面這篇文章主要給大家介紹了關于Spring?Validation接口入?yún)⑿r灥南嚓P資料,需要的朋友可以參考下2023-06-06
Java畢業(yè)設計實戰(zhàn)之健身器材商城系統(tǒng)的實現(xiàn)
只學書上的理論是遠遠不夠的,只有在實戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+Jdbc+Servlet+Ajax+Fileupload+mysql實現(xiàn)健身器材商城系統(tǒng),大家可以在過程中查缺補漏,提升水平2022-03-03
關于SpringBoot獲取IOC容器中注入的Bean(推薦)
本文通過實例代碼給大家詳解了springboot獲取ioc容器中注入的bean問題,非常不錯,具有一定的參考借鑒價值,需要的朋友參考下吧2018-05-05
詳解Java使用Pipeline對Redis批量讀寫(hmset&hgetall)
本篇文章主要介紹了Java使用Pipeline對Redis批量讀寫(hmset&hgetall),具有一定的參考價值,有興趣的可以了解一下。2016-12-12

