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 术语
在XML文件中手动配置
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