Spring框架学习记录

本文详细介绍了Spring框架的基础知识,包括IoC控制反转、DI的两种实现方式(基于XML和注解)、AOP的定义及AspectJ的使用,以及面向切面编程在实际项目中的应用。通过实例演示了如何配置对象、自动注入和切面编程,适合初学者学习和进阶阅读。
摘要由CSDN通过智能技术生成

Spring框架学习

一、写在前面

最近在学习spring框架,当初没有打算写博客,但是在学习的过程中发现spring的小知识点比较多,所以写一遍博客,当作复习,当作巩固,当作纪念。

二、Spring基础

1.Spring是什么

  • Spring 是于 2003 年兴起的一个轻量级的 Java 开发框架,它是为了解决企业应用开发的复杂性而创建的。Spring 的核心是控制反转(IoC)和面向切面编程(AOP)。Spring 是可以在 Java SE/EE 中使用的轻量级开源框架。
  • Spring 的主要作用就是为代码“解耦”,降低代码间的耦合度。就是让对象和对象
    模块和 模块之间关系不是使用代码关联,而是通过配置来说明。即在 Spring 中说明对象(模块)的关系。
  • Spring 根据代码的功能特点,使用 Ioc 降低业务对象之间耦合度。IoC 使得主业务在相互调用过程中,不用再自己维护关系了,即不用再自己创建要使用的对象了。而是由 Spring容器统一管理,自动“注入”,注入即赋值。 而 AOP 使得系统级服务得到了最大复用,且不用再由程序员手工将系统级服务“混杂”到主业务逻辑中了,而是由 Spring 容器统一完成“织入”。

2.Spring的优点

  1. 轻量级
  2. 针对接口编程,解耦合:Spring 提供了 Ioc 控制反转,由容器管理对象,对象的依赖关系。原来在程序代码中的对象创建方式,现在由容器完成。对象之间的依赖解耦合。
  3. AOP编程的支持。
  4. 方便集成各种优秀的框架

3.Ioc控制反转

  1. Ioc是什么
  • 是一个概念,是一种思想。指将传统上由程序代码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。控制反转就是对对象控制权的转移,从程序代码本身反转到了外部容器。通过容器实现对象的创建,属性赋值,
    依赖的管理。
  • IoC 是一个概念,是一种思想,其实现方式多种多样。当前比较流行的实现方式是依赖注入。应用广泛。
  1. 依赖注入:程序代码不做定位查询,这些工作由容器自行完成。
    依赖注入 DI 是指程序运行过程中,若需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部容器,由外部容器创建后传递给程序。
    Spring 的依赖注入对调用者与被调用者几乎没有任何要求,完全支持对象之间依赖关系的管理。

  2. Spring中的依赖注入:Spring 容器是一个超级大工厂,负责创建、管理所有的 Java 对象,这些 Java 对象被称为 Bean。Spring 容器管理着容器中 Bean 之间的依赖关系,Spring 使用“依赖注入”的方式来管理 Bean 之间的依赖关系。使用 IoC 实现对象之间的解耦和。

三、开发准备

1.前期准备
  1. 配置maven的本地仓库
    在这里插入图片描述

  2. quickstart创建maven项目并且加入依赖

<?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>org.example</groupId>
  <artifactId>spring-01-hello</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>spring-01-hello</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <!--spring依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

  1. 定义接口与实体类
  2. 创建 Spring 配置文件
    在 src/main/resources/目录现创建一个 xml 文件,文件名可以随意,但 Spring 建 议的名称为 applicationContext.xml。spring 配置中需要加入约束文件才能正常使用,约束文件是 xsd 扩展名。
  3. 使用容器中的对象,通过ApplicationContext接口和它的实现类
    ClassPathXmlApplicationContext的方法getBean()
  4. 定义测试类

四.基于xml的di

4.1

根据构造方法的不同分为:根据注入方式的不同,常用的有两类:set 注入、构造注入,这里列出set注入的使用方法

4.2. 构造实体类与接口


在这里插入图片描述

4.3 创建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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--
       spring的配置文件
       1.beans : 是根标签,spring把java对象成为bean。
       2.spring-beans.xsd 是约束文件,和mybatis指定  dtd是一样的。
    -->
    <!--告诉spring创建对象
      声明bean , 就是告诉spring要创建某个类的对象
      id:对象的自定义名称,唯一值。 spring通过这个名称找到对象
      class:类的全限定名称(不能是接口,因为spring是反射机制创建对象,必须使用类)

      spring就完成 SomeService someService = new SomeServiceImpl();
      spring是把创建好的对象放入到map中, spring框架有一个map存放对象的。
         springMap.put(id的值, 对象);
         例如 springMap.put("someService", new SomeServiceImpl());

      一个bean标签声明一个对象。
  -->
    <bean id="someService" class="org.example.service.impl.someServiceImpl" />

    <!--
       spring能创建一个非自定义类的对象吗, 创建一个存在的某个类的对象。
    -->
    <bean id="mydate" class="java.util.Date" />

</beans>

代码分析:

spring的配置文件
   1.beans : 是根标签,spring把java对象成为bean。
   2.spring-beans.xsd 是约束文件,和mybatis指定  dtd是一样的。

声明bean , 就是告诉spring要创建某个类的对象
例子: <bean id="someService" class="org.example.service.impl.someServiceImpl" />
  id:对象的自定义名称,唯一值。 spring通过这个名称找到对象
  class:类的全限定名称(不能是接口,因为spring是反射机制创建对象,必须使用类)
  之后
  spring就完成 SomeService someService = new SomeServiceImpl();
  spring是把创建好的对象放入到map中, spring框架有一个map存放对象的。
     springMap.put(id的值, 对象);
     例如 springMap.put("someService", new SomeServiceImpl());
4.4 创建test
    @Test
    public void test02(){
        //使用Spring容器创建对象
        //1.指定Spring配置文件的名称
        String config = "beans.xml";
        //2.创建表示Spring容器的对象:ApplicationContext
        ApplicationContext  applicationContext = new ClassPathXmlApplicationContext(config);
        //3.从容器中获取某个对象
        someService someService = (org.example.service.someService) applicationContext.getBean("someService");//参数是配置文件中bean的ID值
        //4.使用对象
        someService.dosome();
    /*
    Spring默认创建对象的时间:在创建Spring容器时,会创建配置文件中的所有Spring对象
    Spring创建对象默认调用的是无参构造方法
     */

代码分析:

  • Spring默认创建对象的时间:在创建Spring容器时,会创建配置文件中的所有Spring对象
  • Spring创建对象默认调用的是无参构造方法
4.5 给属性赋值:在applicationContext.xml文件中中实现

java对象的方法中必须要有setter方法才能赋值,property中的赋值就是调用了相应对象中的setter()方法。


    <bean id="myStudent" class="org.example.ba02.Student">
        <property name="name" value="ycx"/>
        <property name="age" value="20"/>
        <property name="school" ref="mySchool"/>
    </bean>

    <bean id="mySchool" class="org.example.ba02.School">
        <property name="name" value="yingcai"/>
        <property name="address" value="xianyang"/>
    </bean>
    <!--使用有参构造方法创建对象-->
    <bean id="myStudent1" class="org.example.ba02.Student">
        <constructor-arg name="age" value="20"/>
        <constructor-arg name="name" value="wwy"/>
        <constructor-arg name="school" ref="mySchool"/>
    </bean>

代码分析:

	声明student对象
    注入:就是赋值的意思
    简单类型: spring中规定java的基本数据类型和String都是简单类型。
    di:给属性赋值
    1. set注入(设值注入) :spring调用类的set方法, 你可以在set方法中完成属性赋值
     1)简单类型的set注入
        <bean id="xx" class="yyy">
           <property name="属性名字" value="此属性的值"/>
           一个property只能给一个属性赋值
           <property....>
        </bean>
    2) 引用类型的set注入 : spring调用类的set方法
    <bean id="xxx" class="yyy">
     <property name="属性名称" ref="bean的id(对象的名称)" />
    </bean>
  • 简单类型是 property name = “” value = “”
  • 引用类型是 property name = “” ref “这里写的是bean的id
  • 在这里,spring先扫描了applicationContext的所有标签,例子中就是Student和School标签,创建了对应的对象,然后在Student的属性赋值的时候发现了School类型的对象,就使用了刚刚创建的School对象

test的代码以及结果:

    public void test02(){
        String config = "ba02/applicationContext.xml";
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(config);
        org.example.ba02.Student student = (org.example.ba02.Student) applicationContext.getBean("myStudent");
        System.out.println("加入引用的Student对象:" + student);

在这里插入图片描述

4.6 引用类型的自动注入 autowire

byName方式:

  • byName方式:是引用类型的自动注入,Spring框架根据某些规则可以给引用类型赋值。在applicationContext.xml文件中中实现
  • 要求:Java类中引用类型的属性名(POJO)和spring容器中(配置文件)中bean的id的名称一样
    并且数据类型是一致的。
    <bean id="myStudent" class="org.example.ba04.Student" autowire="byName">
        <property name="name" value="ycx"/>
        <property name="age" value="20"/>
<!--        <property name="school" ref="mySchool"/>-->
    </bean>

    <bean id="school" class="org.example.ba04.School">
        <property name="name" value="yingcai"/>
        <property name="address" value="xianyang"/>
    </bean>

代码分析:
这里的代码中myStudent标签中的属性并没有写school,但是又autowire = “byName",这样spring会将Java类中引用类型的属性名(POJO)和spring容器中(配置文件)中bean的id名称进行匹配,刚好这里有一个标签id为school的。并将这个引用类型的对象加入。
test和结果:
在这里插入图片描述
byType方式:按类型注入

  1. java类中引用类型的数据类型和spring容器中(配置文件)的class属性
    是同源关系的,这样的bean能够赋值给引用类型。在applicationContext.xml中实现
  2. 同源就是一类的意思:
    1.java类中引用类型的数据类型和bean的class的值是一样的。
    2.java类中引用类型的数据类型和bean的class的值父子类关系的。
    3.java类中引用类型的数据类型和bean的class的值接口和实现类关系的
  3. 语法:<bean id="xx" class="yyy" autowire="byType">
  4. 代码:
    <bean id="myStudent" class="org.example.ba03.Student" autowire="byType">
        <property name="name" value="张三"/>
        <property name="age" value="20"/>
<!--        <property name="school" ref="mySchool"/>-->
    </bean>

    <bean id="mySchool" class="org.example.ba03.School">
        <property name="name" value="yingcai"/>
        <property name="address" value="xianyang"/>
    </bean>

过程分析:
Spring会找到class里面的对象类型,比如Student,student里面有三个属性name,age,school,其中school属性的类型是School,于是spring
就会在applicationContext.xml配置文件的标签中找class是school的标签并且自动的注入。
注意:按类型注入期待的是单一一个的匹配,即不能有多个相同类型的符合条件对象只有一个

4.7 指定多个Spring配置文件:

在这里插入图片描述

  • 其中total表示表示主配置文件:用来包含其他的配置文件的。主配置文件一般是不定义对象的
  • 语法:
 <import resource = "其他配置文件的路径" />
  • 关键字:
    "classpath:"表示类路径叫做包含关系的配置文件:
  • 例子:
    <import resource="classpath:ba05/Spring-School.xml"/>
    <import resource="classpath:ba05/Spring-Student.xml"/>
  • 也可以使用通配符*,但是在使用通配符时,主配置文件的名称不能包含在通配符的范围内(此时要求父配置文件名不能满足*所能匹配的格式)否则会发生循环递归包含错误。即自身一直调用自身。
  • 例如当 使用<import resource="classpath:ba05/Spring-*.xml"/>时,total就不能命名为spring-total了。
  • 多个配置文件的优势
    1.每个文件的大小比集成在一个文件中要小很多,效率高。
    2.避免多人竞争带来的冲突
    3.多文件的分配方式:
    如果你的项目中有多个模块(相关的功能),常见的方式是一个模块对应一个配置文件
    按类的功能分:比如数据库一个配置文件,事务功能一个配置文件等等
    4.包含关系的配置文件:ba05包以及Mytest04
    注意:在使用通配符时,主配置文件的名称不能包含在通配符的范围内,且通配符中的配置文件需要放在一级目录中

五、基于注解的di

5.1 通过注解完成java对象创建,属性赋值。
5.2. 步骤:
  • 1.使用注解就需要spring-aop的依赖,在加入spring-context的同时会间接的加入
  • 2.在类中加入spring的依赖
  • 3.在spring的配置文件中,加入一个组件扫描器的标签,说明注解在你的项目中的位置。声明组件扫描器:
    base-package:指定注解在项目中的包名
    component-scan:spring会遍历扫描 base-package:指定的包
    把包中和子包中的所有类全都扫描一遍,找到类中的注解,按照注解的功能创建对象或者给属性赋值
    举例:
    <context:component-scan base-package="com.bjpowernode.ba04"/>
5.3 @Component
  1. 这是来自Spring框架的一个注解,是用来创建对象的,等同于的功能
    属性value : 就是对象的名称,也就是bean的id值。value的值是唯一的,创建的对象在spring容器中就一个。
    位置:要求在类的上面
    需要在spring的配置文件中声明组件扫描器
@Component
public class Student {
    public Student() {
        System.out.println("Student的无参构造方法");
    }

    private String name;
    private Integer age;

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

    public void setName(String name) {
        System.out.println("调用了setName方法");
        this.name = name;
    }

    public void setAge(Integer age) {
        System.out.println("调用了setAge方法");
        this.age = age;
    }
}

  1. test以及结果:
    在这里插入图片描述
5.4创建对象的其他注解
  • spring中和@Component功能一致,创建对象的注解还有:
  • 1.@Repository(用在持久层类的上面) : 放在dao的实现类上面,
  •           表示创建dao对象,dao对象是能访问数据库的。
    
  • 2.@Service(用在业务层类的上面):放在service的实现类上面,
  •          创建service对象,service对象是做业务处理,可以有事务等功能的。
    
  • 3.@Controller(用在控制器的上面):放在控制器(处理器)类的上面,创建控制器对象的,
  •          控制器对象,能够接受用户提交的参数,显示请求的处理结果。
    
  • 以上三个注解的使用语法和@Component一样的。 都能创建对象,但是这三个注解还有额外的功能。
  • @Repository,@Service,@Controller是给项目的对象分层的。
5.5给简单类型赋值注解: @Value

属性:value:String类型的,表示简单类型的属性值的
位置
1.在属性定义的上面,无需set方法,推荐使用

@Component
public class Student {
    public Student() {
        System.out.println("Student的无参构造方法");
    }
    @Value(value = "ycx")
    private String name;
    @Value(value = "20")
    private Integer age;

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

    public void setName(String name) {
        System.out.println("调用了setName方法");
        this.name = name;
    }

    public void setAge(Integer age) {
        System.out.println("调用了setAge方法");
        this.age = age;
    }
}

2.在set方法的上面

    @Value(value = "ycx")
    public void setName(String name) {
        System.out.println("调用了setName方法");
        this.name = name;
    }
    @Value(value = "69")
    public void setAge(Integer age) {
        System.out.println("调用了setAge方法");
        this.age = age;
    }
}
  1. test以及结果
    在这里插入图片描述
5.6 给引用类型赋值:@Autowired
  1. spring框架提供的,使用的是自动注入原理,支持byName,byType(默认)
    里面有默认的属性required,@Autowired(required = false)时即使没找到bean标签也会显示null不会报错的,需要在引用属性上使用注解@Autowired
  2. 属性:required ,是一个boolean类型的,默认true
    •   required=true:表示引用类型赋值失败,程序报错,并终止执行。
      
    •   required=false:引用类型如果赋值失败, 程序正常执行,引用类型是null
      

一般来说推荐使用true因为这样可以暴露出错误
位置

  • 在属性定义的上方,无需set方法,推荐使用
    例子:引用类型School
@Component(value = "mySchool")
public class School {
    @Value(value = "西北大学")
    private String name;
    @Value(value = "陕西省")
    private String address;

    @Override
    public String toString() {
        return "School{" +
                "name='" + name + '\'' +
                ", address='" + address + '\'' +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

在Student类中使用@Autowired:

public class Student {
    public Student() {
        System.out.println("Student的无参构造方法");
    }

    private String name;

    private Integer age;
//    @Autowired(required = false)
//    @Qualifier(value = "mySchool1")
    @Autowired
    private School school;
  • 在set上方
  1. test以及结果
    在这里插入图片描述
5.7自动赋值注解: @Resource
  1. @Resource注解(由jdk提供,spring框架提供了对这个注解的支持,可以使用它给引用类型赋值)
    使用的也是自动注入原理,支持byName,ByType,默认是byName
    位置:
    1)在属性定义的上方,无需set方法,推荐使用
    2)在set上方
  2. byType 注入引用类型属性
    @Resource 注解若不带任何参数,采用默认按名称的方式注入,按名称不能注入 bean,
    则会按照类型进行 Bean 的匹配注入。
  3. byName :@Resource 注解指定其 name 属性,则 name 的值即为按照名称进行匹配的 Bean 的 id。

注解与xml的比较

配置文件:代码多并且配置文件代码与对象代码是独立的。但是不直观不清晰经常改的值用xml配置文件
注解:不经常修改的适合使用
注解优点是:

  • 方便
  • 直观
  • 高效(代码少,没有配置文件的书写那么复杂)。
    其弊端也显而易见:以硬编码的方式写入到 Java 代码中,修改是需要重新编译代码的。
    XML 方式优点是:
  • 配置和代码是分离的
  • 在 xml 中做修改,无需编译代码,只需重启服务器即可将新的配置加载。
    xml 的缺点是:编写麻烦,效率低,大型项目过于复杂。

六、aop 面向切面编程

1. aop的定义
1. AOP 为 Aspect Oriented Programming AOP 是 Spring 框架中的一个重要内容。
利用 AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,
提高程序的可重用性,同时提高了开发的效率。
2. 面向切面编程,就是将交叉业务逻辑封装成切面,利用 AOP 容器的功能将切面织入到主业务
逻辑中。
3. 所谓交叉业务逻辑是指,通用的、与主业务逻辑无关的代码,如安全检查、事务、日志、缓存等。
若不使用 AOP,则会出现代码纠缠,即交叉业务逻辑与主业务逻辑混合在一起。
这样,会使主业务逻辑变的混杂不清。
2. aspectJ框架
  1. 对于 AOP 这种编程思想,很多框架都进行了实现。Spring 就是其中之一,可以完成面向切面编程。然而,AspectJ 也实现了 AOP 的功能,且其实现方式更为简捷,使用更为方便,而且还支持注解式开发。所以,Spring 又将 AspectJ 的对于 AOP 的实现也引入到了自己的框架中。在 Spring 中使用 AOP 开发时,一般使用 AspectJ 的实现方式
  2. AspectJ 是一个优秀面向切面的框架,它扩展了 Java 语言,提供了强大的切面实现
  3. 依赖:
<dependencies>
    <!--单元测试-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <!--Spring核心ioc-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <!--Spring事务-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
    <!--mybatis依赖-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.1</version>
    </dependency>
    <!--mybatis和spring集成依赖-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.1</version>
    </dependency>
    <!--mysql驱动-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.9</version>
    </dependency>
    <!--阿里的连接池-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.12</version>
    </dependency>
  </dependencies>
3. aspectJ的使用

一、使用时间,即切面的执行时间,这个执行时间在规范中叫做Advice(通知,增强)
在aspectj框架中使用注解表示的

  • @Before
  • @AfterReturning
  • @Around
  • @AfterThrowing
  • @After

、使用位置:使用的是切入点表达式(其中返回值和方法名是必须的)

execution(public * *(..)) 
指定切入点为:任意公共方法。
execution(* set*(..)) 
指定切入点为:任何一个以“set”开始的方法。
execution(* com.xyz.service.*.*(..)) 
指定切入点为:定义在 service 包里的任意类的任意方法。
execution(* com.xyz.service..*.*(..))
指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“..”出现在类名中时,后
面必须跟“*”,表示包、子包下的所有类。
execution(* *..service.*.*(..))
指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点
execution(* *.service.*.*(..))
指定只有一级包下的 serivce 子包下所有类(接口)中所有方法为切入点
execution(* *.ISomeService.*(..))
指定只有一级包下的 ISomeSerivce 接口中所有方法为切入点
execution(* *..ISomeService.*(..))
指定所有包下的 ISomeSerivce 接口中所有方法为切入点
execution(* com.xyz.service.IAccountService.*(..)) 
指定切入点为:IAccountService 接口中的任意方法。
execution(* com.xyz.service.IAccountService+.*(..)) 
指定切入点为:IAccountService 若为接口,则为接口中的任意方法及其所有实现类中的任意方法;若为类,则为该类及其子类中的任意方法。
execution(* joke(String,int)))
指定切入点为:所有的 joke(String,int)方法,且 joke()方法的第一个参数是 String,第二个参数是 int。如果方法中的参数类型是 java.lang 包下的类,可以直接使用类名,否则必须使用全限定类名,如 joke( java.util.List, int)execution(* joke(String,*))) 
指定切入点为:所有的 joke()方法,该方法第一个参数为 String,第二个参数可以是任意类型

三、使用aspectj实现aop的基本步骤:

  1. 新建maven项目
  2. 加入依赖:
  • spring依赖
  • aspectj依赖
  • junit单元测试
  1. 创建目标类:接口和他的实现类。
    在这里插入图片描述

  2. 创建切面类:普通类
    在这里插入图片描述

     切面类代码解释:
     @Aspect是aspectj框架中的注解
     作用:表示当前类是切面类
     切面类:用来给业务方法增加功能的类,这个类中有切面的功能代码
     位置:在类定义的上面
    
      * 定义方法,方法是实现切面功能的
      * 方法的定义要求
      * 1.是公共方法public
      * 2.方法名称自定义
      * 3.方法没有返回值
      * 4.方法可以有参数也可以没有,如果有参数,参数不是自定义的,有几个参数可以使用。
    
  3. 创建spring的配置文件:声明对象,把对象交给容器统一管理
    声明对象你可以使用注解或者xml配置文件
    1)声明目标对象
    2)声明切面类对象
    3)声明aspectj框架中的自动代理生成器标签。
    自动代理生成器:用来完成代理对象的自动创建功能的。
    在这里插入图片描述

  4. 创建测试类,从spring容器中获取目标对象(实际就是代理对象)。
    通过代理执行方法,实现aop的功能增强。
    在这里插入图片描述

4.注解
  1. @Before表示前置通知注解
    @Before表示前置通知注解
    属性value:是切入点表达式,表示切面的功能执行的位置
    位置:在方法的上面
    特点:
  • .在目标方法之前先执行的
  • 不改变目标方法的执行结果
  • 不会影响目标方法的执行
    例子:
    @Before(value = "execution(public void org.example.ba01前置通知.SomeServiceImpl.doSome(String,Integer))")
    public void myBefore(){
        System.out.println("前置通知,切面功能:在目标方法执行之前输出时间" + new Date());
    }

    @Before(value = "execution(public void org.example.ba01前置通知.SomeServiceImpl.doSome(String,Integer))")
    public void myBefore1(JoinPoint joinPoint){
        System.out.println("使用JoinPoint获取方法的信息:" +joinPoint.getSignature() +joinPoint.getSignature().getName());
    }
 * 指定通知方法中的参数:JoinPoint
 * JoinPoint:业务方法,即要加入切面功能的业务方法,在这个例子中就是doSome方法
 *      作用是:可以在通知方法中获取方法执行时的信息,例如方法名,方法实参等。
 *      如果切面功能中需要用到方法的信息,就加入JoinPoint
 *      这个JoinPoint的值是由框架赋予的,且必须是第一个位置的参数

test结果:

com.sun.proxy.$Proxy8
前置通知,切面功能:在目标方法执行之前输出时间Sat Jun 12 16:39:43 CST 2021
使用JoinPoint获取方法的信息:void org.example.ba01前置通知.SomeService.doSome(String,Integer)doSome
目标方法doSome()执行
  1. @AfterReturning 后置通知

    后置通知定义方法的定义要求
    1.是公共方法public
    2.方法名称自定义
    3.方法没有返回值
    4.方法有参数,推荐是Object,参数名称是自定义的
    属性:
    1.value 切入点表达式
    2.returning 自定义的变量,表示目标方法的返回值的,自定义的变量名必须和通知方法的形参名一样,在例子中就是res和res一样
    位置:在方法定义的上面
    特点:
    1.在目标方法之后执行
    2.能够获取目标方法的返回值,可以根据这个返回值做不同的处理功能
    相当于 Object res = doOther()
    3.可以修改返回值
    这个JoinPoint的值是由框架赋予的,且必须是第一个位置的参数
    

例子:

    @AfterReturning(value = "execution(* *..SomeServiceImpl.doOther(..))",returning = "res")
    public void myAfterReturning(JoinPoint joinPoint, Object res){
        /*
        修改res = "ycx666";结果如下:
        后置通知,在业务方法执行后在执行,获取的返回值是:ycx666
        说明传入的res对象的值可以改变
         */
        System.out.println("后置通知  方法的信息:" + joinPoint.getSignature().getName() +joinPoint.getSignature());
        System.out.println("后置通知,在业务方法执行后在执行,获取的返回值是:" + res);
    }

测试结果:
在这里插入图片描述

  1. @Around 环绕通知

    属性:value 使用切入点表达式
    位置:方法定义的上面
    特点:
    1.它是功能最强的通知
    2.在目标对象的前和后都能增强功能
    3.控制目标方法是否被调用执行
    4.修改原来的目标方法的执行结果。影响最后的调用结果。
    环绕通知,等同于jdk动态代理的    InvocationHandler 接口
    参数proceedingJoinPoint等同于Method,作用就是执行目标方法,返回值就是执行结果,可以被修改
    

例子:

 @Around(value = "execution(* *..SomeServiceImpl.doFirst(..))")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        //实现环绕通知
        //获取第一个参数
        Object args[] = proceedingJoinPoint.getArgs();
        if(args != null && args.length>1){
            //进行判断或者其他功能
        }


        //1.目标方法调用
        Object result = null;
        System.out.println("环绕通知:在目标方法之前,输出时间" + new Date());
        result = proceedingJoinPoint.proceed();//等同于method.invoke()
        System.out.println("环绕通知:在目标方法之后,提交事务。");
        //如果业务需要,更改结果
        if(result != null){
            result = "ycx 好帅";
        }
        return result;

    }

在这里插入图片描述

  1. @AfterThrowing

     异常通知方法的定义格式
     1.public
     2.没有返回值
     3.方法名称自定义
     4.方法可以没有参数,如果有是JoinPoint
     属性:
     1.value:切入点表达式
     2.Throwing 自定义的变量,表示目标方法抛出的异常对象,变量名必须和方法的参数名一样
     特点:
     1.在目标方法抛出异常时执行
     2.可以做异常的监控程序。
    
  2. joinpoint的另一种使用
    当使用Pointcut定义在一个方法的上面,此时这个方法的名称就是切入点表达式的别名
    其他的通知中,value属性就可以使用这个方法名称,代替了切入点表达式

@Aspect
public class MyAspect {

    @After(value = "myJoinPoint()")
    public void myAround() {
        System.out.println("执行了After方法");
    }
    @Before(value = "myJoinPoint()")
    public void myBefore(){
        System.out.println("执行了Before方法");
    }
    /*
    因为多次使用了切入点表达式是重复的。可以复用的,那么可以使用PointCut
    属性:
    1.value:切入点表达式

    位置:
    在方法的上面

    特点:
	当使用JoinPoint定义在一个方法的上面,此时这个方法的名称就是切入点表达式的别名
    其他的通知中,value属性就可以使用这个方法名称,代替了切入点表达式
     */
    @Pointcut(value = "execution(* *..SomeServiceImpl.doAfter(..))")
    public void myJoinPoint(){
        //无需代码
    }
}
  1. @After

     最终通知:总是会被执行的代码。
     1.public
     2.没有返回值
     3.方法名称自定义
     4.方法可以没有参数,如果有是JoinPoint
     属性:
     1.value:切入点表达式
     位置:
     在方法的上面
     特点:
     1.总是会执行
     2.在目标方法之后执行的 
    

当目标类没有接口的时候spring就会默认使用cglib的方式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值