SpringBoot是如何實現AOP和IOC的

SpringBoot是如何自動實現IOC和AOP的


一、概念解析(AOP & IOC/DI)

本文主要是通過代碼實現Spring Boot中的IOC和AOP配置。一些概念性的東西可以去我的博客瞭解。這裏的AOP和IOC是屬於Spring容器框架的範疇。和SpringBoot關係不大,因爲Spring Boot的初中是整合簡化了Spring和Spring MVC的開發。

1、關於IOC/DI的概念分析(簡單全面)。☞點擊鏈接前往;

這一片文章是多年前在學習使用Spring的過程中整理的。裏面包括了很多大牛的整理總結,是這一篇文章使我對這些生硬的概念有了深入的理解。

2、關於AOP的概念解析 ☞點擊鏈接前往

這是來源於《簡書》一個作者的文章分享。結合概念與實例的分析,讓你比較生動形象的理解和使用,這裏感謝作者的分享。

二、在項目中SpringBoot中式如何去配置IOC的

這裏標題可能有點會產生歧義,這裏旨在介紹在項目開發中是如何配置IOC的(注重的是功能使用,而不是Spring Boot底層的源碼閱讀)。

1、概念擴展:在Spring&Spring中我們式如何註冊Bean的?

在使用Spring,Spring MVC中我們使用通常頭兩種方式:

1)、基於XML的配置方式;

打個比方: 假如使用Spring基於XML的配置的話,開發的基本流程就是: Java Bean實體編寫——>XML(配置Bean)

	<bean id="helloWorld" class="com.test.spring.beans.HelloWorld">
	        <property name="name" value="Spring"></property>
	</bean>

2)、基於註解的方式配置;

舉個例子: 假如使用基於註解的配置方式的話,首先需要在Spring配置文件中開啓註解掃描,當然也可以寫Java配置文件,添加@Configuration用於開啓註解掃描。這裏方便起見就使用配置文件(XML)的方式做演示。然後開啓註解掃描也有兩種不同的配置方式:

而自動裝配實現就需要註解掃描,這時發現了兩種開啓註解掃描的方式,即**context:annotation-config/context:component-scan**

<context:annotation-config>:註解掃描是針對已經在Spring容器裏註冊過的Bean

<context:component-scan>:不僅具備<context:annotation-config>的所有功能,還可以在指定的package下面掃描對應的bean

在使用的過程中通常用: context:component-scan,可以定義掃描的具體位置。

<context:component-scan base-package="com.test"/>

在以上配置文件中開啓註解掃描機制之後呢,以後的Bean可開發就可以直接使用註解的方式定義Bean了(@Service)。使用@Autowired注入已經註冊的Bean。前提條件就是要保證添加註解的package在XML配置的包內,否則可能導致Bean未註冊的情況。

2、SpringBoot默認掃描路徑問題:

一般來說spring boot默認的掃描路徑是啓動類當前的包和子包。不在自動掃描路徑下,需要修改自定義掃描包路徑。

@SpringBootApplication
@EnableTransactionManagement(proxyTargetClass = true)
@MapperScan(basePackages = {"com.frame.springboot.dao", "com.frame.springboot.base"})
public class SpringbootApplication {
 
 
    public static void main(String[] args) {
 
        SpringApplication app = new SpringApplication(SpringbootApplication.class);
        app.addListeners(new MyApplicationStartedEventListener());
        app.run(args);
 
    }
 
    static class MyApplicationStartedEventListener implements ApplicationListener<ApplicationStartedEvent> {
        private Logger logger = LoggerFactory.getLogger(MyApplicationStartedEventListener.class);
 
        @Override
        public void onApplicationEvent(ApplicationStartedEvent event) {
            SpringApplication app = event.getSpringApplication();
            app.setBannerMode(Banner.Mode.OFF);// 不顯示banner信息
            logger.info("==MyApplicationStartedEventListener==");
        }
    }
 
}

例如以上這個類的包和子類。

3、自定義SpringBoot包掃描路徑問題:

在Spring中默認Bean註解掃描路徑是當前啓動類所在的包下和同級目錄下。但是當我們需要自定義包掃描路徑的時候可以使用下面的註解配置basePackages屬性指定。

@ComponentScan(basePackages = {"com.xxx.service1.*","com.xxx.service2.**"})

如下是一個自己寫的Demo的例子:
package com.xcy;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScans;
import org.springframework.context.annotation.Configuration;

/**
 * Maven命令行創建Spring Boot項目啓動類
 *
 */
@Configuration
@SpringBootApplication
@ComponentScan(basePackages = {"com.xcy.**"})
public class App 
{
    public static void main( String[] args )
    {
        System.out.println( "Hello World!" );
        SpringApplication.run(App.class,args);
    }
}

三、在項目中SpringBoot中式如何去配置AOP的

針對AOP的配置方案有兩種;

1、第一種: 包路徑掃描

第一種方式是針對包的位置進行代理,編寫切點,切面,通知等一步一步的操作。

具體實現方式可以參照這一篇博文:

execution(public * com.xcy..*.*(..))

2、第二種: 請求方式(格式)

針對請求方式,比如RequestMapping,PostMapping,GetMapping。以下的代碼就是針對該種方式進行操作寫的一個環繞通知,一步解決AOP代碼問題。

主要實現代碼如下:

package com.xcy.config;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

@Aspect
@Component
@Slf4j
public class BaseAspect {
    @Around("@annotation(org.springframework.web.bind.annotation.RequestMapping)"
            + " || @annotation(org.springframework.web.bind.annotation.PostMapping)"
            + " || @annotation(org.springframework.web.bind.annotation.GetMapping)"
            + "")
    public Object api(ProceedingJoinPoint joinPoint) {
        long startTime = System.currentTimeMillis();
        String method = joinPoint.getTarget().getClass().getSimpleName() + "." + joinPoint.getSignature().getName();
        // 調用接口方法
        Object response = null;
        try {
            Object body = joinPoint.getArgs().length > 0 ? joinPoint.getArgs()[0] : null;
            String jsonBody = StringUtils.isEmpty(body)?"請求參數爲空":body.toString();
            log.info("Api接口Start:{},輸入參數:{}",method,jsonBody);
            response = joinPoint.proceed();
            log.info("Api接口End:{},返回參數:{}",method,JSON.toJSON(response));
            long endTime = System.currentTimeMillis();
            log.info("程序運行時間:{}",(endTime - startTime) + "ms");
        } catch (Throwable e) {
            e.printStackTrace();
            log.info("Api接口Error:{},Cause:{}",method,e);
            return e.toString();
        } finally {

        }
        return response;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章