Spring是什么?

  • 狭义上的Spring——Spring Framework:Spring框架是一个分层的、面向切面与Java应用的一站式轻量级开源框架。
  • 广义上的Spring——Spring技术栈:指以Spring框架为核心的Spring技术栈。这些技术栈涵盖了从企业级应用到云计算等各方面内容
    • Spring Data:数据访问模块,对JDBC和ORM提供了很好的支持。
    • Spring Security:是一款可定制化的身份验证和访问控制框架
    • Spring Boot:简化新Spring应用的初始搭建及开发过程
    • Spring Cloud:开发人员可以“开箱即用”地实现分布式系统中常用的服务

Spring框架概述

Spring框架常用模块:

  1. 核心容器:
  • spring-core和spring-beans:提供框架的基础部分,包括IoC和DI功能。BeanFactory是一个复杂工厂模式的实现,无需编程就能实现单例,并允许开发人员将配置和特定的依赖从实际程序逻辑中解耦
  • Context(spring-context):建立在Core和Beans模块提供的功能基础之上,它是一种在框架类型下实现对象存储操作的手段,有一点像JNDI注册。Context继承了Beans的特性,并且增加了对国际化的支持、事件广播、资源加载和创建上下文(如一个Servlet容器)。ApplicationContext接口的Context模块的主要表现形式。
  • spring-expression:提供了一种强大的表达式语言,用来在运行时查询和操作对象图。它是作为JSP2.1规范所制定的统一表达式语言的一种扩展。这种语言支持对属性值、属性参数、方法调用、数组内容存储、收集器和索引、逻辑和算数的操作及命名变量,并通过名称从Spring的控制反转容器中取回对象。表达式模块还支持列表投影、选择和通用列表聚合
  1. AOP和Instrumentation

spring-aop:提供AOP的实现,从而能够实现方法拦截器和切入点完全分离代码。使用源码级别元数据的功能,也可以在代码中加入行为信息。

spring-instrument:提供了类Instrumentation的支持和在某些应用程序服务器使用类加载器实现。

  1. 消息

自Spring Framework 4版本开始提供spring-messaging模块

  1. 数据访问/集成

由JDBC、ORM、OXM、JMS和Transaction模块组成。

  1. Web

  2. 测试

Spring设计模式

  • Spring使用POJO模式强大的功能来实现企业应用程序的轻量级和最小入侵性的开发
  • Spring使用依赖注入模式(DI)实现松耦合,并使系统可以更加面向接口编程
  • Spring使用Decorator(装饰器模式)和Proxy(代理模式)进行声明式编程
  • Spring使用Template(模版模式)消除样板代码

BeanFactory源码理解

参考: BeanFactory 简介以及它 和FactoryBean的区别(阿里面试)
参考: Spring源码分析——BeanFactory体系之接口详细分析

BeanFacotry是spring中比较原始的Factory。如XMLBeanFactory就是一种典型的BeanFactory。原始的BeanFactory无法支持spring的许多插件,如AOP功能、Web应用等。

ApplicationContext接口,它由BeanFactory接口派生而来,ApplicationContext包含BeanFactory的所有功能,通常建议比BeanFactory优先

在Spring中,BeanFactory是IOC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。BeanFactory只是个接口,并不是IOC容器的具体实现,但是Spring容器给出了很多种实现,如 DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等,其中XmlBeanFactory就是常用的一个,该实现将以XML方式描述组成应用的对象及对象间的依赖关系。

BeanFactory仅提供了六种方法供调用:

  • boolean containsBean(String beanName):判断工厂中是否包含给定名称的bean的定义
  • Object getBean(String beanName):返回给定名称注册的bean实例。根据bean的配置情况,如果是singleton模式(默认)将会返回一个共享实例,否则返回一个新建实例。如果没有找到指定bean则报异常
  • Object getBean(String beanName, Class class):返回以给定名称注册的bean实例,并转换为给定class类型
  • Class getType(String beanName):返回给定名称的bean的Class类,如果没有找到指定实例,则报NoSuchBeanDefinitionException异常
  • boolean isSingleton(String beanName):判断给定的bean定义是否为单例模式
  • String[] getAliases(String name):返回给定bean名称的所有别名

源码如下:

package org.springframework.beans.factory;

public interface BeanFactory {

    
    //用来引用一个实例,或把它和工厂产生的Bean区分开,就是说,如果一个FactoryBean的名字为a,那么,&a会得到那个Factory
    String FACTORY_BEAN_PREFIX = "&";

    //四个不同形式的getBean方法,获取实例
    Object getBean(String name) throws BeansException;
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;
    <T> T getBean(Class<T> requiredType) throws BeansException;
    Object getBean(String name, Object... args) throws BeansException;

    boolean containsBean(String name); //是否包含定义

    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;  //是否为单实例

    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;  //是否为原型(多实例)

    boolean isTypeMatch(String name, Class<?> targetType)
            throws NoSuchBeanDefinitionException;   //名称、类型是否匹配

    Class<?> getType(String name) throws NoSuchBeanDefinitionException; //获取类型

    String[] getAliases(String name);   //根据实例的名字获取实例的所有别名

}

Bean容器的启动过程及源码分析

参考自: 深入剖析 Spring 框架的 BeanFactory

bean实例化之前,必须启动bean容器。所以分为以下两个阶段:

  1. bean容器的启动阶段
  2. 容器中bean的实例化阶段

启动阶段

先读取注解中或者xml中对bean的配置,然后解析对bean的各种定义,将这些定义转换为一个BeanDefinition对象,其中保存了一个bean的各种信息

public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
        implements BeanDefinition, Cloneable {
    private volatile Object beanClass;                      //保存bean的class属性
    private String scope = SCOPE_DEFAULT;                   //是否单例
    private boolean abstractFlag = false;                   //是否抽象
    private boolean lazyInit = false;                       //是否懒加载
    private int autowireMode = AUTOWIRE_NO;                 //是否自动装配
    private int dependencyCheck = DEPENDENCY_CHECK_NONE;    //是否检查依赖
    private String[] dependsOn;                             //依赖于哪些bean(这些bean必须提前初始化)
    private ConstructorArgumentValues constructorArgumentValues;    //通过构造函数注入的依赖
    private MutablePropertyValues propertyValues;           //保存通过setter注入的依赖
    private String factoryBeanName;                         //用于FactoryBean
    private String factoryMethodName;                       //用于FactoryBean
    private String initMethodName;                          //初始化方法名
    private String destroyMethodName;                       //销毁方法名

读取完成后通过BeanDefinitionRegistry将这些BeanDefinition对象注册到beanFactory中

public interface BeanDefinitionRegistry extends AliasRegistry {
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
        throws BeanDefinitionStoreException;
    void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
    BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
    boolean containsBeanDefinition(String beanName);
    String[] getBeanDefinitionNames();
    int getBeanDefinitionCount();
    boolean isBeanNameInUse(String beanName);
}

注册到工厂的实现类中。BeanFactory的实现类,需要实现BeanDefinitionRegistry 接口:

@SuppressWarnings("serial")
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
        implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
    /** Map of bean definition objects, keyed by bean name */
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);
    
    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {
        // ... ...
       this.beanDefinitionMap.put(beanName, beanDefinition);
       // ... ...
    }

我们看到BeanDefinition被注册到了 DefaultListableBeanFactory, 保存在它的一个ConcurrentHashMap中。

其他工厂源码可以看引用的文章

bean实例化阶段

实例化阶段主要是通过反射或者CGLIB对bean进行实例化,在这个阶段Spring又给我们暴露了很多的扩展点:

各种的Aware接口,比如 BeanFactoryAware,MessageSourceAware,ApplicationContextAware

对于实现了这些Aware接口的bean,在实例化bean时Spring会帮我们注入对应的:BeanFactory, MessageSource,ApplicationContext的实例。

ApplicationContext(应用上下文)是高级工厂的接口,能维护不同bean及其依赖项的注册表,并且可以通过getBean来检索bean实例。

一般情况下,应用程序应该避免调用ApplicationContext内的接口。这样就可以做到完全不依赖Spring API

bean的命名

bean都有一个或者多个标识符,但这些标识符在托管bean的容器中必须是唯一的。

用户可以不为bean提供名称或者标识符。如果没有显式提供名称或者标识符,则容器会为该bean自动生成唯一的名称。但是如果想要通过名称引用该bean,则必须提供一个名称。

命名bean时尽量使用标准java约定。

实例化bean的方式

所谓bean的实例化,就是根据配置来创建对象的过程。一般来说是通过bean的class属性来指定需要实例化的对象的类型或类。

使用class属性有以下两种方式:

  • 容器本身通过反射机制来调用指定类的构造函数,从而创建bean。这与java的new运算符相同。
  • 通过静态工厂方法创建的类中包含静态方法。通过调用静态方法返回对象的类型可能与class一样,也可能完全不一样。

概括起来,bean的实例化有以下三种形式:

  1. 通过构造函数实例化

Spring IOC容器可以管理几乎所有想让他管理的类,而不仅仅是POJO。

当开发人员使用构造方法来创建bean时,Spring对类来说,并没有什么特殊。也就是说在开发的类不需要实现任何特定的接口或者以特定的方式进行编码。但是,根据所使用的IOC的类型,可能需要一个默认(无参)的构造方法

  1. 使用静态工厂方法实例化

当采用静态工厂方法创建bean时,除了需要指定class属性外,还需要通过factory-method属性来制定创建bean实例的工厂方法,Spring会调用此方法并返回对象。

  1. 使用工厂实例方法实例化

通过调用工厂实例的非静态方法进行实例化,与通过静态工厂方法实例化类似。

使用这种方式时,class属性设置为空,而factory-bean属性必须指定为当前(或者其祖先)容器中包含工厂方法的bean的名称,而该工厂bean的工厂方法本身必须通过factory-method来设定。

注入方式

在Spring框架中,主要有以下两种注入方式。

  1. 基于构造函数

基于构造函数的DI是通过调用具有多个参数的构造函数的容器来完成的。每个参数表示依赖关系,这与调用具有特定的静态工厂方法来构造bean几乎是等效的。

  1. 基于setter方法

自动装配

@SpringBootApplication =

@Configuration + @EnableAutoConfiguration + @ComponentScan

bean scope

==默认时,所有的Spring bean都是单例的。== 开发人员可以通过在bean中添加scope来修改这个默认值。

范围描述
singleton每个Spring容器有一个实例(默认)
prototype允许bean可以被多次实例化(使用一次就创建一个实例)
request定义bean的scope为HTTP请求。每个HTTP请求都有自己的的实例。只有在使用有Web功能的Spring上下文时才有效
session定义bean的scope是HTTP会花。使用限制同上
application定义了每个ServletContext有一个实例
websocket定义了每个WebSocket有一个实例。使用限制同request

bean的生命周期

bean的生命周期分为以下几个阶段:

参考自: JAVA面试题:Spring中bean的生命周期

image

转换为文字描述:

bean建立:

1:Bean的建立:

容器寻找Bean的定义信息并将其实例化。

2:属性注入:

使用依赖注入,Spring按照Bean定义信息配置Bean所有属性

3:BeanNameAware的setBeanName():

如果Bean类有实现org.springframework.beans.BeanNameAware接口,工厂调用Bean的setBeanName()方法传递Bean的ID。

4:BeanFactoryAware的setBeanFactory():

如果Bean类有实现org.springframework.beans.factory.BeanFactoryAware接口,工厂调用setBeanFactory()方法传入工厂自身。

5:BeanPostProcessors的ProcessBeforeInitialization():

如果有org.springframework.beans.factory.config.BeanPostProcessors和Bean关联,那么其postProcessBeforeInitialization()方法将被将被调用。

6:bean中添加了注解@PostConstruct的方法:

如果在bean中存在添加了@PostConstruct注解的方法,则执行。

7:initializingBean的afterPropertiesSet():

如果Bean类已实现org.springframework.beans.factory.InitializingBean接口,则执行他的afterProPertiesSet()方法

8:Bean定义文件中定义init-method:

如果在bean的定义中存在init-method,则执行到这个阶段,就会执行initBean()方法

到此为止,bean创建工作完成,可以被应用系统使用

bean销毁:

1:DisposableBean的destroy():

在容器关闭时,如果Bean类有实现org.springframework.beans.factory.DisposableBean接口,则执行他的destroy()方法

2:Bean定义文件中定义destroy-method:

销毁时如果有定义destroy-method方法,则执行这个方法。

==研究源码——为啥是这样的?==
link

基于注解的配置

@Required

表明受影响的bean属性必须在bean的定义中或者自动装配过程中通过明确的属性值在配置时来填充。如果受影响bean的属性没有被填充,那么容器就会抛出异常。

@Autowired

可以将该注解注释到setter上以提供setter注入。在使用构造器注入并且只有一个构造器时,这个注解可以省略。

默认情况下,当出现0个候选bean时,自动装配就会失败。

@Primary

自动装配时可能存在多个候选者,该注解指定了一个优先提供的特殊bean

@Resource

JSR-250中的注释,也可以用来支持将其注解在字段或bean属性的setter方法上注入。

可以使用name属性来指定注入的bean名称,例如

@Resource(name = "myBean")

@PostConstruct和@PreDestroy

CommonAnnotationBeanPostProcessor不但能识别@Resource注解,而且能识别JSR-250生命周期注解。这两个注解可以用于缓存等操作。CommonAnnotationBeanPostProcessor是Spring开闭原则COP的体现之一。

基于Java的容器配置

Spring中心的Java配置支持的核心就是@Configuration注解的类和@Bean注解的方法。通过注解,我们无需再编写xml来配置容器,在Java中使用注解进行配置即可。

@Bean

用来指定一个方法实例,配置和初始化一个新对象交给Spring IoC容器管理。一般@Bean用在@Configuration中

@Configuration

表示被注解的类主要目的是用作bean定义的来源。

最简单的Configuration类如下:

@Configuration
public class AppConfig {
    @Bean
    public MyService myService(){
        return new MyServiceImpl();
    }
}

事件监听器

ApplicationContext中的事件处理是通过ApplicationEvent类和ApplicationListener接口提供的。如果一个实现ApplicationListener接口的bean被部署到上下文中,则每当ApplicationEvent发布到ApplicationContext时,都会通知该bean。事实上这就是标准的Observer(观察者)模式。

在Spring4.2之后,事件基础架构得到了显著的改进,并提供了基于注解的模型和发布任意事件的能力,这是一个不一定从ApplicationEvent扩展的对象。当这样的对象被发布时,开发人员把他包装在一个事件中。

Spring提供以下标准事件:

  • ContextRefreshedEvent:当ApplicationContext被初始化或刷新时触发
  • ContextClosedEvent:当ApplicationContext被关闭时触发。
  • RequestHandleEvent:Web应用中,当一个HTTP请求结束时触发
  • ContextStartedEvent:当容器调用start()方法时触发
  • ContextStopEvent:当容器调用stop()时触发

如何完成一套自定义事件的收发?

  1. 定义自定义事件

很简单,继承ApplicationEvent即可。

public class MyEvent extends ApplicationEvent {
    private String message; // 可以通过事件包装类传递参数
    public MyEvent(Object source, String message) {
        super(source);
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}
  1. 设置监听器

监听器本身也是个bean,并且继承自ApplicationListener。在运行时如果有注册的事件发出,那么这个监听器将会通过制定方法相应这个事件。

@Component
public class MyListener implements ApplicationListener<MyEvent> { // 通过泛型注册事件

    @Override
    public void onApplicationEvent(MyEvent event) {
        System.out.println("received message:" + event.getMessage());
    }
}

或者通过注解来注册监听器。可以通过注解的参数或者传入泛型来缩小监听的事件。

@Component
public class MyListener {
    @EventListener({MyEvent.class}) // 与方法参数二选一即可
    public void onApplicationEvent(MyEvent event) {
        System.out.println("received message:" + event.getMessage());
    }
}

==在自己测试的时候通过@PostConstruct发送事件,继承接口可以成功监听,但是基于注解却不能成功监听。为什么?==

  1. 设置事件广播器

设置想要发出事件的广播器。广播器的设置比较复杂,首先要实现ApplicationEventPublisherAware,并且引入一个ApplicationEventPublisher,最后通过这个对象进行广播。

@Component
public class MyBean implements ApplicationEventPublisherAware {
    private ApplicationEventPublisher publisher;

    //这里是接口的方法实现
    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher = applicationEventPublisher;
    }

    //发送消息
    public void sendMessage(){
        publisher.publishEvent(new MyEvent(this, "hello event listener!")); //新建事件并包装发出
    }
}

AOP编程

通过另一种思考程序结构的方式来补充OOP。OOP模块化的关键单元是类,而在AOP中,模块化的单元是切面(Aspect)。**切面可以实现跨多个类型和对象之间的事务管理、日志等方面的模块化。

AOP核心概念:

  1. Aspect(切面):将关注点进行模块化。某些关注点可能横跨多个对象,如事务管理。在SpringAOP中,切面可以适用常规类或者使用@Aspect注解的常规类来实现切面
  2. Join Point(连接点):在程序执行过程中某个特定的点,若某方法调用时或处理异常时。在SpringAOP中,一个连接点总是代表一个方法的执行。
  3. Advice(通知):在切面的某个特定的连接点上执行的动作。通知有各种类型,其中包括“arround”、“before”、“after”等通知。许多AOP框架,包括Spring,都是以拦截器来实现通知模型的,并维护一个以连接点为中心的拦截器链。
  4. Pointcut(切入点):匹配连接点的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(如当执行某个特定名称的方法时)。切入点表达式和连接点匹配是AOP的核心。Spring默认使用AspectJ切入点语法。
  5. Instroduction(引入):声明额外的方法或者某个类型的字段。Spring允许引入新的接口(及一个对应的实现)到任何被通知的对象。例如,可以使用一个引入来使bean实现IsModified接口,以便简化缓存机制。在AspectJ社区,Introduction也被称为Inter-type Declaration(内部类型声明)
  6. Target Object(目标对象):被一个或者多个切面所通知的对象。也有人把它称为Advised(被通知)对象。既然SpringAOP是通过运行时代理实现的,那么这个对象永远是一个Proxied(被代理)对象。
  7. AOP Proxy(AOP代理):AOP框架创建的对象,用来实现Aspect Contract(切面契约),包括通知方法执行等功能。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。
  8. Weaving(织入):把切面连接到其他的应用程序类型或对象上,并创建一个Advisied(被通知)的对象。这些可以在编译时(如使用AspectJ编译器)、类加载时和运行时完成。

Spring和其他纯Java AOP框架一样,在运行时完成织入。其中有关Advice(通知)的类型主要有以下几种:

  1. Before Advice(前置通知):在某连接点之前执行的通知,但这个通知不能阻止连接点前的执行(除非他抛出一个异常)。
  2. After Returning Advice(返回后通知):在某连接点正常完成后执行的通知,如果一个方法没有抛出任何异常,将正常返回
  3. After Throwing Advice(抛出异常后通知:在方法抛出异常退出时执行
  4. After(Finally) Advice(最后通知):当某连接点退出时执行(不论是正常退出还是异常退出)
  5. Around Advice(环绕通知):包围一个连接点的通知,如方法调用。这是最强大的一种通知类型。他/她可以在方法调用前后完成自定义通知,它也会选择是否继续执行连接点,或者直接返回他们自己的返回值或抛出异常来结束执行。这是最常用的一种通知类型。当然推荐使用尽量简单的通知类型来实现需要的功能。

Spring AOP

SpringAOP使用纯java实现,它不需要专门的编译过程。SpringAOP不需要控制类装载器层次,因此它适用于Servlet容器或应用服务器

Spring目前仅支持方法调用作为连接点之用

Spring实现AOP的方法域其他框架不同。Spring并不是要尝试提供最完整的AOP实现,相反,它其实侧重于提供一种AOP实现和Spring IoC的整合,用于解决企业级开发中的常见问题

因此,Spring AOP通常都和SpringIoC容器一起使用。AspectJ使用普通的bean定义语法,其他AOP实现相比,这是一个显著的区别。有些是使用SpringAOP无法轻松或者高效完成的,如通知一个细粒度的对象。这时,使用AspectJ是最好的选择

Spring可以无缝整合SpringAOP、IoC和AspectJ,使所有的AOP应用完全融入基于Spring的应用体系。

AOP代理

在java中,AOP使用代理模式完成。关于java下的三种代理,参见另一篇专门学习代理模式的笔记

使用@AspectJ

  1. 启用@AspectJ

可以通过XML或者Java配置来启用@AspectJ支持。

基于Java配置:

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
    ...
}

基于XML:

<aop:aspectj-autoproxy />

  1. 声明Aspect

在启用@AspectJ支持的情况下,在应用上下文定义任意带有一个@AspectJ注解的切面的==bean==都将被Spring自动识别并用于配置SpringAOP。

使用Java配置声明:


  1. 声明Pointcut

SpringAOP只支持Spring bean方法执行连接点操作,所以可以把切入点看做匹配Spring bean的方法执行。一个切入点声明有两部分:一部分包含名称和任意参数的签名,另一部分是切入点表达式,该表达式决定了用哪个方法执行。在@AspectJ中,一个切入点实际上就是一个普通的方法定义提供的一个签名。切入点表达式使用@Pointcut注释来表示,需要注意的是,这个方法的返回类型必须为void。

以下示例定义了一个切入点anyOldTransfer,这个切入点匹配了任意名为“transfer”的方法执行。

@Pointcut("executution( * transfer(..)")
private void anyOleTransfer(){} // 一个基于Java配置的Pointcut的方法体应该为空

使用Spring AOP的例子

这里使用我自己的例子,踩了一些坑会在代码中注释出来。

由于Spring采用的是AspectJ的切入点语言,所以先介绍正规的AspectJ 5 切入点表达式

AspectJ切入点表达式

参考: AspectJ切入点语法详解

在SpringAOP中,目前只有执行方法这一个切入点,所以它支持的AspectJ切入点指示符如下:

  • execution:用于匹配方法执行的连接点
  • within:用于匹配指定类型内的方法执行(多为指定包内的方法)
  • this:用于匹配当前AOP代理对象类型的执行方法。注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配。
  • args:用于匹配当前执行的方法传入的参数为指定类型的执行方法
  • @within
  • @target
  • @args
  • @annotation
  1. 命名切入点

execution()是最常用的切点函数,其语法如下:

**execution(<修饰符模式>? <返回类型模式> <方法名模式>(<参数模式>) <异常模式>?) **

除了返回类型模式、方法名模式和参数模式外,其它项都是可选的。

其中的一些匹配规则和正规表达式比较像,比如通配符:

  • *:匹配任何数量的字符,可以用在返回类型模式、方法名模式(部分)等各个带有字符串的地方
  • ..:两个点,匹配任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数。
  • +:匹配指定类型的子类型;仅能作为后缀放在类型模式后面

同时,AspectJ可以使用&&、||、!来组合切入点表达式。注意是必须是切入点表达式,而不是匹配符。实例如下:

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

@Pointcut("within(con.xyz.someapp.trading..*)")
private void inTrading(){}

@Pointcut("anyPublicOperation() && inTrading()")

其他使用示例,详见引用文章。

我的例子:

  1. Target Object(目标对象)
@Component("MyBean")
public class MyBean{

    public void testAspect(){
        System.out.println("testAspect() invoked!");
    }
}
  1. Aspect(切面)
@Component // 注意,切面也要是一个bean。否则Spring不会将其纳入SpringAOP中管理
@Aspect
public class MyAspect {

    @Pointcut("execution(* *Aspect())")
    private void onEvent(){}

    @Before(value = "onEvent()")
    public void exeBeforeEvent(){
        System.out.println("exeBeforeEvent() invoked!");
    }
}