JDK動态代理
代理設計模式的原理: 使用一個代理将對象包裝起來, 然後用該代理對象取代原始對象. 任何對原始對象的調用都要通過代理. 代理對象決定是否以及何時将方法調用轉到原始對象上。
ServiceProxy.java 代理類,由此類建立代理對象來進行代理。
package com.zzxtit.spring.aop.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ServiceProxy implements InvocationHandler{
private Object target;
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法:" + method.getName() + "被執行");
for(Object arg : args) {
System.out.println("-參數:" + arg);
}
Object returnValue = method.invoke(target, args);
System.out.println("方法:" + method.getName() + "執行完畢,傳回值:" + returnValue);
return returnValue;
}
public Object createProxy(Object target) {
this.target = target;
return Proxy.newProxyInstance(this.target.getClass().getClassLoader(), this.target.getClass().getInterfaces(), this);
}
}
BankService.java 對象接口類
package com.zzxtit.spring.aop.proxy;
import java.math.BigDecimal;
public interface BankService {
public void transfer(String target, String source, BigDecimal money);
public String withdraw(String account, BigDecimal money);
}
BankServiceImpl.java 對象接口實作類
package com.zzxtit.spring.aop.proxy;
import java.math.BigDecimal;
public class BankServiceImpl implements BankService {
public void transfer(String target, String source, BigDecimal money) {
System.out.println("方法:transfer 被執行,參數:" + target + ", 參數:" + source + ", 參數:" + money);
}
public String withdraw(String account, BigDecimal money) {
return "hello AOP";
}
}
Main.java 測試類
package com.zzxtit.spring.aop.proxy;
import java.math.BigDecimal;
public class Main {
public static void main(String[] args) {
BankService bs = new BankServiceImpl();
// bs.transfer("張三", "李四", new BigDecimal("123456"));
// JDK 動态代理
BankService bsProxy = (BankService) new ServiceProxy().createProxy(bs);
// bsProxy.transfer("張三", "李四", new BigDecimal("123456"));
bsProxy.withdraw( "李四", new BigDecimal("500"));
}
}
AOP開發模式介紹
AOP(Aspect-Oriented Programming: 面向切面程式設計): 是一種一種程式設計範式,是對傳統 OOP(Object-Oriented Programming, 面向對象程式設計) 的補充.
AOP 的主要程式設計對象是切面(aspect), 而切面子產品化橫切關注點。
比如在處理業務操作時,我們想在其中某個步驟添加某些操作,或者列印日志,不對原來的代碼進行修改就可以增加功能
AOP 的好處:
1.降低子產品耦合度
2.使系統容易擴充
3.更好的代碼複用性
AOP 術語
Spring AOP相關Jar包 spring-aop-4.3.3.RELEASE.jar, aspectjweaver-1.8.5.jar aspectjrt-1.8.5.jar
在XML檔案中手動配置
在xml檔案中需添加命名空間 xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation= http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
<aop:config>
<aop:pointcut expression="execution(* springboot.aop.xml.BankServiceImpl.*(..))" id="loggerPointCut"/>
<aop:aspect ref="loggerAspect">
<aop:before method="logerBefore" pointcut-ref="loggerPointCut"/>
<aop:after method="logerAfter" pointcut-ref="loggerPointCut"/>
<aop:after-returning method="logerAfterReturning" pointcut-ref="loggerPointCut" returning="returnValue"/>
<aop:around method="logerAround" pointcut-ref="loggerPointCut"/>
<aop:after-throwing method="loggerAfterThrowing" pointcut-ref="loggerPointCut" throwing="exception"/>
</aop:aspect>
spring AOP 面向切面編程:攔截方法進行操作
表達式:expression="execution(* springboot.aop.xml.*.*(..))"
第一個* 表示不限制傳回值類型
第二個* 表示springboot.aop.xml包下所有的JAVA BEAN
第三個* javabean 所有的方法
(..) 表示對方法的參數沒有限制
LoggerAspect.java 日志切面類
package springboot.aop.xml;
import java.math.BigDecimal;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class LoggerAspect {
public void logerBefore(JoinPoint jp) {
String methodName = jp.getSignature().getName();
System.out.println("method: " + methodName + "将要被執行!");
Object[] args = jp.getArgs();
for(Object arg : args) {
System.out.println("=============參數:>" + arg);
}
}
public void logerAfter(JoinPoint jp) {
String methodName = jp.getSignature().getName();
System.out.println("method: " + methodName + "已經被執行!");
Object[] args = jp.getArgs();
for(Object arg : args) {
System.out.println("=============參數:>" + arg);
}
}
public void logerAfterReturning(JoinPoint jp, Object returnValue) {
String methodName = jp.getSignature().getName();
System.out.println("後置傳回通知 =========>method: " + methodName + "已經被執行!");
System.out.println("後置傳回通知的傳回值:" + returnValue);
}
//環繞通知 需要配置傳回值,否則目标方法所有的傳回值為null
public Object logerAround(ProceedingJoinPoint pjp) {
String methodName = pjp.getSignature().getName();
System.out.println("環繞通知 =========>method: " + methodName + "方法正在執行!");
Object[] args = pjp.getArgs();//獲得參數
args[0] = "測試資料";
try {
Object returnValue = pjp.proceed(args);
System.out.println("環繞通知 =========>method: " + methodName + "已經被執行,傳回值:! " + returnValue);
// returnValue = "hello World!!";
return new BigDecimal("999999999999999");
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
public void loggerAfterThrowing(JoinPoint jp, Exception exception) {
System.out.println("後置異常通知:" + exception);
}
}
BankService.java 銀行業務接口類
package springboot.aop.xml;
import java.math.BigDecimal;
public interface BankService {
public BigDecimal transfer(String target, String source, BigDecimal money);
public void test();
}
BankServiceImpl.java 銀行業務實作類
package springboot.aop.xml;
import java.math.BigDecimal;
public class BankServiceImpl implements BankService{
public BigDecimal transfer(String target, String source, BigDecimal money) {
System.out.println(source + "向" + target + "轉賬:" + money);
throw new RuntimeException("故意出的異常!!");
// return new BigDecimal("12345612");
}
public void test() {
System.out.println("=============BankServiceImpl=====test===========>");
}
}
Main.java 測試類
package springboot.aop.xml;
import java.math.BigDecimal;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext-xml-aop.xml");
BankService bs = ioc.getBean("bankService", BankService.class);
bs.transfer("目标1", "目标2", new BigDecimal("100000000"));
}
}
基于注解配置
首先要在XML檔案中添加類掃描器和配置
<context:component-scan base-package="com.zzxtit.spring.aop.anno"></context:component-scan>
<!-- 開啟spring AOP 注解方式 自動代理 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
切面類需要用@Aspect注解标注,@Component将這個類标記為切面的Spring Bean元件
再在需要的方法前用注解标注為通知方法
@Aspect
@Component
public class AuthAspect {
@Before("execution(* com.zzxtit.spring.aop.anno.*.*(..))")
public void beforeAdvice(JoinPoint jp) {
System.out.println("===================權限控制=====================");
}
@After("execution(* com.zzxtit.spring.aop.anno.*.*(..))")
public void afterAdvice(JoinPoint jp) {
System.out.println("-----------權限控制後置通知-----------------");
}
}
重用切入點定義:通過 @Pointcut 注解将一個切入點聲明成簡單的方法. 切入點的方法體通常是空的。其他通知可以通過方法名稱引入該切入點。
/**
* 通過pointCut進行切入點的代碼抽離
*/
@Pointcut("execution(* com.zzxtit.spring.aop.anno.*.*(..))")
public void loggerPointCut() {
}
// @Before("execution(* com.zzxtit.spring.aop.anno.*.*(..))")
@Before("loggerPointCut()")
public void beforeAdvice(JoinPoint jp) {
System.out.println("===================權限控制=====================");
}
若使用 @Order 注解,指定切面的優先級,否則它們的優先級是不确定的,值從0開始,越小優先級越高。
@Order(1)
@Aspect
@Component
public class AuthAspect {
在工程中查找jar包中類使用快捷鍵ctrl+shift+T
查找工程中的類使用快捷鍵ctrl+shift+R