关于Spring的学习总结(三)

文章目录


一、理解AOP

AOP(Aspect Oriented Programming),面向切面思想,是Spring的三大核心思想之一(两外两个:IOC-控制反转、DI-依赖注入)。

(一)什么是AOP

用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

下面有一些图片辅助理解:
关于Spring的学习总结(三)
关于Spring的学习总结(三)
关于Spring的学习总结(三)
关于Spring的学习总结(三)

(二)AOP中的相关概念

  • Aspect(切面)Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
  • Joint point(连接点)表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
  • Pointcut(切点)表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
  • Advice(增强)Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
  • Target(目标对象)织入 Advice 的目标对象。
  • Weaving(织入)将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程。

二、使用Spring实现AOP

导入依赖

<dependency>
		<groupId>org.aspectj</groupId>
  	<artifactId>aspectjweaver</artifactId>
  	<version>1.9.4</version>
</dependency>

使用Aspectj实现AOP有俩种方式:基于XML的声明式基于注解的声明式

(一)基于XML的声明式

基于XML的声明式是指通过XML文件来定义切面、切入点以及通知,所有切面、切入点和通知都必须定义在<aop:config>元素中。

Spring配置文件中的<bean>元素下可以包含多个<aop:config>元素,一个<aop:config>元素中有可以包含属性和子元素,子元素包括<aop:pointcut><aop:advisor><aop:aspect>,配置的时候也必须按照此顺序来。

常用配置代码如下:

<?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">

       <!--定义切面Bean-->
       <bean id="myAspect" class="com.weirdo.aspectj"/>
       <aop:config>
              <!--1.配置切面-->
              <aop:aspect id="aspect" ref="myAspect">
                     <!--2.配置切入点-->
                     <aop:pointcut id="myPointCut" expression="execution(* com.weirdo.aspectj.* (..))"/>
              <!--3.配置通知-->
                     <!--前置通知-->
                     <aop:before method="myBefore" pointcut-ref="myPointCut"/>
                     <!--后置通知-->
                     <aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="returnVal"/>
                     <!--环绕通知-->
                     <aop:around method="myAround" pointcut-ref="myPointCut"/>
                     <!--异常通知-->
                     <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/>
                     <!--最终通知-->
                     <aop:after method="myAfter" pointcut-ref="myPointCut"/>
              </aop:aspect>
       </aop:config>
</beans>
  1. UserDao.java
public interface UserDao {
    public void addUser();
    public void deleteUser();
}
  1. UserDaoImpl.java
public class UserDaoImpl implements UserDao{

    @Override
    public void addUser() {
        System.out.println("添加新用户");
    }

    @Override
    public void deleteUser() {
        System.out.println("删除用户");
    }
}
  1. MyAspectj.java
/**
 * 切面类,此面写通知
 */
public class MyAspectj {
    //前置通知
    public void myBefore(JoinPoint joinPoint){
        System.out.println("前置通知:模拟执行权限检查");
        System.out.println("目标类"+joinPoint.getTarget());
        System.out.println("被植入的强处理的目标方法为"+joinPoint.getSignature().getName());
    }
    //后置通知
    public void myAfterReturning(JoinPoint joinPoint){
        System.out.println("后置通知:模拟记录日志");
        System.out.println("被植入强处理的目标方法为"+joinPoint.getSignature().getName());
    }
    /**
     * 环绕通知
     * proceedingJoinPoint 是 JoinPoint的子接口,表示可执行目标方法
     * 1.必须是Object类型的返回值
     * 2.必须接受一个参数,类型为ProceedingJoinPoint
     * 3.必须是throws Throwable
     */
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        //开始
        System.out.println("环绕开始:执行目标方法之前,模拟开启事务");
        //执行当前目标方法
        Object obj = proceedingJoinPoint.proceed();
        //结束
        System.out.println("环绕结束:执行目标方法之后,模拟关闭事务");
        return obj;
    }
    //异常通知
    public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
        System.out.println("异常通知:出现错误"+e.getMessage());
    }
    //最终通知
    public void myAfter(){
        System.out.println("最终通知:模拟方法结束后释放资源");
    }
}
  1. applicationContext.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: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">

    <!--目标类-->
    <bean id="userDao" class="com.weirdo.dao.UserDaoImpl"/>
    <!--切面-->
    <bean id="myAspectj" class="com.weirdo.dao.MyAspectj"/>
    <aop:config>
        <!--1.配置切面-->
        <aop:aspect id="aspect" ref="myAspectj">
            <!--2.配置切入点-->
            <aop:pointcut id="myPointCut" expression="execution(* com.weirdo.dao.MyAspectj.*.*(..))"/>
            <!--3.配置通知-->
            <!--前置通知-->
            <aop:before method="myBefore" pointcut-ref="myPointCut"/>
            <!--后置通知-->
            <aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="joinPoint"/>
            <!--环绕通知-->
            <aop:around method="myAround" pointcut-ref="myPointCut"/>
            <!--异常通知-->
            <aop:after-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"/>
            <!--最终通知-->
            <aop:after method="myAfter" pointcut-ref="myPointCut" />
        </aop:aspect>
    </aop:config>
</beans>
  1. TestXMLAspectj.java
public class TestXMLAspectj {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = (UserDao) context.getBean("userDao");
        userDao.deleteUser();
    }
}

(二)基于注解的声明式

  1. 在UserDaoImpl类上添加@Repository("userDao")
  2. MyAspect.java
/**
 * 切面类,此面写通知
 */
@Aspect
@Component
public class MyAspectj {
    //定义切入点表达式
    @Pointcut("execution(* com.weirdo.dao.MyAspectj.*.*(..))")
    //使用一个返回值为void 方法体为空的方法来命名切入点
    public void myPointCut(){}
    //前置通知
    @Before("myPointCut()")
    public void myBefore(JoinPoint joinPoint){
        System.out.println("前置通知:模拟执行权限检查");
        System.out.println("目标类"+joinPoint.getTarget());
        System.out.println("被植入的强处理的目标方法为"+joinPoint.getSignature().getName());
    }
    //后置通知
    @AfterReturning(value = "myPointCut()")
    public void myAfterReturning(JoinPoint joinPoint){
        System.out.println("后置通知:模拟记录日志");
        System.out.println("被植入强处理的目标方法为"+joinPoint.getSignature().getName());
    }
    /**
     * 环绕通知
     * proceedingJoinPoint 是 JoinPoint的子接口,表示可执行目标方法
     * 1.必须是Object类型的返回值
     * 2.必须接受一个参数,类型为ProceedingJoinPoint
     * 3.必须是throws Throwable
     */
    @Around("myPointCut()")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        //开始
        System.out.println("环绕开始:执行目标方法之前,模拟开启事务");
        //执行当前目标方法
        Object obj = proceedingJoinPoint.proceed();
        //结束
        System.out.println("环绕结束:执行目标方法之后,模拟关闭事务");
        return obj;
    }
    //异常通知
    @AfterThrowing(value = "myPointCut()",throwing = "e")
    public void myAfterThrowing(JoinPoint joinPoint,Throwable e){
        System.out.println("异常通知:出现错误"+e.getMessage());
    }
    //最终通知
    @After("myPointCut()")
    public void myAfter(){
        System.out.println("最终通知:模拟方法结束后释放资源");
    }
}

  1. applicationContext.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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       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 http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

       <!--指定需要扫描的包,使用注解生效-->
       <context:component-scan base-package="com.weirdo"/>
       <!--启动基于注解的声明式支持-->
       <aop:aspectj-autoproxy />
</beans>
上一篇:vue之路由跳转的3种方式


下一篇:fetch()函数使用的一些技巧