Spring--AOP

SpringAOP原理

一、AOP概念解释

1.什么是AOP?

“横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块, 并将其命名为"Aspect”,即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共 同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未 来的可操作性和可维护性。
使用"横切"技术,AOP 把软件系统分为两个部分:核心关注点横切关注点。业务处理的主要流 程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生 在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP 的作用在于分离系统 中的各种关注点,将核心关注点和横切关注点分离开来
2.AOP主要应用场景:

  1. Authentication 权限
  2. Caching 缓存
  3. Context passing 内容传递
  4. Error handling 错误处理
  5. Lazy loading 懒加载
  6. Debugging 调试
  7. logging, tracing, profiling and monitoring 记录跟踪 优化 校准
  8. Performance optimization 性能优化 9
  9. Persistence 持久化
  10. Resource pooling 资源池
  11. Synchronization 同步
  12. Transactions 事务

3.AOP核心概念
1、切面(aspect):类是对物体特征的抽象,切面就是对横切关注点的抽象
2、横切关注点:对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点。
3、连接点(joinpoint):被拦截到的点,因为 Spring 只支持方法类型的连接点,所以在 Spring 中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器
4、切入点(pointcut):对连接点进行拦截的定义
5、通知(advice):所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、 异常、最终、环绕通知五类
6、目标对象:代理的目标对象
7、织入(weave):将切面应用到目标对象并导致代理对象创建的过程
8、引入(introduction):在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法 或字段。

AOP通知类型
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice
在这里插入图片描述
4.AOP两种代理方式
Spring 提供了两种方式来生成代理对象: JDKProxy 和 Cglib,具体使用哪种方式生成由 AopProxyFactory 根据 AdvisedSupport 对象的配置来决定。默认的策略是如果目标类是接口, 则使用JDK动态代理技术,否则使用Cglib来生成代理。
JDK动态接口代理
1.JDK 动态代理主要涉及到 java.lang.reflect 包中的两个类:Proxy 和 InvocationHandler。 InvocationHandler是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类 的代码,动态将横切逻辑和业务逻辑编制在一起。Proxy 利用 InvocationHandler 动态创建 一个符合某一接口的实例,生成目标类的代理对象。
CGLib 动态代理
2.CGLib全称为Code Generation Library,是一个强大的高性能,高质量的代码生成类库, 可以在运行期扩展 Java 类与实现 Java 接口,CGLib 封装了 asm,可以再运行期动态生成新 的 class。和 JDK 动态代理相比较:JDK 创建代理有一个限制,就是只能为接口创建代理实例, 而对于没有通过接口定义业务方法的类,则可以通过CGLib创建动态代理。

二.搭建环境

  • 导入spring相关jar包
    在这里插入图片描述

  • 使用maven环境

  • 导入需使用依赖

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>

xml配置文件引入aop约束:

<?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/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>

三.实现AOP的三种方法

方式一:使用Spring的API接口

<!--方式一:使用原生Spring API接口-->
    <!--配置aop:需要导入aop的约束-->
    <aop:config>
        <!--切入点expression:表达式,execution(要执行的位置!)-->
        <aop:pointcut id="pointcut" expression="execution(* com.nb.service.UserServiceImpl.*(..))"/>

        <!--执行环绕增加  把log类切入到UserServiceImpl的方法里-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>

代码实现:
UserService接口:

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void query();
}

UserServiceImpl实现类:

public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("增加一个用户");
    }
    @Override
    public void delete() {
        System.out.println("删除一个用户");
    }
    @Override
    public void update() {
        System.out.println("修改 一个用户");
    }
    @Override
    public void query() {
        System.out.println("查询一个用户");
    }
}

Log前置日志切入:

//前置日志
public class Log implements MethodBeforeAdvice {
    //method:要执行的目标对象的方法
    //args:参数
    //target:目标对象
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");
    }
}

AfterLog后置日志切入:

//后置日志
public class AfterLog implements AfterReturningAdvice {
    //returnValue:返回值
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了"+method.getName()+"方法,返回结果为"+returnValue);
    }
}

测试:

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //动态代理代理的是接口
        UserService userService = (UserService) context.getBean("userService");
        userService.add();
    }
}

在这里插入图片描述

第二种:自定义实现AOP【切面定义】:

<!--方式二:自定义类-->
    <bean id="diy" class="com.bjwl.diy.DiyPointCut"/>
    
    <aop:config>
        <!--自定义切面:aspect   ref: 要引用的类-->
       <aop:aspect ref="diy">
            <!--切入点-->
            <aop:pointcut id="point" expression="execution(* com.bjwl.service.StudentServiceImpl.*(..))"/>
           <!--通知-->
           <aop:before pointcut-ref="point" method="before"/>
            <aop:before pointcut-ref="point" method="after"/>
       </aop:aspect>
    </aop:config>
//自定义切面
public class DiyPointCut {
    public void before(){
        System.out.println("----方法执行前----");
    }

    public void after(){
        System.out.println("----方法执行后----");
    }
}

测试:

public class MyTest {
    public static void main(String[] args) {
//        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //动态代理代理的是接口
//        UserService userService = (UserService) context.getBean("userService");
//        userService.add();
        ApplicationContext context = new ClassPathXmlApplicationContext("test.xml");
        StudentService student = (StudentService) context.getBean("student");
        student.add();
    }
}

第三种:注解实现AOP:

        <!--方式三-->
        <bean id="anno" class="com.bjwl.diy.AnnotationPointCut"/>
        <!--开启注解支持  JDK(默认):proxy-target-class="false"   
            cglib:proxy-target-class="true"
        -->
         <aop:aspectj-autoproxy/>
//注解实现AOP
@Aspect//标注这个类是一个切面
public class AnnotationPointCut {
    @Before("execution(* com.bjwl.service.StudentServiceImpl.*(..))")
    public void before(){
        System.out.println("方法执行前");
    }
    @After("execution(* com.bjwl.service.StudentServiceImpl.*(..))")
    public void after(){
        System.out.println("方法执行后");
    }
    //再环绕增强中,可以给定参数,代表我们要获取处理切入的点
    @Around("execution(* com.bjwl.service.StudentServiceImpl.*(..))")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕前");
        //执行方法
        Object proceed = joinPoint.proceed();
        Signature signature = joinPoint.getSignature();//获得签名,类的信息
        System.out.println("signature = " + signature);
        System.out.println("环绕后");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值