Spring-AOP(面向切面)
文章目录
- AOP(面向切面)
- 一、代理
- 静态代理
- 代码实现
- 1、创建一个接口和一个实现类(目标类)
- 2、创建代理类实现接口,在创建一个此接口的属性,且创建有参构造
- 3、创建测试类进行测试
- 动态代理
- 代码实现
- 使用Pxoy类去实现动态代理,输出日志
- 测试动态代理的效果
- 二、基于的注解AOP实现日志
- 代码实现加解析
- 前置准备、导入依赖
- 一、创建一个接口和实现类代表目标类,可以直接使用上面CalculatorImpl类,在实现类类中标记@Component注解
- 二、创建bean.xml
- 三、创建切面类完成日志
- 测试
AOP(面向切面)
概念:切面就是将代码逻辑做横向切面,相同逻辑的部分分装到一个进行统一管理
作用:1、提高代码的复用性 2、增强代码的维护性
一、代理
概念: 在讲到面向切面之前必不可少的就是需要知道什么是代理,代理就是继承一个目标类的代理类,在代理类来完成一些比较繁琐的代码如:输出日志等,当需要使用到核心代码时再去调用目标类的方法,这样子目标类就会变得更加的简洁明了。
简单的来说就是:一个明星(目标类)他只需要负责唱歌、跳舞等核心的事情,而明星的经济人(代理类)就需要告诉明星他需要到哪里进行演出、到哪里进行演讲等这些琐事
静态代理
逻辑:创建一个接口和实现类(目标类),在创建一个代理类也实现此接口,这时代理类就有了目标类的所有方法,此时就可以实现某一个方法在代理类中的方法进行输出日志、调用目标类的方法等操作,测试时就可以直接进行调用代理类即可。
代码实现
这里就拿一个简单的计算机的逻辑来讲解
1、创建一个接口和一个实现类(目标类)
2、创建代理类实现接口,在创建一个此接口的属性,且创建有参构造
3、创建测试类进行测试
此时就相当于完成了近似于代理的操作
动态代理
概念:动态代理就是将目标类中的所有方法都进行统一处理,实现方法就是去使用Poxy类中的方法
代码实现
这里创建目标类这一步就直接调用上面静态代理中的即可
使用Pxoy类去实现动态代理,输出日志
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
public class dynamicsProxy{
// 目标对象(需要被代理的对象)
private Object object;
public dynamicsProxy(Object object) {
this.object = object;
}
// 返回代理对象
public Object getProxy(){
// 加载动态生成代理类的加载器
ClassLoader classLoader = object.getClass().getClassLoader();
// 获取对象的所有接口
Class<?>[] interfaces = object.getClass().getInterfaces();
// 设置代理目标对象的过程
InvocationHandler invocationHandler = new InvocationHandler() {
//proxy:需要被代理的对象
//method:需要被重写的类
//args[]:重写方法中的形参
@Override
public Object invoke(Object proxy,
Method method,
Object[] args) throws Throwable {
// 执行核心代码前的日志
System.out.println("[动态代理][日志],方法名为:"+method.getName()+"方法的参数"+ Arrays.toString(args));
Object result = method.invoke(object, args);
// 执行核心代码后的日志
System.out.println("[动态代理][日志]结束了,运行结果为:"+result);
return result;
}
};
return Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);
}
}
测试动态代理的效果
二、基于的注解AOP实现日志
概念:注解AOP相对于上面动态和静态代理实现日志便捷了许多,且还添加了许多的功能
代码实现加解析
前置准备、导入依赖
<!-- spring Aop依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>6.0.2</version>
</dependency>
<!-- spring Aspects依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.0.2</version>
</dependency>
<!-- sprint context依赖-->
<!-- 当你引入Spring Context依赖之后,表示将Spring的基础依赖引入了-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.2</version>
</dependency>
一、创建一个接口和实现类代表目标类,可以直接使用上面CalculatorImpl类,在实现类类中标记@Component注解
二、创建bean.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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.spring`在这里插入代码片`framework.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
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--
基于AOP的实现:
1、将目标对象和切面交给IOC容器(注解+扫描)
2、开启AspectJ的自动自动代理,为目标对象自动生成代理
3、将切面类通过注解@Aspect表示
-->
<!-- 将目标对象和切面交给IOC容器(注解+扫描)-->
<context:component-scan base-package="org.example.annoAop"></context:component-scan>
<!-- 开启AspectJ的自动自动代理,为目标对象自动生成代理-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
三、创建切面类完成日志
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Aspect//切面类
@Component//ioc容器
public class LogAspect {
// 设置切面点和通知的类型
// 通知类型:
// 切入点表达式:execution【固定格式】(public int【权限修饰符,方法返回值(可以写*表示两值任意都可以)】
// org.example.annoAop【方法所在类的全类名(这里写*表是包名任意,写*..表示报名任意同时报的层次深度任意)】.CalculatorImpl
// add【方法名】(int,int)【形参列表,..表示任意】
// )
// 前置通知 @Before(切入点表达式)
@Before("execution(public int org.example.annoAop.CalculatorImpl.*(..))")
public void beforeMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("前置通知,方法名为:"+methodName);
}
// 后置通知 @After(切入点表达式)
@After("execution(* org.example.annoAop.CalculatorImpl.*(..))")
public void afterMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
System.out.println("后置通知,方法名为:"+methodName+"形参列表为:"+ Arrays.toString(args));
}
// 返回通知 @AfterReturning
@AfterReturning(value = "execution(public int org.example.annoAop.CalculatorImpl.*(..))",returning = "obj")
public void afterReturning(JoinPoint joinPoint,Object obj){
String methodName = joinPoint.getSignature().getName();
System.out.println("返回通知,方法名为:"+methodName+"返回结果:"+obj);
}
// 异常通知 @AfterThrowing
@AfterThrowing(value = "execution(public int org.example.annoAop.CalculatorImpl.*(..))",throwing = "e")
public void afterThrowing(JoinPoint joinPoint,Throwable e){
String methodName = joinPoint.getSignature().getName();
System.out.println("异常通知,方法名为:"+methodName+"报错信息:" + e);
}
// 环绕通知 @Around()
// 概念:环绕通知可以使用try..catch..finally完成以上四种通知
@Around(value = "pointcut()")
public Object around(ProceedingJoinPoint pJoinPoint){
String name = pJoinPoint.getSignature().getName();
Object[] args = pJoinPoint.getArgs();
Object result = null;
try {
System.out.println("环绕通知:前置效果,方法名为:" + name);
//执行内部方法
result = pJoinPoint.proceed();
System.out.println("环绕通知:返回效果,方法名为:" + name + "返回结果:" + result);
}catch (Throwable e) {
e.printStackTrace();
System.out.println("环绕通知:异常效果,方法名为:"+ name +"报错信息:" + e);
} finally{
System.out.println("环绕通知:后置效果,方法名为:" + name + "形参列表:" + Arrays.toString(args));
}
return result;
}
// 重用切入点表达式
@Pointcut("execution(public int org.example.annoAop.CalculatorImpl.*(..))")
public void pointcut(){}
}
测试
详细内容