日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

动态代理-AOP

發(fā)布時(shí)間:2023/12/3 编程问答 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 动态代理-AOP 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1 什么是AOP?

  • Aspect Oriented Programming的縮寫(xiě),面向切面編程,切面指定就是動(dòng)態(tài)代理的方法,作用是在不改變業(yè)務(wù)層方法源代碼的基礎(chǔ)上對(duì)方法進(jìn)行增強(qiáng),底層使用的是動(dòng)態(tài)代理技術(shù),面向切面編程也可以理解成面向動(dòng)態(tài)代理編程。

2 AOP相關(guān)概念

  • Target(目標(biāo)對(duì)象):被代理的對(duì)象就是目標(biāo)對(duì)象
  • Proxy(代理對(duì)象):被增強(qiáng)后的對(duì)象就是代理對(duì)象
  • Joinpoint(連接點(diǎn)):就是目標(biāo)對(duì)象中所有被攔截到的方法
  • Pointcut(切入點(diǎn)):就是目標(biāo)對(duì)象中被增強(qiáng)的方法
  • Advice(通知):執(zhí)行目標(biāo)方法之前或者之后調(diào)用的方法就是通知
  • Aspect(切面):通知方法和切入點(diǎn)方法結(jié)合所在的位置叫做切面
  • Weaving(織入):通知方法和切入點(diǎn)方法結(jié)合的過(guò)程,織入之后的結(jié)果就是切面

總結(jié)一下:
連接點(diǎn)是所有被攔截到的方法,切入點(diǎn)是所有被增強(qiáng)的方法,連接點(diǎn)不一定是切入點(diǎn),但是切入點(diǎn)一定是連接點(diǎn)。在執(zhí)行目標(biāo)對(duì)象方法之前或者之后要做的事叫做通知,通知中有增強(qiáng)的業(yè)務(wù)。將切入點(diǎn)和通知組織到一起叫織入,織入形成的結(jié)果就是切面。

3 AOP配置實(shí)現(xiàn)步驟

<1>【第一步】導(dǎo)入相關(guān)依賴:spring-context、aspectjweaver

<!--spring核心依賴--> <dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.9.RELEASE</version> </dependency> <!--切入點(diǎn)表達(dá)式依賴,作用:通過(guò)表達(dá)式找到哪些方法需要增強(qiáng),也就是找到切入點(diǎn)--> <dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.6</version> </dependency>

<2>【第二步】定義通知類(lèi)和目標(biāo)對(duì)象

  • AOP目標(biāo)接口:
public interface StudentService {//查詢?nèi)?/span>public abstract List<Student> findAll() throws IOException;public void transfer(Integer outId, Integer inId, double money); }
  • AOP目標(biāo)實(shí)現(xiàn)類(lèi):
@Service("studentService") public class StudentServiceImpl implements StudentService {@Overridepublic List<Student> findAll() throws IOException {System.out.println("查詢所有學(xué)生信息findAll...");return null;}@Overridepublic void transfer(Integer outId,Integer inId,double money){//1 張三的賬戶-1000元System.out.println("調(diào)用dao:張三("+outId+")的賬戶"+(-money)+"元");//2 李四的賬戶+1000元System.out.println("調(diào)用dao:李四("+inId+")的賬戶"+money+"元");}//接口中沒(méi)有該方法,不會(huì)被攔截public void show(){System.out.println("----------------");} }

注:代理的為接口對(duì)象,接口中沒(méi)有的方法,實(shí)現(xiàn)類(lèi)自己的方法不會(huì)被增強(qiáng)

  • 通知類(lèi):
import org.aspectj.lang.ProceedingJoinPoint;//通知類(lèi),告訴spring在增強(qiáng)的前后需要做什么事 public class Advice {public void before(){//前置通知:開(kāi)啟事務(wù)System.out.println("前置通知:開(kāi)啟事務(wù)");}public void afterReturn(){//后置通知:提交事務(wù)System.out.println("后置通知:提交事務(wù)");}public void afterThrowable(){//異常通知:回滾事務(wù)System.out.println("異常通知:回滾事務(wù)");}public void after(){//最終通知:釋放資源System.out.println("最終通知:釋放資源");}// 環(huán)繞通知:是Spring給我們提供的一種手動(dòng)調(diào)用目標(biāo)對(duì)象方法或者其他通知方法的方式// spring在調(diào)用環(huán)繞通知方法時(shí)會(huì)傳遞一個(gè)封裝了目標(biāo)方法的對(duì)象,叫做ProceedingJoinPointpublic Object around(ProceedingJoinPoint pjp){Object result =null;try {//前置通知before();//執(zhí)行目標(biāo)方法,相當(dāng)于動(dòng)態(tài)代理中的 result=method.invoke(...)result = pjp.proceed();//后置通知afterReturn();} catch (Throwable throwable) {//異常通知afterThrowable();throwable.printStackTrace();} finally {//最終通知after();}return result;} }

<3>xml文件配置AOP

1 配置目標(biāo)對(duì)象,添加到spring容器中2 配置通知對(duì)象,添加到spring容器中3 配置切入點(diǎn)方法和通知方法織入過(guò)程,也就配置切面
  • 純XML配置:
<?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:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><!--1.配置service--><bean id="studentService" class="com.itheima.service.impl.StudentServiceImpl"/><!--2.配置通知對(duì)象--><bean id="myAdvice" class="com.itheima.aop.Advice"/><!--3.配置AOP--><aop:config><!--3.1配置AOP切入點(diǎn)表達(dá)式[可放在任意位置]--><!--*空格代表void 方法名.*:.StudentServiceImpl.*:.方法名(..)方法參數(shù), ..代表參數(shù)任意--><aop:pointcut id="pt" expression="execution(* com.itheima.service.impl.*.*(..))"/><!--<aop:pointcut id="pt" expression="execution(* com.itheima.service.impl.StudentServiceImpl.*(..))"/>--><!--3.2配置切面--><aop:aspect ref="myAdvice"><!--前置通知--><aop:before method="before" pointcut-ref="pt"/><!--后置通知--><aop:after-returning method="afterReturn" pointcut-ref="pt"/><!--異常通知--><aop:after-throwing method="afterThrowable" pointcut-ref="pt"/><!--最終通知--><aop:after method="after" pointcut-ref="pt"/><!--使用環(huán)繞通知--><!--<aop:around method="around" pointcut-ref="pt"/>--></aop:aspect></aop:config> </beans>
  • 注解配置:
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component;//通知類(lèi),告訴spring在增強(qiáng)的前后需要做什么事 @Component("advice") @Aspect//告知是一個(gè)切面類(lèi),掃描時(shí)會(huì)掃描它的注解//代替:<aop:aspect ref="advice"> public class Advice {@Pointcut("execution(* com.itheima.service.impl.*.*(..))")//id為方法名[首字母小寫(xiě)]public void pt() {}//==注意:使用注解配置AOP,后置通知和異常通知會(huì)在最終通知之后調(diào)用, // 在spring-context的5.1.9版本中是這樣的,在更高的版本中可能得到了解決, // (5.2.6及以上版本解決了)。 // 但是我們可以使用環(huán)繞通知解決這個(gè)問(wèn)題,推薦使用環(huán)繞通知。==**/* @Before("pt()")public void before(JoinPoint joinPoint) {Object[] args = joinPoint.getArgs();//前置通知:開(kāi)啟事務(wù)System.out.println("前置通知:開(kāi)啟事務(wù)"+args[0]);}*/ //兩種得到傳遞參數(shù)的方法 如果目標(biāo)方法沒(méi)有傳參,則不執(zhí)行@Before("execution(* com.itheima.service.impl.*.*(..))&&args(x)")public void before(int x) {//前置通知:開(kāi)啟事務(wù)System.out.println("前置通知:開(kāi)啟事務(wù)" + x);}@AfterReturning("pt()")public void afterReturn() {//后置通知:提交事務(wù)System.out.println("后置通知:提交事務(wù)");}@AfterThrowing("pt()")public void afterThrowable() {//異常通知:回滾事務(wù)System.out.println("異常通知:回滾事務(wù)");}@After("pt()")public void after() {//最終通知:釋放資源System.out.println("最終通知:釋放資源");}// 環(huán)繞通知:是Spring給我們提供的一種手動(dòng)調(diào)用目標(biāo)對(duì)象方法或者其他通知方法的方式// spring在調(diào)用環(huán)繞通知方法時(shí)會(huì)傳遞一個(gè)封裝了目標(biāo)方法的對(duì)象,叫做ProceedingJoinPoint//@Around("pt()")public Object around(ProceedingJoinPoint pjp) {Object result = null;try {//前置通知//Object[] args = pjp.getArgs();//before();//執(zhí)行目標(biāo)方法,相當(dāng)于動(dòng)態(tài)代理中的 result=method.invoke(...)result = pjp.proceed();//后置通知afterReturn();} catch (Throwable throwable) {//異常通知afterThrowable();throwable.printStackTrace();} finally {//最終通知after();}return result;} }

注:
使用注解配置AOP,后置通知和異常通知會(huì)在最終通知之后調(diào)用,
在spring-context的5.1.9版本中是這樣的,在更高的版本中可能得到了解決,
(5.2.6及以上版本解決了)。
但是我們可以使用環(huán)繞通知解決這個(gè)問(wèn)題,推薦使用環(huán)繞通知

  • 核心配置類(lèi)代替XML
import org.springframework.context.annotation.*;@Configuration//表示代表替換applicationContext.xml的標(biāo)識(shí)[可以不寫(xiě)] @ComponentScan("com.itheima")//開(kāi)啟Spring注解掃描 @EnableAspectJAutoProxy//開(kāi)啟Spring的AOP注解支持 public class SpringConfig { }

4.底層動(dòng)態(tài)代理類(lèi)似原理[studentService動(dòng)態(tài)代理工廠]

package com.itheima.proxy; import com.itheima.aop.Advice; import com.itheima.service.StudentService; import com.itheima.service.impl.StudentServiceImpl; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;public class StudentServiceProxyFactory {public static StudentService createStudentServiceProxy() {Advice advice = new Advice();//1.創(chuàng)建真實(shí)對(duì)象StudentService studentService = new StudentServiceImpl();//可采用set注入//2.創(chuàng)建代理對(duì)象/**ClassLoader loader,創(chuàng)建代理對(duì)象的class對(duì)象Class<?>[] interfaces,告訴代理對(duì)象要和目標(biāo)對(duì)象實(shí)現(xiàn)相同的接口,就具有相同的功能。InvocationHandler h,處理增強(qiáng)的邏輯*/ClassLoader classLoader = studentService.getClass().getClassLoader();Class<?>[] interfaces = studentService.getClass().getInterfaces();//獲取所有的直接實(shí)現(xiàn)的接口StudentService service = (StudentService) Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {/*** @param proxy 代理對(duì)象* @param method 調(diào)用代理對(duì)象的方法,findAll、findById、transfer、update。。。* @param args 調(diào)用代理對(duì)象方法傳遞進(jìn)來(lái)的參數(shù)們* @return 此處的返回值將返回給調(diào)用處* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result = null;if (method.getName().equals("transfer") || method.getName().equals("delete")) {try {//1.開(kāi)啟事務(wù)advice.before();//2.執(zhí)行操作,調(diào)用目標(biāo)方法result = method.invoke(studentService, args);//3.提交事務(wù)advice.afterReturn();} catch (Exception e) {e.printStackTrace();//4.如果有異常則回滾事務(wù)advice.afterThrowable();} finally {//5.釋放資源advice.after();}} else {//執(zhí)行操作,調(diào)用目標(biāo)方法result = method.invoke(studentService, args);}return result;}});return service;} }

注:只可對(duì)單一實(shí)現(xiàn)類(lèi)對(duì)象進(jìn)行增強(qiáng)

總結(jié)

以上是生活随笔為你收集整理的动态代理-AOP的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。