概念:經過代理類爲原始類(目標類)增長額外功能java
好處:利於原始類(目標類)的維護
從這點看和靜態代理同樣同樣的spring
<!--Spring aop支持--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.1.14.RELEASE</version> </dependency> <!--aspectj框架包--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.8.8</version> </dependency> <!--編制切面包--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.3</version> </dependency>
package proxy.service.impl; import proxy.service.UserService; public class UserServiceImpl implements UserService { @Override public void login(String username, String password) { System.out.println("UserServiceImpl.login 我是【service核心】"); } }
2. 方法前置加強代碼,須要實現MethodBeforeAdvice接口express
package proxy.service.aop;import org.springframework.aop.BeforeAdvice;import org.springframework.aop.MethodBeforeAdvice;import java.lang.reflect.Method;/** * @Classname MyAdvice * @Description 實現spring aop 包下MethodBeforeAdvice接口添加前置通知 這樣只要在切面上的方法在執行前 * 均要加強: MyBefore.before 【service外圍】 */
publicclass MyBefore implements MethodBeforeAdvice{/** * * @param method 目標方法 login() * @param args login()的參數username\password * @param target 目標對象userServiceImpl * @throws Throwable */@Override
public void before(Method method, Object[] args, Object target)throws Throwable { System.out.println("MyBefore.before 【service外圍】");
}
}
3.配置文件編程
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--目標類 此時和代理類無關--> <bean id = "userService" class="proxy.service.impl.UserServiceImpl"/> <bean id = "orderService" class="proxy.service.impl.OrderServiceImpl"/> <!--通知類--> <bean id= "myBefore" class="proxy.service.aop.MyBefore"/> <!--aop配置標籤,會自動添加工做空間--> <aop:config> <!--定義接入點,即那些方法須要被增強--> <aop:pointcut id="pointcut" expression="execution(* *(..))"/> <!--切入點和通知的結合--> <aop:advisor advice-ref="myBefore" pointcut-ref="pointcut"/> </aop:config> </beans>
4. 測試app
/** * spring動態代理 */ @Test public void test3() { ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext6.xml"); OrderService userService = (OrderService)ctx.getBean("orderService"); userService.order(); }
5.debug查看得到的確實是代理類框架
1.Spring工廠經過原始對象的id值獲取的是代理對象jvm
2.獲取代理對象後,能夠經過聲明接口類型,進行對象的存儲。ide
動態代理和以前的靜態代理不一樣,他不須要java文件而後經過類加載子系統,加載進運行時數據區,這裏是直接使用字節碼相關技術,在JVM內存中直接生成當前類的代理類。也就沒有那麼多的java類讓咱們去管理,也就解決了這個痛點。另外他實用配置的方式對全部須要加強的類進行切入點的統一配置,這樣就沒有了代碼冗餘。函數
那麼確定會有人提出問題,這個只能對方法的前置進行加強太雞肋了。有沒有更好的辦法,能夠在以前和以後均能加強呢?測試
您能想到的問題spring都想到了。接着往下看。使用MethodInterceptor這個就能夠實現。 特別注意是這個報下的:import org.aopalliance.intercept.MethodInterceptor;spring使用了aop聯盟的相關接口來處理這個問題,並非原生的spring的解決方案。
package proxy.service.aop; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class MyArround implements MethodInterceptor { /** * * @param invocation 和MethodBeforeAdvice.before()方法中的Method參數同樣,只是更爲強大的封裝 * @return Object 原始方法的返回值 * @throws Throwable */ @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("MyArround.invoke 【service外圍】前面的"); Object res = null; try {//統一對異常進行拋出 res = invocation.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } System.out.println("MyArround.invoke 【service外圍】後面的"); return res; //return false; //影響原始方法的返回值。 } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--目標類 此時和代理類無關--> <bean id = "userService" class="proxy.service.impl.UserServiceImpl"/> <bean id = "orderService" class="proxy.service.impl.OrderServiceImpl"/> <!--通知類--> <bean id= "myBefore" class="proxy.service.aop.MyBefore"/> <!--aop配置標籤,會自動添加工做空間--> <aop:config> <!--定義接入點,即那些方法須要被增強--> <aop:pointcut id="pointcut" expression="execution(* *(..))"/> <!--切入點和通知的結合--> <aop:advisor advice-ref="myBefore" pointcut-ref="pointcut"/> </aop:config> </beans>
<aop:pointcut id=「pointcut」 expression=「execution(* *(…))」/>表示對因此方法進行加強。
方法切入點表達式
execution(* *(…)) :因此方法進行加強
* login(…) :login方法進行加強
* login(String,String):login 方法且兩個參數爲String的方法加強
* register(proxy.service.User) register方法且參數爲User加強
類切入點
* proxy.service.impl.UserServiceImpl.*(…) 類中的全部方法加入了加強功能
* .UserServiceImpl.(…) 類只在一級包,對類全部方法加強
* …UserServiceImpl.(…) 類存在多級包,UserServiceImpl類下的全部方法加強
包切入點表達式
\ * proxy.service.impl..(…) 切入點包中的全部類,必須在impl中,不能在impl包的子包中
* proxy.service.impl….(…)
execution
能夠知足你的全部想象,能夠作全部的事情:方法切入、類切入、包切入
args
execution(* *(String,String)) 等價於:args(String,String)
within 和args互補
主要用於進行類、包切入點表達式的匹配
execution(* …UserServiceImpl.(…))等價於within(…UserServiceImpl)
execution( com.baizhiedu.proxy….(…))等價於ithin(com.baizhiedu.proxy…*)
@annotation
package proxy; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @Classname Log * @Description 用於切面 */ @Target(ElementType.METHOD) //使用在方法上 @Retention(RetentionPolicy.RUNTIME) //使用在運行時環境中 public @interface Log { }
使用時:<aop:pointcut id="" expression="@annotation(proxy.Log)"/>
面向切面編程的步驟:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"> <!--目標類 此時和代理類無關--> <bean id = "userService" class="proxy.service.impl.UserServiceImpl"/> <bean id = "orderService" class="proxy.service.impl.OrderServiceImpl"/> <!--通知類--> <!-- <bean id= "myBefore" class="proxy.service.aop.MyBefore"/>--> <bean id="myArround" class="proxy.service.aop.MyArround"/> <!--aop配置標籤,會自動添加工做空間--> <aop:config> <!--定義接入點,即那些方法須要被增強--> <aop:pointcut id="pointcut" expression="execution(* *(..))"/> <!--切入點和通知的結合--> <aop:advisor advice-ref="myArround" pointcut-ref="pointcut"/> </aop:config> </beans>
測試:
@Test public void test4() { ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext6.xml"); UserService userService = (UserService)ctx.getBean("userService"); userService.login("zhangsan","111111"); userService.regester(new User(2, "222222")); }
測試結果
MyArround.invoke 【service外圍】前面的
UserServiceImpl.login 我是【service核心】
MyArround.invoke 【service外圍】後面的
MyArround.invoke 【service外圍】前面的
UserServiceImpl.regester 我是【service核心】
MyArround.invoke 【service外圍】後面的
動態字節碼技術原理:
動態代理細節分析:
1.spring建立的動態代理類在哪裏呢?
spring框架在運行的時,經過動態字節碼技術,在jvm中建立的,運行在jvm內部, 等程序結束後會和jvm類一塊兒消失。
2.什麼叫動態字節碼技術?
Java運行一個類, 其實就是運行這個類的編譯後的字節碼--->object
java在類加載的時候會把字節碼加載到jvm的內存中。
3.那麼問題來了,動態字節碼的字節碼從哪裏來的呢?
(動態字節碼其實就是不須要(.Java文件生成.class文件的過程)字節碼的一個過程),它是由一些第三方動態字節碼框架(如ASM,Javassist,cglib)直接在jvm中生成字節碼(動態字節碼),當jvm結束,動態字節碼也跟着消失了。
結論:動態代理不須要定義類文件,都是在jvm中自動建立的,因此不會有靜態代理,類文件數量過多,影響項目管理的問題。