靜態代理在使用時,需要定義接口或者父類,被代理對象(即目標對象)與代理對象一起實現相同的接口或者是繼承相同父類。
舉例:
package com.deserts.proxy.staticproxy;/*** @ClassName ProxyTest* @Description TODO* @Author deserts* @Date 2020/10/16 12:12*/interface IShoeFactory{void produce();
}class ShoeFactory implements IShoeFactory{@Overridepublic void produce() {System.out.println("生產鞋子");}
}class ShoeProxy implements IShoeFactory{private IShoeFactory shoeFactory;public ShoeProxy (IShoeFactory shoeFactory){this.shoeFactory = shoeFactory;}@Overridepublic void produce() {shoeFactory.produce();}
}public class ProxyTest {public static void main(String[] args) {ShoeProxy proxy = new ShoeProxy(new ShoeFactory());proxy.produce();}
}
優點:在不修改目標對象的功能前提下, 能通過代理對象對目標功能擴展
缺點:因為代理對象需要與目標對象實現一樣的接口,所以會有很多代理類;或者需要實現很多接口,代碼混亂。
中臺前端框架,一旦接口增加方法,目標對象與代理對象都要維護。
基本介紹:
代理對象,不需要實現接口,但是目標對象要實現接口,否則不能用動態代理
代理對象的生成,是利用JDK的API,動態的在內存中構建代理對象
動態代理也叫做:JDK代理、接口代理
Spring aop,舉例:
package com.deserts.proxy.jdkproxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** @ClassName JDKProxyTest* @Description TODO* @Author deserts* @Date 2020/10/16 12:23*/interface IShoeFactory{void produce();
}
class ShoeFactory implements IShoeFactory{@Overridepublic void produce() {System.out.println("生產鞋子中");}
}class ShoeProxyFactory{private Object target;public ShoeProxyFactory(Object target) {this.target = target;}public Object getProxyInstance(){return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object returnResult = method.invoke(target, args);return returnResult;}});}
}public class JDKProxyTest {public static void main(String[] args) {IShoeFactory proxy = (IShoeFactory)new ShoeProxyFactory(new ShoeFactory()).getProxyInstance();proxy.produce();}}
①執行加減乘除運算
②日志:在程序執行期間追蹤正在發生的活動
創建計算器接口,讓計算器類去實現;創建動態代理的類,利用構造器或者方法傳入計算器對象,創建獲取被代理類的方法。
1.ICalculator接口
public interface ICalculator {int add(int i, int j);int sub(int i, int j);int mul(int i, int j);int div(int i, int j);
}
Java前端框架、2.Calculator類
package com.deserts.aop.proxy;/*** @ClassName Calculator* @Description TODO* @Author deserts* @Date 2020/10/15 19:23*/
public class Calculator implements ICalculator{@Overridepublic int add(int i, int j) {int result = i + j;return result;}@Overridepublic int sub(int i, int j) {int result = i - j;return result;}@Overridepublic int mul(int i, int j) {int result = i * j;return result;}@Overridepublic int div(int i, int j) {int result = i / j;return result;}
}
3.代理類
package com.deserts.aop.proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.function.Predicate;/*** @ClassName ProxyFactory* @Description TODO* @Author deserts* @Date 2020/10/15 19:26*/
public class ProxyFactory {Object target;public ProxyFactory(Object target){this.target = target;}public Object getProxy(){return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object returnResult = null;try {Log.before(method.getName(), args);returnResult = method.invoke(target, args);Log.after(method.getName(), returnResult);} catch (Exception e) {Log.exceptionInfo(method.getName(), e);} finally {Log.end();}return returnResult;}});}
}
4.封裝日志功能,錯誤信息的類
package com.deserts.aop.proxy;import java.util.Arrays;/*** @ClassName Log* @Description TODO* @Author deserts* @Date 2020/10/15 19:54*/
public class Log {public static void before(String methodName, Object... args){System.out.println(methodName + "即將執行,參數為:" + Arrays.asList(args));}public static void after(String methodName, Object result){System.out.println(methodName + "執行結束,結果為:" + result);}public static void exceptionInfo(String methodName, Exception e){System.out.println(methodName + "執行過程出現了異常,異常信息為:" + e.getCause());}public static void end(){System.out.println("程序執行結束");}
}
5.測試類
package com.deserts.aop.proxy;/*** @ClassName Client* @Description TODO* @Author deserts* @Date 2020/10/15 19:34*/
public class Client {public static void main(String[] args) {ICalculator proxy = (ICalculator)new ProxyFactory(new Calculator()).getProxy();proxy.add(1,2);proxy.div(2, 0);}
}
AOP(Aspect-Oriented Programming,面向切面編程):是一種新的方法論,是對傳統 OOP(Object-Oriented Programming,面向對象編程)的補充。
VB中的框架?AOP編程操作的主要對象是切面(aspect),而切面用于模塊化橫切關注點(公共功能)。
在應用AOP編程時,仍然需要定義公共功能,但可以明確的定義這個功能應用在哪里,以什么方式應用,并且不必修改受影響的類。這樣一來橫切關注點就被模塊化到特殊的類里——這樣的類我們通常稱之為“切面”
AOP的好處:
① 每個事物邏輯位于一個位置,代碼不分散,便于維護和升級
② 業務模塊更簡潔,只包含核心業務代碼
Spring框架是什么?③ AOP圖解
從每個方法中抽取出來的同一類非核心業務。
封裝橫切關注點信息的類,每個關注點體現為一個通知方法。
中框架和邊框架。切面必須要完成的各個具體工作
被通知的對象
向目標對象應用通知之后創建的代理對象
橫切關注點在程序代碼中的具體體現,對應程序執行的某個特定位置。例如:類某個方法調用前、調用后、方法捕獲到異常后等。
定位連接點的方式。每個類的方法中都包含多個連接點,所以連接點是類中客觀存在的事物。如果把連接點看作數據庫中的記錄,那么切入點就是查詢條件——AOP可以通過切入點定位到特定的連接點。切點通過org.springframework.aop.Pointcut 接口進行描述,它使用類和方法作為連接點的查詢條件。
Spring Framework、
AspectJ:Java社區里最完整最流行的AOP框架。
在Spring2.0以上版本中,可以使用基于AspectJ注解或基于XML配置的AOP。
1.導入jar包
2.引入AOP命名空間
Springboot?
3.配置掃描包,配置被代理的類和日志類
日志類需要@Aspect注解,@Before注解將方法配置為前置通知,需要指定value值,值為切入點表達式。同時需要@Component注解讓Spring組件掃描去管理。
被代理類也需要@Component注解,讓Spring去管理:
spring開發框架、
測試類使用時需要創建Spring容器去管理被配置好的類的對象:
運行結果:
Spring bean、① @Before:前置通知,在方法執行之前執行
② @After:后置通知,在方法執行之后執行
③ @AfterRunning:返回通知,在方法返回結果之后執行
④ @AfterThrowing:異常通知,在方法拋出異常之后執行
⑥ @Around:環繞通知,圍繞著方法執行
Spring框架很難么?
IOC容器中保存的是組件的代理對象,可以理解成這個代理對象也去實現了目標對象所實現的接口
如:
運行結果:
vb中框架是哪個,
可以看到,該對象是class com.sun.proxy.$Proxy7代理對象,底層原理是jdk動態代理。
除了實現接口的方式去代理,spring還提供了cglib代理,為沒有接口的類進行代理。
如:
網頁設計中框架是什么、測試類:
運行結果:
可以看到cglib為我們創建了代理對象
1)匹配一個或多個字符,如:execution(public int com.deserts.aop.proxy.Calculator.*(int,int))
匹配任意類型的參數,如:execution(public int com.deserts.aop.proxy.Calculator.add(*,*))
只能匹配一層路徑,如:execution(public int com.deserts.aop.*.Calculator.add(int,int))
4)權限類型前不能寫,可以不寫權限類型。public(可選類型)。
2)匹配多層路徑,如:execution(public int com.deserts…Calculator.*(int,int))
最精確的:execution(public int com.deserts.aop.proxy.Calculator.add(int,int))
最模糊的:execution(*.*(…)) 不建議寫,見方法就切
在AspectJ中,切入點表達式可以通過 “&&”、“||”、“!”等操作符結合起來。
表達式 | 含義 |
---|---|
execution (* *.add(int,…)) || excution( *.sub(int, …)) | |
!execution (* *.add(int,…)) | 匹配不是任意類中第一個參數為int類型的add方法 |
正常執行:@Before(前置通知) ==== @After(后置通知) ==== @AfterReturning(正常返回)
異常執行: @Before(前置通知) ==== @After(后置通知) ==== @AfterThrowing(方法異常)
主要通過傳入通知方法的JoinPoint joinPoint參數,在方法內調用參數相關方法進行獲取,如:
接收返回值:
接收異常信息:
1)spring對通知方法的沒有太大約束,唯一的約束是參數列表的參數不能亂寫,如果有未知的參數,需要告訴spring這個參數是用來做什么的,因為這些通知方法是spring所管理的,主要是通過反射來調用的。
2)如returning、throwing指定接收返回值、異常的參數,參數類型要盡量的大,如Object、Exception
抽取可重用的切入點表達式:
示例:
示例:
測試:將四個普通通知和環繞通知都打開,運行查看結果:
通知順序:
新的順序:
讓普通通知感受到異常:將該異常拋出去
都是普通通知時,會根據切面類的名稱的字母順序,小的在外層,如LogUtil類在VaAspect類的外層,先進去后出來
若想改變切面運行順序,可以加上@Order注解,如:
value值越小,優先級越高,也就是在最外層。
當有環繞通知時,環繞通知會先于該切面類的普通通知去執行,因為該環繞通知是加在這個切面類的。
如環繞通知加到LogUtils上:
1.日志記錄
2.權限驗證
3.安全檢查
4.事務控制
步驟
在IOC容器中創建管理目標類的bean和切面類的bean
配置切面類中的通知方法,配置切面時需要引用上面配置好的切面類
測試
注解:快速方便
配置:功能完善
選擇:重要的用配置,不重要的用注解
版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。
工作时间:8:00-18:00
客服电话
电子邮件
admin@qq.com
扫码二维码
获取最新动态