最詳細的spring(IOC、AOP)教程

SixA1024發表於2020-12-14

Spring(IOC、AOP)

概述

spring是一個輕量級的開源的javaEE框架

spring可以解決企業開發的複雜性

特點

  1. 方便解耦,簡化開發
  2. AOP程式設計支援
  3. 方便程式測試
  4. 方便整合各種優秀框架
  5. 降低javaEE API使用難度
  6. 方便進行事務的操作

入門案例

建立maven工程,匯入依賴

 <!-- spring的依賴,匯入這一個就可以了,它會把它所依賴的spring-beans、spring-core、spring-expression都匯入進來 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <!-- spring依賴的日誌包 -->
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1.1</version>
        </dependency>
		<!-- 測試包 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

編寫User類

public class User {

  public void test(){
    System.out.println("User。。。。。。。。。。。。。");
  }
}

編寫bean.xml,這是spring的配置檔案

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user" class="com.sixa.spring5.User"></bean>

</beans>

編寫測試類

@Test
  public void test1(){
    //載入配置檔案
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
    //獲取配置的bean
    User user = (User) applicationContext.getBean("user");
    user.test();     //User。。。。。。。。。。。。。
  }

IOC

控制反轉(Inversion of Control),是物件導向程式設計的一種設計原則,可以用來減低計算機程式碼之間的耦合度。通過控制反轉,物件在被建立的時候,由一個調控系統內所有物件的外界實體將其所依賴的物件的引用傳遞給它。也可以說,依賴被注入到物件中。

把物件的建立和物件之間的呼叫過程都交給spring去管理

底層原理

  1. xml解析,工程模式,反射

在這裡插入圖片描述

  1. IOC 介面

    IOC思想基於IOC容器完成,IOC容器底層就是物件工廠

    spring提供IOC容器實現兩種方式:

    ​ 1)BeanFactory:

    ​ IOC容器基本實現,是spring內部使用的介面,一般不推薦開發人員使用

    ​ 它在載入配置檔案的時候不會去建立物件,在獲取(使用)物件的時候才會去建立物件

    ​ 2)ApplicationContext:

    ​ BeanFactory的子介面,提供更多更強大的功能,一般推薦開發人員使用

    ​ 在載入配置檔案的時候就會建立物件

  2. ApplicationContext介面的實現類

    FileSystemXmlApplicationContext:對應碟符路徑

    ClassPathXmlApplicationContext:對應類路徑

Bean管理

spring建立物件

spring注入屬性

####1.基於xml方式

建立物件

<bean id="user" class="com.sixa.spring5.User"></bean> <!-- 預設會去呼叫無參構造 -->

#####注入屬性(DI)

######1)set方法注入

User類

public class User {

  private String name;

  public void setName(String name){
    this.name = name;
  }
  
  public void test(){
    System.out.println("name:"+name);
  }
}

bean.xml配置

 <bean id="user" class="com.sixa.spring5.User">
        <property name="name" value="張三"></property>
 </bean>

測試

@Test
  public void test1(){
    //載入配置檔案
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
    //獲取配置的bean
    User user = (User) applicationContext.getBean("user");
    user.test();  //name:張三
  }

######2)有參構造方法注入

User類

public class User {

  private String name;
  private int age;

  public User(String name, int age) {
    this.name = name;
    this.age = age;
  }

  public void test(){
    System.out.println("name:"+name+",age:"+age);
  }
}

bean.xml配置

 <bean id="user" class="com.sixa.spring5.User">
        <constructor-arg name="name" value="張三"></constructor-arg>
        <constructor-arg name="age" value="20"></constructor-arg>
        <!-- 用索引方法注入
        <constructor-arg index="0" value="張三"></constructor-arg>
        <constructor-arg index="1" value="20"></constructor-arg>
        -->
 </bean>

測試

 @Test
  public void test1(){
    //載入配置檔案
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
    //獲取配置的bean
    User user = (User) applicationContext.getBean("user");
    user.test(); //name:張三,age:20
  }

#####其他屬性注入

######字面量

1.注入null值

User類

public class User {

  private String name;
  private int age;

  public void setName(String name) {
    this.name = name;
  }

  public void setAge(int age) {
    this.age = age;
  }

  public void test(){
    System.out.println("name:"+name+",age:"+age);
  }
}

bean.xml配置

  <bean id="user" class="com.sixa.spring5.User">
        <!-- 注入null -->
        <property name="name">
            <null></null>
        </property>
       
        <property name="age" value="20"></property>
  </bean>

測試

 @Test
  public void test1(){
    //載入配置檔案
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
    //獲取配置的bean
    User user = (User) applicationContext.getBean("user");
    user.test();  //name:null,age:20
  }

2.注入特殊符號

bean.xml配置

  <bean id="user" class="com.sixa.spring5.User">
        <property name="name">
            <value><![CDATA[<<張三>>]]></value>
        </property>

        <property name="age" value="20"></property>
  </bean>

測試

 @Test
  public void test1(){
    //載入配置檔案
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
    //獲取配置的bean
    User user = (User) applicationContext.getBean("user");
    user.test();  //name:<<張三>>,age:20
  }

######外部bean注入

UserDao介面

public interface UserDao {
  void add();
}

UserDaoImp類

public class UserDaoImp implements UserDao {
  @Override
  public void add() {
    System.out.println("add.................");
  }
}

UserService介面

public interface UserService {
  void add();
}

UserServiceImp類

public class UserServiceImp implements UserService {
  private UserDao userDao;
  public void setUserDao(UserDao userDao){
    this.userDao = userDao;
  }
  @Override
  public void add() {
    userDao.add();
  }
}

bean.xml配置

<bean id="userService" class="com.sixa.spring5.service.UserServiceImp">
        <property name="userDao" ref="userDao"></property>
</bean>

<bean id="userDao" class="com.sixa.spring5.dao.UserDaoImp"></bean>

測試

 @Test
  public void test2(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
    UserService userService = (UserService) applicationContext.getBean("userService");
    userService.add();  //add.................
  }

######內部bean注入

Dept類

public class Dept {
  private String name;

  public void setName(String name) {
    this.name = name;
  }

  @Override
  public String toString() {
    return "Dept{" +
            "name='" + name + '\'' +
            '}';
  }
}

User類

public class User {

  private String name;
  private int age;
  private Dept dept;

  public void setDept(Dept dept) {
    this.dept = dept;
  }

  public void setName(String name) {
    this.name = name;
  }

  public void setAge(int age) {
    this.age = age;
  }

  public void test(){
    System.out.println("name:"+name+",age:"+age+",dept:"+dept);
  }
}

bean.xml配置

 <bean id="user" class="com.sixa.spring5.User">
        <property name="name" value="張三"></property>
        <property name="age" value="20"></property>
        <property name="dept">
            <bean id="dept" class="com.sixa.spring5.Dept">
                <property name="name" value="開發部"></property>
            </bean>
        </property>
  </bean>

測試

@Test
public void test1(){
  //載入配置檔案
  ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
  //獲取配置的bean
  User user = (User) applicationContext.getBean("user");
  user.test();  //name:張三,age:20,dept:Dept{name='開發部'}
}

######級聯賦值

第一種寫法

 <bean id="user" class="com.sixa.spring5.User">
        <property name="name" value="張三"></property>
        <property name="age" value="20"></property>
        <property name="dept" ref="dept"></property>
 </bean>
 <bean id="dept" class="com.sixa.spring5.Dept">
        <property name="name" value="開發部"></property>
 </bean>

第二種寫法,需要在User類中有getDept方法

User類

public class User {

  private String name;
  private int age;
  private Dept dept;

  public void setDept(Dept dept) {
    this.dept = dept;
  }

  //需要有get方法
  public Dept getDept() {
    return dept;
  }

  public void setName(String name) {
    this.name = name;
  }

  public void setAge(int age) {
    this.age = age;
  }

  public void test(){
    System.out.println("name:"+name+",age:"+age+",dept:"+dept);
  }
}

bean.xml配置

<bean id="user" class="com.sixa.spring5.User">
        <property name="name" value="張三"></property>
        <property name="age" value="20"></property>
        <property name="dept" ref="dept"></property>
        <property name="dept.name" value="軟體部"></property>
</bean>
<bean id="dept" class="com.sixa.spring5.Dept">
     <property name="name" value="開發部"></property>
</bean>

測試

  @Test
  public void test1(){
    //載入配置檔案
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
    //獲取配置的bean
    User user = (User) applicationContext.getBean("user");
    user.test();  //name:張三,age:20,dept:Dept{name='軟體部'}
  }
注入集合型別屬性

Course類

public class Course {
  private String name;

  public void setName(String name) {
    this.name = name;
  }

  @Override
  public String toString() {
    return "Course{" +
            "name='" + name + '\'' +
            '}';
  }
}

Student類

public class User {

  private String name;
  private int age;
  private Dept dept;

  public void setDept(Dept dept) {
    this.dept = dept;
  }

  public Dept getDept() {
    return dept;
  }

  public void setName(String name) {
    this.name = name;
  }

  public void setAge(int age) {
    this.age = age;
  }

  public void test(){
    System.out.println("name:"+name+",age:"+age+",dept:"+dept);
  }
}

bean.xml配置

     <bean id="student" class="com.sixa.spring5.Student">
        <!-- 注入陣列屬性 -->
        <property name="arrays">
            <array>
                <value>java基礎</value>
                <value>資料庫</value>
            </array>
        </property>
        <!-- 注入list屬性 -->
        <property name="list">
            <list>
                <value>張三</value>
                <value>小三</value>
            </list>
        </property>
        <!-- 注入map屬性 -->
        <property name="maps">
            <map>
                <entry key="JAVA" value="java"></entry>
                <entry key="PHP" value="php"></entry>
            </map>
        </property>
        <!-- 注入set屬性 -->
        <property name="sets">
            <set>
                <value>redis</value>
                <value>MySQL</value>
            </set>
        </property>
        <!-- 注入list屬性,集合型別是物件 -->
        <property name="courseList">
            <list>
                <ref bean="course1"></ref>
                <ref bean="course2"></ref>
            </list>
        </property>
    </bean>
    <bean id="course1" class="com.sixa.spring5.Course">
        <property name="name" value="spring"></property>
    </bean>
    <bean id="course2" class="com.sixa.spring5.Course">
        <property name="name" value="mybatis"></property>
    </bean>

測試

  @Test
  public void test3(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
    Student student = (Student)applicationContext.getBean("student");
    System.out.println(student.toString()); //Student{arrays=[java基礎, 資料庫], list=[張三, 小三], maps={JAVA=java, PHP=php}, sets=[redis, MySQL], courseList=[Course{name='spring'}, Course{name='mybatis'}
  }
提取公共集合

Book類

public class Book {
  private List<String> books;

  public void setBooks(List<String> books) {
    this.books = books;
  }

  @Override
  public String toString() {
    return "Book{" +
            "books=" + books +
            '}';
  }
}

bean.xml配置

更改約束檔案

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

bean配置

    <util:list id="bookList">
        <value>三體1</value>
        <value>三體2</value>
        <value>三體3</value>
    </util:list>

    <bean id="book" class="com.sixa.spring5.Book">
        <property name="books" ref="bookList"></property>
    </bean>

測試

  @Test
  public void test4(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
    Book book = (Book)applicationContext.getBean("book");
    System.out.println(book); //Book{books=[三體1, 三體2, 三體3]}
  }

2.基於註解

bean2.xml配置,開啟註解掃描

<?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.xsd
                           http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
  
    <context:component-scan base-package="com.sixa.spring5"></context:component-scan>

</beans>

UserDaoImp類

package com.sixa.spring5.dao;

import org.springframework.stereotype.Repository;
//註解分類Component,Controller(控制層),Service(service層),Repository(dao層)
//其實這些註解的作用都一樣,你也可以把Service註解加到控制層,但是大家都會遵守這個規範
//value可以省略,預設是該類的類名首字母小寫
@Repository(value = "userDao")
public class UserDaoImp implements UserDao {
  @Override
  public void add() {
    System.out.println("add.................");
  }
}

測試

  @Test
  public void test8(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean2.xml");
    UserDao userDao = (UserDao) applicationContext.getBean("userDao");
    userDao.add();  //add.................
  }

掃描細節配置

    <!-- 不使用預設的過濾器,只掃描Repository註解的類 -->
    <context:component-scan base-package="com.sixa.spring5" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>
    
	<!-- 使用預設的過濾器,但不掃描Component註解的類 -->
    <context:component-scan base-package="com.sixa.spring5">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Component"/>
    </context:component-scan>
基於註解注入屬性

@Autowired(根據屬性型別進行自動注入)、@Qualifier(根據屬性名稱注入)、@Resource(可以根據屬性型別或屬性名稱注入)

@Value(注入普通型別)

開啟註解掃描

<?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.xsd
                           http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.sixa.spring5"></context:component-scan>


</beans>

UserDaoImp類

@Repository
public class UserDaoImp implements UserDao {
  @Override
  public void add() {
    System.out.println("dao add.................");
  }
}

UserServiceImp類

@Service
public class UserServiceImp implements UserService {
  //不需要寫set方法
  @Autowired
  private UserDao userDao;
  @Override
  public void add() {
    System.out.println("service add............");
    userDao.add();
  }
}

測試

  @Test
  public void test9(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean2.xml");
    UserService userService = (UserService) applicationContext.getBean("userServiceImp");
    userService.add(); //service add............
                       //dao add.................
  }

@Qualifier要配合@Autowired一起使用

@Service
public class UserServiceImp implements UserService {
  //不需要寫set方法
  @Qualifier("userDaoImp")
  @Autowired
  private UserDao userDao;
  @Override
  public void add() {
    System.out.println("service add............");
    userDao.add();
  }
}

@Resource 既可以根據型別注入,也可以根據名稱注入

@Service
public class UserServiceImp implements UserService {
  //@Resource  根據型別注入
  @Resource(name = "userDaoImp") //根據名稱注入
  private UserDao userDao;
  @Override
  public void add() {
    System.out.println("service add............");
    userDao.add();
  }
}

@Value 注入普通屬性

  @Value(value = "abc")
  private String name;

#####完全註解開發

編寫配置類

@Configuration
@ComponentScan(basePackages = "com.sixa.spring5")
public class SpringConfig {

}

測試

  @Test
  public void test9(){                           //注意new的是這個類的物件
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
    UserService userService = (UserService) applicationContext.getBean("userServiceImp");
    userService.add();
  }

FactoryBean

普通bean:在配置檔案中定義bean的型別就是返回的型別

工廠bean:在配置檔案中定義bean的型別可以和返回型別不一樣

People類

public class People {
  private String name;

  public void setName(String name) {
    this.name = name;
  }

  @Override
  public String toString() {
    return "People{" +
            "name='" + name + '\'' +
            '}';
  }
}

MyBean類,實現FactoryBean介面

public class MyBean implements FactoryBean<People> {
  @Override
  public People getObject() throws Exception {
    People people = new People();//返回的型別取決於這的型別
    people.setName("張三");
    return people;
  }

  @Override
  public Class<?> getObjectType() {
    return null;
  }

  @Override
  public boolean isSingleton() {
    return false;
  }
}

bean.xml配置

 <bean id="myBean" class="com.sixa.spring5.MyBean"></bean>

測試

  @Test
  public void test5(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
    People people = (People) applicationContext.getBean("myBean");
    System.out.println(people);//People{name='張三'}
  }

bean的作用域

在spring裡面可以設定bean例項是單例項還是多例項

預設情況下,bean是單例項

 <bean id="book" class="com.sixa.spring5.Book"></bean>

測試

  @Test
  public void test4(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");//載入配置檔案的時候就會建立單例項物件
    Book book1 = (Book)applicationContext.getBean("book");
    Book book2 = (Book)applicationContext.getBean("book");
    System.out.println(book1.hashCode()); //1765250898
    System.out.println(book2.hashCode()); //1765250898
  }

在bean標籤裡,可以使用scop屬性來配置該bean是單例還是多例,singleton(單例,預設)、prototype(多例)

    <bean id="book" class="com.sixa.spring5.Book" scope="prototype"></bean>

測試

  @Test
  public void test4(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");//載入配置檔案的時候不會建立物件
    Book book1 = (Book)applicationContext.getBean("book");//getBean時建立多例項物件
    Book book2 = (Book)applicationContext.getBean("book");
    System.out.println(book1.hashCode()); //1765250898
    System.out.println(book2.hashCode()); //670971910
  }

bean的生命週期

  1. 通過構造器建立bean例項(無參構造)
  2. 為bean的屬性設定值和對其他bean的引用(set)
  3. 把bean例項傳遞給bean後置處理器的方法postProcessBeforeInitialization
  4. 呼叫bean的初始化的方法(需要配置初始化的方法)
  5. 把bean例項傳遞給bean後置處理器的方法postProcessAfterInitialization
  6. bean可以使用了(物件獲取到了)
  7. 當容器關閉時,呼叫bean的銷燬的方法(需要配置銷燬的方法)

Order類

public class Order {
  private String name;

  public Order(){
    System.out.println("第一步,呼叫建構函式");
  }

  public void setName(String name) {
    this.name = name;
    System.out.println("第二步,呼叫set方法設定值");
  }

  public void init(){
    System.out.println("第三步,呼叫初始化方法");
  }

  public void destroy(){
    System.out.println("第五步,呼叫銷燬方法");
  }

  @Override
  public String toString() {
    return "Order{" +
            "name='" + name + '\'' +
            '}';
  }
}

後置處理器,MyBeanPost類,實現BeanPostProcessor介面

public class MyBeanPost implements BeanPostProcessor {
  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    System.out.println("bean初始化之前執行的方法");
    return bean;
  }

  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    System.out.println("bean初始化之後執行的方法");
    return bean;
  }
}

bean1.xml配置


    <bean id="order" class="com.sixa.spring5.Order" init-method="init" destroy-method="destroy">
        <property name="name" value="手機"></property>
    </bean>
    <!-- 配置後置處理器 -->
    <bean id="myBeanPost" class="com.sixa.spring5.MyBeanPost"></bean>

測試

  @Test
  public void test6(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
    Order order = (Order) applicationContext.getBean("order");
    System.out.println("第四步,獲取物件");
    System.out.println(order);
    //關閉容器
    ((ClassPathXmlApplicationContext)applicationContext).close();
  }
//輸出結果
/**
第一步,呼叫建構函式
第二步,呼叫set方法設定值
bean初始化之前執行的方法
第三步,呼叫初始化方法
bean初始化之後執行的方法
第四步,獲取物件
Order{name='手機'}
第五步,呼叫銷燬方法
**/

xml自動裝配

根據指定的裝配規則(屬性名稱或屬性型別),spring自動將匹配的屬性值進行注入

通過名字注入byName

Dept類

public class Dept {
  private String name;

  public void setName(String name) {
    this.name = name;
  }

  @Override
  public String toString() {
    return "Dept{" +
            "name='" + name + '\'' +
            '}';
  }
}

Emp類

public class Emp {
  private Dept dept;//必須和注入bean的id一致

  public void setDept(Dept dept) {
    this.dept = dept;
  }

  @Override
  public String toString() {
    return "Emp{" +
            "dept=" + dept +
            '}';
  }
}

bean1.xml配置

    <bean id="emp" class="com.sixa.spring5.Emp" autowire="byName"></bean>

    <!-- id必須和類的屬性值一致 -->
    <bean id="dept" class="com.sixa.spring5.Dept">
        <property name="name" value="開發部"></property>
    </bean>

測試

  @Test
  public void test7(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean1.xml");
    Emp emp = (Emp)applicationContext.getBean("emp");
    System.out.println(emp);  //Emp{dept=Dept{name='開發部'}}
  }

通過型別注入byType

    <bean id="emp" class="com.sixa.spring5.Emp" autowire="byType"></bean>

	<!-- 相同型別的bean只能有一個 -->
    <bean id="dept" class="com.sixa.spring5.Dept">
        <property name="name" value="開發部"></property>
    </bean>

AOP

###概述

面向切面程式設計,通過預編譯方式和執行期間動態代理實現程式功能的統一維護的一種技術。AOP是OOP的延續,是軟體開發中的一個熱點,也是Spring框架中的一個重要內容,是函數語言程式設計的一種衍生範型。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。

底層原理

AOP底層使用動態代理

有兩種情況的動態代理

  1. 有介面的動態代理:JDK動態代理
  2. 沒有介面的動態代理:CGLIB動態代理

JDK動態代理

UserDao介面

public interface UserDao {
  int add(int a,int b);
  String update();
}

UserDaoImp類,實現UserDao介面

public class UserDaoImp implements UserDao {

  @Override
  public int add(int a, int b) {
    System.out.println("add方法執行了");
    return a + b;
  }

  @Override
  public String update() {
    System.out.println("update方法執行了");
    return "update";
  }
}

實現JDK動態代理

public class JDKProxy {
  public static void main(String[] args) {
    Class[] userDaoClass = {UserDao.class};
    UserDao userDao = new UserDaoImp();
    UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), userDaoClass, new UserDaoProxy(userDao));
    int result = userDaoProxy.add(1, 2);
    System.out.println(result);
    /**
    方法執行前。。。。。。。。addargs:[1, 2]
    add方法執行了
    方法執行後
    3
    **/
  }
}
class UserDaoProxy implements InvocationHandler{

  private Object obj;

  public UserDaoProxy(Object obj){
    this.obj = obj;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

    //方法執行前
    System.out.println("方法執行前。。。。。。。。"+method.getName()+"args:"+ Arrays.toString(args));
    //被增強的方法
    Object res = method.invoke(obj,args);
    //方法執行後
    System.out.println("方法執行後");
    return res;
  }
}

AOP術語

  1. 連線點:可以被增強的方法叫做連線點

  2. 切入點:實際被真正增強的方法叫做切入點

  3. 通知(增強):實際被增強的部分叫做通知(增強)

    五種通知型別:前置通知,後置通知,環繞通知,異常通知,最終通知

  4. 切面:把通知(增強)應用到切入點的過程叫做切面

AOP結合AspectJ

匯入依賴

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
        <dependency>
            <groupId>aopalliance</groupId>
            <artifactId>aopalliance</artifactId>
            <version>1.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.6.4</version>
        </dependency>

切入點表示式

作用:知道對哪個類中的哪個方法進行增強

語法結構:

execution([許可權修飾符][返回型別][類全路徑]方法名稱)

AOP操作(AspectJ註解)

User1類(被增強類)

@Component
public class User1 {
  public void add(){
    System.out.println("add...............");
  }
}

User1Proxy類(增強類)

@Component
@Aspect
public class User1Proxy {
  //前置通知
  @Before("execution(* com.sixa.spring5.aopanno.User1.add(..))")
  public void before(){
    System.out.println("before............");
  }

  //後置通知(返回通知),被增強方法有異常不執行
  @AfterReturning("execution(* com.sixa.spring5.aopanno.User1.add(..))")
  public void afterReturn(){
    System.out.println("afterReturning............"); 
  }
  //環繞通知
  @Around("execution(* com.sixa.spring5.aopanno.User1.add(..))")
  public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    System.out.println("around前置............");
    proceedingJoinPoint.proceed();
    System.out.println("around後置............");   //被增強方法有異常不執行
  }

  //後置通知
  @After("execution(* com.sixa.spring5.aopanno.User1.add(..))")
  public void after(){
    System.out.println("after............");
  }

  //異常通知,被增強方法有異常執行
  @AfterThrowing("execution(* com.sixa.spring5.aopanno.User1.add(..))")
  public void afterThrowing(){
    System.out.println("afterThrowing............");
  }
}

配置類

@Configuration
@ComponentScan(basePackages = "com.sixa.spring5")
@EnableAspectJAutoProxy
public class SpringConfig {

}

測試

  @Test
  public void test10(){
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
    User1 user1 = (User1) applicationContext.getBean("user1");
    user1.add(); 
    /**
    around前置............
    before............
    add...............
    around後置............
    after............
    afterReturning............
    **/
  }

抽取公共切入點

  //抽取公共切入點
  @Pointcut("execution(* com.sixa.spring5.aopanno.User1.add(..))")
  public void joinPoint(){
    
  }
  //前置通知
  @Before("joinPoint()")
  public void before(){
    System.out.println("before............");
  }

如果有兩個增強類對被增強類的同一個方法進行增強,可以用@Order註解設定優先順序

PersonProxy類

@Component
@Aspect
@Order(1)
public class PersonProxy {
  @After("execution(* com.sixa.spring5.aopanno.User1.add(..))")
  public void after(){
    System.out.println("person after............");
  }
}

User1Proxy類

@Component
@Aspect
@Order(2)
public class User1Proxy {
  //抽取公共切入點
  @Pointcut(value = "execution(* com.sixa.spring5.aopanno.User1.add(..))")
  public void myPoint(){

  }
  //前置通知
  @Before(value = "myPoint()")
  public void before(){
    System.out.println("before............");
  }

  //後置通知(返回通知),被增強方法有異常不執行
  @AfterReturning("execution(* com.sixa.spring5.aopanno.User1.add(..))")
  public void afterReturn(){
    System.out.println("afterReturning............");
  }
  //環繞通知
  @Around("execution(* com.sixa.spring5.aopanno.User1.add(..))")
  public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    System.out.println("around前置............");
    proceedingJoinPoint.proceed();
    System.out.println("around後置............");   //被增強方法有異常不執行
  }

  //後置通知
  @After("execution(* com.sixa.spring5.aopanno.User1.add(..))")
  public void after(){
    System.out.println("after............");
  }

  //異常通知,被增強方法有異常執行
  @AfterThrowing("execution(* com.sixa.spring5.aopanno.User1.add(..))")
  public void afterThrowing(){
    System.out.println("afterThrowing............");
  }
}

AOP操作(AspectJ XML配置)

Book類(被增強類)

public class Book {
  public void buy(){
    System.out.println("buy............");
  }
}

BookProxy類(增強類)

public class BookProxy {
  public void before(){
    System.out.println("before..........");
  }
}

bean3.xml配置

<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="book" class="com.sixa.spring5.aopxml.Book"></bean>
    <bean id="bookProxy" class="com.sixa.spring5.aopxml.BookProxy"></bean>

    <aop:config>
        <aop:pointcut id="p" expression="execution(* com.sixa.spring5.aopxml.Book.buy(..))"></aop:pointcut>

        <aop:aspect ref="bookProxy">
            <aop:before method="before" pointcut-ref="p"></aop:before>
        </aop:aspect>
    </aop:config>

</beans>

測試

  @Test
  public void test11(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean3.xml");
    Book book = (Book)applicationContext.getBean("book");
    book.buy();
    /**
    before..........
    buy............
    **/
  }

相關文章