国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Spring IOC & AOP

這篇具有很好參考價(jià)值的文章主要介紹了Spring IOC & AOP。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

IOC容器

概念

IOC,全程Inversion of Control(控制反轉(zhuǎn))
通過(guò)控制反轉(zhuǎn)(創(chuàng)建對(duì)象的權(quán)限交給框架,所以叫反轉(zhuǎn))創(chuàng)建的對(duì)象被稱(chēng)為Spring Bean,這個(gè)Bean和用new創(chuàng)建出來(lái)的對(duì)象是沒(méi)有任何區(qū)別的。

官方解釋?zhuān)篠pring 通過(guò) IoC 容器來(lái)管理所有 Java 對(duì)象的實(shí)例化和初始化,控制對(duì)象與對(duì)象之間的依賴(lài)關(guān)系。
舉個(gè)例子:有一杯水,杯子相當(dāng)于IOC容器,杯子里面的水相當(dāng)于Bean對(duì)象,這個(gè)杯子從開(kāi)始裝水,到水被喝完結(jié)束這過(guò)程,就相當(dāng)于容器控制對(duì)象的生命周期(從對(duì)象被創(chuàng)建,到最后被銷(xiāo)毀的過(guò)程)

控制反轉(zhuǎn)

  • 控制反轉(zhuǎn)是一種思想而非技術(shù)。

  • 控制反轉(zhuǎn)是為了降低程序耦合度,提高程序擴(kuò)展力。

  • 控制反轉(zhuǎn),反轉(zhuǎn)的是什么?

    • 將對(duì)象的創(chuàng)建權(quán)利交出去,交給第三方容器負(fù)責(zé)。
    • 將對(duì)象和對(duì)象之間關(guān)系的維護(hù)權(quán)交出去,交給第三方容器負(fù)責(zé)。
  • 控制反轉(zhuǎn)這種思想如何實(shí)現(xiàn)呢?

    • DI(Dependency Injection):依賴(lài)注入

IOC過(guò)程說(shuō)明
Spring IOC & AOP,筆記,spring,java,后端

依賴(lài)注入

DI(Dependency Injection):依賴(lài)注入,依賴(lài)注入實(shí)現(xiàn)了控制反轉(zhuǎn)的思想。

依賴(lài)注入:

  • 指Spring創(chuàng)建對(duì)象的過(guò)程中,將對(duì)象依賴(lài)屬性(XML)通過(guò)配置進(jìn)行注入

依賴(lài)注入常見(jiàn)的實(shí)現(xiàn)方式包括兩種:

  • 第一種:set注入
  • 第二種:構(gòu)造注入

所以結(jié)論是:IOC 就是一種控制反轉(zhuǎn)的思想, 而 DI 是對(duì)IoC的一種具體實(shí)現(xiàn)。

Bean管理說(shuō)的是:Bean對(duì)象的創(chuàng)建,以及Bean對(duì)象中屬性的賦值(或者叫做Bean對(duì)象之間關(guān)系的維護(hù))。

依賴(lài)注入DI在Spring中的實(shí)現(xiàn)方式

Spring 的 IoC 容器就是 IoC思想的一個(gè)落地的產(chǎn)品實(shí)現(xiàn)。IoC容器中管理的組件也叫做 bean。在創(chuàng)建 bean 之前,首先需要?jiǎng)?chuàng)建IoC 容器。Spring 提供了IoC 容器的兩種實(shí)現(xiàn)方式:

①BeanFactory

這是 IoC 容器的基本實(shí)現(xiàn),是 Spring 內(nèi)部使用的接口。面向 Spring 本身,不提供給開(kāi)發(fā)人員使用。

②ApplicationContext

BeanFactory 的子接口,提供了更多高級(jí)特性。面向 Spring 的使用者,幾乎所有場(chǎng)合都使用 ApplicationContext 而不是底層的 BeanFactory。

③ApplicationContext接口 的主要實(shí)現(xiàn)類(lèi)

Spring IOC & AOP,筆記,spring,java,后端

類(lèi)型名 簡(jiǎn)介
ClassPathXmlApplicationContext 通過(guò)讀取類(lèi)路徑下的 XML 格式的配置文件創(chuàng)建 IOC 容器對(duì)象
FileSystemXmlApplicationContext 通過(guò)文件系統(tǒng)路徑讀取 XML 格式的配置文件創(chuàng)建 IOC 容器對(duì)象
ConfigurableApplicationContext ApplicationContext 的子接口,包含一些擴(kuò)展方法 refresh() 和 close() ,讓 ApplicationContext 具有啟動(dòng)、關(guān)閉和刷新上下文的能力。
WebApplicationContext 專(zhuān)門(mén)為 Web 應(yīng)用準(zhǔn)備,基于 Web 環(huán)境創(chuàng)建 IOC 容器對(duì)象,并將對(duì)象引入存入 ServletContext 域中。

基于XML管理Spring Bean管理

一般項(xiàng)目里很少用這種XML管理Bean的,這里就不演示了,想看的可以轉(zhuǎn)戰(zhàn)
尚硅谷Spring6

基于注解管理Spring Bean管理

何為注解?注解是代碼中的一種特殊標(biāo)記

開(kāi)啟組件掃描過(guò)程

用注解創(chuàng)建Spring Bean的過(guò)程
(沒(méi)有用SpringBoot,默認(rèn)只用Spring,如果用SpringBoot就可以用@ComponentScan等等來(lái)指定掃描路徑)

Spring 默認(rèn)不使用注解裝配 Bean,因此我們需要在 Spring 的 XML 配置中,通過(guò) context:component-scan 元素開(kāi)啟 Spring Beans的自動(dòng)掃描功能。開(kāi)啟此功能后,Spring 會(huì)自動(dòng)從掃描指定的包(base-package 屬性設(shè)置)及其子包下的所有類(lèi),如果類(lèi)上使用了 @Component 注解,就將該類(lèi)裝配到容器中。

看一下XML配置文件
com.atguigu.spring6這包下的文件就會(huì)被掃描,只要掃描到 @Component 注解,就將該類(lèi)裝配到容器中

<?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.xsd">
    <!--開(kāi)啟組件掃描功能,這個(gè)包下的文件就會(huì)被掃描-->
    <context:component-scan base-package="com.atguigu.spring6"></context:component-scan>
</beans>

注意:在使用 context:component-scan 元素開(kāi)啟自動(dòng)掃描功能前,首先需要在 XML 配置的一級(jí)標(biāo)簽 中添加 context 相關(guān)的約束。

情況一:最基本的掃描方式
和上面的例子一樣,當(dāng)前路徑下全掃

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

情況二:指定要排除的組件
可以排除某些不想被掃描的類(lèi)

<context:component-scan base-package="com.atguigu.spring6">
    <!-- context:exclude-filter標(biāo)簽:指定排除規(guī)則 -->
    <!-- 
 		type:設(shè)置排除或包含的依據(jù)
		type="annotation",根據(jù)注解排除,expression中設(shè)置要排除的注解的全類(lèi)名
		type="assignable",根據(jù)類(lèi)型排除,expression中設(shè)置要排除的類(lèi)型的全類(lèi)名
	-->
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <!--<context:exclude-filter type="assignable" expression="com.atguigu.spring6.controller.UserController"/>-->
</context:component-scan>

情況三:僅掃描指定組件

<context:component-scan base-package="com.atguigu" use-default-filters="false">
    <!-- context:include-filter標(biāo)簽:指定在原有掃描規(guī)則的基礎(chǔ)上追加的規(guī)則 -->
    <!-- use-default-filters屬性:取值false表示關(guān)閉默認(rèn)掃描規(guī)則 -->
    <!-- 此時(shí)必須設(shè)置use-default-filters="false",因?yàn)槟J(rèn)規(guī)則即掃描指定包下所有類(lèi) -->
    <!-- 
 		type:設(shè)置排除或包含的依據(jù)
		type="annotation",根據(jù)注解排除,expression中設(shè)置要排除的注解的全類(lèi)名
		type="assignable",根據(jù)類(lèi)型排除,expression中設(shè)置要排除的類(lèi)型的全類(lèi)名
	-->
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
	<!--<context:include-filter type="assignable" expression="com.atguigu.spring6.controller.UserController"/>-->
</context:component-scan>

使用注解定義 Bean

Spring 提供了以下多個(gè)注解,這些注解可以直接標(biāo)注在 Java 類(lèi)上,將它們定義成 Spring Bean。

功能其實(shí)都一樣,比如說(shuō)我的controller類(lèi)叫XXXcontroller,并不是非要用@Controller來(lái)標(biāo)記,用@Service等其他注解也一樣可以完成目標(biāo),這里用不同名字的注解是為了更好的區(qū)分類(lèi)功能。

注解 說(shuō)明
@Component 該注解用于描述 Spring 中的 Bean,它是一個(gè)泛化的概念,僅僅表示容器中的一個(gè)組件(Bean),并且可以作用在應(yīng)用的任何層次,例如 Service 層、Dao 層等。 使用時(shí)只需將該注解標(biāo)注在相應(yīng)類(lèi)上即可。
@Repository 該注解用于將數(shù)據(jù)訪(fǎng)問(wèn)層(Dao 層)的類(lèi)標(biāo)識(shí)為 Spring 中的 Bean,其功能與 @Component 相同。
@Service 該注解通常作用在業(yè)務(wù)層(Service 層),用于將業(yè)務(wù)層的類(lèi)標(biāo)識(shí)為 Spring 中的 Bean,其功能與 @Component 相同。
@Controller 該注解通常作用在控制層(如SpringMVC 的 Controller),用于將控制層的類(lèi)標(biāo)識(shí)為 Spring 中的 Bean,其功能與 @Component 相同。

注入注解@Autowired和@Resource

實(shí)驗(yàn)一:@Autowired注入

單獨(dú)使用@Autowired注解,默認(rèn)根據(jù)類(lèi)型裝配?!灸J(rèn)是byType】
看下Autowired源碼

package org.springframework.beans.factory.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
    boolean required() default true;
}

源碼中有兩處需要注意:

  • 第一處:該注解可以標(biāo)注在哪里?
    @Target的元注解就標(biāo)記了注解的位置

    • ElementType.CONSTRUCTOR 構(gòu)造方法上
    • ElementType.METHOD 方法上
    • ElementType.PARAMETER 形參上
    • ElementType.FIELD 屬性上
    • ElementType.ANNOTATION_TYPE 注解上
  • 第二處:該注解有一個(gè)required屬性,默認(rèn)值是true,表示在注入的時(shí)候要求被注入的Bean必須是存在的,如果不存在則報(bào)錯(cuò)。如果required屬性設(shè)置為false,表示注入的Bean存在或者不存在都沒(méi)關(guān)系,存在的話(huà)就注入,不存在的話(huà),也不報(bào)錯(cuò)。

@Autowired的幾種注入方式

這幾個(gè)場(chǎng)景都是說(shuō)明注解可以用的地方

①場(chǎng)景一:屬性注入(用的最多)

創(chuàng)建UserDao接口(假裝有數(shù)據(jù)庫(kù))
PS:Dao層和那個(gè)Mapper層是一個(gè)東西

package com.atguigu.spring6.dao;

public interface UserDao {

    public void print();
}

創(chuàng)建UserDaoImpl實(shí)現(xiàn)

package com.atguigu.spring6.dao.impl;

import com.atguigu.spring6.dao.UserDao;
import org.springframework.stereotype.Repository;

@Repository
public class UserDaoImpl implements UserDao {

    @Override
    public void print() {
        System.out.println("Dao/Mapper層執(zhí)行結(jié)束");
    }
}

創(chuàng)建UserService接口

package com.atguigu.spring6.service;

public interface UserService {

    public void out();
}

創(chuàng)建UserServiceImpl實(shí)現(xiàn)類(lèi)

package com.atguigu.spring6.service.impl;

import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Override
    public void out() {
        userDao.print();
        System.out.println("Service層執(zhí)行結(jié)束");
    }
}

創(chuàng)建UserController類(lèi)

package com.atguigu.spring6.controller;

import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {

    @Autowired
    private UserService userService;

    public void out() {
        userService.out();
        System.out.println("Controller層執(zhí)行結(jié)束。");
    }

}

測(cè)試一

package com.atguigu.spring6.bean;

import com.atguigu.spring6.controller.UserController;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserTest {

    private Logger logger = LoggerFactory.getLogger(UserTest.class);

    @Test
    public void testAnnotation(){
  		//從配置文件中讀取Bean配置信息
        ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
        //獲取Bean
        UserController userController = context.getBean("userController", UserController.class);
        //調(diào)用方法
        userController.out();
        logger.info("執(zhí)行成功");
    }
}

測(cè)試結(jié)果:

所有調(diào)用的方法均可以正常執(zhí)行
Spring IOC & AOP,筆記,spring,java,后端

以上構(gòu)造方法和setter方法都沒(méi)有提供,經(jīng)過(guò)測(cè)試,仍然可以注入成功。

②場(chǎng)景二:set注入

修改UserServiceImpl類(lèi)

package com.atguigu.spring6.service.impl;

import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    @Autowired
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void out() {
        userDao.print();
        System.out.println("Service層執(zhí)行結(jié)束");
    }
}

修改UserController類(lèi)

package com.atguigu.spring6.controller;

import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {

    private UserService userService;
	//這種就相當(dāng)于把整個(gè)Service注入了,那么userService就可以直接調(diào)用
    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public void out() {
        userService.out();
        System.out.println("Controller層執(zhí)行結(jié)束。");
    }

}

測(cè)試:成功調(diào)用

③場(chǎng)景三:構(gòu)造方法注入

修改UserServiceImpl類(lèi)

package com.atguigu.spring6.service.impl;

import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    @Autowired
    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void out() {
        userDao.print();
        System.out.println("Service層執(zhí)行結(jié)束");
    }
}

修改UserController類(lèi)

package com.atguigu.spring6.controller;

import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {

    private UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    public void out() {
        userService.out();
        System.out.println("Controller層執(zhí)行結(jié)束。");
    }

}

測(cè)試:成功調(diào)用

④場(chǎng)景四:形參上注入

修改UserServiceImpl類(lèi)

package com.atguigu.spring6.service.impl;

import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    private UserDao userDao;
	//直接把注解加入到參數(shù)上面,就可以自動(dòng)注入,后面就可以調(diào)用了
    public UserServiceImpl(@Autowired UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void out() {
        userDao.print();
        System.out.println("Service層執(zhí)行結(jié)束");
    }
}

修改UserController類(lèi)

package com.atguigu.spring6.controller;

import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {

    private UserService userService;

    public UserController(@Autowired UserService userService) {
        this.userService = userService;
    }

    public void out() {
        userService.out();
        System.out.println("Controller層執(zhí)行結(jié)束。");
    }

}

測(cè)試:成功調(diào)用

⑤場(chǎng)景五:只有一個(gè)構(gòu)造函數(shù),無(wú)注解(可以省略)

修改UserServiceImpl類(lèi)

package com.atguigu.spring6.service.impl;

import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    // @Autowired 這個(gè)注解在只有一個(gè)有參構(gòu)造是可以省略!
    private UserDao userDao;
	//唯一的一個(gè)有參構(gòu)造,但凡多一個(gè)都注入不了
    public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void out() {
        userDao.print();
        System.out.println("Service層執(zhí)行結(jié)束");
    }
}

測(cè)試通過(guò)

當(dāng)有參數(shù)的構(gòu)造方法只有一個(gè)時(shí),@Autowired注解可以省略。

說(shuō)明:有多個(gè)構(gòu)造方法時(shí)呢?大家可以測(cè)試(再添加一個(gè)無(wú)參構(gòu)造函數(shù)),測(cè)試報(bào)錯(cuò)

⑥場(chǎng)景六:@Autowired注解和@Qualifier注解聯(lián)合

@Autowired注解是默認(rèn)byType進(jìn)行注入的
@Qualifier注解是默認(rèn)根據(jù)名稱(chēng)進(jìn)行注入的
添加dao層實(shí)現(xiàn)

package com.atguigu.spring6.dao.impl;

import com.atguigu.spring6.dao.UserDao;
import org.springframework.stereotype.Repository;

@Repository
public class UserDaoRedisImpl implements UserDao {

    @Override
    public void print() {
        System.out.println("Redis Dao層執(zhí)行結(jié)束");
    }
}

此時(shí)UserDao 就會(huì)有兩個(gè)實(shí)現(xiàn)類(lèi)(之前還創(chuàng)建了一個(gè)UserDao的實(shí)現(xiàn)類(lèi),加上這個(gè)就兩個(gè))。因?yàn)槭歉鶕?jù)類(lèi)型注入的,裝配的過(guò)程就會(huì)出現(xiàn)兩個(gè)對(duì)應(yīng)UserDao的對(duì)象。

測(cè)試:測(cè)試異常

錯(cuò)誤信息中說(shuō):不能裝配,UserDao這個(gè)Bean的數(shù)量等于2

怎么解決這個(gè)問(wèn)題呢?當(dāng)然要byName,根據(jù)名稱(chēng)進(jìn)行裝配了。

修改UserServiceImpl類(lèi)
用Qualifier指定bean(實(shí)現(xiàn)類(lèi))的名字,默認(rèn)首字母小寫(xiě)

package com.atguigu.spring6.service.impl;

import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {
	//先類(lèi)型,類(lèi)型注入不了就按指定名字
    @Autowired
    @Qualifier("userDaoImpl") // 指定bean(實(shí)現(xiàn)類(lèi))的名字,默認(rèn)首字母小寫(xiě)
    private UserDao userDao;

    @Override
    public void out() {
        userDao.print();
        System.out.println("Service層執(zhí)行結(jié)束");
    }
}

總結(jié)

  • @Autowired注解可以出現(xiàn)在:屬性上、構(gòu)造方法上、構(gòu)造方法的參數(shù)上、setter方法上。
  • 當(dāng)帶參數(shù)的構(gòu)造方法只有一個(gè),@Autowired注解可以省略。()
  • @Autowired注解默認(rèn)根據(jù)類(lèi)型注入。如果要根據(jù)名稱(chēng)注入的話(huà),需要配合@Qualifier注解一起使用。

實(shí)驗(yàn)二:@Resource注入

@Resource注解也可以完成屬性注入。那它和@Autowired注解有什么區(qū)別?

  • @Resource注解是JDK擴(kuò)展包中的,也就是說(shuō)屬于JDK的一部分。所以該注解是標(biāo)準(zhǔn)注解,更加具有通用性。
  • @Autowired注解是Spring框架自己的,而非JDK的。
  • @Resource注解默認(rèn)根據(jù)名稱(chēng)裝配byName,未指定name時(shí),使用屬性名作為name。通過(guò)name找不到的話(huà)會(huì)自動(dòng)啟動(dòng)通過(guò)類(lèi)型byType裝配。
  • @Autowired注解默認(rèn)根據(jù)類(lèi)型裝配byType,如果想根據(jù)名稱(chēng)裝配,需要配合@Qualifier注解一起用。
  • @Resource注解用在屬性上、setter方法上。
  • @Autowired注解用在屬性上、setter方法上、構(gòu)造方法上、構(gòu)造方法參數(shù)上。

源碼這里就不詳細(xì)展開(kāi)了
Spring IOC & AOP,筆記,spring,java,后端

Resource注入的兩種情況

我們知道Resource會(huì)根據(jù)名字進(jìn)行注入,那么這個(gè)名字在制定了名字時(shí),也就是@Resource(value="名稱(chēng)")時(shí),就會(huì)按照這個(gè)指定的名字注入。
如果沒(méi)有指定這個(gè)名字,就會(huì)按照被注入類(lèi)的屬性名稱(chēng)(不是類(lèi)里的屬性,是哪個(gè)myUserDao的名稱(chēng))來(lái)注入。
不指定時(shí):
Spring IOC & AOP,筆記,spring,java,后端
**指定名稱(chēng)時(shí):**按照已經(jīng)設(shè)定好的屬性來(lái)注入
Spring IOC & AOP,筆記,spring,java,后端

幾種注入方式

①場(chǎng)景一:根據(jù)name注入

修改UserDaoImpl類(lèi)

package com.atguigu.spring6.dao.impl;

import com.atguigu.spring6.dao.UserDao;
import org.springframework.stereotype.Repository;

@Repository("myUserDao")
public class UserDaoImpl implements UserDao {

    @Override
    public void print() {
        System.out.println("Dao層執(zhí)行結(jié)束");
    }
}

修改UserServiceImpl類(lèi)

package com.atguigu.spring6.service.impl;

import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    @Resource(name = "myUserDao")
    private UserDao myUserDao;

    @Override
    public void out() {
        myUserDao.print();
        System.out.println("Service層執(zhí)行結(jié)束");
    }
}

測(cè)試通過(guò),這種是正常的根據(jù)設(shè)定好的名稱(chēng)去注入

②場(chǎng)景二:name未知注入

修改UserDaoImpl類(lèi),我這里定義好了名字,但是注入的位置沒(méi)有標(biāo)記

package com.atguigu.spring6.dao.impl;

import com.atguigu.spring6.dao.UserDao;
import org.springframework.stereotype.Repository;

@Repository("myUserDao")
public class UserDaoImpl implements UserDao {

    @Override
    public void print() {
        System.out.println("Dao層執(zhí)行結(jié)束");
    }
}

修改UserServiceImpl類(lèi)

package com.atguigu.spring6.service.impl;

import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    @Resource
    private UserDao myUserDao;

    @Override
    public void out() {
        myUserDao.print();
        System.out.println("Service層執(zhí)行結(jié)束");
    }
}

測(cè)試通過(guò)

當(dāng)@Resource注解使用時(shí)沒(méi)有指定name的時(shí)候,還是根據(jù)name進(jìn)行查找,這個(gè)name是屬性名。

③場(chǎng)景三 其他情況

修改UserServiceImpl類(lèi),userDao1屬性名不存在

package com.atguigu.spring6.service.impl;

import com.atguigu.spring6.dao.UserDao;
import com.atguigu.spring6.service.UserService;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    @Resource
    private UserDao userDao1;

    @Override
    public void out() {
        userDao1.print();
        System.out.println("Service層執(zhí)行結(jié)束");
    }
}

測(cè)試異常

根據(jù)異常信息得知:顯然當(dāng)通過(guò)name找不到的時(shí)候,自然會(huì)啟動(dòng)byType進(jìn)行注入,以上的錯(cuò)誤是因?yàn)閁serDao接口下有兩個(gè)實(shí)現(xiàn)類(lèi)導(dǎo)致的。所以根據(jù)類(lèi)型注入就會(huì)報(bào)錯(cuò)。

@Resource的set注入可以自行測(cè)試

總結(jié):

@Resource注解:默認(rèn)byName(指注解上的那個(gè)value名字)注入,沒(méi)有指定name時(shí)把屬性名當(dāng)做name,根據(jù)name找不到時(shí),才會(huì)byType注入。byType注入時(shí),某種類(lèi)型的Bean只能有一個(gè)。

手寫(xiě)IOC

我們都知道,Spring框架的IOC是基于Java反射機(jī)制實(shí)現(xiàn)的,下面我們先回顧一下java反射。

回顧反射

反射(上)

新建一個(gè)Car類(lèi)用于反射測(cè)試

public class Car {

    private String name;

    private int age;

    private String color;
	
	...一大堆setter getter constructor 這里不贅述了

    private void run() {
        System.out.println("私有方法-run.....");
    }
}

要測(cè)試的內(nèi)容:

  • 用反射獲取class對(duì)象的多種方式
  • 獲取構(gòu)造方法
  • 通過(guò)反射獲取類(lèi)中屬性
  • 獲取方法
通過(guò)反射獲取class對(duì)象的幾種方法
public class TestCar {
    //獲取Class對(duì)象的多種方式
    @Test
    public void test01() throws Exception {
        // 1 類(lèi)名.class
        Class class1=Car.class;
        // 2 對(duì)象.getClass()
        Car car = new Car();
        Class class2 = car.getClass();
        // 3 Class.forName("類(lèi)的全路徑")
        //因?yàn)橛锌赡苈窂讲粚?duì)獲取不到,所以要捕獲或者拋出異常
        Class class3 = Class.forName("com.cc.reflect.Car");
        //把剛剛獲取的類(lèi)實(shí)例化
        Car o=(Car) class3.getDeclaredConstructor().newInstance();
		//輸出類(lèi)的信息
        System.out.println(class1.toString());
        System.out.println(class2.toString());
        System.out.println(class3.toString());
    }
}

打印輸出,可以看到,直接獲取到了類(lèi)的路徑信息
Spring IOC & AOP,筆記,spring,java,后端

反射(中)
通過(guò)反射獲取class對(duì)象的構(gòu)造方法

這里有個(gè)條件,把Car的其中一個(gè)構(gòu)造方法改成私有的,原本是公開(kāi)的
Spring IOC & AOP,筆記,spring,java,后端

package com.cc.reflect;

import org.junit.jupiter.api.Test;
import java.lang.reflect.Constructor;

public class TestCar {
    //獲取構(gòu)造方法
    @Test
    public void test02() throws Exception {
        //獲取class文件
    	//這里的car有三個(gè)屬性,并且已經(jīng)寫(xiě)好了構(gòu)造、setter、getter
        Class clazz = Car.class;
        //獲取所有構(gòu)造方法(public的),放在數(shù)組里(這個(gè)數(shù)組因?yàn)槭潜闅v公有方法的,所以這個(gè)數(shù)組只能發(fā)現(xiàn)并存放一個(gè)共有的構(gòu)造)
        Constructor[] constructors=clazz.getConstructors();
        for (Constructor c: constructors) {
            System.out.println("構(gòu)造方法名稱(chēng)"+c.getName()+ "參數(shù)個(gè)數(shù)"+c.getParameterCount());
        }
		
		//獲取所有構(gòu)造方法(public和private的),放在數(shù)組里(這個(gè)數(shù)組因?yàn)槭潜闅v公有+私有方法的,所以這個(gè)數(shù)組發(fā)現(xiàn)并存放所有的構(gòu)造(無(wú)論是共有還是私有))
		//PS:Declared聲稱(chēng)的
        Constructor[] constructors1=clazz.getDeclaredConstructors();
        for (Constructor c: constructors1) {
            System.out.println("全部構(gòu)造方法名稱(chēng)"+c.getName()+ "參數(shù)個(gè)數(shù)"+c.getParameterCount());
        }
    }
}

運(yùn)行測(cè)試
Spring IOC & AOP,筆記,spring,java,后端

指定有參構(gòu)造創(chuàng)建對(duì)象
  • public的構(gòu)造方法
    @Test
    public void test03() throws Exception {
    	//獲取class文件
        Class clazz = Car.class;
        //指定有參數(shù)構(gòu)造創(chuàng)建對(duì)象
        //1 構(gòu)造public
        Constructor c1 = clazz.getConstructor(String.class, int.class, String.class);
        Car car1 = (Car)c1.newInstance("夏利", 10, "紅色");
        System.out.println(car1.toString());
    }

Spring IOC & AOP,筆記,spring,java,后端

  • private的構(gòu)造方法
    @Test
    public void test03() throws Exception {
        //獲取class文件
        Class clazz = Car.class;
        //2 構(gòu)造private
        Constructor c2 = clazz.getDeclaredConstructor(String.class, int.class, String.class);
        //默認(rèn)是訪(fǎng)問(wèn)不了私有方法的,把這個(gè)選項(xiàng)打開(kāi)才能訪(fǎng)問(wèn)到,實(shí)例化的方法和之前一樣
        c2.setAccessible(true);
        Car car2 = (Car)c2.newInstance("捷達(dá)", 20, "藍(lán)色");
        System.out.println(car2);
    }

Spring IOC & AOP,筆記,spring,java,后端

反射(下)

在類(lèi)里新增兩個(gè)方法,一個(gè)public一個(gè)private
Spring IOC & AOP,筆記,spring,java,后端

通過(guò)反射獲取屬性

主要是先獲取field屬性數(shù)組,遍歷,匹配,設(shè)置可進(jìn)入,set值。

@Test
    public void test04() throws Exception {
        //獲取class文件
        Class clazz = Car.class;
        //通過(guò)無(wú)參構(gòu)造創(chuàng)建car對(duì)象
        Car car = (Car)clazz.getDeclaredConstructor().newInstance();
        //獲取屬性,屬性集合是個(gè)數(shù)組
        Field[] fields=clazz.getDeclaredFields();
        //遍歷這個(gè)數(shù)組獲取屬性,操作屬性
        for (Field field : fields) {
            //當(dāng)匹配到name屬性時(shí),進(jìn)入操作
            if (field.getName().equals("name")){
                //將私有屬性允許操作
                field.setAccessible(true);
                //設(shè)置值(car對(duì)象的name屬性設(shè)置為 五菱宏光)
                field.set(car,"五菱宏光");
            }
        }
        System.out.println(car.toString());
    }
通過(guò)反射獲取類(lèi)內(nèi)方法
  • 獲取所有public方法
    @Test
    public void test05(){
        //創(chuàng)建對(duì)象
        Car car = new Car("奔馳",10,"黑色");
        Class clazz = car.getClass();
        //1.獲取類(lèi)內(nèi)部的public方法
        //和上面的屬性大同小異,也是通過(guò)用數(shù)組接收類(lèi)內(nèi)部的所有方法,組成一個(gè)Method數(shù)組
        Method[] methods=clazz.getMethods();
        for (Method m:methods){
            System.out.println("所有public方法的名字"+m.getName());
        }
    }
  • 在獲取的基礎(chǔ)上執(zhí)行某個(gè)public方法
    反射里面的執(zhí)行一般用invoke方法
    public Object invoke(Object obj, Object... args)Spring IOC & AOP,筆記,spring,java,后端
    @Test
    public void test05() throws Exception {
        //創(chuàng)建對(duì)象
        Car car = new Car("奔馳",10,"黑色");
        Class clazz = car.getClass();
        //1.獲取類(lèi)內(nèi)部的public方法
        //和上面的屬性大同小異,也是通過(guò)用數(shù)組接收類(lèi)內(nèi)部的所有方法,組成一個(gè)Method數(shù)組
        Method[] methods=clazz.getMethods();
        for (Method m:methods){
            //System.out.println("所有public方法的名字"+m.getName());
            if (m.getName().equals("test1")){
                //遍歷到名字為test1的方法時(shí)執(zhí)行
                m.invoke(car);
            }
        }
    }

執(zhí)行結(jié)果:
Spring IOC & AOP,筆記,spring,java,后端

  • 獲取所有private方法
    方法和上面一樣類(lèi)似,就是改一下調(diào)用的方法
    @Test
    public void test06() throws Exception {
        //創(chuàng)建對(duì)象
        Car car = new Car("奔馳",10,"黑色");
        Class clazz = car.getClass();
        //1.獲取類(lèi)內(nèi)部的public方法
        //也是通過(guò)用數(shù)組接收類(lèi)內(nèi)部的所有方法,組成一個(gè)Method數(shù)組,通過(guò)getDeclaredMethods獲取
        Method[] methods=clazz.getDeclaredMethods();
        for (Method m:methods){
            System.out.println("所有public+private方法的名字"+m.getName());
        }
    }

輸出結(jié)果:輸出所有私有和公有方法

  • 在獲取的基礎(chǔ)上執(zhí)行某個(gè)private方法
    @Test
    public void test06() throws Exception {
        //創(chuàng)建對(duì)象
        Car car = new Car("奔馳",10,"黑色");
        Class clazz = car.getClass();
        //1.獲取類(lèi)內(nèi)部的public方法
        //也是通過(guò)用數(shù)組接收類(lèi)內(nèi)部的所有方法,組成一個(gè)Method數(shù)組,通過(guò)getDeclaredMethods獲取
        Method[] methods=clazz.getDeclaredMethods();
        for (Method m:methods){
            if (m.getName().equals("test2")){
                //遍歷到名字為test2的私有方法時(shí)執(zhí)行
                m.setAccessible(true);
                m.invoke(car);
            }
        }
    }

執(zhí)行結(jié)果:
Spring IOC & AOP,筆記,spring,java,后端

手寫(xiě)IOC

先空著,占個(gè)位,以后再補(bǔ)

AOP

計(jì)算器的例子

聲明計(jì)算器接口Calculator,包含加減乘除的抽象方法

public interface Calculator {
    int add(int i, int j);
    int sub(int i, int j);
    int mul(int i, int j);
    int div(int i, int j);
}

創(chuàng)建對(duì)應(yīng)實(shí)現(xiàn)類(lèi)

public class CalculatorImpl implements Calculator {
    
    @Override
    public int add(int i, int j) {
    
        int result = i + j;
    
        System.out.println("方法內(nèi)部 result = " + result);
    
        return result;
    }
    .....很多接口方法實(shí)現(xiàn)
}

此時(shí)來(lái)了一個(gè)需求,我要在輸出的時(shí)候打日志log
Spring IOC & AOP,筆記,spring,java,后端
如果一行一行代碼寫(xiě),就會(huì)變成這樣

public class CalculatorLogImpl implements Calculator {
    
    @Override
    public int add(int i, int j) {
    
        System.out.println("[日志] add 方法開(kāi)始了,參數(shù)是:" + i + "," + j);
    
        int result = i + j;
    
        System.out.println("方法內(nèi)部 result = " + result);
    
        System.out.println("[日志] add 方法結(jié)束了,結(jié)果是:" + result);
    
        return result;
    }
    ......省略很多相似的實(shí)現(xiàn)方法,但是都是帶了很多l(xiāng)og
}

打個(gè)比方我要是有一天需要改每一個(gè)日志的輸出方法,那么就會(huì)非常難搞,每一個(gè)都得這么改,過(guò)于復(fù)雜。

引出的問(wèn)題

①現(xiàn)有代碼缺陷**

針對(duì)帶日志功能的實(shí)現(xiàn)類(lèi),我們發(fā)現(xiàn)有如下缺陷:

  • 對(duì)核心業(yè)務(wù)功能有干擾,導(dǎo)致程序員在開(kāi)發(fā)核心業(yè)務(wù)功能時(shí)分散了精力
  • 附加功能分散在各個(gè)業(yè)務(wù)功能方法中,不利于統(tǒng)一維護(hù)

②解決思路

解決這兩個(gè)問(wèn)題,核心就是:解耦。我們需要把附加功能從業(yè)務(wù)功能代碼中抽取出來(lái)。

③困難

解決問(wèn)題的困難:要抽取的代碼在方法內(nèi)部,靠以前把子類(lèi)中的重復(fù)代碼抽取到父類(lèi)的方式?jīng)]法解決。所以需要引入新的技術(shù)。

現(xiàn)有解決方案-代理模式

代理模式概念

①介紹
二十三種設(shè)計(jì)模式中的一種,屬于結(jié)構(gòu)型模式。它的作用就是通過(guò)提供一個(gè)代理類(lèi),讓我們?cè)谡{(diào)用目標(biāo)方法的時(shí)候,不再是直接對(duì)目標(biāo)方法進(jìn)行調(diào)用,而是通過(guò)代理類(lèi)間接調(diào)用。讓不屬于目標(biāo)方法核心邏輯的代碼從目標(biāo)方法中剝離出來(lái)——解耦。調(diào)用目標(biāo)方法時(shí)先調(diào)用代理對(duì)象的方法,減少對(duì)目標(biāo)方法的調(diào)用和打擾,同時(shí)讓附加功能能夠集中在一起也有利于統(tǒng)一維護(hù)。
Spring IOC & AOP,筆記,spring,java,后端
使用代理后:
相當(dāng)于把目標(biāo)對(duì)象用代理對(duì)象包裹起來(lái)
Spring IOC & AOP,筆記,spring,java,后端
②生活中的代理

  • 廣告商找大明星拍廣告需要經(jīng)過(guò)經(jīng)紀(jì)人
  • 合作伙伴找大老板談合作要約見(jiàn)面時(shí)間需要經(jīng)過(guò)秘書(shū)
  • 房產(chǎn)中介是買(mǎi)賣(mài)雙方的代理

③相關(guān)術(shù)語(yǔ)

  • 代理:將非核心邏輯剝離出來(lái)以后,封裝這些非核心邏輯的類(lèi)、對(duì)象、方法。
  • 目標(biāo):被代理“套用”了非核心邏輯代碼的類(lèi)、對(duì)象、方法。
靜態(tài)代理

創(chuàng)建靜態(tài)代理類(lèi)(代理類(lèi)是另外的類(lèi),不是在原有類(lèi)上操作的):
這也就是相當(dāng)于解耦了,唯一的區(qū)別是不用動(dòng)原來(lái)的核心代碼,但是本質(zhì)上相當(dāng)于copy出來(lái)了一份,在copy的基礎(chǔ)上進(jìn)行修改增強(qiáng)。

public class CalculatorStaticProxy implements Calculator {
    
    // 將被代理的目標(biāo)對(duì)象聲明為成員變量
    private Calculator target;
    
    public CalculatorStaticProxy(Calculator target) {
        this.target = target;
    }
    
    @Override
    public int add(int i, int j) {
    
        // 附加功能由代理類(lèi)中的代理方法來(lái)實(shí)現(xiàn)
        System.out.println("[日志] add 方法開(kāi)始了,參數(shù)是:" + i + "," + j);
    
        // 通過(guò)目標(biāo)對(duì)象來(lái)實(shí)現(xiàn)核心業(yè)務(wù)邏輯
        int addResult = target.add(i, j);
    
        System.out.println("[日志] add 方法結(jié)束了,結(jié)果是:" + addResult);
    
        return addResult;
    }
}

靜態(tài)代理確實(shí)實(shí)現(xiàn)了解耦,但是由于代碼都寫(xiě)死了,完全不具備任何的靈活性。就拿日志功能來(lái)說(shuō),將來(lái)其他地方也需要附加日志,那還得再聲明更多個(gè)靜態(tài)代理類(lèi),那就產(chǎn)生了大量重復(fù)的代碼,日志功能還是分散的,沒(méi)有統(tǒng)一管理。

提出進(jìn)一步的需求:將日志功能集中到一個(gè)代理類(lèi)中,將來(lái)有任何日志需求,都通過(guò)這一個(gè)代理類(lèi)來(lái)實(shí)現(xiàn)。這就需要使用動(dòng)態(tài)代理技術(shù)了。

動(dòng)態(tài)代理

我們創(chuàng)建一個(gè)代理類(lèi),來(lái)幫助我們?cè)诓僮髑昂筒僮骱筝敵鋈罩?br>Spring IOC & AOP,筆記,spring,java,后端
生產(chǎn)代理對(duì)象的工廠(chǎng)類(lèi):
源碼,看一下就行

public class ProxyFactory {

    private Object target;

    public ProxyFactory(Object target) {
        this.target = target;
    }

    public Object getProxy(){

        /**
         * newProxyInstance():創(chuàng)建一個(gè)代理實(shí)例
         * 其中有三個(gè)參數(shù):
         * 1、classLoader:加載動(dòng)態(tài)生成的代理類(lèi)的類(lèi)加載器
         * 2、interfaces:目標(biāo)對(duì)象實(shí)現(xiàn)的所有接口的class對(duì)象所組成的數(shù)組
         * 3、invocationHandler:設(shè)置代理對(duì)象實(shí)現(xiàn)目標(biāo)對(duì)象方法的過(guò)程,即代理類(lèi)中如何重寫(xiě)接口中的抽象方法
         */
        ClassLoader classLoader = target.getClass().getClassLoader();
        Class<?>[] interfaces = target.getClass().getInterfaces();
        InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                /**
                 * 幾個(gè)參數(shù)的含義
                 * proxy:代理對(duì)象
                 * method:代理對(duì)象需要實(shí)現(xiàn)的方法,即其中需要重寫(xiě)的方法
                 * args:method所對(duì)應(yīng)方法的參數(shù)
                 */
                Object result = null;
                try {
                    System.out.println("[動(dòng)態(tài)代理][日志] "+method.getName()+",參數(shù):"+ Arrays.toString(args));
                    result = method.invoke(target, args);
                    System.out.println("[動(dòng)態(tài)代理][日志] "+method.getName()+",結(jié)果:"+ result);
                } catch (Exception e) {
                    e.printStackTrace();
                    System.out.println("[動(dòng)態(tài)代理][日志] "+method.getName()+",異常:"+e.getMessage());
                } finally {
                    System.out.println("[動(dòng)態(tài)代理][日志] "+method.getName()+",方法執(zhí)行完畢");
                }
                return result;
            }
        };

        return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
    }
}

AOP概念以及相關(guān)術(shù)語(yǔ)

AOP(Aspect Oriented Programming)是一種設(shè)計(jì)思想,是軟件設(shè)計(jì)領(lǐng)域中的面向切面編程,它是面向?qū)ο缶幊痰囊环N補(bǔ)充和完善,它以通過(guò)預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理方式實(shí)現(xiàn),在不修改源代碼的情況下,給程序動(dòng)態(tài)統(tǒng)一添加額外功能的一種技術(shù)。利用AOP可以對(duì)業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低,提高程序的可重用性,同時(shí)提高了開(kāi)發(fā)的效率。

相關(guān)術(shù)語(yǔ)

①橫切關(guān)注點(diǎn)

分散在每個(gè)各個(gè)模塊中解決同一樣的問(wèn)題,如用戶(hù)驗(yàn)證、日志管理、事務(wù)處理、數(shù)據(jù)緩存都屬于橫切關(guān)注點(diǎn)。

從每個(gè)方法中抽取出來(lái)的同一類(lèi)非核心業(yè)務(wù)。在同一個(gè)項(xiàng)目中,我們可以使用多個(gè)橫切關(guān)注點(diǎn)對(duì)相關(guān)方法進(jìn)行多個(gè)不同方面的增強(qiáng)。

這個(gè)概念不是語(yǔ)法層面的,而是根據(jù)附加功能的邏輯上的需要:有十個(gè)附加功能,就有十個(gè)橫切關(guān)注點(diǎn)。
Spring IOC & AOP,筆記,spring,java,后端

②通知(增強(qiáng))

增強(qiáng),通俗說(shuō),就是你想要增強(qiáng)的功能,比如 安全,事務(wù),日志等。

每一個(gè)橫切關(guān)注點(diǎn)上要做的事情都需要寫(xiě)一個(gè)方法來(lái)實(shí)現(xiàn),這樣的方法就叫通知方法。

  • 前置通知:在被代理的目標(biāo)方法執(zhí)行
  • 返回通知:在被代理的目標(biāo)方法成功結(jié)束后執(zhí)行(正常結(jié)束
  • 異常通知:在被代理的目標(biāo)方法異常結(jié)束后執(zhí)行(拋出異常
  • 后置通知:在被代理的目標(biāo)方法最終結(jié)束后執(zhí)行(最后執(zhí)行
  • 環(huán)繞通知:使用try…catch…finally結(jié)構(gòu)圍繞整個(gè)被代理的目標(biāo)方法,包括上面四種通知對(duì)應(yīng)的所有位置
    Spring IOC & AOP,筆記,spring,java,后端
③切面

封裝通知方法的類(lèi)。

Spring IOC & AOP,筆記,spring,java,后端

④目標(biāo)

被代理的目標(biāo)對(duì)象。

⑤代理

向目標(biāo)對(duì)象應(yīng)用通知之后創(chuàng)建的代理對(duì)象。

⑥連接點(diǎn)

這也是一個(gè)純邏輯概念,不是語(yǔ)法定義的。

把方法排成一排,每一個(gè)橫切位置看成x軸方向,把方法從上到下執(zhí)行的順序看成y軸,x軸和y軸的交叉點(diǎn)就是連接點(diǎn)。通俗說(shuō),就是spring允許你使用通知的地方
Spring IOC & AOP,筆記,spring,java,后端

⑦切入點(diǎn)

定位連接點(diǎn)的方式。

每個(gè)類(lèi)的方法中都包含多個(gè)連接點(diǎn),所以連接點(diǎn)是類(lèi)中客觀存在的事物(從邏輯上來(lái)說(shuō))。

如果把連接點(diǎn)看作數(shù)據(jù)庫(kù)中的記錄,那么切入點(diǎn)就是查詢(xún)記錄的 SQL 語(yǔ)句。

Spring 的 AOP 技術(shù)可以通過(guò)切入點(diǎn)定位到特定的連接點(diǎn)。通俗說(shuō),要實(shí)際去增強(qiáng)的方法

切點(diǎn)通過(guò) org.springframework.aop.Pointcut 接口進(jìn)行描述,它使用類(lèi)和方法作為連接點(diǎn)的查詢(xún)條件。

作用
  • 簡(jiǎn)化代碼:把方法中固定位置的重復(fù)的代碼抽取出來(lái),讓被抽取的方法更專(zhuān)注于自己的核心功能,提高內(nèi)聚性。

  • 代碼增強(qiáng):把特定的功能封裝到切面類(lèi)中,看哪里有需要,就往上套,被套用了切面邏輯的方法就被切面給增強(qiáng)了。

基于注解AOP

前期說(shuō)明

Spring IOC & AOP,筆記,spring,java,后端
本質(zhì)上:動(dòng)態(tài)代理的底層是靜態(tài)代理,在配置好動(dòng)態(tài)代理類(lèi)之后,系統(tǒng)按照你的配置要求,對(duì)目標(biāo)類(lèi)生成靜態(tài)代理類(lèi),執(zhí)行的時(shí)候就去自動(dòng)執(zhí)行增強(qiáng)過(guò)的動(dòng)態(tài)代理類(lèi)了
Spring IOC & AOP,筆記,spring,java,后端

  • 動(dòng)態(tài)代理分為JDK動(dòng)態(tài)代理和cglib動(dòng)態(tài)代理
  • 當(dāng)目標(biāo)類(lèi)有接口的情況使用JDK動(dòng)態(tài)代理和cglib動(dòng)態(tài)代理,沒(méi)有接口時(shí)只能使用cglib動(dòng)態(tài)代理
  • JDK動(dòng)態(tài)代理動(dòng)態(tài)生成的代理類(lèi)會(huì)在com.sun.proxy包下,類(lèi)名為$proxy1,和目標(biāo)類(lèi)實(shí)現(xiàn)相同的接口
  • cglib動(dòng)態(tài)代理動(dòng)態(tài)生成的代理類(lèi)會(huì)和目標(biāo)在在相同的包下,會(huì)繼承目標(biāo)類(lèi)
  • 動(dòng)態(tài)代理(InvocationHandler):JDK原生的實(shí)現(xiàn)方式,需要被代理的目標(biāo)類(lèi)必須實(shí)現(xiàn)接口。因?yàn)檫@個(gè)技術(shù)要求代理對(duì)象和目標(biāo)對(duì)象實(shí)現(xiàn)同樣的接口(兄弟兩個(gè)拜把子模式)。
  • cglib:通過(guò)繼承被代理的目標(biāo)類(lèi)(認(rèn)干爹模式)實(shí)現(xiàn)代理,所以不需要目標(biāo)類(lèi)實(shí)現(xiàn)接口。
  • AspectJ:是AOP思想的一種實(shí)現(xiàn)。本質(zhì)上是靜態(tài)代理,將代理邏輯“織入”被代理的目標(biāo)類(lèi)編譯得到的字節(jié)碼文件,所以最終效果是動(dòng)態(tài)的。weaver就是織入器。Spring只是借用了AspectJ中的注解。

前期準(zhǔn)備

引入AOP的依賴(lài)
創(chuàng)建一個(gè)計(jì)算器類(lèi)的接口以及對(duì)應(yīng)實(shí)現(xiàn)類(lèi)

public interface Calculator {
    
    int add(int i, int j);
    
    int sub(int i, int j);
    
    int mul(int i, int j);
    
    int div(int i, int j);
    
}

實(shí)現(xiàn)類(lèi):

@Component
public class CalculatorImpl implements Calculator {
    
    @Override
    public int add(int i, int j) {
    
        int result = i + j;
    
        System.out.println("方法內(nèi)部 result = " + result);
    
        return result;
    }
    
   ....省略其他代碼
}

配置文件

首先要在xml配置文件中開(kāi)啟組件掃描
在Spring的配置文件中配置:
創(chuàng)建一個(gè)bean.xml文件,并寫(xiě)入相關(guān)配置

    <!--  開(kāi)啟組件掃描  -->
    <context:component-scan base-package="com.cc.annotationAop"></context:component-scan>
    <!--  開(kāi)啟AspectJ的自動(dòng)代理,為目標(biāo)對(duì)象自動(dòng)生成代理  -->
    <aop:aspectj-autoproxy />

切入點(diǎn)表達(dá)式語(yǔ)法

Spring IOC & AOP,筆記,spring,java,后端

看一下切入點(diǎn)表達(dá)式的構(gòu)成

切入點(diǎn)表達(dá)式:
execution(訪(fǎng)問(wèn)修飾符 增強(qiáng)方法返回類(lèi)型 方法所在類(lèi)全類(lèi)名.方法名(方法參數(shù)))

Spring IOC & AOP,筆記,spring,java,后端
來(lái)點(diǎn)細(xì)節(jié):

  • 用*號(hào)代替“權(quán)限修飾符”和“返回值”部分表示“權(quán)限修飾符”和“返回值”不限

  • 在包名的部分,一個(gè)“*”號(hào)只能代表包的層次結(jié)構(gòu)中的一層,表示這一層是任意的。

    • 例如:*.Hello匹配com.Hello,不匹配com.atguigu.Hello
  • 在包名的部分,使用“*…”表示包名任意、包的層次深度任意

  • 在類(lèi)名的部分,類(lèi)名部分整體用*號(hào)代替,表示類(lèi)名任意

  • 在類(lèi)名的部分,可以使用*號(hào)代替類(lèi)名的一部分

    • 例如:*Service匹配所有名稱(chēng)以Service結(jié)尾的類(lèi)或接口
  • 在方法名部分,可以使用*號(hào)表示方法名任意

  • 在方法名部分,可以使用*號(hào)代替方法名的一部分

    • 例如:*Operation匹配所有方法名以O(shè)peration結(jié)尾的方法
  • 在方法參數(shù)列表部分,使用(…)表示參數(shù)列表任意

  • 在方法參數(shù)列表部分,使用(int,…)表示參數(shù)列表以一個(gè)int類(lèi)型的參數(shù)開(kāi)頭

  • 在方法參數(shù)列表部分,基本數(shù)據(jù)類(lèi)型和對(duì)應(yīng)的包裝類(lèi)型是不一樣的

    • 切入點(diǎn)表達(dá)式中使用 int 和實(shí)際方法中 Integer 是不匹配的
  • 在方法返回值部分,如果想要明確指定一個(gè)返回值類(lèi)型,那么必須同時(shí)寫(xiě)明權(quán)限修飾符

    • 例如:execution(public int Service.(…, int)) 正確
      例如:execution(
      int *…Service.(…, int)) 錯(cuò)誤

@Before()和@After()

以前置通知和后置通知為例,對(duì)方法進(jìn)行增強(qiáng)。

// @Aspect表示這個(gè)類(lèi)是一個(gè)切面類(lèi)
@Aspect
// @Component注解保證這個(gè)切面類(lèi)能夠放入IOC容器
@Component
public class LogAspect {

    // 設(shè)置切入點(diǎn)和通知類(lèi)型
    // 切入點(diǎn)表達(dá)式:execution(訪(fǎng)問(wèn)修飾符 增強(qiáng)方法返回類(lèi)型 方法所在類(lèi)全類(lèi)名.方法名(方法參數(shù)))
    // 通知類(lèi)型:
    // 前置@Before(value = "切入點(diǎn)表達(dá)式配置切入點(diǎn)"),這里增強(qiáng)了CalculatorImpl的add方法的任意參數(shù)
    @Before(value = "execution(public int com.cc.annotationAop.CalculatorImpl.add(..))")
    public void beforeMethod(JoinPoint joinPoint){
        //可以根據(jù)JoinPoint獲取對(duì)象信息(方法名,參數(shù)等等)
        String methodName = joinPoint.getSignature().getName();
        String args = Arrays.toString(joinPoint.getArgs());
        System.out.println("Logger-->前置通知,方法名:"+methodName+",參數(shù):"+args);
    }
    // 后置@After(),這里增強(qiáng)了CalculatorImpl的任意方法的任意參數(shù)
    @After("execution(* com.cc.annotationAop.CalculatorImpl.*(..))")
    public void afterMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->后置通知,方法名:"+methodName);
    }
}

測(cè)試一下:
注意:AOP的情況下,手動(dòng)創(chuàng)建對(duì)象是沒(méi)辦法增強(qiáng)的

public class testAop {
    @Test
    public void testAdd(){
        //xml方式獲取對(duì)象(注意,對(duì)象必須由框架創(chuàng)建,不能手動(dòng)new,手動(dòng)new就不執(zhí)行AOP了)
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        Calculator calculator = ac.getBean( Calculator.class);
        calculator.add(1, 2);
    }
}

測(cè)試通過(guò),可以發(fā)現(xiàn),按照預(yù)想結(jié)果輸出,JoinPoint信息也能輸出
Spring IOC & AOP,筆記,spring,java,后端
以上就是兩種通知類(lèi)型,還有許多其他的通知形式

@AfterReturning()

和@After()差不太多,主要是多了一個(gè)方法返回值
如果和@After()同時(shí)存在,那么先執(zhí)行@AfterReturning()的增強(qiáng)內(nèi)容,再執(zhí)行@After()的增強(qiáng)內(nèi)容,優(yōu)先級(jí)略高
必須方法正常執(zhí)行結(jié)束以后,有返回值,才會(huì)觸發(fā)AfterReturning
如果方法都被異常中斷了,沒(méi)有返回值,那么返回值結(jié)果也就沒(méi)有意義了,所以觸發(fā)的條件就是方法正常結(jié)束
Spring IOC & AOP,筆記,spring,java,后端

	// 返回@AfterReturning() 在被代理的目標(biāo)方法成功結(jié)束后執(zhí)行,可以獲取到目標(biāo)方法的執(zhí)行結(jié)果
    // 注意,returning的返回值是增強(qiáng)方法結(jié)果的返回值,對(duì)應(yīng)的屬性名字要和傳入的參數(shù)名字保持一致
    //此處如果多個(gè)屬性的話(huà),value就要標(biāo)注了
    @AfterReturning(value = "execution(* com.cc.annotationAop.CalculatorImpl.*(..))",returning = "result")
    public void afterReturningMethod(JoinPoint joinPoint,Object result){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->返回通知,方法名:"+methodName+",結(jié)果:"+result);
    }

執(zhí)行測(cè)試
Spring IOC & AOP,筆記,spring,java,后端

@AfterThrowing()

在被代理的目標(biāo)方法異常結(jié)束后執(zhí)行,可以獲取異常信息

    // 異常@AfterThrowing()
    // 目標(biāo)方法執(zhí)行,在被代理的目標(biāo)方法拋出異常后執(zhí)行,可以獲取到目標(biāo)方法拋出的異常信息
    // 注意:注解里throwing屬性的名字,要與傳入?yún)?shù)的名稱(chēng)保持一致
    @AfterThrowing(value = "execution(* com.cc.annotationAop.CalculatorImpl.*(..))",throwing = "ex")
    public void afterThrowingMethod(JoinPoint joinPoint,Throwable ex){
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Logger-->異常通知,方法名:" + methodName + ",結(jié)果:" + ex);
    }

在對(duì)應(yīng)的類(lèi)中加入手動(dòng)異常
Spring IOC & AOP,筆記,spring,java,后端
執(zhí)行測(cè)試
Spring IOC & AOP,筆記,spring,java,后端

@Around()

(刪掉剛剛手動(dòng)異常的內(nèi)容)
環(huán)繞通知就是在之前增強(qiáng)的基礎(chǔ)上,再包一層,其他增強(qiáng)也會(huì)執(zhí)行
注意,這里如果想執(zhí)行方法,就不能用JoinPoint了,JoinPoint只能獲取方法信息,無(wú)法促使方法執(zhí)行,這里改用ProceedingJoinPoint對(duì)象,才可以執(zhí)行方法
環(huán)繞通知也有和AfterReturning一樣的返回值,可以操作返回值

    // 環(huán)繞@Around()
    // 在方法的執(zhí)行前后都會(huì)執(zhí)行,只有execution執(zhí)行點(diǎn)一個(gè)參數(shù)
    // 注意,這里如果想執(zhí)行方法,就不能用JoinPoint了,JoinPoint只能獲取方法信息,無(wú)法促使方法執(zhí)行
    // 這里改用ProceedingJoinPoint對(duì)象,可以執(zhí)行方法
    // 環(huán)繞通知也可以有AfterReturning的返回值,可以操作返回值
    @Around("execution(* com.cc.annotationAop.CalculatorImpl.*(..))")
    public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint){
        Object result = null;
        try {
            System.out.println("環(huán)繞通知-->目標(biāo)對(duì)象方法執(zhí)行之前");
            //目標(biāo)對(duì)象(連接點(diǎn))方法的執(zhí)行
            result = proceedingJoinPoint.proceed();
            System.out.println("環(huán)繞通知-->目標(biāo)對(duì)象方法返回值之后");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("環(huán)繞通知-->目標(biāo)對(duì)象方法出現(xiàn)異常時(shí)");
        } finally {
            System.out.println("環(huán)繞通知-->目標(biāo)對(duì)象方法執(zhí)行完畢");
        }
        return result;
    }

執(zhí)行測(cè)試
Spring IOC & AOP,筆記,spring,java,后端

通知類(lèi)型總結(jié)

  • 前置通知:在被代理的目標(biāo)方法執(zhí)行
  • 返回通知:在被代理的目標(biāo)方法成功結(jié)束后執(zhí)行(正常結(jié)束
  • 異常通知:在被代理的目標(biāo)方法異常結(jié)束后執(zhí)行(拋出異常
  • 后置通知:在被代理的目標(biāo)方法最終結(jié)束后執(zhí)行(最后執(zhí)行
  • 環(huán)繞通知:使用try…catch…finally結(jié)構(gòu)圍繞整個(gè)被代理的目標(biāo)方法,包括上面四種通知對(duì)應(yīng)的所有位置

各種通知的執(zhí)行順序:

  • Spring版本5.3.x以前:
    • 前置通知
    • 目標(biāo)操作
    • 后置通知
    • 返回通知或異常通知
  • Spring版本5.3.x以后:
    • 前置通知
    • 目標(biāo)操作
    • 返回通知或異常通知
    • 后置通知

重(chong)用切入點(diǎn)表達(dá)式

表達(dá)式可以復(fù)用,要不然每個(gè)都寫(xiě)一遍太麻煩,而且不好維護(hù)

注意:如果不是在同一個(gè)切面(切面類(lèi))使用的話(huà),比如:A切面類(lèi)里定義好的重用表達(dá)式,在B切面類(lèi)使用,就要在路徑前加上包名來(lái)區(qū)分

在重用表達(dá)式定義的類(lèi)里面使用,就直接用方法名即可
如果不在重用表達(dá)式定義的類(lèi)里面使用,需要包名+類(lèi)名+方法名

①重用切入點(diǎn)表達(dá)式聲明

@Pointcut("execution(* com.cc.annotationAop.CalculatorImpl.add(..))")
public void pointCut(){}

②在同一個(gè)切面(類(lèi))中使用

@Before("pointCut()")
public void beforeMethod(JoinPoint joinPoint){
    String methodName = joinPoint.getSignature().getName();
    String args = Arrays.toString(joinPoint.getArgs());
    System.out.println("Logger-->前置通知,方法名:"+methodName+",參數(shù):"+args);
}

③在不同切面(類(lèi))中使用

	//@Before("引用全路徑.類(lèi)名.表達(dá)式定義()")
    @Before("com.cc.annotationAop.LogAspect.pointCut()")
    public void beforeMethod(JoinPoint joinPoint){
        String methodName = joinPoint.getSignature().getName();
        String args = Arrays.toString(joinPoint.getArgs());
        System.out.println("Logger-->前置通知,方法名:"+methodName+",參數(shù):"+args);
    }

切面優(yōu)先級(jí)

相同目標(biāo)方法上同時(shí)存在多個(gè)切面時(shí),切面的優(yōu)先級(jí)控制切面的內(nèi)外嵌套順序

  • 優(yōu)先級(jí)高的切面:外面
  • 優(yōu)先級(jí)低的切面:里面

使用@Order注解可以控制切面的優(yōu)先級(jí):

  • @Order(較小的數(shù)):優(yōu)先級(jí)高
  • @Order(較大的數(shù)):優(yōu)先級(jí)低

Spring IOC & AOP,筆記,spring,java,后端

基于XML的AOP

前期準(zhǔn)備參考注解的AOP
具體實(shí)現(xiàn)的形式在bean.xml里面進(jìn)行

<context:component-scan base-package="com.atguigu.aop.xml"></context:component-scan>

<aop:config>
    <!--配置切面類(lèi)-->
    <aop:aspect ref="loggerAspect">
        <aop:pointcut id="pointCut" 
                   expression="execution(* com.atguigu.aop.xml.CalculatorImpl.*(..))"/>
        <aop:before method="beforeMethod" pointcut-ref="pointCut"></aop:before>
        <aop:after method="afterMethod" pointcut-ref="pointCut"></aop:after>
        <aop:after-returning method="afterReturningMethod" returning="result" pointcut-ref="pointCut"></aop:after-returning>
        <aop:after-throwing method="afterThrowingMethod" throwing="ex" pointcut-ref="pointCut"></aop:after-throwing>
        <aop:around method="aroundMethod" pointcut-ref="pointCut"></aop:around>
    </aop:aspect>
</aop:config>

現(xiàn)在項(xiàng)目上用的不多,就不詳細(xì)展開(kāi)了文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-570636.html

到了這里,關(guān)于Spring IOC & AOP的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶(hù)投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Spring核心思想之IOC和AOP

    Spring核心思想之IOC和AOP

    IOC和AOP不是Spring提出的,在spring之前就已經(jīng)存在,只不過(guò)更偏向于理論化,Spring在技術(shù)層次把這兩個(gè)思想做了?常好的實(shí)現(xiàn)(Java)。 什么是IoC? IoC Inversion of Control (控制反轉(zhuǎn)/反轉(zhuǎn)控制),注意它是?個(gè) 技術(shù)思想 ,不是?個(gè)技術(shù)實(shí)現(xiàn)。 描述的事情 :Java開(kāi)發(fā)領(lǐng)域?qū)ο蟮膭?chuàng)建,

    2024年02月09日
    瀏覽(22)
  • Spring IOC 與 AOP 基礎(chǔ)原理,一篇搞定

    控制反轉(zhuǎn),一切對(duì)象交給Spring來(lái)創(chuàng)建于管理,將創(chuàng)建與使用對(duì)象的代碼進(jìn)行分離作用。實(shí)現(xiàn)代碼的解耦。 因?yàn)橐郧暗膶?duì)象創(chuàng)建都是在程序的創(chuàng)建,管理。這是所謂的正轉(zhuǎn),如今的對(duì)象的創(chuàng)建是在IOC中,在 IOC Container中獲取。這就是反轉(zhuǎn)。 DI,denpendecy inject。依賴(lài)注入,在應(yīng)用

    2024年01月21日
    瀏覽(17)
  • 深入解析Spring的IOC與AOP及其在項(xiàng)目中的應(yīng)用

    在現(xiàn)代的軟件開(kāi)發(fā)中,為了提高代碼的可維護(hù)性、可擴(kuò)展性以及降低代碼的耦合度,使用設(shè)計(jì)模式和面向切面編程(AOP)成為了程序員們常用的技術(shù)手段。Spring作為一個(gè)優(yōu)秀的Java開(kāi)發(fā)框架,提供了IOC和AOP兩個(gè)核心特性,極大地簡(jiǎn)化了開(kāi)發(fā)工作。本文將深入探討Spring的IOC和AO

    2024年02月13日
    瀏覽(35)
  • 全面掌握Spring框架:深入解析IOC、AOP、事務(wù)管理與注解使用

    全面掌握Spring框架:深入解析IOC、AOP、事務(wù)管理與注解使用

    探索Spring框架的深層次知識(shí),包括Spring IOC容器的初始化流程、AOP的實(shí)現(xiàn)機(jī)制、事務(wù)管理的細(xì)節(jié)、循環(huán)依賴(lài)問(wèn)題的處理、條件注解的應(yīng)用、JavaConfig的使用方法、PostProcessor的角色、@Autowired和@Value注解的高級(jí)應(yīng)用,以及${}與#{}的區(qū)別。

    2024年03月13日
    瀏覽(33)
  • Spring02-Spring注解的使用、基于注解的IOC、純注解配置、整合Junit、AOP入門(mén)、基于配置文件的AOP、切入點(diǎn)表達(dá)式、基于配置的文件環(huán)繞通知

    學(xué)習(xí)基于注解的 IOC 配置,即注解配置 和 XML 配置要實(shí)現(xiàn)的功能都是一樣的,都是要降低程序間的耦合。只是配置的形式不一樣。 關(guān)于實(shí)際的開(kāi)發(fā)中到底使用xml還是注解,每家公司有著不同的使用習(xí)慣 , 所以這兩種配置方式我們都需要掌握。 把 Spring 的 xml 配置內(nèi)容改為使用

    2024年02月03日
    瀏覽(19)
  • Spring筆記之Spring對(duì)IoC的實(shí)現(xiàn)

    控制反轉(zhuǎn)是一種思想 控制反轉(zhuǎn)是為了降低程序耦合度,提高程序擴(kuò)展力,達(dá)到OCP原則,達(dá)到DIP原則。 控制反轉(zhuǎn),反轉(zhuǎn)的是什么? 將對(duì)象的創(chuàng)建權(quán)力交出去,交給第三方容器負(fù)責(zé) 將對(duì)象和對(duì)象之間關(guān)系的維護(hù)權(quán)交出去,交給第三方容器負(fù)責(zé) 控制反轉(zhuǎn)思想的實(shí)現(xiàn)方式:依賴(lài)注

    2024年02月14日
    瀏覽(15)
  • Spring學(xué)習(xí)筆記之Spring IoC注解式開(kāi)發(fā)

    注解的存在主要是為了簡(jiǎn)化XML的配置。Spring6倡導(dǎo)全注解開(kāi)發(fā) 注解怎么定義,注解中的屬性怎么定義? 注解怎么使用 通過(guò)反射機(jī)制怎么讀取注解 注解怎么定義,注解中的屬性怎么定義? 以上是自定義了一個(gè)注解:Component 該注解上面修飾的注解包括:Target注解和Retention注解,

    2024年02月12日
    瀏覽(29)
  • Spring AOP官方文檔學(xué)習(xí)筆記(二)之基于注解的Spring AOP

    1.@Aspect注解 (1) @Aspect注解用于聲明一個(gè)切面類(lèi),我們可在該類(lèi)中來(lái)自定義切面,早在Spring之前,AspectJ框架中就已經(jīng)存在了這么一個(gè)注解,而Spring為了提供統(tǒng)一的注解風(fēng)格,因此采用了和AspectJ框架相同的注解方式,這便是@Aspect注解的由來(lái),換句話(huà)說(shuō),在Spring想做AOP框架之前,

    2023年04月17日
    瀏覽(25)
  • Spring AOP官方文檔學(xué)習(xí)筆記(三)之基于xml的Spring AOP

    1.聲明schema,導(dǎo)入命名空間 (1)如果我們想要使用基于xml的spring aop,那么,第一步,我們需要在xml配置文件中聲明spring aop schema,導(dǎo)入命名空間,如下這是一個(gè)標(biāo)準(zhǔn)的模板 (2)在xml配置文件中,所有的切面以及通知等都必須放置于aop:config標(biāo)簽內(nèi) 2.聲明一個(gè)切面 3.聲明一個(gè)切

    2024年02月02日
    瀏覽(24)
  • Spring AOP官方文檔學(xué)習(xí)筆記(四)之Spring AOP的其他知識(shí)點(diǎn)

    Spring AOP官方文檔學(xué)習(xí)筆記(四)之Spring AOP的其他知識(shí)點(diǎn)

    1.選擇哪種AOP (1) 使用Spring AOP比使用完整版的AspectJ更方便簡(jiǎn)單,因?yàn)椴恍枰陂_(kāi)發(fā)和構(gòu)建過(guò)程中引入AspectJ編譯器以及織入器,如果我們只希望通知能夠在Spring Bean上執(zhí)行,那么選用Spring AOP就可以了,如果我們希望通知能夠在不由Spring所管理的對(duì)象上執(zhí)行,那么就需要使用AspectJ,如果

    2024年02月03日
    瀏覽(20)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包