一攘乒、什么是Aop?
- Aop與Oop一樣脐彩,都是一種編程思想。
- Aop面向切換編程犁河, 其實(shí)就是無侵入的進(jìn)行功能增強(qiáng)鳖枕,使用Aop可以實(shí)現(xiàn)業(yè)務(wù)代碼和系統(tǒng)代碼分離(如日志記錄、權(quán)限控制桨螺、性能統(tǒng)計(jì)等通用功能)宾符。
- Aop的實(shí)現(xiàn)方式主要有幾種:AspectJ、Spring Aop灭翔、Spring整合AspectJ魏烫。
- Aop的核心思想,就是通過織入去增強(qiáng)代碼肝箱,織入又分為靜態(tài)織入和動(dòng)態(tài)織入哄褒。
- 靜態(tài)織入:指的是不修改原代碼,只對(duì)class文件進(jìn)行修改煌张,實(shí)現(xiàn)功能增強(qiáng)呐赡。(AspectJ使用的就是這種,一般不使用)。
- 動(dòng)態(tài)織入:指的是運(yùn)行時(shí)骏融,通過動(dòng)態(tài)代理技術(shù)链嘀,產(chǎn)生代理對(duì)象,完成功能增強(qiáng)档玻。(Spring Aop怀泊、Spring整合AspectJ使用的就是這種)。
二窃肠、Aop的基本概念
Aop核心功能圖解
- 目標(biāo)對(duì)象
被一個(gè)或多個(gè)切面增強(qiáng)的對(duì)象包个。 - 連接點(diǎn)(joinpoint)
程序執(zhí)行中的某個(gè)具體的執(zhí)行點(diǎn)。 - 切入點(diǎn)(pointcut)
對(duì)連接點(diǎn)的特征進(jìn)行描述,可以用正則表達(dá)式碧囊。增強(qiáng)處理和一個(gè)切入點(diǎn)表達(dá)式相關(guān)聯(lián)树灶,并在與這個(gè)切入點(diǎn)匹配的某個(gè)連接點(diǎn)上運(yùn)行。 - 代理對(duì)象
由Aop框架所創(chuàng)建的對(duì)象糯而,實(shí)現(xiàn)執(zhí)行增強(qiáng)處理的方法天通。 - 織入(weave)
將增強(qiáng)處理連接到應(yīng)用程序中的類型或?qū)ο笊系倪^程。 - 通知(advice)(增強(qiáng)功能)
攔截到連接點(diǎn)之后要執(zhí)行的代碼熄驼,分為前置通知像寒、后置通知、異常通知瓜贾、最終通知诺祸、環(huán)繞通知五類。 - 切面(aspect)
一個(gè)模塊化的橫切邏輯祭芦,可能會(huì)橫切多個(gè)對(duì)象筷笨。 - 通知器
是一個(gè)特殊的切面。 - 引入(introduction)
在不修改代碼的前提下龟劲,引入可以在運(yùn)行期為類動(dòng)態(tài)地添加一些方法或字段胃夏。
三、動(dòng)態(tài)代理
為什么使用代理昌跌?
簡單來說仰禀,代理就是幫【目標(biāo)對(duì)象】去完成它應(yīng)該做,但不想或不擅長做的事情蚕愤。(如社保代繳答恶,黃牛掛號(hào)等)
代理技術(shù)分為靜態(tài)代理和動(dòng)態(tài)代理。
- 靜態(tài)代理:
編寫一個(gè)代理類萍诱,去代理【目標(biāo)對(duì)象】亥宿。就是在寫源碼的時(shí)候,為目標(biāo)類編寫一個(gè)對(duì)應(yīng)的代理類(java文件)砂沛。 - 動(dòng)態(tài)代理:
在運(yùn)行期間,通過反射曙求,對(duì)【目標(biāo)對(duì)昂】產(chǎn)生一個(gè)【代理對(duì)象】碍庵。就是在運(yùn)行時(shí),通過不同的技術(shù)實(shí)現(xiàn)悟狱,去創(chuàng)建新的對(duì)象静浴。
為什么使用動(dòng)態(tài)代理?
增強(qiáng)對(duì)象的功能挤渐。這種增強(qiáng)苹享,符合開閉原則,不會(huì)對(duì)目標(biāo)對(duì)象就行修改,只需要擴(kuò)展就可以實(shí)現(xiàn)增強(qiáng)得问。
動(dòng)態(tài)代理技術(shù)常用的兩種(兩個(gè)方式生成的代理類都是繼承了Proxy):JDK動(dòng)態(tài)代理技術(shù)囤攀、CGLib動(dòng)態(tài)代理技術(shù)。
- JDK動(dòng)態(tài)代理技術(shù)(基于接口):
【目標(biāo)類】必須有接口才能使用的這種方式宫纬。
【代理類】和【目標(biāo)類】其實(shí)都是【目標(biāo)類接口】的一個(gè)實(shí)現(xiàn)類焚挠,使用的是接口實(shí)現(xiàn)。 - CGLib動(dòng)態(tài)代理技術(shù)(基于繼承):
【目標(biāo)類】只要不是final修飾的就可以(可以被繼承的普通類)漓骚,不需要有接口蝌衔。
【代理類】是【目標(biāo)類】的子類,通過繼承的方式產(chǎn)生的蝌蹂。
Spring默認(rèn)使用的是JDK噩斟,可以人為指定使用CGLib.
其它區(qū)別:
- JDK1.7之前,CGLib運(yùn)行比JDK要快孤个,之后效率差不多剃允,但是JDK產(chǎn)生代理對(duì)象的效率要高。
- CGLib底層是通過ASM字節(jié)碼工具包去實(shí)現(xiàn)的字節(jié)碼重寫硼身。而JDK只是相當(dāng)于幫程序員在后臺(tái)寫了java文件硅急,并編譯、加載 佳遂。
JDK和CGLib產(chǎn)生代理對(duì)象的方式和處理步驟是怎么樣的营袜?
- JDK動(dòng)態(tài)技術(shù)產(chǎn)生代理對(duì)象:
- 產(chǎn)生代理對(duì)象
Proxy.newProxyInstance(classloader,interfaces,【InvocationHandler實(shí)現(xiàn)類】)
/**
* 主要作用就是生成代理類 使用JDK的動(dòng)態(tài)代理實(shí)現(xiàn) 它是基于接口實(shí)現(xiàn)的
*
* @author think
*
*/
public class JDKProxyFactory {
/**
* @return
*/
public Object getProxy(Object target) {
Class<?> clazz = UserService.class;
Class<?> clazz2 = target.getClass();
System.out.println(clazz);
System.out.println(clazz2);
System.out.println(clazz2.getInterfaces());
System.out.println(target.getClass().getInterfaces());
// 如何生成一個(gè)代理類呢?
// 1丑罪、編寫源文件(java文件)----目錄類接口interface實(shí)現(xiàn)類(調(diào)用了目標(biāo)對(duì)象的方法)
// class Proxy4{
// InvocationHandler
// 目標(biāo)對(duì)象
// 目標(biāo)對(duì)象的方法
// void saveUer(){
// 動(dòng)態(tài)生成的
// 需要自定義編寫
// InvocationHandler.invoke(){
// 編寫其他邏輯
// 調(diào)用目標(biāo)對(duì)象的方法
// }
// }
// }
// 2荚板、編譯源文件為class文件
// 3、將class文件加載到JVM中(ClassLoader)
// 4吩屹、將class文件對(duì)應(yīng)的對(duì)象進(jìn)行實(shí)例化(反射)
// Proxy是JDK中的API類
// 第一個(gè)參數(shù):目標(biāo)對(duì)象的類加載器
// 第二個(gè)參數(shù):目標(biāo)對(duì)象的接口
// 第三個(gè)參數(shù):代理對(duì)象的執(zhí)行處理器
// Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), new Class[] { clazz2 },new MyInvocationHandler(target));
Object proxy = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz },
new MyInvocationHandler(target));
return proxy;
}
2.調(diào)用InvocationHandler去完成增強(qiáng)跪另。
/**
* JDK動(dòng)態(tài)代理使用的動(dòng)態(tài)增強(qiáng)的類
*
* @author think
*
*/
public class MyInvocationHandler implements InvocationHandler {
// 目標(biāo)對(duì)象的引用
private Object target;
// 通過構(gòu)造方法將目標(biāo)對(duì)象注入到代理對(duì)象中
public MyInvocationHandler(Object target) {
super();
this.target = target;
}
/**
* 代理對(duì)象會(huì)執(zhí)行的方法
* 第一個(gè)參數(shù):代理對(duì)象本身
* 第二個(gè)參數(shù):目標(biāo)對(duì)象的方法對(duì)象(Method對(duì)象),一個(gè)方法針對(duì)一個(gè)Method對(duì)象
* 第三個(gè)參數(shù):目標(biāo)對(duì)象的方法參數(shù)
*
* 代理對(duì)象執(zhí)行的邏輯:
* 需要執(zhí)行目標(biāo)對(duì)象的原方法煤搜?
* 如何執(zhí)行目標(biāo)對(duì)象的原方法呢免绿?
* 該處使用的是反射
* 【要調(diào)用方法的Method對(duì)象.invoke(要調(diào)用方法的對(duì)象,要調(diào)用方法的參數(shù))】
* 只是在調(diào)用目標(biāo)對(duì)象的原方法前邊和后邊可能要加上一些增強(qiáng)功能的代碼
*
* 增強(qiáng)代碼比如:
* 在原方法調(diào)用之前,開啟事務(wù)擦盾,源方法結(jié)束之后嘲驾,提交和回滾事務(wù)
*
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("這是jdk的代理方法");
// 下面的代碼,是反射中的API用法
// 該行代碼迹卢,實(shí)際調(diào)用的是[目標(biāo)對(duì)象]的方法
// 利用反射辽故,調(diào)用[目標(biāo)對(duì)象]的方法
Object returnValue = method.invoke(target, args);
// AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
// ReflectiveMethodInvocation.proceed()進(jìn)行遞歸增強(qiáng)
// 增強(qiáng)的部分
return returnValue;
}
}
3.生成的代理對(duì)象
public final class $Proxy4 extends Proxy implements Proxy4 {
private static Method m1;
private static Method m6;
private static Method m2;
private static Method m7;
private static Method m11;
private static Method m13;
private static Method m0;
private static Method m8;
private static Method m12;
private static Method m3;
private static Method m5;
private static Method m10;
private static Method m4;
private static Method m9;
public $Proxy4(InvocationHandler var1) throws {
super(var1);
}
.......
public final void saveUser() 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"));
m6 = Class.forName("com.sun.proxy.$Proxy4").getMethod("getInvocationHandler", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m7 = Class.forName("com.sun.proxy.$Proxy4").getMethod("getProxyClass", Class.forName("java.lang.ClassLoader"), Class.forName("[Ljava.lang.Class;"));
m11 = Class.forName("com.sun.proxy.$Proxy4").getMethod("getClass");
m13 = Class.forName("com.sun.proxy.$Proxy4").getMethod("notifyAll");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m8 = Class.forName("com.sun.proxy.$Proxy4").getMethod("wait");
m12 = Class.forName("com.sun.proxy.$Proxy4").getMethod("notify");
m3 = Class.forName("com.sun.proxy.$Proxy4").getMethod("saveUser");
m5 = Class.forName("com.sun.proxy.$Proxy4").getMethod("newProxyInstance", Class.forName("java.lang.ClassLoader"), Class.forName("[Ljava.lang.Class;"), Class.forName("java.lang.reflect.InvocationHandler"));
m10 = Class.forName("com.sun.proxy.$Proxy4").getMethod("wait", Long.TYPE);
m4 = Class.forName("com.sun.proxy.$Proxy4").getMethod("isProxyClass", Class.forName("java.lang.Class"));
m9 = Class.forName("com.sun.proxy.$Proxy4").getMethod("wait", Long.TYPE, Integer.TYPE);
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
4.測試
@Test
public void testJDKProxy() {
// 1、創(chuàng)建目標(biāo)對(duì)象
UserService service = new UserServiceImpl();
// 2腐碱、生成代理對(duì)象
JDKProxyFactory proxyFactory = new JDKProxyFactory();
// 得到代理對(duì)象
UserService proxy = (UserService) proxyFactory.getProxy(service);
// 生成class文件
generatorClass(proxy);
// 3誊垢、調(diào)用目標(biāo)對(duì)象的方法
service.saveUser();
System.out.println("===============");
// 4、調(diào)用代理對(duì)象的方法
proxy.saveUser();
}
- CGLib動(dòng)態(tài)技術(shù)產(chǎn)生代理對(duì)象:
1.生成代理對(duì)象
Enhance enhance = new...
//設(shè)置超類
//設(shè)置回調(diào)----代理對(duì)象的方法被調(diào)用時(shí)
enhancer.create
/**
* 主要作用就是生成代理類 使用CGLib動(dòng)態(tài)代理技術(shù)實(shí)現(xiàn) 它是基于繼承的方式實(shí)現(xiàn)的
* cglib底層是通過asm包去實(shí)現(xiàn)的字節(jié)碼的編寫
* @author think
*
*/
public class CgLibProxyFactory {
/**
* @param clazz
* @return
*/
public Object getProxyByCgLib(Class<?> clazz) {
// 創(chuàng)建增強(qiáng)器
Enhancer enhancer = new Enhancer();
// 設(shè)置需要增強(qiáng)的類的類對(duì)象
enhancer.setSuperclass(clazz);
// 設(shè)置回調(diào)函數(shù)
enhancer.setCallback(new MyMethodInterceptor());
// 獲取增強(qiáng)之后的代理對(duì)象
Object object = enhancer.create();
// generatorClass(enhancer);
return object;
}
/*
* private void generatorClass(Enhancer enhancer) { FileOutputStream out = null;
* try { byte[] bs = DefaultGeneratorStrategy.INSTANCE.generate(enhancer);
* FileOutputStream fileOutputStream = new FileOutputStream("Enhancer_cglib" +
* ".class"); fileOutputStream.write(bs); fileOutputStream.flush();
* fileOutputStream.close(); } catch (Exception e) { e.printStackTrace(); }
* finally { if (out != null) { try { out.close(); } catch (IOException e) { //
* TODO Auto-generated catch block } } }
*
* }
*/
}
2.調(diào)用MethodInterceptor去完成增強(qiáng)。
/**
* cglib動(dòng)態(tài)代理使用的動(dòng)態(tài)增強(qiáng)的類
*
* @author think
*
*/
public class MyMethodInterceptor implements MethodInterceptor {
/***
* Object proxy:這是代理對(duì)象喂走,也就是[目標(biāo)對(duì)象]的子類
* Method method:[目標(biāo)對(duì)象]的方法
* Object[] arg:參數(shù)
* MethodProxy methodProxy:代理對(duì)象的方法
*/
@Override
public Object intercept(Object proxy, Method method, Object[] arg, MethodProxy methodProxy) throws Throwable {
// 因?yàn)榇韺?duì)象是目標(biāo)對(duì)象的子類
// 該行代碼殃饿,實(shí)際調(diào)用的是父類目標(biāo)對(duì)象的方法
System.out.println("這是cglib的代理方法");
// 通過調(diào)用子類[代理類]的invokeSuper方法,去實(shí)際調(diào)用[目標(biāo)對(duì)象]的方法
// Object returnValue = method.invoke(target, arg);
Object returnValue = methodProxy.invokeSuper(proxy, arg);
// method.invoke(target, arg);
//目標(biāo)對(duì)象的saveUser(){}
//代理對(duì)象saveUser(){
//super();
//}
// 代理對(duì)象調(diào)用代理對(duì)象的invokeSuper方法缴啡,而invokeSuper方法會(huì)去調(diào)用目標(biāo)類的invoke方法完成目標(biāo)對(duì)象的調(diào)用
return returnValue;
}
}
3.測試
@Test
public void testCgLibProxy() {
// 創(chuàng)建目標(biāo)對(duì)象
UserService service = new UserServiceImpl();
// 生成代理對(duì)象
CgLibProxyFactory proxyFactory = new CgLibProxyFactory();
UserService proxy = (UserService) proxyFactory.getProxyByCgLib(service.getClass());
// 調(diào)用目標(biāo)對(duì)象的方法
service.saveUser();
System.out.println("===============");
// 調(diào)用代理對(duì)象的方法
proxy.saveUser();
}
使用代理對(duì)象增強(qiáng)功能的原理壁晒?
代理對(duì)象執(zhí)行時(shí),會(huì)調(diào)用InvocationHandler或者M(jìn)ethodInterceptor去完成增強(qiáng)业栅。
代理對(duì)象執(zhí)行
1.JDK
InvocationHandler{
target
invoke(proxy,method,args){
//調(diào)用目標(biāo)對(duì)象(反射的API)
method.invoke(target,args)
}
}
2.CGLib
MethodInterceptor{
intercept(proxy,method,args,proxyMethod){
//調(diào)用目標(biāo)對(duì)象(代理對(duì)象的API)
//代理對(duì)象又會(huì)調(diào)用目標(biāo)對(duì)象
proxyMethod.invokeSuper(proxy,args)
//method.invoke(target,args)
}
}
什么時(shí)候會(huì)調(diào)用以上的方法秒咐?
當(dāng)代理對(duì)象被創(chuàng)建的時(shí)候,是不會(huì)調(diào)用以上的方法的碘裕。
當(dāng)代理對(duì)象被訪問其中方法的時(shí)候携取,才會(huì)調(diào)用以上兩種(互斥)方法。