拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 Spring5 学习笔记

Spring5 学习笔记

白鹭 - 2022-01-25 2072 0 0

1. Spring 概述

1.1 Spring 简介

??Spring Framework 是一个使用Java开发的、轻量级的、开源框架,它的主要作用是为了解耦合,Spring 的核心技术是 IOC(控制反转)AOP(面向切面编程)

  • 官方网站: https://spring.io

??Spring 框架提高了很多功能,包括IOC容器、AOP、资料访问、事务、测验功能、定时任务、快取等等,

001_Spring简介_功能模块

1.2 优点

2. IOC 控制反转

2.1 IOC 是什么

??IOC (Inversion of Control, 控制反转) 是一种理论,指导开发人员如何使用物件、管理物件,将物件的生命周期交给容器来管理,通过容器管理物件,开发人员只需要拿到物件,执行物件的方法即可,

  • 控制:管理物件的创建、属性赋值、生命周期的管理,
  • 正转:让开发人员掌控物件的创建、属性赋值,即整个生命周期的管理,
  • 反转:把开发人员管理物件的权限转移给容器来实作,让容器完成管理,

2.2 IOC 的技术实作

??DI (Dependency Injection, 依赖注入) 是 IOC 的一种技术实作,开发人员通过物件的名称获取已初始化的物件,而物件的创建、属性赋值、物件间的呼叫等都由容器内部实作,

2.3 IOC-创建物件 牛刀小试

Source Code

2.3.1 测验步骤

  1. 创建 maven-quickstart 项目,并调整项目结构(字符编码、JDK版本等)
  2. 添加依赖
    • spring-context
    • junit
  3. 定义界面和实作类
    • 界面: SomeService
      • 方法: doSome(): void
    • 实作类: SomeServiceImpl
  4. 创建 Spring 组态档(.xml),宣告需要创建的物件
    • 通过<bean>标签宣告物件,一个标签对应一个物件,
  5. 使用容器中的物件
    • 创建 ApplicationContext 物件
    • 通过 getBean() 获取容器中的物件

2.3.2 依赖档案

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.bpf</groupId>
    <artifactId>M01-ioc-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.12</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

2.3.3 界面与实作类

  • HIDE
  • SomeService.java
  • SomeServiceImpl.java
package com.bpf.service;

public interface SomeService {

    void doSome();
}
package com.bpf.service.impl;

import com.bpf.service.SomeService;

public class SomeServiceImpl implements SomeService {

    public SomeServiceImpl() {
        System.out.println("[SomeServiceImpl] 无参构造方法");
    }

    @Override
    public void doSome() {
        System.out.println("[SomeServiceImpl] someService()...");
    }
}

2.3.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      自定义物件的名称,保持唯一,
        class   自定义物件的全限定类名,不能是界面,

        >>> Spring 根据 id 和 class 创建物件,并将物件放入一个 map 物件中,
    -->
    <bean id="someService"  />
    <bean id="someService1"  />

    <bean id="mydate"  />
</beans>

2.3.5 测验创建物件

测验创建物件: CreateBeanTest.java
package com.bpf.service;

import com.bpf.service.impl.SomeServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.Arrays;
import java.util.Date;

public class CreateBeanTest {

    /**
     * 传统方式: new 获取物件
     */
    @Test
    public void testCreateBeanClassical() {
        SomeService someService = new SomeServiceImpl();
        someService.doSome();
    }

    /**
     * 使用 Spring 容器方式获取物件
     */
    @Test
    public void testCreateBean() {
        // 创建容器物件
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");

        // 通过 getBean() 获取 bean 物件
        SomeService someService = (SomeService) ctx.getBean("someService");
        // 呼叫物件方法
        someService.doSome();
    }

    /**
     * Spring 创建物件,呼叫的是类的哪个构造器呢?
     *  默认呼叫的是类的无参构造器!
     */
    @Test
    public void testCreateStyle() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
        SomeService someService = (SomeService) ctx.getBean("someService");
        someService.doSome();
        // 在无参构造器上添加输出陈述句,如果把无参构造器改成有参构造器,执行测验方法时会报错:无法找到默认的构造方法,

        /** 执行结果
         * [SomeServiceImpl] 无参构造方法
         * [SomeServiceImpl] someService()...
         */
    }

    /**
     * Spring 创建物件,是什么时候创建的呢?
     *   Spring在创建容器物件 ApplicationContext时,会读取组态档,并创建档案中宣告的所有java物件,
     *
     * 优点:获取物件速度快,
     * 缺点:占用存储器,
     */
    @Test
    public void testCreateTime() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");

        /** 执行结果
         * [SomeServiceImpl] 无参构造方法
         * [SomeServiceImpl] 无参构造方法
         */
    }

    /**
     * 获取Spring容器中的物件信息
     */
    @Test
    public void testGetCtxInfo() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");

        // 容器中物件的数量
        int count = ctx.getBeanDefinitionCount();
        // 容器中物件的名称
        String[] names = ctx.getBeanDefinitionNames();

        System.out.println("容器中物件的数量:" + count);
        System.out.println("容器中物件的名称:" + Arrays.toString(names));

        /** 执行结果
         * [SomeServiceImpl] 无参构造方法
         * [SomeServiceImpl] 无参构造方法
         * 容器中物件的数量:2
         * 容器中物件的名称:[someService, someService1]
         */
    }

    /**
     * 创建非自定义物件
     */
    @Test
    public void testOtherBean() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
        Date date = (Date) ctx.getBean("mydate");
        System.out.println("date = " + date);

        /** 执行结果
         * [SomeServiceImpl] 无参构造方法
         * [SomeServiceImpl] 无参构造方法
         * date = Wed Dec 22 19:35:37 CST 2021
         */
    }
}

2.4 Spring 的组态档

??Spring 组态档通常命名为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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<!-- 
    1) 根标签是 beans
    2) xxx.xsd 是当前XML档案的约束档案
    3) 在 beans 标签内宣告 bean 物件,
       一个 bean 就是一个java物件,
 -->
</beans>

002_Spring组态档

??Spring 支持多组态档方式,Spring 管理多组态档常用的是包含关系,即在主组态档中使用import标签包含其他组态档,在其他组态档中定义宣告各自的信息,

<!-- 主组态档 -->

<!-- 路径中可以使用通配符 * 同时引入多个档案 -->
<import resource="classpath:其他组态档路径" />

2.5 Spring IOC ? 创建物件

2.5.1 Spring 容器创建物件的特点

  1. 容器物件是ApplicationContext,它是一个界面,常用的实作类是ClassPathXmlApplicationContext,并且通过getBean()方法获取已初始化的物件,
  2. Spring 创建物件默认呼叫类的无参构造器
  3. Spring 在创建容器物件后,会读取组态档,并创建档案中宣告的所有java物件,然后都放在map物件(ConcurrentMap)中,

2.5.2 XML方式

??Spring 通过在组态档中使用bean标签宣告物件,使用id属性指定创建的物件名称,使用class属性指定创建的物件型别,

<!-- 组态档中宣告一个 bean 标签代表一个 java物件 -->
<bean id="物件名称"  />

2.5.3 注解方式

??使用注解代替组态档中的bean标签,在Java类上使用注解,通过value属性指定创建的物件名称(相对于标签的id属性),同时还需要在组态档中开启注解扫描并指定扫描的包路径,

?Spring 提供了四个注解

注解 说明
@Component 表示普通的java物件
@Repository 常用于创建DAO层的物件,持久层物件,表示可以访问数据库
@Service 常用于创建Service层的物件,业务层物件,表示拥有事务功能
@Controller 常用于创建Controller层的物件,控制器物件,表示可以接收和处理请求,

?组态档开启注解扫描:

<!-- base-package 指定要扫描的包路径,Spring 会自动扫描包及其子包内表有上述注解之一的类,并创建和管理, -->
<context:componet-scan base-package="包路径" />

<!-- 如何扫描多个包? -->
<!-- 1. 使用多个标签 -->
<context:componet-scan base-package="xx.yy.pack01" />
<context:componet-scan base-package="xx.yy.pack02" />

<!-- 2. 使用分隔符:分号(;)或逗号(,) -->
<context:componet-scan base-package="xx.yy.pack01;xx.yy.pack02" />

<!-- 3. 使用共同的父包 -->
<context:componet-scan base-package="xx.yy" />

2.6 Spring IOC ? 属性注入

Source Code

2.6.1 XML方式

(1)set注入(设值注入)

特点

  • 注入的属性必须存在对应的 setter 方法
  • 如果属性在物件中不存在,但存在 setter 方法,依然不会报错,
  • Spring 容器只负责呼叫 setter 方法,与方法的具体实作无关,
<!-- 简单型别注入: 基本资料型别、String型别 -->
<bean id="xxx" >
    <property name="属性名" value="https://www.cnblogs.com/bpf-1024/p/xxx" />
    ...
</bean>

<!-- 参考Java物件 -->
<bean id="xxx" >
    <property name="属性名" ref="其他bean标签的id值" />
    ...
</bean>
<!-- 或 -->
<bean id="xxx" >
    <property name="属性名">
        <bean ></bean>
    </property>
    ...
</bean>

<!-- 注入null值 -->
<bean id="xxx" >
    <property name="属性名">
        <null/>
    </property>
    ...
</bean>

<!-- 集合型别 -->
<bean id="xxx" >
    <property name="属性名">
        <!-- 阵列 -->
        <array>
            <value>xxx</value>
        </array>
    </property>

    <property name="属性名">
        <!-- List -->
        <list>
            <value>xxx</value>
            <ref bean="其他bean标签的id值" />
        </list>
    </property>

    <property name="属性名">
        <!-- Set -->
        <set>
            <value>xxx</value>
        </set>
    </property>

    <property name="属性名">
        <!-- Map -->
        <map>
            <entry key="xxx" value="https://www.cnblogs.com/bpf-1024/p/yyy" />
        </map>
    </property>

    <property name="属性名">
        <!-- 阵列 -->
        <array>
            <value>xxx</value>
        </array>
    </property>
</bean>

(2)构造注入

特点

  • 不需要属性的 setter 方法
  • 需要有相对应的含参构造器
<!-- 
    index   对应构造器的形参索引,从0开始,可以省略
    name    对应构造器的形参名
    value   对应构造器的形参值
    ref     对应其他的Java Bean
 -->
<bean id="xxx" >
    <constructor-arg name="构造器形参名" value="https://www.cnblogs.com/bpf-1024/p/xxx" />
    <constructor-arg index="构造器形参索引" value="https://www.cnblogs.com/bpf-1024/p/xxx" />
    ...
</bean>

(3)参考型别自动注入

  • byName: 根据名称注入,当组态档中bean标签的id值与物件的属性名匹配且属于同个型别时,可以进行注入,
  • byType: 根据型别注入,当组态档中bean标签的class值与物件的属性型别同源时,可以进行注入,
    • bean标签的class值与物件的属性型别相同时,
    • bean标签的class值与物件的属性型别存在父子关系时,
    • bean标签的class值与物件的属性型别存在界面-实作类关系时,

特点

  • byName 方式通过 bean 标签的id属性,需要保证id唯一
  • byType 方式提供 bean 标签的class属性,需要保证只能存在一个同源的bean,否则会报错,
  • 参考型别自动注入本质上使用的是setter方法进行属性赋值的,
<!-- 参考型别自动注入 -->
<bean id="xxx"  autowired="byName | byType">
    ...
</bean>

(4)小作业

主要功能:模拟用户注册操作,

  • 物体类 User,保存用户资料,
  • 定义一个 UserDao 界面,提供方法 insertUser(User),同时定义界面的实作类 MySqlUserDao,方法实作输出 "通过MySQL插入用户:用户资料",
  • 定义一个 UserService 界面,提供方法 addUser(User),同时定义界面的实作类 UserServiceImpl,并实作方法,

要求:使用 Spring 创建和管理界面的实作类物件,并通过 Spring 获取物件完成用户注册操作,

Source Code

2.6.2 注解方式

(1)@Value

  • @Value注解只能为属性赋普通型别的值,
  • @Value注解的位置:
    • 属性宣告上:无需setter方法
    • setter方法上:需要setter方法,并且会呼叫setter方法
  • 赋的值可以通过外部组态档(.properties)指定,
<!-- 组态档中引入外部组态档 -->
<context:property-placeholder location="classpath:properties档案的路径" />
  • HIDE
  • anno-value-applicationContext.xml
  • bean-value.properties
  • Student.java
  • TestAnnoValue.java
<?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: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/context https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.bpf.anno.value" />
    <context:property-placeholder location="classpath:bean-value.properties" />
</beans>
stu.name=凯特斯
stu.age=13
package com.bpf.anno.value;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class Student {

    /**
     * @Value 注解:为属性赋值
     * 使用位置:
     *      1. 属性宣告上:无需setter方法
     *      2. setter方法上:需要setter方法且会呼叫setter方法
     */
    @Value("${stu.name}")
    private String name;
    private Integer age;

    public void setName(String name) {
        System.out.println("name = " + name);
        this.name = name;
    }

    @Value("${stu.age}")
    public void setAge(Integer age) {
        System.out.println("age = " + age);
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
package com.bpf.xml;

import com.bpf.anno.value.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestAnnoValue {

    @Test
    public void test() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("anno-value-applicationContext.xml");
        Student student = (Student) ctx.getBean("student");
        System.out.println("student = " + student);

        /** 执行结果
         * age = 13
         * student = Student{name='凯特斯', age=13}
         */
    }
}

(2)@Autowired

  • @Autowired注解可以为属性赋参考型别的值,默认方式是byType
  • @Autowired注解的位置:
    • 属性宣告上:无需setter方法
    • setter方法上:需要setter方法,并且会呼叫setter方法
/**
 * Autowired 注解原始码
 *   包含了 required 属性,默认值为true,表示当赋值的属性必须有值且赋值成功,当赋值的物件为null时,会抛出例外,
 */
public @interface Autowired {
    boolean required() default true;
}
  • HIDE
  • anno-autowired-applicationContext.xml
  • UserService.java
  • StudentServiceImpl.java
  • Student.java
  • TestAnnoAutowired.java
<?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: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/context https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.bpf.anno.autowired" />
</beans>
package com.bpf.anno.service;

public interface UserService {

    void sayHello();
}
package com.bpf.anno.autowired;

import com.bpf.anno.service.UserService;
import org.springframework.stereotype.Service;

@Service
public class StudentServiceImpl implements UserService {

    @Override
    public void sayHello() {
        System.out.println("<com.bpf.anno.autowired> [StudentServiceImpl] sayHello()...");
    }
}
package com.bpf.anno.autowired;

import com.bpf.anno.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class Student {

    /**
     * @Autowired 注解:为属性赋值
     * 使用位置:
     *      1. 属性宣告上:无需setter方法
     *      2. setter方法上:需要setter方法且会呼叫setter方法
     * 属性:
     *      boolean required: 表示此属性是否必须,默认值为true,表示当对应的java物件为null时会抛出例外,
     *          org.springframework.beans.factory.NoSuchBeanDefinitionException
     */
    // @Autowired(required = false)
    @Autowired
    private UserService userService;

    public void sayHello() {
        userService.sayHello();
    }
}
package com.bpf.xml;

import com.bpf.anno.autowired.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestAnnoAutowired {

    @Test
    public void test() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("anno-autowired-applicationContext.xml");
        Student student = (Student) ctx.getBean("student");
        student.sayHello();

        /** 执行结果
         * <com.bpf.anno.autowired> [StudentServiceImpl] sayHello()...
         *
         * 当 StudentServiceImpl 类去掉 @Service 注解,Student 类中参考型别 userService 注解改成 @Autowired(required=false) 时:
         * 会抛出空指标例外,因为在 Student 的 sayHello() 方法中,userService未成功赋值,所以在真正使用上并不会修改 required
         */
    }
}

(3)@Qualifer

??当使用@Autowired注解进行参考型别注入时,由于默认方式为byType,当存在多个同源的bean时,会抛出例外:org.springframework.beans.factory.NoUniqueBeanDefinitionException,这时候就需要使用byName方式了,

  • @Qualifer注解结合@Autowired注解使用可以实作byName方式的参考型别自动注入,
  • 注解位置同上,
/**
 * Qualifer 注解中只有一个属性 value, 用来指定 bean 的名称即 id,
 */
public @interface Qualifier {
    String value() default "";
}

(4)@Resource

  • @Resource注解是JDK自带的注解,但 Spring 支持这样的注解使用,
  • @Resource注解只能为属性赋参考型别的值,默认方式是byName
    • 当使用byName无法匹配到任何bean时,会使用byType方式,
    • 通过指定name属性让注解只通过byName方式注入bean,
  • 在 JDK8 及之前是自带此注解的,更高的版本需要手动汇入依赖,
<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.2</version>
</dependency>

2.7 Spring IOC 总结

??IOC 就是用来管理物件、管理依赖关系的,通过 IOC 可以实作解决处理业务逻辑物件之间的耦合关系,即 Service 和 DAO 之间的解耦合,

  • 不适合交给Spring管理的物件:
    • 物体类
    • servlet、listener、filter 等 WEB 中的物件,因为它们是由 Tomcat 创建和管理的物件,

补充

> 完全注解开发

> Spring Bean 的生命周期

3. AOP 面向切面编程

3.1 AOP 是什么

??AOP (Aspect Orient Programming, 面向切面编程) 是一种编程思想,它可以在不改变源代码的基础上,给业务方法新增功能,

??AOP 是一种动态的思想,它是在程序运行期间,为特定的业务创建代理,通过代理来增加切面功能,而这个代理是存在于存储器中的,

什么是切面

  • 给业务功能新增的功能就是切面,
  • 切面一般是非业务功能,而且一般都是可复用的,
  • 比如:日志功能、事务功能、权限检查、自变量检查、信息统计等等,

AOP的作用

  • 给业务功能新增方法不需改变源代码,
  • 让开发人员专注业务逻辑,提高开发效率,
  • 实作业务功能与非业务功能解耦合,
  • 切面复用,

3.2 AOP 中的重要术语

术语 翻译 解释
Aspect 切面 给业务方法新增的功能
JoinPoint 连接点 即业务方法
Pointcut 切入点 切面的执行位置,一个或多个连接点的集合,即增加切面的所有业务方法,
Target 目标物件 业务方法的执行者
Advice 通知 切面的执行时间

??AOP 中重要的三个要素:AspectPointcutAdvice,表示在 Advice时间、在 Pointcut位置 执行 Aspect切面

3.3 AOP 的使用时机

  • 当某些方法需要增加相同功能,而源代码又不方便修改时
  • 当给业务方法增加非业务功能时

3.4 AOP 的技术实作

??常用的 AOP 实作技术是 SpringAspectJ

  • Spring:Spring 框架实作了 AOP 思想中的部分功能,但其操作比较繁琐和笨重,
  • AspectJ:独立的框架,专门负责 AOP,属于 Eclipse 基金会,
    • 官网: https://www.eclipse.org/aspectj/

3.5 AspectJ 框架

??AspectJ 框架中可以使用 注解XML组态档 的方式实作 AOP,

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.3.12</version>
</dependency>

3.5.1 注解方式

Source Code

(1)Advice 通知注解

?AspectJ 框架中表示切面执行的时间是五种通知注解,分别代表不同的执行时间,

注解 通知型别 执行时间
@Before 前置通知 业务方法前执行
@AfterReturning 后置通知 业务方法后执行
@Around 环绕通知 业务方法前和后都执行
@AfterThrowing 例外通知 业务方法程序中出现例外时执行
@After 最终通知 业务方法后执行

(2)Pointcut 切入点表达式

?AspectJ 框架中表示切面执行的位置是切入点表达式,本质上可以看作是业务方法的定位标志,

execution(访问权限? 回传值型别 全限定类名?方法名(自变量串列) 例外型别?)
  • ? 代表可选,
    • 最简形式:execution(回传值型别 方法名(自变量串列))
  • 四个部分之间通过空格分开,并且都可以使用通配符??,
通配符 含义
* 代表任意字符
.. 用在方法自变量中,表示任意自变量串列
用在包名中,表示当前包及其子包路径
+ 用在类名后,表示当前类及其子类
用在界面后,表示当前界面及其实作类

(3)@Before 前置通知

  • 注解
    • 前置通知在目标方法执行之前起作用,
  • 属性
    • value: 切入点表达式
  • 方法定义
    • public void 方法名(自变量)
    • 第一个自变量只能是JoinPoint
    • JoinPoint: 表示连接点,即执行的业务方法,可以获取方法的相关信息,如自变量、方法名等,
package com.bpf.before.handler;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Date;

@Component
@Aspect
public class MyBeforeAspect {

    @Before("execution(public void com.bpf.before.service.impl.SomeServiceImpl.doSome(String) )")
    public void addExecTime() {
        System.out.println("[MyBeforeAspect] (前置通知) 当前执行时间:" + new Date());
    }

    @Before("execution(void do*(..))")
    public void noteExecMethod(JoinPoint point) {
        System.out.println("[MyBeforeAspect] (前置通知) 当前正在运行的方法是:");
        System.out.println("\tSign: " + point.getSignature());
        System.out.println("\tTarget: " + point.getTarget());
        System.out.println("\tKind: " + point.getKind());
        System.out.println("\tArgs: " + Arrays.toString(point.getArgs()));
    }
}

(4)@AfterReturning 后置通知

  • 注解
    • 前置通知在目标方法执行之后起作用,
  • 属性
    • value: 切入点表达式
    • returning: 宣告自定义变量名,必须与形参中的变量名一致,代表目标方法的执行结果,
  • 方法定义
    • public void 方法名(自变量)
    • 第一个自变量只能是JoinPoint
    • JoinPoint: 表示连接点,即执行的业务方法,可以获取方法的相关信息,如自变量、方法名等,
    • Object: 表示目标方法的执行结果,推荐使用Object
  • 特点
    • 当业务方法的回传值型别是 基本资料型别及其包装类 或 String 时,切面方法无法改变回传值内容,
    • 当业务方法的回传值型别是 其他参考型别的Java物件时,切面方法可以改变回传值内容,
package com.bpf.afterreturning.handler;

import com.bpf.afterreturning.bean.Person;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class MyAfterReturningAspect {
							
标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *