天天看點

Spring基礎筆記{} SPEL表達式

Spring基礎筆記

Spring帶給了我們什麼便利?

注解版本的IOC如何玩?

元件注冊

元件注冊的過程中有哪些過濾規則?

如何控制元件的作用域(單例多例)?

六種注冊元件的方式?

生命周期

什麼是bean的生命周期

在bean的生命周期的各個階段我們可以插手做什麼?

屬性指派

我們有哪些手段給bean的屬性指派?

自動裝配

什麼是自動裝配?

Spring提供哪些注解可以幫我們實作自動裝配?

java規範提供了哪些注解幫助我們實作了自動裝配?

Spring提供的@Profile實作适配多環境?

注解版本的AOP如何玩?

Spring帶給了我們什麼?

假如我們是第一次學習Spirng,我們大可不必關心spring帶給我了我們什麼便利,因為spring大概是web階段築基需要學習的第一個主流架構,初學難免會遇見各種各樣的錯誤,是以盡管放心大膽的去學習如何使用就行了.先會用,其它的不用多想

過幾天,用着熟悉了如何使用,再考慮spring究竟帶給了我們什麼便利也不遲, 那麼,spring究竟帶給了我們什麼便利呢?

IOC(Inverse of Control),把對象的建立權反轉給Spring容器,實作了解耦

我們使用Spring提供給我們的注解,把bean注冊進IOC容器,進而把bean之間的依賴關系全部交給Spring管理,現在想想這絕對是一件超級贊的事情,可能原來的我會想,我自己可以new對象,幹嘛非讓Spring插一腳,是,假如我的項目就是個小demo全文一共三對象,完全顧的上來,那麼spring對我來說就是累贅,但是!慢慢的接觸的工程大了就會發現,離開了Spring,自己去管理那些對象之間的依賴會累死人的,而且SpringIOC的誕生也應正了bean的管理,完全不需要我們關系,我們關心的就是,我們如何向Spring索取依賴的Bean就行了,是以說Spring真的是web愛好者的小春天.

AOP(Aspect Oriented Programming),spring的aop

面向切面程式設計,aop說白了就是代碼複用,把大量的重複代碼從我們的業務邏輯中抽取出去,等程式運作的時候再動态的植入抽取出去的代碼,這是件要多優雅就多優雅的事情!

聲明式事務

後端天天和資料庫打交道,事務安全這樣的不可能遇不見,我們這一代學習的人還真的是幸運,因為spring提供的聲明式事務,我們不用再去苦苦的去寫程式設計式事務,而且,現在spring4全面支援注解開發,我們甚至連配置檔案都不用寫,加一個注解就引入了spring的事務子產品,激動不?

友善內建其他開源架構

spring最擅長的事情就是整合這使它的生态變的超級龐大

例外給大家推薦一篇很棒的部落格,裡面詳細圖文論述了,spring究竟帶給了我們什麼?

spring實作解耦論述

注解版Spring-IOC怎麼玩?

隻使用IOC,導入Spring-Context就可以

<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.7.RELEASE</version>           

這個過程,我們要注意IOC以下幾點

元件注冊及其過濾規則我串聯在下面相鄰的兩部分裡,這裡先提一下Spring提供的所有過濾規則類型

在spring啟動,開始掃描包,注冊元件時,如果我們想按照我們的需求往IOC裡面添加元件,就需要指定過濾規則,下面這五種類型的過濾規則,都源于我們在主配置類(相當于配置檔案)上添加的@ComponentScan()包掃描注解

按照注解過濾ANNOTATION

@ComponentScan(// 這個注解替換了原來配置檔案中的包掃描

value="com.changwu.tryspring",
useDefaultFilters=false,
includeFilters = {
  @Filter(type = FilterType.ANNOTATION, classes = Service.class)}
  )           

按照給定的類型過濾

@Configuration

value="com.changwu.tryspring",
    useDefaultFilters=false,
    includeFilters = {
     //隻要是給定的類型的類,就可以加載進IOC,包括它的子類
      @Filter(type = FilterType.ASSIGNABLE_TYPE,classes = BookDao.class)
    }
    )           

按照切面 (ASPECTJ) 基本不用

按照正則 REGEX

自定義規則 CUSTOM

value="com.changwu.tryspring",
useDefaultFilters=false,
includeFilters = {
       // 隻要是給定的類型的類,就可以加載進IOC,包括它的子類
        @Filter(type = FilterType.CUSTOM,classes = MyTypeFilter.class)
}           

)

注解版本中,配置類替換了原來的配置檔案

@Configuration 注解标記本類為配置類

那麼我不加@Configuration注解,這個類就不能成為配置類嗎? 裡面通過@Bean注解就添加不進bean來了? 不是的, @Configuration标記會被Spring使用Cglib技術攔截下來,換言之我們得到的bean,不再是我們自己new 的那種小白bean,而是被代理過的Bean

那麼什麼才是百分百的配置類呢? 配置類是我們調用如下代碼所需要的那個類

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();

applicationContext.register(配置類.class);           

如下代碼! 原來的包掃描被@ComponentScan 取代!!! 裡面可以配置多個資訊

基礎包資訊value="com.XXX"

排除被哪個注解标記的類excludeFilters = @Filter(type... classes...)

隻要被哪個注解标記的類,(配置檔案版本的分兩步!第一步禁用掉預設的過濾器),注解版同樣分兩步useDefaultFilters=false, includeFilters = @Filter(classes= Service.class)

excludeFilters 把bean排除出IOC容器!!! 一般都是按照注解, 後面是 !!注解!!的Class數組,

另外,過濾器Filter的過濾類型,預設為按照注解過濾!!!

還可以在配置類中往IOC容器中組成對象!!!@Bean,用的時候 @Autowired

預設情況下,無論擷取多少次,都是單執行個體的!!!

/**

  • 配置類,等同于原來的配置檔案

    *

  • @Author: Changwu
  • @Date: 2019/3/31 16:57

    */

value="com.changwu.tryspring",
     excludeFilters = @Filter( type =FilterType.ANNOTATION, classes = Repository.class)
    )           

// excludeFilters 把bean排除出IOC容器!!! 一般都是按照注解, 後面是 !!注解!!的Class數組,

public class MainConfig {

/**
 * 給容器注冊bean
 * 類型: 為傳回值的類型
 * id 預設使用方法名
 * @return
 */
@Bean(value = "stu")  // value可以自定義名字
public Student getStu(){
    return  new Student("張三",23);
}           

}

此外在java8中,有如下代碼,@ComponentSacn的倒數第二個注解是@Repeatble 意味着我們可以在主配置類上寫多個@ComponentScan

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.TYPE)

@Documented

@Repeatable(ComponentScans.class)

public @interface ComponentScan {...}

自定義TypeFilter的過濾規則

自定義注解,要求實作FilterType接口,實作他的match(),該方法裡面的兩個參數都是接口類型的,我們可以從它裡面取出關于IOC所掃描到的所有類的源資訊,滿足我們的預期,傳回true,表示允許注冊進IOC

public class MyTypeFilter implements TypeFilter {

*

  • @param metadataReader // 擷取到目前的正在掃描的類的資訊
  • @param metadataReaderFactory // 可以擷取到,其他任何類的資訊
  • @return
  • @throws IOException

@Override

public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {

AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();//擷取目前類的注解資訊
ClassMetadata classMetadata = metadataReader.getClassMetadata();// 擷取目前正在掃描類的 類資訊, 比如實作了什麼接口,有什麼方法
Resource resource = metadataReader.getResource();// 擷取類的資源資訊, 比如在那個盤,路徑

String className = classMetadata.getClassName();
System.out.println("---->"+className);
// 自定義,如果有@Repository注解,注入進去
Set<String> annotationTypes = annotationMetadata.getAnnotationTypes();

for (String annotationType : annotationTypes) {
    System.out.println("++++++"+annotationType);
}
if (annotationMetadata.hasAnnotation("org.springframework.stereotype.Service")){
    return true;
}           

/* if (className.contains("er")){

return true;           

}*/

return false;           

@Scope, 設定元件的作用域

容器中的對象預設都是單執行個體的!!!,我們使用@Scope 注解改變這種狀态

/*
String SCOPE_SINGLETON = "singleton";  單例
String SCOPE_PROTOTYPE = "prototype";  多例           
@Scope(scopeName = "prototype")
@Bean(value = "stu2")
public Student getStu2(){
    return new Student("李四",24);
}
           

觀察在單執行個體和多執行個體情況下,bean建立的時機

預設在單執行個體的情況下,ioc容器建立完,直接加載bean

但是以後每次擷取都會都是從IOC容器中擷取

懶加載@Lazy! 針對單執行個體Bean,我們知道,容器啟動的時候,就會建立一個唯一的bean,我們使用懶加載可以做到,在第一次使用的時候加載bean

多執行個體情況下,隻有在擷取類時,IOC容器才會建立bean,!!!

以後每次擷取Bean,ioc容器都會調用那個方法去new Bean

六種給容器注冊元件的方法

着重看一種,按照條件注冊bean @Conditional springboot底層大量的使用!!!

它的實作方式和自定義過濾條件相似

@Conditional : 按照條件注冊bean

// 既可以标在類上,也可以标在方法上

@Target({ElementType.TYPE, ElementType.METHOD})

public @interface Conditional {

/**
 * 所有的條件,都必須是注冊過的!!!.
 */
Class<? extends Condition>[] value();           

解釋一下怎麼用:

它是個接口,需要的屬性名為 value, 值是Class數組 ,可以進一步看到它的泛型是Condition接口 我們自己實作接口,重寫裡面的方法match(),根據方法提供的參數可以擷取到我們想要的任何資訊,傳回true表示滿足條件,注冊bean,否則不注冊

其他注冊元件的方法

方式1: 包掃描+注解 (@Cotroller @Service @Repository等等注解的類可以被IOC掃描到,添加進容器)

這種方法有局限性!!!, 隻能是我們自己寫的類, id為首字母小寫的類名

方式2: 在配置類内部 使用: @Bean注解,

更多的使用它注冊第三方bean

方式3: 在配置類頭上使用  @Import(XXX.class,YYY.class)

解放了第二種通過@Bean還的自己new的缺陷!!! 組冊進IOC中的元件的預設id,是元件的全類名

方式4: @Import({Color.class, MyImportSelector.class})

實作ImportSelector接口: 傳回需要導入的元件的全類名數組,完成批量導入

方式5: 手動注冊@Import({Color.class,MyImportSelector.class, MyImportBeanDefinitionRegisrar.class})

實作ImportBeanDefinitionRegistrar接口: 它裡面的registerBeanDefinitions()方法的第二個參數就是BeanDefinitionRegistry, 所有bean注冊進IOC容器都經由他完成,是以我們可以手動注冊bean, 還可以通過第一個參數擷取目前标注@Import注解的類的全部注解資訊,加上第二個參數可以擷取目前IOC容器的全部資訊,動态判斷是否要注入類到IOC

同時,第五種在SpringBoot中得到了大量的使用,實作SpringBoot的自動配置

方式6 : 使用Spring提供的FactoryBean (工廠bean)

自己實作FactoryBean接口,重寫三個方法

@Bean,把自己實作的工廠bean添加到IOC

測試自己實作的BeanFactory的類型,是我們指定的泛型的類型的!!!

想擷取到工廠的話, 需要添加字首&

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig02.class);

Object getFB1 = applicationContext.getBean("&getFB");

Object getFB2 = applicationContext.getBean("getFB");

Bean的生命周期

通過第一階段的學習,我們順利把bean的建立權反轉給了Spring,下面就來看看,IOC是如何控制Bean的生命周期的!

什麼是bean的生命周期?

其實,就是下面的過程

Bean建立------> 初始化------> 銷毀

在IOC管理bean生命周期的過程中,我們可以插手做什麼?

我們可以自定義bean的初始化和銷毀方法,bean在到達相應的生命周期時,ioc會調用我們指定的方法,施加在bean上

原來的配置檔案版本,需要我們寫 init-method 和distroy-method

如何實作?

方法1: 使用@Bean注解完成

@Bean(initMethod="init",destroyMethod = "destory")

測試類

@Test

public void text6(){
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(LifeConfig.class);
    Object car = applicationContext.getBean("car");
    System.out.println(car);
    applicationContext.close();
}           

單執行個體情況下: 對象建立後并指派好了後,執行init方法,容器關閉對象銷毀

多執行個體情況下: 對象建立後并指派好了後,執行init方法,IOC不會管理bean,需要我們自己去銷毀bean

用處?

在配置資料源是大量使用,對象建立後需要初始話很多的資料,對象銷毀了,很多資源要釋放

方法2: 讓Bean實作spring提供的兩個接口InitializingBean和DisposableBean,重寫這兩個接口的方法

public interface InitializingBean {

// Bean初始化後調用
void afterPropertiesSet() throws Exception;           

public interface DisposableBean {

// Bean銷毀後調用

void destroy() throws Exception;           

單例模式下: 在IOC一初始化就進行bean構造,并且執行完了的afterPropertiesSet()初始化,容器關閉,bean執行銷毀DisposableBean()

方法3: 使用JS205規範提供的兩個注解@PostConstruct和@PreDestory完成

@PostConstruct :在bean建立完成并且屬性指派好了後執行本初始化方法

@PreDestory: 在容器銷毀bean之前,通知我們進行清理工作

這兩個注解都是标注在方法上的!!!

@Retention (RUNTIME)

@Target(METHOD)

public @interface PostConstruct {

public @interface PreDestroy {

方法4: 使用Spring提供給我們的元件,接口BeanPostProcessor

這個元件很重要,Spring的底層,尤其是AOP底層大量使用它

裡面有兩個方法

public interface BeanPostProcessor {

// 該方法,會在我們說的前三種初始化方法調用之前, 提前調用!!!

// 傳回值,可以是我們新建立好的這個bean,也可以包裝一下bean 再傳回

/* Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean
 * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
 * or a custom init-method). The bean will already be populated with property values.
 * The returned bean instance may be a wrapper around the original.
 */
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;           

// 在上面我們所說的三種初始化方法調用之後,立刻調用!!!

Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;           

實作自己的BeanPostprocessor

@Component

public class MyBeanPostProcessor implements BeanPostProcessor {

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

System.out.println("postProcessBeforeInitialization執行了!!!"+"    beanname=="+beanName+"  bean=="+bean);
return bean;           

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

System.out.println("postProcessAfterInitialization執行了!!!"+"    beanname=="+beanName+"  bean=="+bean);
return bean;           

測試結果如下圖

劃重點!!! --- BeanPostprocessor意味bean的後置處理器,前面也說了,它是spring提供給我們的一個輔助類型的元件,我們可以自定義BeanPostProcessor,在所有滿足加入IOC容器的bean初始化方法前後執行BeanPostProcessor接口裡的postProcessAfterInitialization() 和 postProcessBeforeInitialization()方法,完成對bean的增強,AOP的底層使用的 後置處理器 是spring自動給我們加載進來的,他的這種特性,為AOP,動态植入代碼的實作,提供的前提

接口BeanPostProcessor運作原理

周遊得到容器中所有的BeanPostProcessor,挨個執行beforeInitialization,一旦傳回值為null,說明ioc在沒有這個對象,直接跳出for循環,不會執行BeanPostProcessor.postProcessBeforeInitialization()方法進行處理

populateBean(beanName,mdb,instanceWrapper);給bean的屬性指派

initlizeBean // 初始化

{

applyBeanPostProcessorBeforeInitialization()  // 前置處理
invokeInitMethods(beanName,wrappedBean,mdb); // 執行初始化方法
applyBeanPostProcessorAfterInitialization() ; // 後置處理           

通過這個繼承圖,可以看到,BeanPostProcessor作為後置處理器的頂級接口存在,程式運作打上斷點,也能看到我們自定義的MyBeanPostProcessor,另外需要我們關心的一個實作是 InstantiationAwareBeanPosProcessor這個子接口,AOP的實作,就應用到了它(第二篇部落格會記錄)

使用配置檔案給bean指派

<property name="age" value="18"/>
<property name="name" value="zhangsan"/>           

使用注解的方法,給bean指派

@Value

  1. 基本數值

    @Value("changwu")

private String name;

  1. {} SPEL表達式

    @Value(value ="#{2*2}")

private int age;

  1. ${} 取出配置檔案中的值

    @Value(value ="${student.age}")

其中取出配置檔案中的值,要在主配置類上添加@PropertySource(value = "classpath:/bean.properties")指明配置檔案的路徑

@Value+@PropertySource

前者使用${} 取出環境變量中的屬性(程式運作後配置檔案會加載進環境變量),後者給前者提供定位

@Repeatable(PropertySources.class) // 可以寫多個

public @interface PropertySource {

String name() default "";

//支援同時加載多個配置檔案

String[] value();

// 忽略檔案找不到

boolean ignoreResourceNotFound() default false;

//設定編碼

String encoding() default "";

  • Specify a custom {@link PropertySourceFactory}, if any.
  • By default, a default factory for standard resource files will be used.
  • @since 4.3
  • @see org.springframework.core.io.support.DefaultPropertySourceFactory
  • @see org.springframework.core.io.support.ResourcePropertySource

Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;

自動裝配?

自動裝配就是,Spring利用依賴注入DI,完成對IOC容器中的各個元件依賴關系的指派

@Autowired & @Qualifier & @Primary

@Autowired是Spring自己的注解

那些Bean,都在IOC中,通過@Autowired注解可以完成自動裝配

預設按照類型(XXX.class)去IOC中找到對應的元件

如果存在多個bean,就按照id找( @Autowired标記的引用名)

使用@Autowired,預設如果IOC中不存在該bean,就會報錯

通過設定@Autowired(required=false) 設定元件不必需存在于,IOC

@Autowired+@Qualifier 明确指定裝配的bean的id

@Qualifier("bookDao2")

@Autowired

private BookDao bookDao;

再強調一遍,如果是包掃描的話,Bean在IOC的id是類名首字母小寫,@Qualifier("XXX")不能亂寫,要麼是類名首字母小寫,要麼是我們通過@Bean("XXX")指定的id

@Primary 标記在我們手動添加進去的bean上,強制,首選注入!!!

但是,如果同時存在@Primary和@Qualifier 依然會裝配我們明确指定的Bean

// 構造器, 方法,參數,屬性,全能!!!

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})

public @interface Autowired {

/**
 * Declares whether the annotated dependency is required.
 * <p>Defaults to {@code true}.
 */
boolean required() default true;           

@Autowired标注在方法上,方法的參數,預設從容器中擷取

@Autowired标注在構造器上,構造器需要的參數,預設從容器中擷取

@Bean标注的方法,方法的參數,預設從容器中擷取,在參數位置的@Autowired可以省略不寫

@Resources(JSR205) & @Inject(JSR330)

java規範注解

@Resources

作用: 預設按照元件名稱裝配

缺點: 不能和@Qulifier和@Primary一起使用

@Inject

還麻煩! 需要我們導入依賴

<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>           

@Profile

Spring為我們提供的可以根據目前的環境,動态的激活和切換一系列元件的功能

比如在開發環境下,我們使用A資料庫, 測試環境使用B資料庫, 生産環境使用C資料庫

@Conditional(ProfileCondition.class)

public @interface Profile {

/**
 * The set of profiles for which the annotated component should be registered.
 */
String[] value();
           

支援寫到方法上!滿足@profile指定的環境下,方法的中元件的注入才會生效

支援寫到類上!滿足@profile指定的環境下,整個類的中元件的注入才會生效

準備工作,注冊三個資料源

@PropertySource("classpath:/dbProperlies.properties")

public class MainConfiguration {

@Value("${db.user}")
private String user;

@Value("${db.driverClass}")
private String driverClass;
           
@Profile("text")
@Bean("TextDBSource")
public DataSource dataSourceText(@Value("${db.password}") String pwd) {
    ComboPooledDataSource dataSource = new ComboPooledDataSource();
    dataSource.setUser(user);
    dataSource.setPassword(pwd);
    dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/text");
    try {
        dataSource.setDriverClass(driverClass);
    } catch (PropertyVetoException e) {
        e.printStackTrace();
    }
    return dataSource;
}

@Profile("dev")
@Bean("DevDBSource")
public DataSource dataSourceDev(@Value("${db.password}") String pwd) {
    ComboPooledDataSource dataSource = new ComboPooledDataSource();
    dataSource.setUser(user);
    dataSource.setPassword(pwd);
    dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/person");
    try {
        dataSource.setDriverClass(driverClass);
    } catch (PropertyVetoException e) {
        e.printStackTrace();
    }
    return dataSource;
}

@Profile("pro")
@Bean("ProductDBSource")
public DataSource dataSourceProduct(@Value("${db.password}") String pwd) {
    ComboPooledDataSource dataSource = new ComboPooledDataSource();
    dataSource.setUser(user);
    dataSource.setPassword(pwd);
    dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/ssh1");
    try {
        dataSource.setDriverClass(driverClass);
    } catch (PropertyVetoException e) {
        e.printStackTrace();
    }
    return dataSource;
}           

2 . 結合@Profile注解,在元件上做一個辨別,隻有滿足@Profile辨別條件的才會被注冊進IOC

加了環境辨別的bean,隻有那個環境被激活,才會注冊到容器中,預設是@Profile("default")

沒有環境辨別的bean,任何條件下都會加載進容器

如何改變環境?

使用代碼的方法

@Test

public void text13(){
    // 建立上下文
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
    // 設定需要激活的環境
    applicationContext.getEnvironment().setActiveProfiles("dev");    // 開發環境下注冊元件
    // 加載主配置類
    applicationContext.register(MainConfiguration.class);
    // 啟動重新整理容器
    applicationContext.refresh();
    String[] names = applicationContext.getBeanDefinitionNames();
    for (String name : names) {
        System.out.println(name);
    }
    applicationContext.close();
}           

注解版AOP怎麼玩?

AOP面向切面程式設計,指的是在程式運作期間,動态的将某段代碼,切入到指定方法的指定位置額運作的程式設計方式 

導入AOP子產品

<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.7.RELEASE</version>           

建立業務邏輯類

我們要做的就是在動态的在div運作前,運作後,出現異常時,正常結束,異常結束等不同情況下列印日志

public class MathAop {

public int div(int a,int b){
    return  a-b;
}           

定義一個日志切面類

切面類上的方法會動态的感覺到,切入點div的狀态,在其指定的狀态上,做出不同的反應而無序更改仍和div的代碼

五種通知方法,對應五種不同的注解,具體的細節,在下面的代碼中有注釋

@Aspect

public class AopLog {

抽取切入表達式

注解@Pointcut

表達式可以使用* 通配

execution(pulbic com.changwu.tryspring.aop.MathAop.*(int,int))

execution(* com.changwu.tryspring.aop.MathAop.div(..))

本類使用 : 直接用方法名--> pointCut()

其他類使用: 使用帶包名的全路徑--> com.changwu.tryspring.aop.AopLog.pointCut()

@Pointcut("execution(* com.changwu.tryspring.aop.MathAop.div(..))")
public void pointCut(){}           

前置通知

注解@Before("切入點表達式")

調用時機: 在目标方法執行前執行

可選參數: JoinPoint,裡面封裝着切入點方法的全部資訊,比如方法名,參數等等

@Before("com.changwu.tryspring.aop.AopLog.pointCut()")
public void before(JoinPoint joinPoint){
    Object[] args = joinPoint.getArgs();  // 擷取參數
    String name = joinPoint.getSignature().getName();  //  擷取方法名
    System.out.println("前置通知....參數:"+ Arrays.asList(args)+"  方法名: "+name);
}           

後置通知

注解@After("切入點表達式")

調用時機:無論方法正常結束還是異常結束,都調用

@After("execution(* com.changwu.tryspring.aop.MathAop.div(..))")
public void after(){
    System.out.println("後置通知執行了!!!");
}           

傳回通知

注解@AfterReturning(value="切入點表達式",returning="XXX")

調用時機: 方法正常傳回執行

注意點: 函數的入參,參數名和注解中的XXX要相同,裡面封裝着,函數的傳回值結果

@AfterReturning(value = "com.changwu.tryspring.aop.AopLog.pointCut()",returning = "result")
public void returning(Object result){
    System.out.println("切入點正常執行...運作結果..{"+result+"}");
}           

異常通知

注解@AfterThrowing(value="切入點表達式",throwing="XXX")

調用時機: 切入點出現異常

注意點:函數的入參,參數名和注解中的XXX要相同,裡面封裝着,函數的傳回值結果

XXX裡面封裝着方法的異常資訊

@AfterThrowing(value = "com.changwu.tryspring.aop.AopLog.pointCut()",throwing = "expection")
public void afterThrowing(Exception expection){
    System.out.println("切入點出現異常,異常結果..{"+expection+"}..");
}           

環繞通知

注解@Around("切入點表達式")

調用時機: 切入點執行前後都會執行

參數:ProceedingJoinPoint裡面封裝了關于切入點的所有資訊

proceedingJoinPoint.proceed();傳回值: 為切入點的傳回值, 必須傳回

環繞通知裡面的所有異常全部抛出去,,一旦我們try起來了,異常通知就擷取不到異常,進而傳回通知就認為方法是正常結束的,結果為NULL

@Around("com.changwu.tryspring.aop.AopLog.pointCut()")
public Object arounding(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

    System.out.println("環繞通知開始:  方法名:  "+
            proceedingJoinPoint.getSignature().getName() +
            "将要被執行   參數為:  "+Arrays.asList(proceedingJoinPoint.getArgs()));
    Object result = proceedingJoinPoint.proceed();// 代理調用方法,如過調用方法add抛出異常,就不會執行後面的代碼(是以要抛出去!)
    // 調用方法之後執行
    System.out.println("環繞通知,調用方法之後執行,擷取的結果是:"+result);
    return result;

}
           

注意點: 業務邏輯類,切面類都要添加進IOC

注意點: 切面類都要添加注解@Aspects

補充: JointPoint 可以擷取的切入點的資訊 而且,必須在參數的第一位

好繼續準備,回顧xml版本的spring開發方式需要我們添加如下的配置

基于注解,開啟切面--->@EnableAspectjAutoProxy

@EnableAspectJAutoProxy

@ComponentScan("com.changwu.tryspring.aop")

public class MainAopConfig {

測試:

public void text14(){

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainAopConfig.class);
// 根據類型擷取bean
MathAop mathAop = applicationContext.getBean(MathAop.class);
mathAop.div(2,1);
applicationContext.close();           

原文位址

https://www.cnblogs.com/ZhuChangwu/p/11356665.html