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
-
基本數值
@Value("changwu")
private String name;
-
{} SPEL表達式
@Value(value ="#{2*2}")
private int age;
-
${} 取出配置檔案中的值
@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