Bootstrap

Spring IOC与AOP使用详解

Spring5 IOC与AOP使用详解

一、Spring功能模块划分图

在这里插入图片描述

二、初识IOC

1、什么是IOC?

  IOC(Inversion Of Control),即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,IOC意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好IOC呢?理解好IOC的关键是要明确“谁控制谁,控制什么,为何是反转?有反转就应该有正转,哪些方面反转了。”
  谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IOC是有专门一个容器来创建这些对象,即由IOC容器来控制对象的创建;谁控制谁?当然是IOC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是包括对象,还包括其他资源,比如文件等)。
  为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象。为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转。哪些方面反转了?依赖对象的获取被反转了。

2、IOC底层使用技术

(1)xml配置文件
(2)dom4j解析xml(Java的XML API,是jdom的升级品,用来读写XML文件)
(3)工厂设计模式
(4)反射

3、IOC优点
  1. 资源集中管理,实现资源的可配置和易管理。
  2. 降低了使用资源双方的依赖程度,也就是我们说的耦合度。

三、BeanFactory工厂方式实现解耦(IOC核心)

/**
 * 一个创建Bean对象的工厂
 *
 * Bean:在计算机英语中,有可重用组件的含义。
 * JavaBean:用java语言编写的可重用组件。
 *      javabean >  实体类
 *
 *   它就是创建我们的service和dao对象的。
 *
 *   第一个:需要一个配置文件来配置我们的service和dao
 *           配置的内容:唯一标识=全限定类名(key=value)
 *   第二个:通过读取配置文件中配置的内容,反射创建对象
 *
 *   我的配置文件可以是xml也可以是properties
 */
public class BeanFactory {
    //定义一个Properties对象
    private static Properties props;

    //定义一个Map,用于存放我们要创建的对象。我们把它称之为容器
    private static Map<String,Object> beans;

    //使用静态代码块为Properties对象赋值
    static {
        try {
            //实例化对象
            props = new Properties();
            //获取properties文件的流对象
            InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
            props.load(in);
            //实例化容器
            beans = new HashMap<String,Object>();
            //取出配置文件中所有的Key
            Enumeration keys = props.keys();
            //遍历枚举
            while (keys.hasMoreElements()){
                //取出每个Key
                String key = keys.nextElement().toString();
                //根据key获取value
                String beanPath = props.getProperty(key);
                //反射创建对象
                Object value = Class.forName(beanPath).newInstance();
                //把key和value存入容器中
                beans.put(key,value);
            }
        }catch(Exception e){
            throw new ExceptionInInitializerError("初始化properties失败!");
        }
    }

    /**
     * 根据bean的名称获取对象
     * @param beanName
     * @return
     */
    public static Object getBean(String beanName){
        return beans.get(beanName);
    }

    /**
     * 根据Bean的名称获取bean对象
     * @param beanName
     * @return

    public static Object getBean(String beanName){
        Object bean = null;
        try {
            String beanPath = props.getProperty(beanName);
//            System.out.println(beanPath);
            bean = Class.forName(beanPath).newInstance();//每次都会调用默认构造函数创建对象
        }catch (Exception e){
            e.printStackTrace();
        }
        return bean;
    }*/
}

四、核心容器的两个接口的区别

  • ApplicationContext:

    ​ 它在构建核心容器时,创建对象采用的策略是立即加载的方式(只要一读取完配置文件马上就创建配置文件中配置的对象)

  • Beanfactory:

    ​ 它在构建核心容器时,创建对象采用的策略是懒加载的方式(什么时候根据id获取对象了,什么时候才真正的创建对象)

五、spring对bean的管理细节

1、创建bean的三种方式

第一种方式:使用默认构造函数创建。
在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时。
采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则对象无法创建。

<?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">
    <!--
        1.常用的属性介绍
            id:类唯一标识
            class:类全路径(包路径)
        2.创建对象时,默认执行的是无参构造方法完成对象创建
    -->
	<bean id="user" class="com.ycxy.domain.User"></bean>
    
</beans>

第二种方式: 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)

<bean id="instanceFactory" class="com.ycxy.factory.InstanceFactory"></bean>
<bean id="account" factory-bean="instanceFactory" 
      factory-method="getAccount"></bean>

第三种方式:使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)

 <bean id="account" class="com.ycxy.factory.StaticFactory" 
       factory-method="getAccount"></bean>
2、bean对象的作用范围

bean标签的scope属性:
作用:用于指定bean的作用范围
取值: 常用的就是单例的和多例的
singleton:单例的(默认值)
prototype:多例的
request:作用于web应用的请求范围
session:作用于web应用的会话范围
global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session

 <bean id="user" class="com.ycxy.domain.User" scope="prototype"></bean>
3、bean对象的生命周期
单例对象
​	出生:当容器创建时对象出生
​	活着:只要容器还在,对象一直活着
​	死亡:容器销毁,对象消亡
​	总结:单例对象的生命周期和容器相同
多列对象
​	出生:当我们使用对象时spring框架为我们创建
​	活着:对象只要是在使用过程中就一直活着。
​	死亡:当对象长时间不用,且没有别的对象引用时,由Java的垃圾回收器回收。
<bean id="user" class="com.ycxy.domain.User"
      scope="prototype" init-method="init" destroy-method="destroy"></bean>

六、三种注入方式

1、构造器注入(通过有参构造器注入)

使用的标签:constructor-arg
标签出现的位置:bean标签的内部
标签中的属性
type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始
name:用于指定给构造函数中指定名称的参数赋值 (常用的)
以上三个用于指定给构造函数中哪个参数赋值
value:用于提供基本类型和String类型的数据
ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象

优势
在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功。
弊端
改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供。

<bean id="user" class="com.ycxy.domain.User">
    <constructor-arg name="name" value="张三"></constructor-arg>
    <constructor-arg name="age" value="18"></constructor-arg>
    <constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>

<!-- 配置一个日期对象 -->
<bean id="now" class="java.util.Date"></bean>
2、setter注入 (更常用)
1. 普通类型注入

涉及的标签:property
出现的位置:bean标签的内部
标签的属性
name:用于指定注入时所调用的set方法名称
value:用于提供基本类型和String类型的数据
ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象
优势
创建对象时没有明确的限制,可以直接使用默认构造函数
弊端
如果有某个成员必须有值,则获取对象是有可能set方法没有执行。

<bean id="book" class="com.ycxy.domain.Book">
       <!--特殊字符注入方式-->
        <property name="name">
            <value><![CDATA[<<Java核心功法>>]]></value>
        </property>
        <property name="price" value="101"></property>
    	<property name="author" value="xxx"></property>
    	<property name="date">
            <!-- 配置一个日期对象 -->
			<bean id="now" class="java.util.Date"></bean>
    	</property>
</bean>
2.集合类型注入
public class Student {

    //数组类型
    private String[] courses;
    //集合类型
    private List<String> list;

    public void setSet(Set<String> set) {
        this.set = set;
    }

    //set集合类型
    private Set<String> set;
    //Map集合类型
    private Map<String, String> map;

    public void setCourses(String[] courses) {
        this.courses = courses;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public void setMap(Map<String, String> map) {
        this.map = map;
    }
}

<?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:util="http://www.springframework.org/schema/util"
       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">
    <!--
        1.常用的属性介绍
            id:类唯一标识
            class:类全路径(包路径)
        2.创建对象时,默认执行的是无参构造方法完成对象创建
    -->
    <bean id="user" class="com.ycxy.domain.User">
        <property name="id" value="1111"></property>
        <!--特殊字符注入方式-->
        <property name="name">
            <value><![CDATA[<<张三>>]]></value>
        </property>
        <property name="date">
            <!-- 配置一个日期对象 -->
            <bean id="now" class="java.time.LocalDate">
                <constructor-arg name="year" value="2022"></constructor-arg>
                <constructor-arg name="month" value="4"></constructor-arg>
                <constructor-arg name="dayOfMonth" value="13"></constructor-arg>
            </bean>
        </property>
    </bean>

    <!--set注入-->
    <bean id="course" class="com.ycxy.collectiontype.Course">
        <property name="id">
            <value>1001</value>
        </property>
        <property name="name">
            <value>软件工程</value>
        </property>
    </bean>
	
    <util:list id="courseList">
        <value>Java核心功法2</value>
        <value>JavaWeb2</value>
    </util:list>
    <!--集合类型属性的配置-->
    <bean id="stu" class="com.ycxy.collectiontype.Student">
        <!--数组类型注入-->
        <property name="courses">
            <array>
                <value>Java课程</value>
                <value>数据库课程</value>
                <value>SpringBoot</value>
            </array>
        </property>
        <!--
            list集合类型注入
            方式一: 直接使用list标签
            方式二: 引入util约束, 使用util:list标签
        -->
        <!--<property name="list">-->
            <!--<list>-->
                <!--<value>Java核心功法</value>-->
                <!--<value>JavaWeb</value>-->
            <!--</list>-->
        <!--</property>-->
        <property name="list" ref="courseList"></property>

        <!--set集合类型注入-->
        <property name="set">
            <set>
                <value>Java核心卷一</value>
                <value>Java核心卷二</value>
            </set>
        </property>
        <!--map集合类型注入-->
        <property name="map">
            <map>
                <entry key="key1" value="面向对象编程"></entry>
                <entry key="key2" value="计算机网络技术"></entry>
            </map>
        </property>
        <!--list对象类型注入-->
        <property name="courseList">
            <list>
                <ref bean="course"></ref>
                <ref bean="course"></ref>
                <ref bean="course"></ref>
            </list>
        </property>
    </bean>
</beans>

3、接口注入

​ 接口注入模式因为历史较为悠久,在很多容器中都已经得到应用。但由于其在灵活性、易用性上不如其他两种注入模式,因而在 IOC 的专题世界内并不被看好。

七、Bean的生命周期

1、 bean 生命周期

​ (1)通过构造器创建bean实例(无参构造方法)

​ (2)为bean的属性设置值和对其他bean引用(调用set方法)

​ (3)调用bean 的初始化方法(需要进行配置初始化的方法,init-method=“initMethod” )

​ (4)bean的实例创建成功,可以使用

​ (5)当容器关闭时,调用bean的销毁方法(需要进行配置bean的销毁方法,destroy-method=“destroy”)

2、bean 的生命周期演示(五步)

在这里插入图片描述

3、加入bean 的后置处理器,bean 生命周期有七步

(1)通过构造器创建bean实例(无参构造方法)

(2)为bean的属性设置值和对其他bean引用(调用set方法)

(3)把bean实例传递bean后置处理器的前置方法(执行方法:postProcessBeforeInitialization(…))

(4)调用bean 的初始化方法

(5)把bean实例传递bean后置处理器的后置方法(执行方法:postProcessAfterInitialization(…))

(6)bean的实例创建成功,可以使用

(7)当容器关闭时,调用bean的销毁方法

4、bean 的后置处理器,bean 生命周期演示

Java代码

public class Book {

    private String name;

    public Book(){
        System.out.println("第一步, 调用无参构造方法!");
    }

    public void setName(String name){
        this.name = name;
        System.out.println("第二步, 调用set方法设置属性值...");
    }

    public void initMethod(){
        System.out.println("第四步调用初始化方法...");
    }

    public void destroyMethod(){
        System.out.println("第七步调用销毁方法!");
    }
}

public class MyBeanPost implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("第三步,在初始化之前执行的方法...");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("第五步,在初始化之后执行的方法...");
        return bean;
    }
}

public class TestMyBeanPost {

    public static void main(String[] args) {
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean2.xml");
        ac.getBean("book", Book.class);
        System.out.println("第六步, 获取bean对象...");
        ac.close();
    }
}

xml配置

<bean id="book" class="com.ycxy.bean.Book" init-method="initMethod" destroy-method="destroyMethod">
      <property name="name" value="Java面试宝典"></property>
</bean>

<bean id="myBeanPost" class="com.ycxy.bean.MyBeanPost"></bean>

运行结果
在这里插入图片描述

八、外部配置文件引入

1. 添加标签约束

2. 添加jdbc.properties配置文件
properties
driverClass=com.mysql.cj.jdbc.Driver
jdbcUrl=jdbc:mysql://localhost:3306/mybatis
username=root
password=123
3. xml配置文件如下
<?xml version="1.0" encoding="UTF-8"?>
	<!--suppress SpringFacetInspection -->
	<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 http://www.springframework.org/schema/context/spring-context.xsd">
	
	    <!--引入jdbc配置文件-->
	    <context:property-placeholder location="classpath:jdbc.properties"/>
	
	    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
	        <property name="driverClassName" value="${driverClass}"></property>
	        <property name="url" value="${jdbcUrl}"></property>
	        <property name="username" value="${username}"></property>
	        <property name="password" value="${password}"></property>
	    </bean>
	
	</beans>

九、IOC操作Bean基于注解方式

1、Spring针对Bean管理中创建对象提供的注解
  • @Component
  • @Service
  • @Controller
  • @Repository

注:上面四个注解的功能是一样的,都可以用来创建Bean实例

2、基于注解方式实现对象的创建
第一步:引入依赖

在这里插入图片描述

第二步:开启组件扫描
<?xml version="1.0" encoding="UTF-8"?>
<!--suppress SpringFacetInspection -->
<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 http://www.springframework.org/schema/context/spring-context.xsd">

    <!--开启组件扫描
        1. 如果扫描多个包, 多个包可以使用逗号分割开
        2. 扫描上层目录
    -->
    <!--<context:component-scan base-package="com.ycxy.annotation.dao, com.ycxy.annotation.pojo"/>-->
    
    <!--扫描包名为com.ycxy.annotation下的所有文件-->
    <context:component-scan base-package="com.ycxy.annotation"/>
    
    <!--示例1
        use-default-filters="false" 表示现在不适用默认filter, 自己配置filter
        context:include-filter 设置哪些内容扫描
        type="annotation" expression="org.springframework.stereotype.Service"
        表示只扫描该包下的所有的@Service注解
    -->
    <!--
    <context:component-scan base-package="com.ycxy.annotation" use-default-filters="false">
        <context:include-filter type="annotation"
                                expression="org.springframework.stereotype.Service"/>
    </context:component-scan>
	-->
    <!--示例2
		*上面的也必须开启扫描该包下所有文件, 否则不仅仅只是不扫描@Controller注解, 其它注解也不会扫描
        context:exclude-filter 设置哪些内容不进行扫描
        type="annotation" expression="org.springframework.stereotype.Controller"
        表示不扫描该包下的所有的@Controller注解
    -->
    <context:component-scan base-package="com.ycxy.annotation" use-default-filters="false">
        <context:exclude-filter type="annotation" 
                                expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

    
</beans>
第三步:创建类,在类上面添加创建对象的注解

若不写value属性值,则默认实例id为类名称,并且第一个字母小写,如下图:(bean的id为 userService)
在这里插入图片描述

测试代码

import com.ycxy.annotation.service.UserService;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestSpringAnnotation {

    ApplicationContext ac;

    @Before
    public void before(){
        ac = new ClassPathXmlApplicationContext("bean3.xml");
    }

    @Test
    public void test01(){
        UserService userService = ac.getBean("userService", UserService.class);
        System.out.println(userService);
        userService.add();
    }
}

测试结果
在这里插入图片描述

3、基于注解方式实现属性注入

@Autowried:根据属性类型进行自动装配

@Qualifier:根据属性名称进行注入, 配合@Autowried使用

@Resource:可以根据类型注入,可以根据名称注入

@Value:注入普通类型属性

4、完全注解开发

(1)创建配置类,代替xml配置文件
在这里插入图片描述

(2)测试代码

package com.ycxy.annotation;

import com.ycxy.annotation.config.SpringConfig;
import com.ycxy.annotation.service.UserService;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class TestSpringAnnotation {

    ApplicationContext ac;

    @Before
    public void before(){
        ac = new AnnotationConfigApplicationContext(SpringConfig.class);
    }

    @Test
    public void test(){
        UserService userService = ac.getBean("userService", UserService.class);
        System.out.println(userService);
        userService.add();
    }
}

十、AOP

1、什么是AOP?

(1)面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而是的业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

(2)不通过修改源代码的方式,在主干功能里面添加新功能
在这里插入图片描述

2、AOP底层原理
1、AOP底层使用动态代理实现

(1)有两种情况动态代理

​ 第一种,有接口情况,使用JDK动态代理

在这里插入图片描述

​ 第二种,没有接口情况,使用CGLIB动态代理

在这里插入图片描述

2、编写JDK动态代理代码
(1)创建接口,定义方法;
public interface IUserDao {

    int add(int a, int b);

    String update(String id);
}
(2)创建接口实现类,实现方法
public class UserDao implements IUserDao {

    @Override
    public int add(int a, int b) {
        System.out.println("UserDao add...");
        return a+b;
    }

    @Override
    public String update(String id) {
        System.out.println("UserDao update...");
        return id;
    }
}
(3)创建代理类
class UserDaoProxy implements InvocationHandler{

    //创建的代理对象
    private Object obj;

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

    /**
     * 方法执行之前调佣, 可以针对方法添加功能
     * @param method
     * @param args
     */
    public void before(Method method, Object[] args){
        System.out.println(method.getName() + " 方法执行之前... 参数: " + Arrays.toString(args));
    }
    /**
     * 方法执行之后调佣, 可以针对方法添加功能
     * @param method
     * @param args
     */
    public void after(Method method, Object[] args){
        System.out.println(method.getName() + " 方法执行之后...参数: " + Arrays.toString(args));
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //方法执行之前
        before(method, args);
        //被增强的方法
        Object o = method.invoke(obj, args);    //返回的参数
        //方法执行之后
        after(method, args);
        return o;
    }
}
(4)测试JDK动态代理
public class JDKProxy {

    public static void main(String[] args) {

        /*Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), new Class[]{UserDao.class}, (proxy, method, arg)->{
            //增强逻辑
            return null;
        });*/
        IUserDao userDao = (IUserDao)Proxy.newProxyInstance(IUserDao.class.getClassLoader(),
                new Class[]{IUserDao.class}, new UserDaoProxy(new UserDao()));
        System.out.println("add方法结果: " + userDao.add(100, 300));
        System.out.println("------------------------");
        System.out.println("update方法结果: " + userDao.update("345435"));

    }
}

执行结果
在这里插入图片描述

3、AOP术语
  1. 连接点:类里面哪些方法可以被增强,这些方法称为切入点

  2. 切入点:实际被真正增强的方法,称为切入点

  3. 通知(增强)
    (1) 实际增强的逻辑部分,称为通知
    (2) 通知有五种类型
    ① 前置通知
    ② 后置通知
    ③ 环绕通知
    ④ 异常通知
    ⑤ 最终通知

  4. 切面:一个动作,把通知应用到切入点的过程

4、切入点表达式
1、切入点表达式作用:知道对哪个类里面的哪个方法进行增强
2、语法结构:

execution([权限修饰符] [返回类型] [类全路径] [方法名称] ([参数列表]))

​ 举例1:对com.ycxy.service.UserService类里面的add()方法进行增强

​ execution(* com.ycxy.service.UserService.add(…))

​ 举例2:对com.ycxy.service.UserService类里面的所有方法进行增强

​ execution(* com.ycxy.service.UserService.*(…))

​ 举例3:对com.ycxy.service包下的所有类的所有方法进行增强

​ execution(* com.ycxy.service. * . *(…))

5、AOP操作
1、 基于AspectJ注解实现(重点)

(1)创建增强类,编写增强逻辑,并在类上加入@Aspect与@Component注解

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * 增强的类
 */
@Aspect
@Component
@Order(1)   //当多个增强类是, 数字类型值越小, 优先级越高(控制增强类的执行顺序)
public class UserProxy {
    
    /**
     * 相同切入点抽取
     */
    @Pointcut("execution(* com.ycxy.pojo.User.add(..))")
    public void pointdemo(){}

    /**
     * 前置通知, 方法执行之前执行
     */
    @Before("pointdemo()")
    public void before(){
        System.out.println("before ...");
    }

    /**
     * 方法返回值之后执行
     */
    @AfterReturning("pointdemo()")
    public void afterReturning(){
        System.out.println("afterReturning ...");
    }

    /**
     * 后置通知, 方法执行之后执行
     */
    @After("pointdemo()")
    public void after(){
        System.out.println("after ...");
    }

    /**
     * 方法出现异常执行, 在after之后
     */
    @AfterThrowing("execution(* com.ycxy.pojo.User.add(..))")
    public void afterThrowing(){
        System.out.println("afterThrowing ...");
    }

    /**
     * 环绕通知
     */
    @Around("execution(* com.ycxy.pojo.User.add(..))")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕之前 ...");
        //被增强的方法执行
        joinPoint.proceed();
        System.out.println("环绕之后 ...");
    }
}


/**
* 需要被增强的类
*/
@Component
public class User {

    public void add(){
        System.out.println("user add ...");
    }
}

(2)进行通知的配置,在spring配置中开启注解扫描,并开启@Aspect生成代理对象

① 方式一:基于XML方式进行配置

<?xml version="1.0" encoding="UTF-8"?>
<!--suppress SpringFacetInspection -->
<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"
       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/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--开启注解扫描-->
    <context:component-scan base-package="com.ycxy"/>
    
    <!-- 扫描@Aspect注解, 开启Aspect生成代理对象-->
    <aop:aspectj-autoproxy/>

</beans>

② 方式二:基于Java注解方式代替上面XML配置文件

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan(basePackages = {"com.ycxy"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AopConfig {
}

(3)spring 整合junit进行测试

1、依赖导入

<!--spring test start -->
<!-- 单元测试Junit -->
 <dependency>
 <groupId>junit</groupId>
 <artifactId>junit</artifactId>
 <version>4.12</version>
 </dependency>
 <dependency>
 <groupId>org.springframework</groupId>
 <artifactId>spring-test</artifactId>
 <version>5.0.8.RELEASE</version>
 </dependency>
 <!--spring test end -->

2、测试代码

import com.ycxy.pojo.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.beans.factory.annotation.Autowired;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:application.xml")
public class SpringTestAop_01 {

    @Autowired
    User user;

    @Test
    public void test01(){
        user.add();
    }
}

测试结果
在这里插入图片描述

2、 基于AspectJ配置文件实现
<bean id="user" class="com.ycxy.pojo.User"></bean>
<bean id="userProxy" class="com.ycxy.aopanno.UserProxy"></bean>

<!--配置aop增强-->
<aop:config>
    <!--切入点-->
    <aop:pointcut id="p" expression="execution(* com.ycxy.pojo.User.add(..))"/>
    <!--配置切面-->
    <aop:aspect ref="userProxy">
        <!--增强作用在具体的哪个方法上-->
        <aop:before method="before" pointcut-ref="p"/>
    </aop:aspect>
</aop:config>
;