Spring框架学习05-基于传统代理类的Aop实现

本文深入讲解Spring框架中的AOP(面向切面编程)概念,包括通知类型如环绕、前置、后置等,以及切面类型如Advisor、PointcutAdvisor等。通过示例代码详细解析如何使用ProxyFactoryBean创建代理,并实现前置通知和环绕通知。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring框架学习07——基于传统代理类的AOP实现

在Spring中默认使用JDK动态代理实现AOP编程,使用org.springframework.aop.framework.ProxyFactoryBean创建代理是Spring AOP 实现的最基本方式。
1、通知类型
根据Spring中通知在目标类方法中的连接点位置,通知可以分为6种类型:

(1)环绕通知

环绕通知(org.aopalliance.intercept.MethodInterceptor)是在目标方法执行前和执行后实施增强,可应用于日志记录、事务处理等功能。(通俗的讲就是在目标方法前后加额外功能)

(2)前置通知

前置通知(org.springframework.aop.MethodBeforeAdvice)是在目标方法执行前实施增强,可应用于权限管理等功能。(通俗的将就是将额外功能加在目标方法的前面)

(3)后置返回通知

后置返回通知(org.springframework.aop.AfterReturningAdvice)是在目标方法成功执行后实施增强,可应用于关闭流、删除临时文件等功能。

(4)后置(最终)通知

后置通知(org.springframework.aop.AfterAdvice)是在目标方法执行后实施增强,与后置返回通知不同的是,不管是否发生异常都要执行该类通知,该类通知可应用于释放资源。

(5)异常通知

异常通知(org.springframework.aop.ThrowsAdvice)是在方法抛出异常后实施增强,可应用于处理异常、记录日志等功能。

(6)引入通知

引入通知(org.springframework.aop.IntroductionInterceptor)是在目标类中添加一些新的方法和属性,可应用于修改目标类(增强类)。Spring AOP 只支持方法层面的增强,所以该类型的通知只作为了解即可。

2、切面类型

Advisor:代表一般切面,Advice本身就是一个切面,对目标类所有方法进行拦截
PointcutAdvisor:代表具有切点的切面,可以指定拦截目标类哪些方法
IntroductionAdvisor:代表引介切面,针对引介通知而使用切面

3、ProxyFactoryBean

ProxyFactoryBean是org.springframework.beans.factory.FactoryBean 接口的实现类,FactoryBean负责实例化一个Bean实例,ProxyFactoryBean负责为其他Bean实例创建代理实例。ProxyFactoryBean类的常用属性如下:

**target:**代理的目标对象
proxyInterfaces:代理需要实现的接口列表,如果是多个接口,可以使用以下格式赋值:
**InterceptorNames:**需要织入目标的Advice
proxyTargetClass:是否对类代理而不是接口,默认为false,使用JDK动态代理;当为true时,使用CGLIB动态代理
singleton:返回的代理实例是否为单例,默认为true
optimize:当设置为true时强制使用CGLIB动态代理

4、Advisor一般切面的实现

在pom.xml中导入AOP联盟和Spring-aop的相关依赖

 <!-- AOP联盟依赖 -->
    <dependency>
      <groupId>aopalliance</groupId>
      <artifactId>aopalliance</artifactId>
      <version>1.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.1.5.RELEASE</version>
    </dependency>

创建StuDao类和StuDaoimpl类,示例代码如下:

package com.kude.dao;

import com.kude.entity.Student;

public interface StuDao {
    public void add(Student student);
    public void find();
    public void update(Student student);
    public void delete(Student student);
}

package com.kude.dao;

import com.kude.entity.Student;

public class StudentDaoimpl implements StuDao {
    @Override
    public void add(Student student) {
        System.out.println("添加学生");
    }

    @Override
    public void find() {
        System.out.println("查询学生");

    }

    @Override
    public void update(Student student) {
        System.out.println("修改学生");

    }

    @Override
    public void delete(Student student) {
        System.out.println("删除学生");

    }
}

创建切面类MyBeforeAdvice,仅实现前置通知:

public class MyBeforeAdvice implements MethodBeforeAdvice {
    /*
     *参数Object[]:代表你指定的额外功能添加给哪个原始类的原始方法中的()参数
     *参数target:代表原始对象
     *
     * */
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("前置增强");

    }
}

在applicationContext.xml中中配置:

   <!--配置目标类-->
    <bean id="stuDao" class="com.kude.dao.StudentDaoimpl"></bean>
    <!--配置前置通知类型-->
    <bean id="myBeforeAdvice" class="com.kude.proxy.MyBeforeAdvice"></bean>
    <!--Spring AOP 生成代理对象-->
    <bean id="stuDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--配置目标类-->
        <property name="target" ref="stuDao"></property>
        <!--代理实现的接口-->
        <property name="proxyInterfaces" value="com.kude.dao.StuDao"></property>
        <!--采取拦截的名称-->
        <property name="interceptorNames" value="myBeforeAdvice"></property>
    </bean>

测试类:

public void Test(){
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        StuDao stuDao = (StuDao) app.getBean("stuDaoProxy");
        Student stu=new Student();
        stuDao.add(stu);
        stuDao.update(stu);
        stuDao.delete(stu);
    }

运行结果:
前置增强
添加学生
前置增强
修改学生
前置增强
删除学生
环绕通知的实例:
创建一个环绕通知的切面类:

package com.kude.proxy;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class MyAspect implements MethodInterceptor {
    /**
     *
     * @param invocation 代表你的原始类的原始方法
     * @return  代表返回你的原始方法的值
     * @throws Throwable
     */
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        //前置增强
        System.out.println("我是一段额外功能代码  我将在核心代码之前运行");
        //执行目标方法
        Object obj=invocation.proceed();
        //后置增强
        System.out.println("我是一段额外功能代码  我将在核心代码之后运行");
        return obj;
    }
}

application.xml.文件中配置:

 <!--配置目标类-->
    <bean id="stuDao" class="com.kude.dao.StudentDaoimpl"></bean>
    <!--配置通知-->
    <bean id="myAspect" class="com.kude.proxy.MyAspect"></bean>
    <!--配置带切入点的切面-->
    <bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <!--pattern中配置正则表达式:.表示任意字符 *表示任意次数-->
        <property name="pattern" value=".*"></property>
        <property name="advice" ref="myAspect"></property>
    </bean>
    <!--Spring AOP 生成代理对象-->
    <bean id="stuDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--配置目标类-->
        <property name="target" ref="stuDao"></property>
        <!--代理实现的接口-->
        <property name="proxyInterfaces" value="com.kude.dao.StuDao"></property>
        <!--采取拦截的名称-->
        <property name="interceptorNames" value="myAdvisor"></property>
    </bean>

测试:

 public void Test(){
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        StuDao stuDao = (StuDao) app.getBean("stuDaoProxy");
        Student stu=new Student();
        stuDao.add(stu);
        stuDao.update(stu);
        stuDao.delete(stu);
    }

运行结果:
我是一段额外功能代码 我将在核心代码之前运行
添加学生
我是一段额外功能代码 我将在核心代码之后运行
我是一段额外功能代码 我将在核心代码之前运行
修改学生
我是一段额外功能代码 我将在核心代码之后运行
我是一段额外功能代码 我将在核心代码之前运行
删除学生
我是一段额外功能代码 我将在核心代码之后运行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值