參考博客:http://www.javashuo.com/article/p-hqdlakuf-ct.htmlspring
---------------------
做者:huang-yang
來源:CSDN
原文:https://blog.csdn.net/qq_22583741/article/details/79589910
express
這個大佬寫的太厲害了, 索性直接轉了編程
一. AOP概念緩存
AOP(Aspect Oriented Programming , 面向切面編程), 經過預編譯和運行期動態代理實現程序功能的統一維護的一種技術. AOP是OOP的延續,是Spring框架的重要內容, 是函數式編程的一種衍生範型。利用AOP能夠對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度下降,提升程序的可重用性,同時提升了開發的效率。安全
二. AOP實現原理:app
三. AOP術語 [重點掌握]框架
1.target(目標類): 須要被代理的類。例如:UserService
2.Joinpoint(鏈接點): 所謂鏈接點是指那些可能被攔截到的方法。例如:全部的方法
3.PointCut 切入點: 已經被加強的鏈接點。例如:addUser()
4.advice 通知/加強: 加強代碼。例如:after、before
5. Weaving(織入): 是指把加強advice應用到目標對象target來建立新的代理對象proxy的過程.
6.proxy (代理類)
7. Aspect(切面): 是切入點pointcut和通知advice的結合
一個線是一個特殊的面。
一個切入點和一個通知,組成一個特殊的面。 ide
也就是說,咱們最終會得到的, 是advice 和 pointcut 結合起來的proxy代理類
函數式編程
四. AOP 實現方式函數
AOP實現方式包括 手動模式, 半自動模式, 全自動模式
4.1.手動模式:
4.1.1JDK動態代理
(1)目標類: 接口+實現類
public interface ProductService { public void addProduct(); public void updateProduct(); public void deleteProduct(); } public class ProductServiceImpl implements ProductService { @Override public void addProduct() { System.out.println("add Product"); } @Override public void updateProduct() { System.out.println("update Product"); } @Override public void deleteProduct() { System.out.println("delete Product"); } }
(2)切面類: 用於實現通知/加強
public class LoggerAspect { public void before(){ System.out.println("在添加商品前作些什麼"); } public void after(){ System.out.println("在添加商品後作些什麼"); } }
(3)工廠類: 編寫工廠生成代理
public class ProductLogFactory { public static ProductService createService(){ //目標類 final ProductService productService = new ProductServiceImpl(); //切面類 final LoggerAspect loggerAspect = new LoggerAspect(); //代理類 ProductService proxyService = (ProductService) Proxy.newProxyInstance( LoggerAspect.class.getClassLoader(), //參數1 productService.getClass().getInterfaces(), //參數2 new InvocationHandler() { //參數3 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //前執行 loggerAspect.before(); //執行目標類方法 Object obj = method.invoke(productService,args); //後執行 loggerAspect.after(); return obj; } } ); return proxyService; //返回建立的代理類對象 } }
參數1:loader ,類加載器,動態代理類 運行時建立,任何類都須要類加載器將其加載到內存。
* 通常狀況:當前類.class.getClassLoader();
* 目標類實例.getClass().get...
---------------------
參數2:Class[] interfaces 代理類須要實現的全部接口
* 方式1:目標類實例.getClass().getInterfaces() ;注意:只能得到本身接口,不能得到父元素接口
* 方式2:new Class[]{UserService.class}
* 例如:jdbc 驅動 --> DriverManager 得到接口 Connection
---------------------
參數3:InvocationHandler 處理類,接口,必須進行實現類,通常採用匿名內部
* 提供 invoke 方法,代理類的每個方法執行時,都將調用一次invoke
* 參數31:Object proxy :代理對象
* 參數32:Method method : 代理對象當前執行的方法的描述對象(反射)
* 執行方法名:method.getName()
* 執行方法:method.invoke(對象,實際參數)
* 參數33:Object[] args :方法實際參數
(4)測試:
使用被織入了切面方法的代理類的方法
@Test public void test3(){ ProductService ps = ProductLogFactory.createService(); ps.addProduct(); ps.deleteProduct(); ps.updateProduct(); }
4.1.1CGLIB字節碼加強
public class ProductLogFactory1 { public static ProductServiceImpl createService(){ //目標類 final ProductServiceImpl productService = new ProductServiceImpl(); //切面類 final LoggerAspect loggerAspect = new LoggerAspect(); //代理類, 採用cglib, 底層建立目標類的子類 //核心類 Enhancer enhancer = new Enhancer(); //設置父類爲目標類 enhancer.setSuperclass(productService.getClass()); //設置回調函數 enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { //前執行 loggerAspect.before(); //執行目標類的方法 Object obj = method.invoke(productService,args); //執行代理類的父類 目標類是代理類的父類 methodProxy.invokeSuper(proxy,args); //後執行 loggerAspect.after(); return obj; } }); //建立代理 ProductServiceImpl proxyService = (ProductServiceImpl)enhancer.create(); return proxyService; } }
用法和上面相似,得到代理類對象後, 使用其中已經織入切面類方法的方法
4.2.半自動方式: 讓Spring建立代理對象, 從Spring容器中手動得到代理對象
(1)目標類: 接口+實現類
public interface ProductService { public void addProduct(); public void updateProduct(); public void deleteProduct(); } public class ProductServiceImpl implements ProductService { @Override public void addProduct() { System.out.println("add Product"); } @Override public void updateProduct() { System.out.println("update Product"); } @Override public void deleteProduct() { System.out.println("delete Product"); } }
(2)切面類: 與前面的切面類不一樣, 這個切面類實現了MethodInterceptor接口, 環繞通知
public class LoggerAspect implements MethodInterceptor { @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { //前執行 System.out.println("在操做商品前作些什麼"); //手動執行目標方法 Object obj = methodInvocation.proceed(); //後執行 System.out.println("在操做商品後作些什麼"); return obj; } }
(3) Spring配置: 要在applicationContext.xml文件中聲明目標類,切面類 並讓Spring幫忙生成代理類
<!--目標類 --> <bean id="productServiceId" class="service.ProductServiceImpl"></bean> <!-- 切面類--> <bean id="loggerAspectId" class="aspect.LoggerAspect"></bean> <!-- 代理類 --> <bean id="proxyServiceId" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="interfaces" value="service.ProductService"></property> <property name="target" ref="productServiceId"></property> <property name="interceptorNames" value="loggerAspectId"></property> </bean>
* 經過使用工廠bean FactoryBean ,底層調用 getObject() 返回特殊bean
* ProxyFactoryBean 用於建立代理工廠bean,生成特殊代理對象
參數1: interfaces : 肯定接口們
經過<array>能夠設置多個值
只有一個值時,value=""
參數2: target : 肯定目標類
參數3: interceptorNames : 通知 切面類的名稱,類型String[],若是設置一個值 value=""
可選參數4: optimize :強制使用cglib
<property name="optimize" value="true"></property>
底層機制
若是目標類有接口,採用jdk動態代理
若是沒有接口,採用cglib 字節碼加強
若是聲明 optimize = true ,不管是否有接口,都採用cglib
(4)測試
@Test public void test3(){ String xmlPath = "applicationContext.xml"; ApplicationContext context = new ClassPathXmlApplicationContext(xmlPath); ProductService ps = (ProductService)context.getBean("proxyServiceId"); ps.addProduct(); ps.deleteProduct(); ps.updateProduct(); }
4.3.全自動方式: 從Spring容器得到目標類, 經過配置aop, 讓Spring自動生成代理
(1)目標類: 接口+實現類 這裏就不放代碼了
(2)切面類: 與半自動方式類似
(3)xml配置:
<aop:config> <aop:pointcut id="productPointCut" expression="execution(* service.ProductServiceImpl.*(..))"/> <aop:advisor advice-ref="loggerAspectId" pointcut-ref="productPointCut"/> </aop:config>
aop編程 :
3.1 導入命名空間
3.2 使用 <aop:config>進行配置
proxy-target-class="true" 聲明時使用cglib代理
<aop:pointcut> 切入點 ,從目標對象得到具體方法
<aop:advisor> 特殊的切面,只有一個通知 和 一個切入點
advice-ref 通知引用
pointcut-ref 切入點引用
3.3 切入點表達式
execution(* com.itheima.c_spring_aop.*.*(..))
選擇方法 返回值任意 包 類名任意 方法名任意 參數任意
(4)測試:
@Test public void test3(){ String xmlPath = "applicationContext.xml"; ApplicationContext context = new ClassPathXmlApplicationContext(xmlPath); ProductService ps = (ProductService)context.getBean("productServiceId"); ps.addProduct(); ps.deleteProduct(); ps.updateProduct(); }
注意這裏,再也不是獲取由Spring容器建立的代理類了, 而是直接獲取ProductService的bean, 生成代理的定義幾乎徹底不可視了
4.4. 另外一種全自動方式:
(1)目標類: 接口+實現類
(2)切面類: 切面類再也不實現某個接口, 將獲取切入點的操做交給了Spring
public class LoggerAspect { public Object log(ProceedingJoinPoint joinPoint) throws Throwable { //前執行 System.out.println("在操做商品前作些什麼"); //手動執行目標方法 Object obj = joinPoint.proceed(); //後執行 System.out.println("在操做商品後作些什麼"); return obj; } }
(3)xml配置: 指定切面類的bean, 指定切入點是ProductService的全部方法, 指定切面方法爲log
<!-- Aspect --> <bean id="loggerAspectId" class="aspect.LoggerAspect"></bean> <!-- aop --> <aop:config> <aop:pointcut id="loggerCutpoint" expression="execution(* service.ProductService.*(..))"/> <aop:aspect id="logAspect" ref="loggerAspectId"> <aop:around pointcut-ref="loggerCutpoint" method="log"/> </aop:aspect> </aop:config>