[Spring+SpringMVC+Mybatis]框架学习笔记(四):Spring实现AOP

本文深入讲解Spring框架中AOP(面向切面编程)的实现原理与应用,包括AOP概念、动态代理机制、专业术语解释及如何通过Spring配置实现前置、后置、环绕和异常通知。

上一章:[Spring+SpringMVC+Mybatis]框架学习笔记(三):Spring实现JDBC
下一章:[Spring+SpringMVC+Mybatis]框架学习笔记(五):SpringAOP_顾问

第4章 Spring实现AOP

4.1 AOP的概念

AOP: Aspect Oriented Programming 面向切面编程

AOP的实现可以把业务逻辑和系统级的服务进行隔离,使业务逻辑跟各个系统级服务的耦合度降低,提高系统的重用性和开发效率。

业务逻辑:某个类的某个方法本身要实现的功能。

系统级的服务:系统的日志、事务、权限验证

举例:六七十年代,村支书要通知一个事情,原来要一家一家逐个告诉他们;有了广播(AOP)以后,只需要用广播,大家就知道了。

4.2 AOP的实现

AOP底层实现的原理:动态代理

动态代理有两种方式:

  • jdk动态代理(只能代理实现了接口的类)
  • cglib动态代理(都可以) ----> 字节码生成器

4.3 AOP的一些专业术语

  • 切面(Aspect):交叉在各个业务逻辑中的系统服务,例如:安全验证、事务处理、日志记录等
  • 织入(weaving):将切面代码插入到目标对象的某个方法的过程,相当于jdk动态代理中的InvacationHandler接口方法的内容。
  • 连接点(JointPoint):理论上可以被切面织入的所有方法,例如addStudent(),delStudent()...通常所有方法都可以被称为连接点。
  • 切入点(PointCut): 实际上被切面织入的方法。
  • 目标对象(target):就是切入点所属的类,如:StudentService类
  • 通知(Advice):就是切面的实现,切面织入的目标对象,方法,时间点(方式执行前、后、前后、异常)以及内容。
  • 顾问(Advisor):就是通知的封装和延伸。可以将通知以更复杂的方式织入到某些方法中。

把前5个术语串联起来:

将切面织入到目标对象的连接点方法中,使连接点成为切入点。

4.4 Spring实现AOP的步骤

1)搭建SpringAOP开发环境需要引入的jar包;
aopalliance-1.0.jar
spring-aop-4.2.1.RELEASE.jar

2)实现通知

  • 前置通知 implements org.springframework.aop.MethodBeforeAdvice
  • 后置通知 implements org.springframework.aop.AfterReturningAdvice
  • 环绕通知 implements org.aopalliance.intercept.MethodInterceptor
  • 异常通知 implements org.springframework.aop.ThrowsAdvice

3)注册目标类

4)注册通知

5)注册代理生产器,并注入目标类和通知

ps:通知的注意点:

  • 代理生产器配置时,目标类接口可以省略;
  • 后置通知是在目标方法执行之后才执行,它可以得到目标方法的返回值,但是不能改变它;
  • 环绕通知有在目标方法执行之前的代码,也有在目标方法执行之后的代码;可以得到目标方法的返回值,且可以改变它。

4.5 Spring实现AOP实例

4.5.1 目标类接口

package com.steven.spring.sysmgr.service;

import java.util.List;

import com.steven.spring.sysmgr.entity.Student;

public interface IStudentService {
    
    public boolean addStudent(Student student);
    
    public boolean delStudent(Integer studentId);
    
    public boolean updateStudent(Student student);
    
    public List<Student> getStudentList();
}

4.5.2 目标类

package com.steven.spring.sysmgr.service.impl;

import java.util.ArrayList;
import java.util.List;

import com.steven.spring.sysmgr.entity.Student;
import com.steven.spring.sysmgr.service.IStudentService;

public class StudentService implements IStudentService{

    @Override
    public boolean addStudent(Student student) {
        System.out.println("执行增加功能");
        return true;
    }

    @Override
    public boolean delStudent(Integer studentId) {
        //模拟异常,在异常通知中使用
        int i = 1 / 0;

        System.out.println("执行删除功能");
        return true;
    }

    @Override
    public boolean updateStudent(Student student) {
        System.out.println("执行修改功能");
        return true;
    }

    @Override
    public List<Student> getStudentList() {
        System.out.println("执行增加功能");
        return new ArrayList<Student>();
    }

}

4.5.3 实现通知

a) 前置通知

package com.steven.spring.sysmgr.advice;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

/**
 * 前置通知
 * @author chenyang
 *
 */
public class MyBeforeAdvice implements MethodBeforeAdvice {

    @Override
    public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("这是前置通知,应该在方法执行之前打印出来");
    }
    
}

b) 后置通知

package com.steven.spring.sysmgr.advice;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

/**
 * 后置通知
 * @author chenyang
 *
 */
public class MyAfterAdvice implements AfterReturningAdvice {
    //该方法中的代码就是要织入到目标类方法的切面代码
    @Override
    public void afterReturning(Object returnValue, Method paraMethod, 
            Object[] paramArrayOfObject, Object target) throws Throwable {
        System.out.println("方法执行的结果:" + returnValue);
        System.out.println("这是后置通知,应该在方法执行之后打印出来");
        
    }

}

c) 环绕通知

package com.steven.spring.sysmgr.advice;

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

/**
 * 环绕通知
 * @author chenyang
 *
 */
public class MyAroundAdvice implements MethodInterceptor {
    // 环绕通知可以改变方法的返回值
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("这是环绕通知的前面执行部分");
        Object result = invocation.proceed();
        System.out.println("方法的执行结果是:" + result);
        System.out.println("这是环绕通知的后面执行部分");
        result = false;
        return result;
    }
    
}

d) 异常通知

package com.steven.spring.sysmgr.advice;

import org.springframework.aop.ThrowsAdvice;

/**
 * 异常通知
 * @author chenyang
 *
 */
public class MyThrowingAdvice implements ThrowsAdvice {
    public void afterThrowing(Exception e){
        System.out.println("发生的异常:" + e);
    }
    
}

4.5.4 配置文件:注册目标类、通知、代理生成器

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 注册目标类,并描述依赖关系 -->
    <bean id="studentService" class="com.steven.spring.sysmgr.service.impl.StudentService"></bean>
    
    <!-- 注册前置通知 -->
    <bean id="beforeAdvice" class="com.steven.spring.sysmgr.advice.MyBeforeAdvice"></bean>
    
    <!-- 注册后置通知 -->
    <bean id="afterAdvice" class="com.steven.spring.sysmgr.advice.MyAfterAdvice"></bean>
    
    <!-- 注册环绕通知 -->
    <bean id="aroundAdvice" class="com.steven.spring.sysmgr.advice.MyAroundAdvice"></bean>
    
    <!-- 注册异常通知 -->
    <bean id="throwingAdvice" class="com.steven.spring.sysmgr.advice.MyThrowingAdvice"></bean>
    
    <!-- 注册前置通知的代理生成器,注入目标类接口、目标类、通知 -->
    <bean id="myBeforeProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target" ref="studentService"/>
        <property name="interfaces" value="com.steven.spring.sysmgr.service.IStudentService"/>
        <property name="interceptorNames" value="beforeAdvice"/>
    </bean>
    
    <!-- 注册后置通知的代理生成器,注入目标类、通知 -->
    <bean id="myAfterProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target" ref="studentService"/>
        <!-- 这里省略了目标类接口的实现类,因为系统能自己找到 -->
        <property name="interceptorNames" value="afterAdvice"/>
    </bean>
    
    <!-- 注册环绕通知的代理生成器,注入目标类、通知 -->
    <bean id="myAroundProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target" ref="studentService"/>
        <property name="interceptorNames" value="aroundAdvice"/>
    </bean>
    
    <!-- 注册异常通知的代理生成器,注入目标类、通知 -->
    <bean id="myThrowingProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target" ref="studentService"/>
        <property name="interceptorNames" value="throwingAdvice"/>
    </bean>
    
</beans>

ps:

教你什么时候用value,什么时候用ref?

ref是引用(指针),value是布尔、string等得值。

用ref还是value,需要看源码中这个name是什么类型。 全选name的引号中的值,alt + / , 若弹出的类的参数是对象,就用ref;若不是,就用value。

ZOBB6S.png

ZOB6Ts.png

4.5.5 测试

package com.steven.spring.sysmgr.test;

import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.steven.spring.sysmgr.entity.Student;
import com.steven.spring.sysmgr.service.IStudentService;

/**
 * 测试各种通知
 * @author chenyang
 *
 */
public class AdviceTest {
    private ApplicationContext ac = null;
    
    @Before
    public void init(){
        ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    }
    
    //测试前置通知,预期目标:将前置通知打印的内容出现在目标方法内容的前面
    @Test
    public void testBeforeAdvice(){
        IStudentService studentService = (IStudentService) ac.getBean("myBeforeProxy");
        studentService.addStudent(new Student());
    }
    
    //测试后置通知
    @Test
    public void testAfterAdvice(){
        IStudentService studentService = (IStudentService) ac.getBean("myAfterProxy");
        studentService.addStudent(new Student());
    }
    
    //测试环绕通知
    @Test
    public void testAroundAdvice(){
        IStudentService studentService = (IStudentService) ac.getBean("myAroundProxy");
        studentService.addStudent(new Student());
    }
        
    //测试异常通知
        @Test
        public void testThrowingAdvice(){
            IStudentService studentService = (IStudentService) ac.getBean("myThrowingProxy");
            studentService.delStudent(1);
        }   
}

上一章:[Spring+SpringMVC+Mybatis]框架学习笔记(三):Spring实现JDBC
下一章:[Spring+SpringMVC+Mybatis]框架学习笔记(五):SpringAOP_顾问

转载于:https://www.cnblogs.com/steven0325/p/11151597.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值