SpringAOP详解三种实现方式及其使用案例

文章目录

  • 一、AOP
    • (一)什么是AOP
    • (二)AOP与代理模式
    • (三)AOP在Spring中的作用
    • (四)使用Spring实现AOP(重点)
    • (五)Spring中的事务
    • (六)Spring的事务传播级别
  • 二、整合Mybatis案例

一、AOP

(一)什么是AOP

AOP(Aspect Oriented Programming)意为:面向切面编程,体现了横切的思想,意思是在添加某项功能的时候,是以切面插入的方式实现的,对原有的代码不会产生改变。通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生泛型,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的频率。

(二)AOP与代理模式

AOP是基于代理模式实现的,代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问;用最简单的方式来解释代理模式,那就是房屋中介,客户需要直接去找房东,而是直接访问这个“中介”,有中介代理完成房东的事物。
代理模式的好处是,不仅可以执行被代理对象的所有功能,还可以额外的添加功能,比如房屋中介不仅可以给你租房子,还可以为你其他的房屋信息。
代理模式可以分为两种,静态代理与动态代理。而两个常见的动态代理为JDK实现(反射)与CGLIB实现。
代理模式中三个重要角色
1、抽象角色,声明真实的对象和代理对象的共同的接口
2、代理角色:代理对象内部含有真实对象的引用,也就是说,代理对象可以操作真实的对象,因此需要实现和真实对象相同的接口,同时代理对象可以在执行真实对象操作的时候,附加上其它的操作,也就是对真实对象的一个简单的封装。
3、真实角色(被代理角色):代理角色所要代表的角色,也就是我们最终想要引用的对象。
SpringAOP详解三种实现方式及其使用案例_第1张图片

(三)AOP在Spring中的作用

提供声明式事务;允许用户自定义切面

切入点(PointCut):说的通俗点,Spring中AOP的切入点就是指一些方法的集合,而这些方法是被代理、被切入的。
连接点(JoinPoint)程序执行时的某个特定的方法,可以理解为可以执行切入点的位置 ,比如方法前后。
通知(Advice)切面必须要完成的工作,通俗点说,就是在切入点的要实现功能,比如日志。
切面(ASPECT):横切关注点被模块化的特殊对象,它是切入点和通知的整合概念,可以理解为切入点的类。
目标(Target):被通知对象。
代理(Proxy):向目标对象应用通知之后创建的对象。
横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点,如日志、安全、缓存、事务等等……
SpringAOP详解三种实现方式及其使用案例_第2张图片
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice
SpringAOP详解三种实现方式及其使用案例_第3张图片

(四)使用Spring实现AOP(重点)

1、导入AOP依赖包

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

2、建立测试环境

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

    <bean id="userService" class="com.nan.service.UserServiceImpl"/>
    <bean id="log" class="com.nan.log.Log"/>
    <bean id="afterLog" class="com.nan.log.AfterLog"/>
    
</beans>

3、实行AOP的三种方式
(1)实现Spring的AOP接口,重写前置和后置方法【主要SpringAPI接口实现】
i.前置方法类

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

ii.后置方法类

public class AfterLog implements AfterReturningAdvice {
    //o:返回值
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("执行了"+method.getName()+"返回的结果为:"+o);
    }
}

iii.配置文件中配置AOP信息

    <bean id="log" class="com.nan.log.Log"/>
    <bean id="afterLog" class="com.nan.log.AfterLog"/>
    
    <!--方式一:spring API接口-->
    <!--配置aop,需要导入aop的 约束-->
    <aop:config>
        <!--配置切入点-->                      <!--定义连接点为com.nan.service下的UserServiceImpl下的所有方法-->        
        <aop:pointcut id="pointcut" expression="execution(* com.nan.service.UserServiceImpl.*(..))"/>
        <!--选取相应的前后置方法-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>

(2)方式二:自定义来实现AOP【主要是切面定义】
i.自定义前置后置方法

public class DiyPointCut {
    public  void before(){
        System.out.println("执行前===========================================================");
    }
    public  void after() {
        System.out.println("执行后===========================================================");
    }
}

ii.配置配置信息

<bean id="diy" class="com.nan.diy.DiyPointCut"/>
<aop:config>
    <!--与实现类方式相比多了一个**设置切面**-->
    <aop:aspect ref="diy">
        <!--设置切入点和连接点-->
        <aop:pointcut id="pointcut" expression="execution(* com.nan.service.UserServiceImpl.*(..))"/>
        <!--前后插入函数-->
        <aop:before method="before" pointcut-ref="pointcut"/>
        <aop:after method="after" pointcut-ref="pointcut"/>
    </aop:aspect>
</aop:config>

(3)方式三:使用注解实现
i.创建注解方法类

@Aspect
public class AnnotationPointCut {
    @Before("execution(* com.nan.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("方法执行前===================================");
    }
    @After("execution(* com.nan.service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("方法执行后===================================");
    }

    //在环绕增强中,我们可以给定一个参数,代表我们要获取的切入点
    @Around("execution(* com.nan.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("环绕前===================================");

        Signature signature = jp.getSignature();
        Object proceed = jp.proceed();
        System.out.println("环绕后");
    }
}

ii.配置配置文件

    <bean id="annotationPointCut" class="com.nan.diy.AnnotationPointCut"/>
    <aop:aspectj-autoproxy/>

(五)Spring中的事务

  • 声明式事务:Spring提供声明式事务处理机制,它基于AOP实现,无须编写任何事务管理代码,所有工作全在配置文件中完成。
  • 编程式事务:需要在代码中编辑进行事务的管理,会改变原有代码,不建议使用

(六)Spring的事务传播级别

  • PROPAGATION_REQUIRED 要求 如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。
  • PROPAGATION_SUPPORTS 支持 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。但是对于事务同步的事务管理器,PROPAGATION_SUPPORTS与不使用事务有少许不同。
  • PROPAGATION_MANDATORY 强制 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
  • PROPAGATION_REQUIRES_NEW 要求新 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。当数据库分库分表存在跨库的时候需要启用
  • PROPAGATION_NOT_SUPPORTED 不支持 总是非事务地执行,并挂起任何存在的事务。
  • PROPAGATION_NEVER 从不 总是非事务地执行,如果存在一个活动事务,则抛出异常。
  • PROPAGATION_NESTED 嵌套 如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按PROPAGATION_REQUIRED 属性执行

二、整合Mybatis案例

(一)编写实体类

package com.nan.pojo;
import lombok.Data;
@Data
public class User {
    private int id;
    private String name;
    private String pwd;
}

(二)编写核心配置文件mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration核心配置文件-->
<configuration>
    <typeAliases>
        <package name="com.nan.pojo"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper class="com.nan.mapper.UserMapper"/>
    </mappers>
</configuration>

(三)编写接口

public interface UserMapper {
    public List<User> selectUser();
}

(四)编写Mapper.xml

<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--configuration核心配置文件-->
<mapper namespace="com.nan.mapper.UserMapper">

    <select id="selectUser" resultType="user">
        select * from user;
    </select>

</mapper>

(五)编写spring-dao.xml配置文件(关联spring)

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
        
    <!--DataSource:使用spring的数据源替代mybatis的配置-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>
    
    <!--SqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="com/nan/mapper/*.xml"/>
    </bean>
    
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
</beans>

(六)使接口编写实现类

public class UserMapperImpl implements UserMapper{

    private SqlSessionTemplate sqlSession;

    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }

    public List<User> selectUser() {
        return sqlSession.getMapper(UserMapper.class).selectUser();
    }
}

(七)将实现类注入Spring中

<bean id="userMapper" class="com.nan.mapper.UserMapperImpl">
    <property name="sqlSession" ref="sqlSession"/>
</bean>

(八)测试

public class MyTest {
    public static void main(String[] args) throws IOException {

        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
        for (User user : userMapper.selectUser()) {
            System.out.println(user);

        }
    }
}

(九)为Mybatis配置事务(声明式事务,由Aop实现)

  • 声明式事务:AOP的一个实现
  • 编程式事务:需要在代码中编辑进行事务的管理,会改变原有代码,不建议使用

1、配置声明式事务
在这里插入图片描述

2、配置事务类(为哪些方式配置事务)

<!--结合AOP,实现事务的植入-->
<!--配置事务的类-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!--给哪些方法配置事务-->
    <tx:attributes>
        <tx:method name="add" propagation="REQUIRED"/>
        <tx:method name="delete" propagation="REQUIRED"/>
        <tx:method name="update" propagation="REQUIRED"/>
        <tx:method name="query" read-only="true"/>
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>

3、 配置事务切入

<!--配置事务切入-->
<aop:config>
    <aop:pointcut id="txPointCut" expression="execution(* com.nan.mapper.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>

参考:
[1] 【狂神说Java】Spring5最新完整教程IDEA版通俗易懂

你可能感兴趣的:(技术栈)