spring03_03_[掌握]Spring中AOP使用(含源码下载)基于XML、注解的AOP配置

先上代码:项目源码下载(软件:IDEA):

基于XML的AOP配置

链接:https://pan.baidu.com/s/1wSbq5jYeykOxTUmjEvmz5A 
提取码:9sox 

基于注解的AOP配置

链接:https://pan.baidu.com/s/17QO6XzA5UGPHcW8JBrcHeg 
提取码:tw1g 

AOP相关术语

  1. Joinpoint(连接点)

横切程序执行的特定位置,比如类开始初始化前,类初始化之后,类中某个方法调用前、调用后,方法抛出异常后等,这些代码中的特定点就称为“连接点”。

Spring仅支持方法的连接点,即仅能在方法调用前、方法调用后、方法抛出异常时以及方法调用前后这些程序执行点织入增强。

我们知道黑客攻击系统需要找到突破口,没有突破口就无法进行攻击,从这一角度上来说,AOP是一个黑客(因为它要向目前类中嵌入额外的代码逻辑),连接点就是AOP向目标类打入楔子的候选点。

    2.Pointcut(切入点)

一个类中可以有很多个方法,每个方法又有多个Joinpoint,在这么多个方法中,如何定位到自己感兴趣的方法呢?靠的是切点

注意:切点只定位到某个方法上,所以如果希望定位到具体连接点上,还需要提供方位信息  

比如:如果把一个方法理解成数据表中的一条记录的话,那么切入点就好比你select语句的where条件 ,就可以定位到你感兴趣的方法

    3.Advice(通知/增强)

增强的第一层意思就是你的横切逻辑代码(增强逻辑代码)

扫描二维码关注公众号,回复: 5820650 查看本文章

Spring中,增强除了用于描述横切逻辑外,包含一层意思就是横切逻辑执行的方位信息。刚刚说了切点只能定位到方法,在进一步使用方位信息就可以定位到我们感兴趣的连接点了(方法调用前、方法调用后还是方法抛出异常时等)。  

    4.Target(目标对象)

增强逻辑的织入目标类。比如未添加任何事务控制的AccountServiceImplNoTcf  

    5.Weaving(织入)

织入是将增强逻辑/横切逻辑添加到目标类具体连接点上的过程,AOP像一台织布机,将目标类、增强或者引介通过AOP(其实就是动态代理技术)这台织布机天衣无缝地编织到一起。

Spring采用动态代理织入。  

    6.Proxy(代理)

一个类被AOP织入增强后,就产出了一个结果类,它是融合了原类和增强逻辑的代理类。  

    7.Aspect(切面)

切面由切点和增强(引介)组成。

切面=切点+增强

       =切点+方位信息+横切逻辑

       =连接点+横切逻辑

最终切面完成:把横切逻辑织入到哪些方法的方法前/后等

本质:把横切逻辑增强到连接点(切点和方位信息都是为了确定连接点)上

Spring关于JDK/CGLIB动态代理的选择

Spring发现涉及到接口那就使用JDK动态代理,如果不涉及接口就使用CGLIB动态代理

AOP:日志、性能监控、事务、权限控制

基于XML的AOP配置

    需求:在Service层代码的不同方法的不同连接点JoinPoint织入日志

把Account表的service层进行crud模拟(dao层就不需要了)

    引入POM坐标

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
  <version>5.0.2.RELEASE</version>
</dependency>

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

applicationContext.xml配置

 <aop:config> 所有aop配置的根标签
<aop:aspect id="logAspect" ref="log"> 配置切面对象,id自定义,ref横切逻辑类的bean标签id值
<aop:pointcut> 配置切入点
   属性值:id自定义
   属性值:expression 匹配方法的表达式
<aop:before> 被切入的方法前执行
   属性method:printBeforeMethod切入的方法名
   属性pointcut-ref:切入点标签的id属性值

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
">
    <context:component-scan base-package="com.itheima"></context:component-scan>

    <!-- 横切逻辑配置-->
    <bean id="log" class="com.itheima.utils.LogUtil">

    </bean>
    <!-- 配置aop的根标签-->
    <aop:config>
        <!-- 切面配置-->
        <aop:aspect id="logAspect" ref="log">
            <!--
                配置切入点
                before:方法执行之前
                method:切入的方法名
                pointcut:感兴趣的方法,被切入的方法

            -->
            <!--<aop:before method="printBeforeMethod"
                        pointcut="execution( int com.itheima.service.AccountService.saveAccount(com.itheima.pojo.Account))"></aop:before>-->
            <!--
                表达式配置
                方法参数:* 匹配任意参数,必须有参数
                         .. 匹配任意参数,有无参数均可
                方法名:*
                返回值:*
            -->
            <aop:pointcut id="point1" expression="execution( * com.itheima.service.AccountService.*(..))"></aop:pointcut>
            <aop:before method="printBeforeMethod" pointcut-ref="point1"></aop:before>
            <aop:after-returning method="pringAfterReturn" pointcut-ref="point1"></aop:after-returning>
            <aop:after-throwing method="printAfterThrowing" pointcut-ref="point1"></aop:after-throwing>
            <aop:after method="printAfterMethod" pointcut-ref="point1"></aop:after>
        </aop:aspect>
    </aop:config>

</beans>

日志对象:横切逻辑

public class LogUtil {
    //方法执行之前
    public void printBeforeMethod(){
        System.out.println("方法之前执行");
    }

    //方法执行之后打印
    public void printAfterMethod(){
        System.out.println("方法执行之后");
    }

    //方法异常执行打印
    public void printAfterThrowing(){
        System.out.println("方法异常时");
    }

    //方法正常执行打印
    public void pringAfterReturn(){
        System.out.println("方法正常时");
    }
}

service层

@Service("accountServiceImpl")
public class AccountServiceImpl implements AccountService {

    public int saveAccount(Account account) {
        System.out.println("模拟保存账户");
        return 0;
    }

    @Override
    public int updateAccountById(Account account) {
        System.out.println("模拟更新账户");
        return 0;
    }

    @Override
    public int deleteAccountById(int id) {
        System.out.println("模拟删除账户");
        return 0;
    }

    @Override
    public Account queryAccountById(int id) {
        System.out.println("模拟查询账户");
        return null;
    }
}

测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class MainTest {

    @Autowired
    @Qualifier("accountServiceImpl")
    private AccountService accountService;

    @Test
    public void testAop(){
        accountService.saveAccount(new Account());
	    // accountService.queryAccountById(1);
    }
}

环绕通知

它是spring为我们提供的一种可以在代码中手动控制增强方法何时执行的方式,灵活度比较高,设置可以控制原业务逻辑是否执行。

注意:通常情况下,环绕通知都是独立使用的,不要和上面的四种通知类型混合使用

   <aop:around method="printRound" pointcut-ref="point1"></aop:around> 环绕通知配置
 ProceedingJoinPoint进程切入点对象,执行我们的业务逻辑方法
   方法:proceed() 执行我们自己的业务逻辑方法


 applicationContext.xml配置

<aop:around method="printRound" pointcut-ref="point1"></aop:around>

日志对象:横切逻辑

//环绕通知
public Object printRound(ProceedingJoinPoint proceedingJoinPoint){
    Object result = null;
    try{
        System.out.println("方法前执行");
        result = proceedingJoinPoint.proceed();
        System.out.println("方法正常执行");
    }catch (Throwable ex){
        ex.printStackTrace();
        System.out.println("方法异常执行");
    }
    System.out.println("方法后执行");
    return result;
}

基于注解的AOP配置

创建框架启动配置类

@Component
@ComponentScan("com.itheima")
//启用动态代理
@EnableAspectJAutoProxy
public class SpringConfig {
}

测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class MainTest {

    @Autowired
    @Qualifier("accountServiceImpl")
    private AccountService accountService;

    @Test
    public void testAop(){
        accountService.saveAccount( new Account());
    }

}

学习spring中的AOP时要明确的事

  1. AOP的使用场景

AOP的应用场景往往是受限的,它一般只适合于那些具有横切逻辑的应用场合:如性能监测、访问控制、事务管理以及日志记录等。不过,这丝毫不影响AOP作为一种新的软件开发思想在软件开发领域所占有的地位。

    2.开发阶段(我们完成)

        编写核心业务代码

        大部分程序员来做,要求熟悉业务需求。

        抽取公用代码制作成通知,进行AOP配置

        一般由专门的AOP编程人员来做

    3.运行阶段(Spring框架完成)

    Spring框架监控切入点方法的执行。一旦监控到切入点方法被运行,Spring框架使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。

 

猜你喜欢

转载自blog.csdn.net/qq_35670694/article/details/89056359
今日推荐