【框架】--- Spring

前言

本文章意在于尽量完整详细的对 Spring 进行整理、总结,为以后备忘。也尽力让新来者入门 Spring ,内容不尽详细处还望补充。详尽内容请参考Spring官网。暂未完结…

Spring概述

Spring 是由 Rod Johnson 发起,于2004年3月24日,发布了1.0正式版,是一款开源免费的轻量级框架。致力于解决企业级编程开发中的复杂性,实现敏捷开发的应用框架,其本质是一种容器(对象容器和框架容器)。

Spring由七大模块组成,核心功能是 Ioc(控制反转)与 Aop( 面向切面),其中 Ioc 是将底层对象交由Spring容器进行创建和管理,将我们从管理复杂对象依赖的工作中解脱出来。

Srping 也是一种框架容器,Spring可以轻易的和各种框架进行整合,有了Spring的支持我们可以仅以简单的配置和组件就可以轻松整合其他框架,从而使开发复杂应用变得简单高效。

Spring优点

优点:
使用Spring构建的应用易于测试,维护,可以轻松的与其他框架进行整合,降低了对象管理的复杂性和耦合性,

Spring组成部分

Spring 由七大模块组成,每一个模块都可以进行单独使用。Spring模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式。

Spring Core

Spring Core 模块是Spring的核心容器,提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。

Spring AOP

通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。

Spring ORM

负责框架中对象关系映射,提供相关ORM 接入框架的关系对象管理工具。Spring 框架插入了若干个ORM框架,从而提供了 ORM 的对象关系工具,其中包括JDO、Hibernate和iBatisSQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。

Spring DAO

JDBCDAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。

Spring Context
Spring 上下文是一个配置文件,向 Spring框架提供上下文信息。Spring 上下文包括企业服务,例如JNDI、EJB、电子邮件、国际化、校验和调度功能。

Spring Web

Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。

Spring Web MVC

MVC框架是一个全功能的构建 Web应用程序的 MVC 实现。通过策略接口,MVC框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。模型由javabean构成,存放于Map;视图是一个接口,负责显示模型;控制器表示逻辑代码,是Controller的实现。Spring框架的功能可以用在任何J2EE服务器中,大多数功能也适用于不受管理的环境。Spring 的核心要点是:支持不绑定到特定 J2EE服务的可重用业务和数据访问对象。毫无疑问,这样的对象可以在不同J2EE 环境(Web 或EJB)、独立应用程序、测试环境之间重用

使用Spring框架

Spring作为一个框架当然是以 jar 包的形式存在,可以使用 Maven 或是在官网下载。

IOC 功能

Ioc 是将底层对象交由Spring容器进行创建和管理,使得程序中对象的耦合性既降低将我们从管理复杂对象依赖的工作中解脱出来。

下载依赖

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

Spring Context 中包含了以下内容
在这里插入图片描述

使用 xml 创建对象

使用xml文件声明要创建的对象如果对象没有属性则使用单目标签----->在测试类中创建容器对象----->从容器中获取指定的对象----->使用对象。如果要将对象交由Spring创建或管理,则必须在xml文件中或使用注解进行声明,未进行声明的对象不会被Spring创建和管理。

1.创建类
既然 IOC 是为我们创建对象的,那么首先我们应该有一个被创建对象的类。

public class User {

}

2.创建配置文件
Spring 中使用配置文件配置 IOC 要创建的对象。

<?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">
    <!--
        id: 自定义对象别名
        class:对象的权限定类名
    -->
    <bean id="go" class="org.example.User"/>

</beans>

3.获取对象
从容器中取得创建好的对象

 @Test
    public void test(){
        /*配置文件位置*/
        String config = "beans.xml";
        /*创建容器对象*/
        ClassPathXmlApplicationContext cpxac
                = new ClassPathXmlApplicationContext(config);
        /*获取对象*/
        User u = (User) cpxac.getBean("user");
        
        //获取容器中的Bean的个数
        cpxac.getBeanDefinitionCount();
        //获取容器中所有Bean的名称
        cpxac.getBeanDefinitionNames();
        
    }

在xml中为属性赋值

在创建对象时有些需要为属性赋值,为属性赋值有两种方式:配置文件赋值,注解赋值 以下为赋值概述。

创建一个有属性的类:

package org.example;
public class User {
     private int id;
     private String name;
     private int age;
     private  Object obj;
     private  Object obj_1;
     
      public User(int id, String name, int age, Object obj, Object obj_1) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.obj = obj;
        this.obj_1 = obj_1;
    }

	//省略set 和 get  方法...

}

使用配置文件赋值

在xml文件中使用双目 bean 标签在标签中使用子标签声明属性。
使用配置文件赋值又分为两种方式:set注入构造方法注入

set注入:

set注入使用的是类中属性的set方法进行赋值,如果类中没有set 方法在xml文件中尝试赋值时IDEA会发出 xml 错误警告。且set方法应遵守属性set方法的命名规范,即:set方法的方法名应为:set + 属性名首字母大写,如果未遵守此方法,则无法进行赋值,此方法适合为有set方法的属性赋值。

实验得出在使用set注入时为属性赋值时,Spring并未检查类中是否有此属性,而是直接使用了 <property>标签中的属性name来查找类中关于此属性的set方法,使用反射获取此类的set方法进行赋值。因此可以利用这一点可以用来做一些额外的事情。

Spring 在对属性进行赋值时分为 基本类型赋值和引用类型赋值。基本数据类型在赋值时使用 value指定属性 而 引用类型则使用 ref进行赋值,且、此引用类型的对象创建需已经在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">
    <!--
    bean标签属性:
        id: 自定义对象别名
        class:对象的权限定类名
    property标签属性:
        name:对象的属性的名称
        value:对象属性的值
    -->
    <bean id="user" class="org.example.User">
        <property name="id" value="0001" />
        <property name="age" value="20"/>
        <property name="name" value="张三"/>
        <property name="obj" ref="obj"/>
    </bean>
    
 	<!--创建obj对象-->
    <bean id="obj" class="java.lang.Object"/>

</beans>

创建容器对象获取属性省略 …

构造注入

使用类的有参数的构造方法在对象创建的同时为属性赋值。

构造注入是在 xml 文件中的 <bean>中声明的<constructor-arg/>子标签内进行赋值,在此子标签中使用标签的属性进行属性赋值,一个标签只能对构造方法中的一个形参进行赋值,与set注入相同的是 形参中的引用类型 依然使用的是ref标签属性。

<?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:对象的权限定类名
    constructor-arg属性:
        name:构造方法中的参数的名字
        value: 构造方法中的参数的简单类型的值
        ref: 构造方法中的参数的引用类型的值
        index:当前属性在构造方法中的参数的位置,从0开始
        
        type属性和name属性可以省略,index也可以,但是在声明时需要按
        照形式参数列表中的顺序声明。
    -->
    <bean id="user" class="org.example.User">
        <constructor-arg name="id" value="0001" index="0"  />
        <constructor-arg name="age" value="18" index="2" />
        <constructor-arg name="name" value="18" index="1" />
        <constructor-arg name="obj" ref="obj" index="3" />
    </bean>

    <!--创建obj对象-->
    <bean id="obj" class="java.lang.Object"/>

</beans>

自动注入:

使用自动注入的方式为引用类型的属性赋值,可以简化xml 语句的书写量,使得xml 文件更加简洁。

自动注入并非和set注入以及构造注入是同一级别的,自动注入仅仅为了简化在xml 中对引用类型赋值时声明的大量语句,因为在对引用类型赋值时 xml 中已经声明了此对象的创建,可以使用一种方式使Spring自动判断属性的值是xml文件中的那个对象创建标签。从而为引用属性赋值。

使用<bean/> 标签中的 autowire 属性指定 自动注入的方式,根据xml文件中对引用类型其值的对象声明表示,来为此对象中的所有引用类型赋值。

自动注入有四个值可以选:

  1. byName 根据名称注入

    根据xml中声明对象时对此对象的自定义别名(与类中属性的别名须一致)对属性进行赋值。

  2. byType 根据类型注入

    根据xml中生命的所有引用类型的数据类型进行匹配从而进行赋值。

  3. constructor 使用构造方法

    使用引用类型的构造函数创建对象,并使用其值对属性赋值(精准获取)

  4. default 默认的

    由上级标签的 default-autowire 属性确定。

  5. no 不使用自动注入

只展示了一种情况其他情况仅值不同。

<?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:对象的权限定类名
    autowire属性值:
        byName      根据名称注入
        byType      根据类型注入
        constructor 使用构造方法
        default     默认的
        no          不使用自动注入
    -->
    <bean id="user" class="org.example.User" autowire="byName">
        <property name="name" value="zs"/>
        <property name="age" value="15"/>
        <property name="id" value="0001"/>
    </bean>
    <!--创建obj对象-->
    <bean id="obj" class="java.lang.Object"/>
    <bean id="obj_1" class="java.lang.Object"/>

</beans>

多配置文件

为了读写速度也为了简洁明了和避免对xml文件的误操作,因此为们应为每个模块单独创建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">
   	
   	 <!--
   	 	可以使用通配符
   	 	列如: classpath:Spring-*.xml
   	 -->
    <import resource="classpath:beans.xml"/>

</beans>

使用注解创建对象

使用注解来创建对象比使用xml创建对象更加方便,代码量更少。

创建对象的注解:

以下几个注解都可以创建对象,区别在于会被Spring做不同的识别。

注解概述属性
@Component在创建组件时使用value(声明别名)
@Service使用在service层value(声明别名)
@Controller使用在Controller层value(声明别名)
@Repository使用在 Repository层value(声明别名)

1. 声明注解:

@Component(value = "User" )
public class User {
     private int id;
     private String name;
     private int age;
     private  Object obj;
     private  Object obj_1;

    public User() {}

    public User(int id, String name, int age, Object obj, Object obj_1) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.obj = obj;
        this.obj_1 = obj_1;
    }

2.创建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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--
        使用组件扫描器声明使用了注解要创建对象的类所在的目录和子目录
		多个包使用指定父包或使用;隔开
    -->
    <context:component-scan base-package="org.example" />

</beans>

3.使用对象

  @Test
    public void test(){
    String config = "ApplicationContext.xml";
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext(config);
        User user = (User)applicationContext.getBean("User");
        System.out.println(user);

    }

为属性赋值

使用注解为属性赋值。使用以下几个注解:

为简单类型赋值:

注解概述属性
@Value为简单类型属性赋值,可以在属性上声明(此时不需要set方法)或set方法上声明value(要赋的值)

为引用类型赋值:

为引用类型赋值时需要在容器中已经创建了此对象。对要被作为值的对象使用注解创建对象,将对象由Sprting管理。

注解概述属性使用位置
@Autowired默认使用的byName方式required(fales时不会报错,不对属性赋值)set方法和属性
@Qualifier和上面的注解组合使用,表示使用指定名称的对象为属性赋值,value(指定对象的名称)set方法和属性
@ResourcebyName和byType都支持默认byName,如果使用byName失败则尝试使用byType赋值name(表示使用byName,指定对象的名称)set方法和属性

AOP

AOP 为面向切面编程。即在某段代码执行的前后插入特定代码使其拥有额外的功能。原理是使用动态代理的方式。在Spring中这些增强被称为通知,有五种通知,分别表示了在代码的某个顺序执行。使用AOP可以解耦合,增强,减少重复的代码。专注业务逻辑。
官方文档中的AOP在第5章

切面思想

我们在执行某个方法时通常并不是仅仅只有这一个方法执行,而是方法中调用其他方法,其他方法再去调用另一个方法,如此向下调用,直到硬件为我们运算。因此这一串Java代码每一个独立的方法都可以被抽象为一个切片,犹如一屉饼干,在一个切面执行的某个时间点插入其他代码使得方法增强。

面向切面编程: 即在将原始的方法抽象为切面,在不改动原始代码的前提下依然可以对原始代码进行扩展,创建一些通用的方法作为切片插入其中可以代码复用,减少耦合度,在迭代时使我们更加专注与业务而不是原始代码。

术语

了解术语可以使我们使用同一种思想描述一个事物。

  1. 切面(Aspect)
    要进行增强的具体代码。

  2. 切入点 (Pointcut)
    多个连接点方法的集合

  3. 目标 (Target)
    被增强者,被代理者。

  4. 连接点(Join Point)
    连接目标和切面的位置

  5. 通知 (Advice)
    增强方法相对于目标方法的执行时间

通知类型

通知类型是指在目标方法执行时刻的某个时刻执行通知

  1. 前置通知 (Before)
    在目标方法执行之前执行执行增强

  2. 后置通知 (AfterReturning)
    在目标方法执行之后执行执行增强

  3. 环绕通知 (Around )
    可以在目标方法执行执行前和执行后执行增强

  4. 最终通知 (After)
    在目标方法执行完成后一定会执行增强,无论是否发生异常

  5. 异常通知 (AfterThrowing)
    可以在目标方法发生异常时执行增强

使用 AOP

AOP有两种声明方式: XML 方式注解方式。

注解方式使用的是 Aspectj 框架。而配置文件则使用的是Spring AOP。请查看这篇Spring与Aspectj了解其中关系,感谢这位作者的贡献。

在Spring中源码代表了被代理者,Cglib负责进行代理,而Aspectj负责切面

注解方式

注解的方式是指使用切入点表达式在其中声明切面方法的定义。通过反射读取注解获得切面方法的定义,从而增强目标。

当目标类有接口时默认使用的是JDK动态代理来代理,但也可以使用XML配置文件设置为使用Cflib动态代理,而没有接口时使用的是Cglib 来代理。

使用注解的方式需要添加 Asperctj jar包,并且将对象交由Spring管理。

	<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.2.6.RELEASE</version>
    </dependency>
切入点表达式

切入点表达式是切入点的表现形式,是以注解的方式来表示的,切入点表达式中声明了目标类的方法定义。

语法:

切面方法的形参

在切面方法的形参处使用Joinpoint作为形参列表的第一个参数可以获取目标方法的方法信息,如方法名,方法参数等。

表达式符号

表达式符号是指表达式中允许出现的符号。

符号释义
*0~任意 个字符
..在方法参数中使用时,表示任意个参数。在包名后使用时表示当前包和子包路径。
+在类名后使用表示当前类和子类。在接口后使用表示当前接口和其实现类。
表达式语法

表达式是通知注解的一个属性,此属表示了对目标方法的匹配粒度的选择,以下例子中的execution为粒度最小最为精准的匹配属性。
更多请参考 Spring 官方文档5.4.3章节了解更多粒度。

表示可选内容,此部分内容可有可无。

execution(访问权限类型? 返回值类型 包名类名? 方法名(参数类型 参数个数) 抛出的异常 )

在切入前表达式中还可以使用||&&运算符,对多个方法定义灵活的进行匹配。

前置通知

前置通知在目标方法执行之前执行切面。

注解:@Before
示例: @Before(value = "切入点表达式" argNames=“”)

后置通知

注解: @AfterReturning
示例: @AfterReturning(value = "切入点表达式" returning =“参数名” pointcut=“” argNames=“”)
独有的参数: returning 一个存储目标方法执行结果的变量名,在切面方法中添加一个变量来接收执行结果,变量名必须与切面方法的参数名相同。

环绕通知

环绕通知可以在目标方法前后前后都进行增强,可以控制目标方法是否被调用执行,修改目标方法的执行结果。
关于环绕通知中切面类的ProceedingJoinPoint参数请参考官方文档 Access to the Current JoinPoint章节

注解: @Around
示例: @Around(value = "切入点表达式" argNames=“”)

异常通知

异常通知是在目标类法生了指定异常时才会执行切面类中的通知方法,异常在表达式中声明。列如监控一个方法是否发生了异常如果发生异常可以做一些事情,如发送邮件短信等通知。

注解:@AfterThrowing
示例:@AfterThrowing(value = "切入点表达式" throwing=“切面方法中声明的异常参数的参数名”)
独有的参数: throwing参数表示了目标对象所发生的异常,此值将会被赋值给切面方法中同名的异常参数,用来使用此异常对象。

最终通知

最终通知总是会执行,无论是否发生异常,通常用来做程序的清理工作,列如在程序执行结束后释放内存资源等。

注解: @After
示例:@After(value = "切入点表达式" argNames=“”)

自定义切入点

当一个切面类中有重复的切入点表达式时(表达式value参数相同),可以使用 @Pointcut 注解将重复的切入点表达式的value的值 使用一个方法名作为别名代替value中的值,将方法的别名作为通知value属性的值,达到代码复用的效果

注解:@Pointcut
示例:@Pointcut (value = “重复表达式的重复的值” )

例子

例子按照步骤演示了 Spring AOP 的使用过程。

  1. 添加依赖
	<dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.2.6.RELEASE</version>
    </dependency>

2.创建目标类

package org.aop.xml;

public class Target{
    /*前置通知目标方法*/
    public void targetBefore(){
        System.out.println("前置通知的目标方法执行");
    }
    /*后置通知目标方法*/
    public int targetAfterReturning(int a){
        System.out.println("后置通知的目标方法执行");
        return a;
    }
    /*环绕通知目标方法*/
    public int targetAround(int a,int b){
        System.out.println("环绕通知的目标类执行");
        return a+b;
    }
    /*异常通知目标方法*/
    public void targetAfterThrowing() throws Exception {
        throw new Exception();
    }
    /*最终通知目标方法*/
    public void targetAfter(){
        System.out.println("最终通知的目标类类执行了");
    }
}


3.创建切面类

package org.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.SourceLocation;

/**
 *  创建切面类
 */
@Aspect //声明切面类所必须!!!
public class AspectTest {

    /**
     * 前置通知
     * 前置通知在目标方法执行之前执行
     *
     * 方法定义
     * 1.pulic修饰
     * 2.没有返回值
     * 3.参数可有可无,有参数时参数不能自定义,使用JoinPoint
     */
    @Before(value = "execution(public void org.aop.Target.targetBefore())")
     public void myBefore(){
        System.out.println("前置通知的切面方法执行");
    }
    /**
     * 后置通知:
     * 后置通知在目标方法执行后执行,并且可以获取到目标方法的执行结果。
     *
     * 方法定义:
     * 1.public修饰
     * 2.void返回值
     * 3.参数可有可无。可以有一个参数存储目标方法返回值
     *
     * @param i 目标方法的执行结果
     */
    @AfterReturning(value = "execution(public int org.aop.Target.targetAfterReturning(int) )",
            returning = "i")
    public void myAfterReturning (int i){
        //在这里可以使用i变量做一些事情...
        System.out.println("后置通知的切面类执行");
    }
    /**
     * 环绕通知:
     * 环绕通知可以在目标方法执行前和后都进行增强
     *
     * 方法定义:
     * 1.public修饰
     * 2.必须有返回值Object
     * 3.方法参数必须为ProceedingJoinPoint
     *
     * @param pj 通过此对象可以获取目标方法的一些信息
     */
    @Around(value = "execution(public int org.aop.Target.targetAround(int,int))")
    public Object myAround(ProceedingJoinPoint pj) throws Throwable {
        System.out.println("环绕通知——前");
        /*ProceedingJoinPoint代表了目标方法*/
        Object o = pj.proceed();
        /*修改目标参数的结果*/
        int a = (Integer) o + 100;
        System.out.println("环绕通知——后");
        return a;
    }
      /**
       *  异常通知:
       *  在目标方法发生异常时执行此切面方法
       *
       *  方法定义:
       *  1.public 修饰
       *  2.没有返回值
       *  3.有一个Exception属性,如果有则为JoinPoint
       *
       * @param e 指向了目标方法发生的异常对象
       */
    @AfterThrowing (value = "execution(public void org.aop.Target.targetAfterThrowing())",
            throwing = "e")
    public void myAfterThrowing(Exception e) {
        /*使用异常对象*/
        String message = e.getMessage();
        System.out.println("发生了"+e+"异常");
        //发送邮件短信之类...
      }
    /**
     *  最终通知
     *  无论目标方法是否发生异常,此切面类都会在目标方法之后执行。
     *
     *  方法定义
     *  1.public修饰
     *  2.没有返回值
     *  3.没有属性
     */
    @After(value = "execution(public void org.aop.Target.targetAfter())")
    public void myAfter(){
        System.out.println("最终通知的切面方法被执行");
    }
    /**
     * 自定义切入点
     *
     * 方法定义
     * 1.public修饰
     * 2.void返回值
     * 3.无参数
     * 4.方法体无内容
     */
    //例如 @Pointcut注解声明了重复的表达式的值,使用此切面方法名作为其他表达式value的值
    @Pointcut(value = "execution(public int org.aop.Target.targetAfter())")
    public void myPointcut(){}

}

4.创建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:context="http://www.springframework.org/schema/context"
       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/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop 
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--声明目标对象-->
    <bean id="target" class="org.aop.Target"/>
    <!--声明切面类对象-->
    <bean id="aspect" class="org.aop.AspectTest"/>
    <!--声明Aspects 自动代理生成器(注解方式不需要)-->
    <aop:aspectj-autoproxy/>

    <!--如果希望有接口时任然使用Cglib动态代理-->
    <!--<aop:aspectj-autoproxy proxy-target-class="true"/>-->

</beans>

5.创建测试类

package org.aop;

import static org.junit.Assert.assertTrue;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class AppTest {

    @Test
    public void test() throws Exception {

    String config = "ApplicationContext.xml";
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext(config);
    Target target = (Target) applicationContext.getBean("target");

    /*前置通知*/
    target.targetBefore();

    /*后置通知*/
    target.targetAfterReturning(10);

    /*环绕通知*/
    int i = target.targetAround(50, 60);
    System.out.println(i);//目标参数的结果被改变了

    /*异常通知*/
    target.targetAfterThrowing();

    /*最终通知*/
    target.targetAfter();
    }

}

XML方式

XML 的方式使业务代码与 AOP 表达式完全分离在大型项目中尤为合适。
XML 的方式重点在于XML文件,与注解不同的是切面类中不需要@Aspect,而切面表达式在XML文件中声明。

例子
  1. 创建目标类
    省略…与使用注解方式中的目标类相同

  2. 创建切面类

package org.aop.xml;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

/**
 *  创建切面类
 */

public class AspectTest {

    /*前置通知切面方法*/
    public void myBefore(){
        System.out.println("前置通知的切面方法执行");
    }
    /*后置通知切面方法*/
    public void myAfterReturning (int i){
        //在这里可以使用i变量做一些事情...
        System.out.println("后置通知的切面类执行");
    }
    /*环绕通知切面方法*/
    public Object myAround(ProceedingJoinPoint pj) throws Throwable {
        System.out.println("环绕通知——前");
        /*ProceedingJoinPoint代表了目标方法*/
        Object o = pj.proceed();
        /*修改目标参数的结果*/
        int a = (Integer) o + 100;
        System.out.println("环绕通知——后");
        return a;
    }
    /*异常通知切面方法*/
    public void myAfterThrowing(Exception e) {
        /*使用异常对象*/
        String message = e.getMessage();
        System.out.println("发生了"+e+"异常");
        //发送邮件短信之类...
    }
    /*最终通知切面方法*/
    public void myAfter(){
        System.out.println("最终通知的切面方法被执行");
    }


}

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

    <!--声明目标对象-->
    <bean id="target" class="org.aop.xml.Target"/>
    <!--声明切面类对象-->
    <bean id="aspect" class="org.aop.xml.AspectTest"/>

    <!--如果希望有接口时任然使用Cglib动态代理-->
    <!--<aop:aspectj-autoproxy proxy-target-class="true"/>-->

    <aop:config>

       <!--
            声明切入点
            切入点表达式声明了目标类的位置,证明了切入的位置点。
            可以将此标签声明在<aop:aspect/>下。

            使用 <aop:pointcut/> 标签来声明切入点。使用其中属性来指定目标类和切入点

            属性:
            id:此切入点的自定义别名。
            expression:切入点表达式。

       -->
        <aop:pointcut id="targetBefore_pointcut" expression="execution(public void org.aop.xml.Target.targetBefore())" />
        <aop:pointcut id="targetAfterReturning_pointcut" expression="execution(public int org.aop.xml.Target.targetAfterReturning(int))"/>
        <aop:pointcut id="targetAround_pointcut" expression="execution(public int org.aop.xml.Target.targetAround(int,int))"/>
        <aop:pointcut id="targetAfterThrowing_pointcut" expression="execution(public void org.aop.xml.Target.targetAfterThrowing())"/>
        <aop:pointcut id="targetAfter_pointcut" expression="execution(public void org.aop.xml.Target.targetAfter())"/>

        <!--
            切面
            切面使用 <aop:aspect/> 标签来表示,使用此标签来声明切面类,并在此标签中声明子标签:"通知标签"。
            在"通知标签"中声明切面方法、表达式、返回值、参数等完成通知与切面方法的绑定。
            代表了通知注解中的@Pointcut注解。

            属性:
            id:切面类对象的别名
            ref:目标对象的别名
            order:

        -->
        <aop:aspect id="aspectTest" ref="aspect"  >
            <!--
                前置通知

                属性:
                method:为前置通知的切面方法。
                pointcut-ref:已经声明好的切面表达式标签的别名
                pointcut:切面表达式
                arg-names:参数名称(多个参数名使用,隔开)
            -->
            <aop:before method="myBefore" pointcut-ref="targetBefore_pointcut" />
            <!--
                后置通知

                属性:
                method:为前置通知的切面方法。
                pointcut-ref:已经声明好的切面表达式标签的别名
                pointcut:切面表达式
                arg-names:参数名称(多个参数名使用,隔开)
                returning(独有属性):表示目标方法的返回值,需要与切面方法中
            -->
            <aop:after-returning method="myAfterReturning" pointcut-ref="targetAfterReturning_pointcut" returning="i"/>
            <!--
                环绕通知

                属性:
                method:为前置通知的切面方法。
                pointcut-ref:已经声明好的切面表达式标签的别名
                pointcut:切面表达式
                arg-names:参数名称(多个参数名使用,隔开)

            -->
            <aop:around method="myAround" pointcut-ref="targetAround_pointcut"/>
            <!--
                异常通知

                属性
                method:为前置通知的切面方法。
                pointcut-ref:已经声明好的切面表达式标签的别名
                pointcut:切面表达式
                arg-names:参数名称(多个参数名使用,隔开)
                throwing:(独有属性)一个属性名,表示目标类所发生的异常对象,需要与切面方法中的参数同名。
            -->
            <aop:after-throwing method="myAfterThrowing" pointcut-ref="targetAfterThrowing_pointcut" throwing="e" />
            <!--
                最终通知

                参数
                method:为前置通知的切面方法。
                pointcut-ref:已经声明好的切面表达式标签的别名
                pointcut:切面表达式
                arg-names:参数名称(多个参数名使用,隔开)
            -->
            <aop:after method="myAfter" pointcut-ref="targetAfter_pointcut" />
        </aop:aspect>

    </aop:config>

</beans>

  1. 创建测试
省略...与使用注解方式中的测试类相同(注意包名!)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值