spring学习笔记

1、Spring概述


简介

Spring 是最流行的企业 Java 应用程序开发框架。 全球数以百万计的开发人员使用 Spring 框架来创建高性能、易于测试和可重用的代码。

Spring 框架是一个开源的 Java 平台。 它最初由 Rod Johnson 编写,并于 2003 年 6 月在 Apache 2.0 许可下首次发布。

在大小和透明度方面,Spring 是轻量级的。 Spring 框架的基本版本约为 2MB。

Spring 框架的核心特性可用于开发任何 Java 应用程序,但也有一些扩展可用于在 Java EE 平台之上构建 Web 应用程序。 Spring 框架旨在通过启用基于 POJO 的编程模型使 J2EE 开发更易于使用并促进良好的编程实践。

使用 Spring 框架的好处

以下是使用 Spring 框架的几个巨大好处的列表

  • Spring 使开发人员能够使用 POJO 开发企业级应用程序。 仅使用 POJO 的好处是您不需要 EJB 容器产品(例如应用程序服务器),但您可以选择仅使用健壮的 servlet 容器(例如 Tomcat 或某些商业产品)。

  • Spring 以模块化方式组织。 即使包和类的数量很多,您也只需要担心您需要的,而忽略其余的。

  • Spring 并没有重新发明,而是真正利用了一些现有的技术,如几个 ORM 框架、日志框架、JEE、Quartz 和 JDK 计时器以及其他视图技术。

  • 测试使用 Spring 编写的应用程序很简单,因为依赖于环境的代码已移至此框架。 此外,通过使用 JavaBeanstyle POJO,更容易使用依赖注入来注入测试数据。

  • Spring 的 web 框架是一个设计良好的 web MVC 框架,它为 Struts 等 web 框架或其他过度设计或不太流行的 web 框架提供了一个很好的替代方案。

  • Spring 提供了一个方便的 API 来将特定于技术的异常(例如,由 JDBC、Hibernate 或 JDO 抛出)转换为一致的、未经检查的异常。

  • 轻量级 IoC 容器往往是轻量级的,例如,与 EJB 容器相比时尤其如此。 这有利于在内存和 CPU 资源有限的计算机上开发和部署应用程序。

  • Spring 提供了一个一致的事务管理接口,可以缩小到本地事务(例如使用单个数据库)并扩展到全局事务(例如使用 JTA)。


依赖注入 (DI)

Spring 最受认同的技术是控制反转的 依赖注入 (DI)  风格。  Inversion of Control (IoC)  控制反转是一个通用概念,可以用多种不同的方式表达。 依赖注入只是控制反转的一个具体例子。

在编写复杂的 Java 应用程序时,应用程序类应尽可能独立于其他 Java 类,以增加重用这些类的可能性,并在单元测试时独立于其他类对其进行测试。 依赖注入有助于将这些类粘合在一起,同时保持它们的独立性。

究竟什么是依赖注入? 我们分别来看这两个词。 这里依赖部分转化为两个类之间的关联。 例如,A 类依赖于 B 类。现在,我们来看第二部分,注入。 这意味着,B 类将被 IoC 注入到 A 类中。

依赖注入可以通过将参数传递给构造函数的方式发生,也可以通过使用 setter 方法的构造后发生。 由于依赖注入是 Spring 框架的核心,我们将在单独的章节中用相关示例解释这个概念。


面向方面的编程(AOP)

Spring 的关键组件之一是 面向方面编程 (AOP)  框架。 跨越应用程序多个点的功能称为 横切关注点 ,这些横切关注点在概念上与应用程序的业务逻辑分开。 有各种常见的好例子,包括日志记录、声明式事务、安全性、缓存等。

OOP 中模块化的关键单元是类,而 AOP 中模块化的单元是方面。 DI 可帮助您将应用程序对象彼此分离,而 AOP 可帮助您将横切关注点与它们影响的对象分离。

Spring 框架的 AOP 模块提供了面向方面的编程实现,允许您定义方法拦截器和切入点,以清晰地解耦实现应该分离的功能的代码。 我们将在单独的章节中讨论更多关于 Spring AOP 的概念。

2、Spring架构


Core 容器

Core 容器由 Core、Beans、Context 和 Expression Language 模块组成,详细信息如下 −

  • Core  模块提供了框架的基本部分,包括 IoC 和依赖注入功能。

  • Bean  模块提供了 BeanFactory,它是工厂模式的一个复杂的实现。

  • Context  模块建立在 Core 和 Beans 模块提供的坚实基础之上,它是访问任何已定义和配置的对象的媒介。 ApplicationContext 接口是上下文模块的焦点。

  • SpEL  模块提供了一种强大的表达式语言,用于在运行时查询和操作对象图。


数据访问/集成

数据访问/集成层由 JDBC、ORM、OXM、JMS 和 Transaction 事务模块组成,详细信息如下 −

  • JDBC  模块提供了一个 JDBC 抽象层,它消除了繁琐的 JDBC 相关编码的需要。

  • ORM  模块为流行的对象关系映射 API 提供集成层,包括 JPA、JDO、Hibernate 和 iBatis。

  • OXM  模块提供了一个抽象层,它支持 JAXB、Castor、XMLBeans、JiBX 和 XStream 的 Object/XML 映射实现。

  • Java 消息传递服务  JMS  模块包含用于生成和使用消息的功能。

  • Transaction  模块支持对实现特殊接口的类和所有 POJO 的编程和声明式事务管理。


Web

Web 层由 Web、Web-MVC、Web-Socket 和 Web-Portlet 模块组成,具体如下 −

  • Web  模块提供了基本的面向 Web 的集成功能,例如多部分文件上传功能以及使用 servlet 侦听器和面向 Web 的应用程序上下文初始化 IoC 容器。

  • Web-MVC  模块包含 Spring 的 Web 应用程序的 Model-View-Controller(模型-视图-控制器) (MVC) 实现。

  • Web-Socket  模块支持 Web 应用程序中客户端和服务器之间基于 WebSocket 的双向通信。

  • Web-Portlet  模块提供了在portlet 环境中使用的MVC 实现,并反映了Web-Servlet 模块的功能。


杂项

其他重要的模块很少,如 AOP、Aspects、Instrumentation、Web 和 Test 模块,具体如下 −

  • AOP  模块提供了一个面向方面的编程实现,允许您定义方法拦截器和切入点,以清晰地解耦实现应该分离的功能的代码。

  • Aspects  模块提供与 AspectJ 的集成,AspectJ 又是一个强大且成熟的 AOP 框架。

  • Instrumentation  模块提供类检测支持和类加载器实现以用于某些应用程序服务器。

  • Messaging  模块支持将 STOMP 作为 WebSocket 子协议在应用程序中使用。 它还支持注解编程模型,用于路由和处理来自 WebSocket 客户端的 STOMP 消息。

  • Test  模块支持使用 JUnit 或 TestNG 框架对 Spring 组件进行测试。


3、Spring快速入门

user类

 @Data  
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private int age;
}

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

<bean id="user" class="com.zh.pojo.User">
<property name="age" value="18"/>
<property name="name" value="张三"/>
</bean>
</beans>

测试

 public class Test01 {  
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");

User user = applicationContext.getBean("user", User.class);

System.out.println(user);
}
}

4、IOC

构成应用程序主干并由 Spring IoC 容器管理的对象称为  beans 。 bean 是由 Spring IoC 容器实例化、组装和管理的对象。 这些 bean 使用您提供给容器的配置元数据创建。 例如,以 XML 的形式。 前面章节中已经看到的定义。

Bean定义中包含了称为 配置元数据 的信息,容器需要知道以下信息 −

  • 如何创建一个 bean
  • Bean 的生命周期详情
  • Bean 的依赖关系

上述所有配置元数据都转换为一组构成每个 bean 定义的以下属性。

属性 & 描述

1 class

该属性是必需的,它指定了用于创建 bean 的 bean 类。

2 id

此属性唯一地指定 bean 标识符。 在基于 XML 的配置元数据中,您使用 id 或者 name 属性来指定 bean 标识符。

3 scope

此属性指定从特定 bean 定义创建的对象的范围,将在 bean 范围一章中讨论。

4 constructor-arg

这用于注入依赖关系,将在后续章节中讨论。

5 properties

这用于注入依赖关系,将在后续章节中讨论。

6 autowiring mode

这用于注入依赖关系,将在后续章节中讨论。

7 lazy-initialization mode

延迟初始化的 bean 告诉 IoC 容器在第一次被请求时创建一个 bean 实例,而不是在启动时。

8 initialization method

在容器设置了 bean 上的所有必要属性之后调用的回调。 它将在 bean 生命周期章节中讨论。

9 destruction method

当包含 bean 的容器被销毁时要使用的回调。 它将在 bean 生命周期章节中讨论。


5、Spring - Bean

序号

作用域 & 描述

1 singleton

这将 bean 定义范围限定为每个 Spring IoC 容器的单个实例(默认)。

2 prototype

这将单个 bean 定义的范围限定为具有任意数量的对象实例。

3 request

这将 bean 定义的范围限定为 HTTP 请求。 仅在 Web 感知 Spring ApplicationContext 的上下文中有效。

4 session

这将 bean 定义范围限定为 HTTP 会话。 仅在 Web 感知 Spring ApplicationContext 的上下文中有效。

5 global-session

这将 bean 定义范围限定为全局 HTTP 会话。 仅在 Web 感知 Spring ApplicationContext 的上下文中有效。

singleton 作用域

如果作用域设置为 singleton,则 Spring IoC 容器会创建该 bean 定义所定义的对象的一个实例。 此单个实例存储在此类单例 bean 的缓存中,并且该命名 bean 的所有后续请求和引用都返回缓存的对象。 单例在容器初始化的时候创建

 <bean id = "..." class = "..." scope = "singleton">

</bean


prototype 作用域

如果作用域设置为prototype,则每次对特定 bean 发出请求时,Spring IoC 容器都会创建对象的新 bean 实例。 通常,对所有状态完整的 bean 使用 prototype 作用域,对无状态 bean 使用 singleton 作用域。 在取对象的时候创建

 <bean id = "..." class = "..." scope = "prototype">

</bean>


bean的初始化回调和销毁回调

在基于 XML 的配置元数据的情况下,您可以使用  init-method  和 destroy-method 属性来指定具有 void 无参数签名的方法的名称。

另外还有实现接口的方式

 public class User {  
private String name;
private int age;

public void init(){
System.out.println("创建了一个user对象");
}
}
 <bean id="user" class="com.zh.pojo.User" init-method="init">  
<property name="age" value="18"/>
<property name="name" value="张三"/>
</bean>

默认初始化和销毁方法

如果您有太多具有相同名称的初始化和/或销毁方法的 bean,则无需在每个 bean 上声明  init-method  和  destroy-method  . 相反,框架提供了使用beans 元素上的  default-init-method  和  default-destroy-method  属性配置这种情况的灵活性,如下所示

 <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-3.0.xsd"
default-init-method = "init"
default-destroy-method = "destroy">

<bean id = "..." class = "...">
<!-- collaborators and configuration for this bean go here -->
</bean>

</beans>

bean的继承


一个 bean 定义可以包含很多配置信息,包括构造函数参数、属性值以及容器特定的信息,例如初始化方法、static factory 方法名称等。

子 bean 定义继承父定义的配置数据。 子定义可以根据需要覆盖某些值或添加其他值。

Spring Bean 定义继承与 Java 类继承无关,但继承概念是相同的。 您可以将父 bean 定义定义为模板,其他子 bean 可以从父 bean 继承所需的配置。

当您使用基于 XML 的配置元数据时,您可以通过使用  parent  属性来指示子 bean 定义,并将父 bean 指定为该属性的值

例如:

 <?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-3.0.xsd">

<bean id = "helloWorld" class = "com.tutorialspoint.HelloWorld">
<property name = "message1" value = "Hello World!"/>
<property name = "message2" value = "Hello Second World!"/>
</bean>

<bean id ="helloIndia" class = "com.tutorialspoint.HelloIndia" parent = "helloWorld">
<property name = "message1" value = "Hello India!"/>
<property name = "message3" value = "Namaste India!"/>
</bean>
</beans>
 public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");

HelloWorld objA = (HelloWorld) context.getBean("helloWorld");
objA.getMessage1();
objA.getMessage2();

HelloIndia objB = (HelloIndia) context.getBean("helloIndia");
objB.getMessage1();
objB.getMessage2();
objB.getMessage3();
}
}
 World Message1 : Hello World!
World Message2 : Hello Second World!
India Message1 : Hello India!
India Message2 : Hello Second World!
India Message3 : Namaste India!

如果您在此处观察到,我们在创建"helloIndia"bean 时没有传递 message2,但由于 Bean 定义继承,它通过了

Bean 定义模板

您可以创建一个 Bean 定义模板,它可以被其他子 bean 定义使用,而无需花费太多精力。 在定义 Bean 定义模板时,不应指定  class  属性,而应指定  abstract  属性,并应指定值为  true  的 abstract 抽象属性,如以下代码片段所示

 <?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-3.0.xsd">

<bean id = "beanTeamplate" abstract = "true">
<property name = "message1" value = "Hello World!"/>
<property name = "message2" value = "Hello Second World!"/>
<property name = "message3" value = "Namaste India!"/>
</bean>

<bean id = "helloIndia" class = "com.tutorialspoint.HelloIndia" parent = "beanTeamplate">
<property name = "message1" value = "Hello India!"/>
<property name = "message3" value = "Namaste India!"/>
</bean>

</beans>

6、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-3.0.xsd">

<!-- Definition for textEditor bean -->
<bean id = "textEditor" class = "com.tutorialspoint.TextEditor">
<constructor-arg ref = "spellChecker"/>
</bean>

<!-- Definition for spellChecker bean -->
<bean id = "spellChecker" class = "com.tutorialspoint.SpellChecker"></bean>

</beans>
 <beans>

<bean id = "exampleBean" class = "examples.ExampleBean">
<constructor-arg type = "int" value = "2001"/>
<constructor-arg type = "java.lang.String" value = "Zara"/>
</bean>

</beans>
 <beans>

<bean id = "exampleBean" class = "examples.ExampleBean">
<constructor-arg index = "0" value = "2001"/>
<constructor-arg index = "1" value = "Zara"/>
</bean>

</beans>

内部注入beans

 <?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-3.0.xsd">

<bean id = "outerBean" class = "...">
<property name = "target">
<bean id = "innerBean" class = "..."/>
</property>
</bean>

</beans>

注入集合

 <?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-3.0.xsd">

<!-- Definition for javaCollection -->
<bean id = "javaCollection" class = "com.tutorialspoint.JavaCollection">

<!-- results in a setAddressList(java.util.List) call -->
<property name = "addressList">
<list>
<value>INDIA</value>
<value>Pakistan</value>
<value>USA</value>
<value>USA</value>
</list>
</property>

<!-- results in a setAddressSet(java.util.Set) call -->
<property name = "addressSet">
<set>
<value>INDIA</value>
<value>Pakistan</value>
<value>USA</value>
<value>USA</value>
</set>
</property>

<!-- results in a setAddressMap(java.util.Map) call -->
<property name = "addressMap">
<map>
<entry key = "1" value = "INDIA"/>
<entry key = "2" value = "Pakistan"/>
<entry key = "3" value = "USA"/>
<entry key = "4" value = "USA"/>
</map>
</property>

<!-- results in a setAddressProp(java.util.Properties) call -->
<property name = "addressProp">
<props>
<prop key = "one">INDIA</prop>
<prop key = "one">INDIA</prop>
<prop key = "two">Pakistan</prop>
<prop key = "three">USA</prop>
<prop key = "four">USA</prop>
</props>
</property>
</bean>

</beans>

注入 Bean 引用

 <?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-3.0.xsd">

<!-- Bean Definition to handle references and values -->
<bean id = "..." class = "...">

<!-- Passing bean reference for java.util.List -->
<property name = "addressList">
<list>
<ref bean = "address1"/>
<ref bean = "address2"/>
<value>Pakistan</value>
</list>
</property>

<!-- Passing bean reference for java.util.Set -->
<property name = "addressSet">
<set>
<ref bean = "address1"/>
<ref bean = "address2"/>
<value>Pakistan</value>
</set>
</property>

<!-- Passing bean reference for java.util.Map -->
<property name = "addressMap">
<map>
<entry key = "one" value = "INDIA"/>
<entry key = "two" value-ref = "address1"/>
<entry key = "three" value-ref = "address2"/>
</map>
</property>
</bean>

</beans>

注入 null 和空字符串值

如果你需要传递一个NULL值,那么你可以按如下方式传递它

 <bean id = "..." class = "exampleBean">
<property name = "email"><null/></property>
</bean>

如果你需要传递一个空字符串作为一个值,那么你可以按如下方式传递它

 <bean id = "..." class = "exampleBean">
<property name = "email" value = ""/>
</bean>

7、Spring - Beans 自动装配

自动装配模式

以下是自动装配模式,可用于指示 Spring 容器使用自动装配进行依赖注入。 您可以使用 元素的 autowire 属性来为 bean 定义指定  autowire  模式。

模式 & 描述

1 no

这是默认设置,这意味着没有自动装配,您应该使用显式 bean 引用进行装配。 您无需为此连接做任何特别的事情。 这是你在依赖注入一章中已经看到的。

2 byName

按属性名称自动装配。 Spring 容器查看在 XML 配置文件中  autowire  属性设置为  byName  的 bean 的属性。 然后,它会尝试将其属性与配置文件中由相同名称定义的 bean 进行匹配和连接。

3 byType

按属性数据类型自动装配。 Spring 容器查看在 XML 配置文件中  autowire  属性设置为  byType  的 bean 的属性。 然后,如果它的  type  与配置文件中的一个 beans 名称完全匹配,它会尝试匹配和连接一个属性。 如果存在多个此类 bean,则会引发致命错误。

4 constructor

与 byType 类似,但类型适用于构造函数参数。 如果容器中没有一个构造函数参数类型的 bean,则会引发致命错误。

5 autodetect

Spring 首先尝试使用  constructor  的自动装配,如果它不起作用,Spring 会尝试通过  byType  自动装配。

您可以使用  byType  或  constructor  自动装配模式来连接数组和其他类型化的集合。

Spring 自动装配 byName

此模式通过属性名称指定自动装配。 Spring 容器查看 XML 配置文件中  auto-wire  属性设置为  byName  的 bean。 然后,它会尝试将其属性与配置文件中由相同名称定义的 bean 进行匹配和连接。 如果找到匹配项,它将注入这些 bean。 否则,bean 将不会被连接。

例如,如果一个 bean 定义在配置文件中设置为自动装配  byName ,并且它包含一个  spellChecker  属性(即,它有一个  setSpellChecker (...) 方法),Spring 查找名为 spellChecker 的 bean 定义,并使用它来设置属性。 您仍然可以使用 property 标签连接其余属性。 下面的例子将说明这个概念。

 public class Pet {  
private String Type;
private String name;
}

public class Person {
private String name;
private int age;
private Pet pet;
}
 <?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
https://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="pet" class="com.zh.pojo.Pet">
<property name="type" value="猫"/>
<property name="name" value="小花"/>
</bean>
<bean id="person" class="com.zh.pojo.Person" autowire="byName">
<property name="name" value="张三"/>
<property name="age" value="18"/>
</bean>
</beans>
 public class Test01 {  
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");

Person person = applicationContext.getBean("person", Person.class);

System.out.println(person);
}
}


Person(name=张三, age=18, pet=Pet(Type=猫, name=小花))

在bean配置中没有设置person的pet属性,容器自动装配了

注意 :byname是根据属性名和bean的id来识别的,如果属性名和bean的id不符,则自动装配失效

 <bean id="pet1" class="com.zh.pojo.Pet">  
<property name="type" value="猫"/>
<property name="name" value="小花"/>
</bean>
<bean id="person" class="com.zh.pojo.Person" autowire="byName">
<property name="name" value="张三"/>
<property name="age" value="18"/>
</bean>

Person(name=张三, age=18, pet=null)

Spring 自动装配 byType

此模式指定按属性类型自动装配。 Spring 容器查看 XML 配置文件中  autowire  属性设置为  byType  的 bean。 然后,如果它的  type  与配置文件中的一个 beans 名称完全匹配,它会尝试匹配和连接一个属性。 如果找到匹配项,它将注入这些 bean。 否则,bean 将不会被连接。
上述例子中,改变bean的id之后,自动装配失效,这时可以通过buType来实现自动装配

 <bean id="pet1" class="com.zh.pojo.Pet">  
<property name="type" value="猫"/>
<property name="name" value="小花"/>
</bean>
<bean id="person" class="com.zh.pojo.Person" autowire="byType">
<property name="name" value="张三"/>
<property name="age" value="18"/>
</bean>


Person(name=张三, age=18, pet=Pet(Type=猫, name=小花))

byType自动装配有两个相同的类型的bean时,编译器会报错,因为此时容器无法确定需要哪个bean ----如果存在多个此类 bean,则会引发致命错误

自动装配的限制
您仍然可以使用始终覆盖自动装配的 constructor-arg 和 property 设置来指定依赖项。
您不能自动装配所谓的简单属性,例如原始数据、字符串和类。

8、常用注解

要使用注解,必须先在配置文件中引入约束并开启

注解注入在 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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<context:annotation-config/>
<!-- bean definitions go here -->

</beans>

1、注册bean的相关注解

  • @Component :标准一个普通的spring Bean类。
    • @Repository:标注一个DAO组件类。
    • @Service:标注一个业务逻辑组件类。
    • @Controller:标注一个控制器组件类。

@Component可以代替@Repository、@Service、@Controller,因为这三个注解是被@Component标注的。

注册bean注解需要导入约束

 <?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
https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config/> <!--开启注解不是必须的-->
<context:component-scan base-package="com.zh.pojo"/>
</beans>
 @Data  
@NoArgsConstructor
@AllArgsConstructor
@Component
public class Person {
@Value("张三")
private String name;
@Value("18")
private int age;
@Autowired
@Qualifier("pet")
private Pet pet;
}

@Data
@NoArgsConstructor
@AllArgsConstructor
@Component
public class Pet {
@Value("猫")
private String Type;
@Value("大白")
private String name;
}

2、自动装载相关注解

@Autowired:属于Spring 的org.springframework.beans.factory.annotation包下,可用于为类的属性、构造器、方法进行注值

required属性:默认为不为null,改为false可为null

Spring @Autowired 注解

@Autowired  注解提供了对自动装配的位置和方式进行更细粒度的控制。 @Autowired 注解可用于在 setter 方法上自动装配 bean,就像 @Required 注解、构造函数、具有任意名称和/或多个参数的属性或方法一样。

@Autowired 在 Setter 方法上

您可以在 setter 方法上使用  @Autowired  注解来摆脱 XML 配置文件中的 property 元素。 当 Spring 发现与 setter 方法一起使用的 @Autowired 注解时,它会尝试对方法执行 byType 自动装配。

@Autowired 属性

您可以在属性上使用  @Autowired  注解来摆脱 setter 方法。 当您使用 a1 传递自动装配属性的值时,Spring 将自动为这些属性分配传递的值或引用。

@Autowired 在构造函数上

您也可以将 @Autowired 应用于构造函数。 构造函数 @Autowired 注解指示在创建 bean 时应该自动装配构造函数,即使在 XML 文件中配置 bean 时没有使用 constructor-arg元素。 让我们检查以下示例。

@Autowired with (required = false) 选项

默认情况下,@Autowired 注解暗示依赖项是必需的,类似于 @Required 注解,但是,您可以通过使用带有 @Autowired 的  (required=false)  选项来关闭默认行为。

 public class Person {  
private String name;
private int age;
@Autowired
private Pet pet;
}
 <bean id="pet1" class="com.zh.pojo.Pet">  
<property name="type" value="狗"/>
<property name="name" value="小花1"/>
</bean>
<bean id="person" class="com.zh.pojo.Person">
<property name="name" value="张三"/>
<property name="age" value="20"/>
</bean>


Person(name=张三, age=20, pet=Pet(Type=狗, name=小花1))

** @Autowired 默认是byType,如果存在多个相同类型的bean,则报错

Spring @Qualifier 注解

当您创建多个相同类型的 bean 并希望仅将其中一个与属性关联时,可能会出现这种情况。 在这种情况下,您可以将  @Qualifier  注解与  @Autowired  一起使用,通过指定要连接的确切 bean 来消除混淆。

上述例子中,在添加一个bean,则会报错

 <bean id="pet1" class="com.zh.pojo.Pet">  
<property name="type" value="狗"/>
<property name="name" value="小花1"/>
</bean>
<bean id="pet2" class="com.zh.pojo.Pet">
<property name="type" value="狗"/>
<property name="name" value="小花2"/>
</bean>
<bean id="person" class="com.zh.pojo.Person">
<property name="name" value="张三"/>
<property name="age" value="20"/>
</bean>
 public class Person {  
private String name;
private int age;
@Autowired
private Pet pet;
}

这时可以使用@Qualifier注解来区分

 @Data  
@NoArgsConstructor
@AllArgsConstructor
public class Person {
private String name;
private int age;
@Autowired
@Qualifier("pet1")
private Pet pet;
}


Person(name=张三, age=20, pet=Pet(Type=狗, name=小花1))

Spring @Resource注解

不属于spring的注解,而是来自于JSR-250位于java.annotation包下,使用该annotation为目标bean指定协作者Bean。

 @Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource {
String name() default "";
Class type() default java.lang.Object.class;

}


public class AnotationExp {

@Resource(name = "HappyClient")
private HappyClient happyClient;

@Resource(type = HappyPlayAno .class)
private HappyPlayAno happyPlayAno;
}

@Resource和@Autowired的对比

相同点 @Resource的作用相当于@Autowired,均可标注在字段或属性的setter方法上。

不同点

  1. 提供方 @Autowired是Spring的注解,@Resource是javax.annotation注解,而是来自于JSR-250,J2EE提供,需要JDK1.6及以上。

  2. 注入方式 @Autowired只按照Type 注入;@Resource默认按Name自动注入,也提供按照Type 注入;

  3. 属性

@Autowired注解可用于为类的属性、构造器、方法进行注值。默认情况下,其依赖的对象必须存在(bean可用),如果需要改变这种默认方式,可以设置其required属性为false。

还有一个比较重要的点就是,@Autowired注解默认按照类型装配,如果容器中包含多个同一类型的Bean,那么启动容器时会报找不到指定类型bean的异常,解决办法是结合@Qualifier注解进行限定,指定注入的bean名称。

@Resource有两个中重要的属性:name和type。name属性指定byName,如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。

需要注意的是,@Resource如果没有指定name属性,并且按照默认的名称仍然找不到依赖对象时, @Resource注解会回退到按类型装配。但一旦指定了name属性,就只能按名称装配了。

  1. @Resource注解的使用性更为灵活,可指定名称,也可以指定类型 ;@Autowired注解进行装配容易抛出异常,特别是装配的bean类型有多个的时候,而解决的办法是需要在增加@Qualifier进行限定。

Spring @Required 注解

@Required  注解适用于 bean 属性设置器方法,它指示受影响的 bean 属性必须在配置时填充到 XML 配置文件中。 否则,容器会抛出 BeanInitializationException 异常。 下面是一个例子来展示@Required 注解的使用。

 public class Student {
private Integer age;
private String name;

@Required
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}

@Required
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}

@Required 注解的属性,必须在xml中注入,次注解以过时

9、Spring - 基于 Java 的配置

 @Data  
@NoArgsConstructor
@AllArgsConstructor
public class Pet {
private String Type;
private String name;
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
private String name;
private int age;
@Autowired
private Pet pet;
}


@Configuration
public class Config {
@Bean
public Pet pet(){
return new Pet("猫","小花");
}
}


public class Test01 {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);

Pet pet = applicationContext.getBean("pet", Pet.class);

System.out.println(pet);
}
}


Pet(Type=猫, name=小花)

@Configuration & @Bean 注解

@Configuration  注解一个类表明该类可以被 Spring IoC 容器用作 bean 定义的来源。 
@Bean  注解告诉 Spring,使用 @Bean 注解的方法将返回一个对象,该对象应在 Spring 应用程序上下文中注册为 bean。

注入 Bean 依赖项

 @Configuration  
public class Config {

@Bean
public Person person(){
return new Person("张三",18,pet());
}
@Bean
public Pet pet(){
return new Pet("猫","小花");
}
}

就是调用需要的依赖方法

指定 Bean 作用域

 @Configuration
public class AppConfig {
@Bean
@Scope("prototype")
public Foo foo() {
return new Foo();
}
}

生命周期回调

@Bean 注解支持指定任意初始化和销毁回调方法,很像 Spring XML 的 bean 元素上的 init-method 和 destroy-method 属性 −

 public class Foo {
public void init() {
// initialization logic
}
public void cleanup() {
// destruction logic
}
}
@Configuration
public class AppConfig {
@Bean(initMethod = "init", destroyMethod = "cleanup" )
public Foo foo() {
return new Foo();
}
}

@Import 注解

 @Configuration  
@Import(ConfigB.class)
public class ConfigA {
@Bean
public Person person(){
return new Person("张三",19,null);
}
}

@Configuration
public class ConfigB {
@Bean
public Pet pet(){
return new Pet("狗","小花");
}
}


public class Test01 {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ConfigA.class);

Person person = applicationContext.getBean("person", Person.class);
Pet pet = applicationContext.getBean("pet", Pet.class);

System.out.println(person);
System.out.println(pet);
}
}


Person(name=张三, age=19, pet=Pet(Type=狗, name=小花))
Pet(Type=狗, name=小花)

10、Spring AOP

1、概述

Spring Framework 的关键组件之一是 面向方面编程 (AOP)  框架。 面向方面的编程需要将程序逻辑分解成不同的部分,称为 所谓的关注点 。跨越应用程序多个点的功能称为横切关注点。 这些 横切关注点 在概念上与应用程序的业务逻辑是分开的。有各种常见的良好示例,例如日志记录、审计、声明性事务、安全性、缓存等。

OOP 中模块化的关键单元是类,而 AOP 中模块化的单元是方面。 依赖注入可帮助您将应用程序对象彼此分离,而 AOP 可帮助您将横切关注点与它们影响的对象分离。 AOP 就像 Perl、.NET、Java 等编程语言中的触发器。

Spring AOP 模块让拦截器拦截应用程序。 例如,在执行方法时,您可以在方法执行之前或之后添加额外的功能。

2、Spring AOP 核心概念

1 Aspect

具有一组提供横切需求的 API 的模块。 例如,日志记录模块将被称为 AOP 方面的日志记录。 根据要求,应用程序可以具有任意数量的方面。

2 Join point

这代表了您的应用程序中的一个点,您可以在其中插入 AOP 方面。 您也可以说,它是应用程序中使用 Spring AOP 框架执行操作的实际位置。

3 Advice

这是在方法执行之前或之后要采取的实际操作。 这是 Spring AOP 框架在程序执行期间调用的实际代码。

4 PointCut

这是应该执行建议的一组一个或多个连接点。 正如我们将在 AOP 示例中看到的那样,您可以使用表达式或模式指定切入点。

5 Introduction

介绍允许您向现有类添加新方法或属性。

6 Target object

一个或多个方面所建议的对象。 该对象将始终是代理对象。 也称为建议对象。

7 Weaving

Weaving 是将方面与其他应用程序类型或对象链接以创建建议对象的过程。 这可以在编译时、加载时或运行时完成。

Spring 方面可以使用以下中提到的五种建议。

1 before

在方法执行之前运行建议。

2 after

在方法执行后运行通知,无论其结果如何。

3 after-returning

方法执行后运行通知,仅当方法成功完成时。

4 after-throwing

在方法执行后运行通知,仅当方法通过抛出异常退出时。

5 around

在调用建议方法之前和之后运行建议

使用AOP需要以下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:aop = "http://www.springframework.org/schema/aop"
xsi:schemaLocation = "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">


</beans>`

需要导入依赖

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

基于@AspectJ

@AspectJ 指的是一种将方面声明为使用 Java 5 注解进行注解的常规 Java 类的风格。 通过在基于 XML 模式的配置文件中包含以下元素来启用 @AspectJ 支持。

 <aop:aspectj-autoproxy/> 

3、Spring AOP 实现

基于 XML 架构

声明 Aspect

aspect  使用 元素声明,支持 bean 使用  ref  属性引用,如下所示

 <aop:config>
<aop:aspect id = "myAspect" ref = "aBean">
...
</aop:aspect>
</aop:config>

<bean id = "aBean" class = "...">
...
</bean>

声明 Pointcut

Pointcut 有助于确定要使用不同建议执行的感兴趣的连接点(即方法)。 在使用基于 XML Schema 的配置时,Pointcut 将定义如下

 <aop:config>
<aop:aspect id = "myAspect" ref = "aBean">

<aop:pointcut id = "businessService"
expression = "execution(* com.xyz.myapp.service.*.*(..))"/>
...
</aop:aspect>
</aop:config>

<bean id = "aBean" class = "...">
...
</bean>

声明 Advices

您可以使用 <aop:{ADVICE NAME}> 元素在 aop:aspect 中声明五个建议中的任何一个,如下所示。

 <aop:config>
<aop:aspect id = "myAspect" ref = "aBean">
<aop:pointcut id = "businessService"
expression = "execution(* com.xyz.myapp.service.*.*(..))"/>

<!-- a before advice definition -->
<aop:before pointcut-ref = "businessService"
method = "doRequiredTask"/>

<!-- an after advice definition -->
<aop:after pointcut-ref = "businessService"
method = "doRequiredTask"/>

<!-- an after-returning advice definition -->
<!--The doRequiredTask method must have parameter named retVal -->
<aop:after-returning pointcut-ref = "businessService"
returning = "retVal"
method = "doRequiredTask"/>

<!-- an after-throwing advice definition -->
<!--The doRequiredTask method must have parameter named ex -->
<aop:after-throwing pointcut-ref = "businessService"
throwing = "ex"
method = "doRequiredTask"/>

<!-- an around advice definition -->
<aop:around pointcut-ref = "businessService"
method = "doRequiredTask"/>
...
</aop:aspect>
</aop:config>

<bean id = "aBean" class = "...">
...
</bean>


基于@AspectJ

@AspectJ 指的是一种将方面声明为使用 Java 5 注解进行注解的常规 Java 类的风格。 通过在基于 XML 模式的配置文件中包含以下元素来启用 @AspectJ 支持。

 <aop:aspectj-autoproxy/> 

声明 Aspect

Aspects 类与任何其他普通 bean 一样,并且可以像任何其他类一样具有方法和字段,除了它们将使用 @Aspect 注解如下。

 package org.xyz;

import org.aspectj.lang.annotation.Aspect;

@Aspect
public class AspectModule {
}

声明 Pointcut

Pointcut 有助于确定要使用不同建议执行的感兴趣的连接点(即方法)。 在使用基于 @AspectJ 的配置时,Pointcut 声明有两个部分 −

  • 一个切入点表达式,它准确地确定我们感兴趣的方法执行。

  • 一个切入点签名,包括名称和任意数量的参数。 该方法的实际主体是无关紧要的,实际上应该是空的。

以下示例定义了一个名为 "businessService" 的切入点,它将匹配 com.xyz.myapp.service 包下的类中可用的每个方法的执行。

 import org.aspectj.lang.annotation.Pointcut;

@Pointcut("execution(* com.xyz.myapp.service.*.*(..))") // expression
private void businessService() {} // signature

声明 Advices

 @Before("execution(* *.*(..))")
@After("execution(* *.*(..))")

4、实现AOP的三种方式-快速入门

  • 实现接口实现
 public class Before implements MethodBeforeAdvice {  
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getSimpleName()+"的"+method.getName()+"方法执行了");
}
}


public class After implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getSimpleName()+"的"+method.getName()+"方法执行结束了");
}
}

配置AOP

 <bean id="userDao" class="com.zh.pojo.UserDao"/>  
<bean id="after" class="com.zh.log.After"/>
<bean id="before" class="com.zh.log.Before"/>
<aop:aspectj-autoproxy/>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* *.*(..))"/>
<aop:advisor advice-ref="after" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="before" pointcut-ref="pointcut"/>
</aop:config>

运行结果:

 public class Test01 {  
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Dao dao = (Dao) applicationContext.getBean("userDao");

dao.select();

dao.del();
}
}

com.zh.pojo.UserDao的select方法执行了
查询用户
com.zh.pojo.UserDao的select方法执行结束了
com.zh.pojo.UserDao的del方法执行了
删除了一个用户
com.zh.pojo.UserDao的del方法执行结束了
  • 自定义切面实现
 public class Log {  
private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public void before(){
System.out.println("方法执行前:"+simpleDateFormat.format(new Date(System.currentTimeMillis())));
}
public void after(){
System.out.println("方法执行后:"+simpleDateFormat.format(new Date(System.currentTimeMillis())));
}
}

配置AOP

 <bean id="log" class="com.zh.log.Log"/>  
<bean id="userDao" class="com.zh.pojo.UserDao"/>
<aop:aspectj-autoproxy/>
<aop:config>
<aop:aspect id="log" ref="log">
<!-- 在切面外面定义的切点,可以给所有切面引用 -->
<aop:pointcut id="pointcut" expression="execution(* *.*(..))"/>
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>

运行结果:

 public class Log {  
private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
public void before(){
System.out.println("方法执行前:"+simpleDateFormat.format(new Date(System.currentTimeMillis())));
}
public void after(){
System.out.println("方法执行后:"+simpleDateFormat.format(new Date(System.currentTimeMillis())));
}
}

方法执行前:2022-10-12 19:57:10 421
查询用户
方法执行后:2022-10-12 19:57:10 422
方法执行前:2022-10-12 19:57:10 423
删除了一个用户
方法执行后:2022-10-12 19:57:10 423
  • 注解实现
 <bean id="log" class="com.zh.log.Log"/>  
<bean id="userDao" class="com.zh.pojo.UserDao"/>
<aop:aspectj-autoproxy/>
 @Aspect  
public class Log {
private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
@Before("execution(* *.*(..))")
public void before(){
System.out.println("方法执行前:"+simpleDateFormat.format(new Date(System.currentTimeMillis())));
}
@After("execution(* *.*(..))")
public void after(){
System.out.println("方法执行后:"+simpleDateFormat.format(new Date(System.currentTimeMillis())));
}
}

public class Test01 {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
Dao dao = (Dao) applicationContext.getBean("userDao");

dao.select();

dao.del();
}
}


方法执行前:2022-10-12 20:07:49 099
查询用户
方法执行后:2022-10-12 20:07:49 100
方法执行前:2022-10-12 20:07:49 101
删除了一个用户
方法执行后:2022-10-12 20:07:49 101

5、AOP的一些细节

Spring中execution语法

 execution(修饰符 返回类型 切入点类 切入点方法(参数) 异常抛出) 

修饰符: 可选,支持通配符,(public/private/protected)

返回类型: 必填,支持通配符,可以使用 * 来匹配所有的返回值类型

切入点类: 可选,支持通配符,指定切入点类

切入点方法: 必填,支持通配符,指定要匹配的方法名,可以使用"*"通配符来匹配所有方法

参数: 若无可不填,指定方法声明中的形参列表,支持两个通配符,即*和..

其中*代表一个任意类型的参数,而…代表零个或多个任意类型的参数

 () 匹配一个不接受任何参数的方法

(..) 匹配一个接受任意数量参数的方法,可以是零个或多个

(*) 匹配一个接受一个任何类型的参数的方法,只能是一个

(*,String) 匹配一个接受两个参数的方法,其中第一个参数是任意类型,第二个参数必须是String类型

常用实例

 <!-- 【1、拦截所有public方法】 -->
<aop:pointcut expression="execution(public * *(..))" id="pt"/>

<!-- 【2、拦截所有save开头的方法】 -->
<aop:pointcut expression="execution(* save*(..))" id="pt"/>

<!-- 【3、拦截指定类的指定方法, 拦截时候一定要定位到方法】 -->
<aop:pointcut expression="execution(* com.shore.dao.impl.UserDao.save(..))" id="pt"/>

<!-- 【4、拦截指定类的所有方法】 -->
<aop:pointcut expression="execution(* com.shore.dao.impl.UserDao.*(..))" id="pt"/>

<!-- 【5、拦截指定包,以及其自包下所有类的所有方法】 -->
<aop:pointcut expression="execution(* com..*.*(..))" id="pt"/>

<!-- 【6、多条件】 -->
<!-- 或:|| or -->
<aop:pointcut expression="execution(* com.shore.dao.impl.UserDao.save(..)) || execution(* com.shore.dao.impl.MessageDao.save(..))" id="pt" />

<aop:pointcut expression="execution(* com.shore.dao.impl.UserDao.save(..)) or execution(* com.shore.dao.impl.MessageDao.save(..))" id="pt" />

<!-- 且:&& and --> <!-- 语法虽然没错,但,没意义 -->
<aop:pointcut expression="execution(* com.shore.dao.impl.UserDao.save(..)) && execution(* com.shore.dao.impl.MessageDao.save(..))" id="pt" />

<aop:pointcut expression="execution(* com.shore.dao.impl.UserDao.save(..)) and execution(* com.shore.dao.impl.MessageDao.save(..))" id="pt" />

<!-- 【7、取非值:not ! 不拦截指定的规则,拦截除此之外的所有类的方法】 -->
<aop:pointcut expression="!execution(* com.shore.dao.impl.UserDao.save(..))" id="pt"/> <!-- 注意not前必须有空格 -->

<aop:pointcut expression=" not execution(* com.shore.dao.impl.UserDao.save(..))" id="pt"/>

连接点和切入点

JoinPoint

JoinPoint 连接点代表应用程序中的一个点,您可以在其中插入 AOP 方面。 您也可以说,它是应用程序中使用 Spring AOP 框架执行操作的实际位置。 考虑以下示例 −

  • 包中包含的所有方法类。
  • 类的特定方法。


PointCut

PointCut 切入点是一组一个或多个 JoinPoints,应该在其中执行建议。 正如我们将在 AOP 示例中看到的那样,您可以使用表达式或模式指定切入点。 在 Spring 中,PointCut 有助于使用特定的 JoinPoints 来应用建议。 考虑以下示例 −

  • expression = "execution(* com.tutorialspoint. . (..))"
  • expression = "execution(* com.tutorialspoint.Student.getName(..))"

切面中可以设置joinpoint参数更加详细的信息

 @Aspect
public class Log {
private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
@Before("execution(* *.*(..))")
public void before(JoinPoint jp){
System.out.println(jp.getKind());
System.out.println(jp.getSignature());
System.out.println(jp.toLongString());
System.out.println(jp.getTarget());
System.out.println(jp.getClass());
System.out.println("方法执行前:"+simpleDateFormat.format(new Date(System.currentTimeMillis())));
}
public void after(){
System.out.println("方法执行后:"+simpleDateFormat.format(new Date(System.currentTimeMillis())));
}
}


method-execution
void com.zh.pojo.Dao.select()
execution(public abstract void com.zh.pojo.Dao.select())
com.zh.pojo.UserDao@738dc9b
class org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint
方法执行前:2022-10-12 20:29:34 187
查询用户

pointcut注解复用:

 @Aspect  
public class Log {
private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");

@Pointcut("execution(* *.*(..))")
public void p1(){}


@Before("p1()")
public void before(){
System.out.println("方法执行前:"+simpleDateFormat.format(new Date(System.currentTimeMillis())));
}
@After("p1()")
public void after(){
System.out.println("方法执行后:"+simpleDateFormat.format(new Date(System.currentTimeMillis())));
}
@Around("p1()")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("方法环绕前:"+simpleDateFormat.format(new Date(System.currentTimeMillis())));
jp.proceed();
System.out.println("方法环绕后:"+simpleDateFormat.format(new Date(System.currentTimeMillis())));
}
}

详解通知的位置

  • Before  :Before 是一种通知类型,可确保通知在方法执行之前运行。
 @Before("pointcutAll()")  
public void before(){
System.out.println("执行方法之前");
}
  • After :After 是一种通知类型,可确保通知在方法执行后运行。 即方法执行不管抛出异常或报错,都会执行
 @After("pointcutAll()")  
public void after(){
System.out.println("执行方法之后");
}

public void print() {
for (int i = 0; i < 5; i++) {
System.out.println(i);
}

/*int i = 1/0;
System.out.println(i);*/
}

执行方法之前
0
1
2
3
4
执行方法之后

注释另外一部分之后

执行方法之前
执行方法之后
Exception in thread "main" java.lang.ArithmeticException: / by zero
  • After Returning :After Returning 是一种通知类型,它确保只有在方法成功完成时才会在方法执行之后运行通知
 @AfterReturning("pointcutAll()")  
public void afterReturning(){
System.out.println("方法正常执行完成");
}

public void print() {
for (int i = 0; i < 5; i++) {
System.out.println(i);
}

/*int i = 1/0;
System.out.println(i);*/
}

执行方法之前
0
1
2
3
4
方法正常执行完成

注释另外一部分之后

执行方法之前
Exception in thread "main" java.lang.ArithmeticException: / by zero


  • After Throwing :After Throwing 是一种通知类型,它确保通知在方法执行后运行,仅当方法通过抛出异

标签: Java

添加新评论