前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >吃透Spring框架(二)

吃透Spring框架(二)

作者头像
知否技术
发布2022-08-23 18:44:06
1730
发布2022-08-23 18:44:06
举报
文章被收录于专栏:eclipse编程eclipse编程

1. Bean 的生命周期

Bean 的生命周期指的就是由 Spring 管理的对象从创建到销毁的过程,和人生老病死的过程一样。

它主要分为三个阶段:创建 --> 初始化 --> 销毁

1.1 创建阶段

Spring 的工厂创建对象的方式分两类:

1. singleton 模式

当 scope 属性为 singleton ,创建 Spring 工厂的同时创建所有单例对象。

例如:

新建 User 类:

public class User {
    String name;
    int age;

    public User() {
        System.out.println("调用User的构造方法");
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

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

spring 配置文件注册 bean :

<bean id="user" class="com.xxl.model.User">
    <property name="name" value="知否君"/>
    <property name="age" value="23"/>
</bean>

测试:

  @Test
    public void testSpring(){
        ApplicationContext act = new ClassPathXmlApplicationContext("/applicationContext.xml");
    }

执行结果:

我们发现当创建 Spring 工厂的同时就会调用对象的构造方法。因为 spring 中 bean 默认的 scope 就是 singleton ,所以创建工厂的同时默认就会创建多个单例对象。

如果想修改创建单例对象的方式为「获取的时候才创建」,只需要在 bean 标签上面添加如下属性:

lazy-init="true"

例如:

<bean id="user" class="com.xxl.model.User" lazy-init="true">
    <property name="name" value="知否君"/>
    <property name="age" value="23"/>
</bean>

2. prototype 模式

只有获取对象的时候才会创建对象。

修改 bean 标签的 scope 属性:

<bean id="user" class="com.xxl.model.User" scope="prototype">
    <property name="name" value="知否君"/>
    <property name="age" value="23"/>
</bean>

测试:

  @Test
    public void testSpring(){
        ApplicationContext act = new ClassPathXmlApplicationContext("/applicationContext.xml");
        Object user = act.getBean("user");
        System.out.println(user);
    }

执行结果:

通过上面的例子我们发现只有当执行 getBean() 方法的时候才会调用该类的构造方法。

1.2 初始化阶段

spring 中 bean 的初始化操作指的是在创建对象的时候完成一些附加的功能。bean 的初始化操作有两种实现方式:

1.实现 InitializingBean 接口

public class 类名 implements InitializingBean {
    public void afterPropertiesSet(){
       // 初始化方法操作
    }
}

例如:

public class User implements InitializingBean {
    String name;
    int age;
    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

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

    // 初始化操作
    @Override
    public void afterPropertiesSet(){
            this.name = "张无忌";
            this.age = 30;
    }
}

测试:

  @Test
    public void testSpring(){
        ApplicationContext act = new ClassPathXmlApplicationContext("/applicationContext.xml");
        Object user = act.getBean("user");
        System.out.println(user);
    }

执行结果:

2.通过创建普通方法完成初始化

在 User 类中创建一个方法

// 初始化方法
  public void initMethod() {
      this.name = "张无忌";
  }

在配置文件中配置 init-method 属性

<bean id="user" class="com.xxl.model.User" init-method="initMethod" >
    <property name="name" value="知否君"/>
    <property name="age" value="23"/>
</bean>

测试:

  @Test
    public void testSpring(){
        ApplicationContext act = new ClassPathXmlApplicationContext("/applicationContext.xml");
        Object user = act.getBean("user");
        System.out.println(user);
    }

执行结果:

我们发现该初始化方法在创建对象之后修改了 user 对象的名字。

总结:

初始化方法修改了注入的值,所以初始化方法一定在「注入之后」执行。

1.3 销毁阶段

Spring 销毁对象前,会调用对象的销毁方法,完成销毁操作。

Spring 什么时候销毁所创建的对象?当 Spring 工厂关闭时,Spring 工厂会调用我们自定义的销毁方法。

销毁方法的定义有两种方式:

1.实现DisposableBean接口

public class 类名 implements DisposableBean {
    // 销毁操作
    @Override
    public void destroy(){
        // 销毁操作业务
    }
}

2.创建普通方法

在 User 类中创建一个方法

  // 销毁方法
  public void destroyMethod() {
     // 销毁操作业务
  }

在配置文件中配置 destroy-method 属性

 <bean id="user" class="com.xxl.model.User" destroy-method="destroyMethod">
      <property name="name" value="知否君"/>
      <property name="age" value="23"/>
  </bean>

2. Bean 的后置处理

Spring 工厂创建完对象后如果还想对对象干点别的事情,除了初始化阶段,还可以采用Bean的后置处理

Bean 的后置处理:对 Spring 工厂创建的对象进行二次加工处理,就是创建完对象后再干点别的事。

Bean 后置处理的流程:

1.实现 「BeanPostProcessor」 接口

public class BeanProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("后置bean:before 方法");
        return bean;
    }
    
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("后置bean:after 方法");
        if (bean instanceof User) {
            User user = (User) bean;
            user.setName("亚里士多德");
            return user;
        }
        return bean;
    }
}

2.配置文件添加 bean

<bean id="beanProcessor" class="com.xxl.config.BeanProcessor"/>

3.测试

    @Test
    public void testSpring(){
        ApplicationContext act = new ClassPathXmlApplicationContext("/applicationContext.xml");
        Object user = act.getBean("user");
        System.out.println(user);
    }

执行结果:

前面我们学习了对象的初始化方法,那么初始化方法和 Bean 的后置处理的执行顺序是什么?

我们来修改一下 User 类,测试一下:

public class User implements InitializingBean {
    String name;
    int age;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

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

    @Override
    public void afterPropertiesSet(){
        System.out.println("初始化方法");
    }
}

测试执行顺序:

其实在实际开发中,我们很少对 Spring 工厂创建的对象进行初始化操作,一般是采用 Bean 的后置处理的方式来加工对象。

BeanPostProcessor 接口有两个方法,这里简称 before 和 after 方法。

这两个方法都是先获取 Spring 创建的对象,然后再对其加工,加工完成后再交给 Spring。

因为这两个方法的作用一样,所以我们一般采用其中的一个方法,这里建议采用 after 方法。

从上面的例子中我们得到了Spring 操作 bean 的顺序:

3. 代理设计模式

3.1 为啥要用代理设计模式?

咱们先来看一个需求:在所有方法的执行前后输出一段日志。

程序小白可能会这样写:

接口:

public interface CalculateService {
    // 加法
    int add(int a,int b);
    // 减法
    int sub(int a,int b);
}

实现类:

public class CalculateServiceImpl implements CalculateService {
    @Override
    public int add(int a, int b) {
        System.out.println("方法执行前打印");
        int result = a + b;
        System.out.println("方法执行后打印");
        return result;
    }

    @Override
    public int sub(int a, int b) {
        System.out.println("方法执行前打印");
        int result = a - b;
        System.out.println("方法执行后打印");
        return result;
    }
}

但是这样写有 3 个问题:

1.代码混乱:业务代码和非业务代码混在一起,看着太乱了

2.代码重复:如果有多个方法,那就要写一堆输出日志的代码片段,吃力不讨好。

3.代码耦合:如果非业务代码(日志打印)要做修改,那所有相关的业务方法都要改一遍,代码耦合度太高。

那有什么解决办法呢?使用代理

生活中有关代理的例子无处不在,例如:一些大学可以面向全球招生,所以会衍生很多留学中介,这些中介可以帮学校招生。

所以中介的作用就是帮助雇主做事,有了中介,雇主变得很轻松。而在 java 开发中,也存在这样的代理关系,它的专业术语是代理设计模式。

代理设计模式可以很好解决上面开发中遇到的三个问题,帮助我们简化代码、提高工作效率。

3.2 代理设计模式

代理设计模式:通过代理类为目标类做一些额外(非业务)的功能。

专业名词解释

1.目标类(原始类):指的是完成业务的核心类,一般指的是 service 层的各种实现类。

2.目标方法(原始方法):目标类中的方法是目标方法(原始方法)。

3.额外功能(附加功能):打印日志等非业务功能。

代理设计模式开发步骤:

(1)代理类和目标类实现相同的接口

(2)代理类中除了要调用目标类的方法实现业务功能,还要实现额外功能。

例如:

// 接口
public interface CalculateService {
  业务方法
}

// 目标类
public CalculateServiceImpl implements CalculateService {
  业务方法
}

// 代理类:要实现目标类相同的接口
public CalculateServiceProxy implements CalculateService {
 // 业务方法
 // 额外功能
}

3.3 静态代理

静态代理:给每一个目标类手动开发一个代理类。

例如:

public interface CalculateService {
   // 加法
  int add(int a,int b);
   // 减法
  int sub(int a,int b);
}
// 目标类
public CalculateServiceImpl implements CalculateService {
   @Override
    public int add(int a, int b) {
        int result = a + b;
        return result;
    }
    @Override
    public int sub(int a, int b) {
        int result = a - b;
        return result;
    }
}
// 代理类:要实现目标类相同的接口
public CalculateServiceProxy implements CalculateService {
  private CalculateService calculateService = new CalculateServiceImpl();
  @Override
    public int add(int a, int b) {
        System.out.println("方法执行前打印");
        int result = calculateService.add(a,b);
        System.out.println("方法执行后打印");
        return result;
    }
    @Override
    public int sub(int a, int b) {
        System.out.println("方法执行前打印");
        int result = calculateService.sub(a,b);
        System.out.println("方法执行后打印");
        return result;
    }
}

通过上面的例子我们发现静态代理也存在很多问题:

1.如果存在很多目标类,我们就要手动创建一堆代理类,太繁琐。

2.代理类中混杂着目标类方法和额外功能,代码耦合度高。

那有没有这样一种代理模式?

  • 1.目标类和代理类互不干扰
  • 2.代码耦合度低,便于维护

有的,动态代理闪亮登场!

3.4 动态代理

动态代理:也是通过代理类为目标类做一些额外的功能,但是不用手动写一堆代理类,而是动态地为目标类创建代理类。

开发流程:

  1. 引入依赖
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.16.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.9.5</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.5</version>
</dependency>

这里我们主要是引入了 aspectj 这个技术,aspectj 是 spring 社区中非常流行的基于动态代理技术的框架。

  1. 创建目标类和目标方法

接口:

public interface CalculateService {
    // 加法
    int add(int a,int b);
    // 减法
    int sub(int a,int b);
}

实现类(目标类):

public class CalculateServiceImpl implements CalculateService {
     @Override
    public int add(int a, int b) {
        int result = a + b;
        System.out.println("加法操作。。。");
        return result;
    }

    @Override
    public int sub(int a, int b) {
        int result = a - b;
        System.out.println("减法操作。。。");
        return result;
    }
}

3.在 spring 配置文件中注册 bean

 <bean id="calculateService"  class="com.xxl.service.impl.CalculateServiceImpl" />

4.实现额外功能

这里我们需要创建一个类实现 MethodInterceptor 接口:

/**
 * @Desc: 动态代理完成非业务功能
 * @Author: 知否技术
 * @date: 下午8:49 2022/5/4
 */
public class PrintLog implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("在目标方法执行之前打印。。。。。");
        // 执行目标方法
        Object object = methodInvocation.proceed();
        System.out.println("在目标方法执行之后打印。。。。。");
        return object;
    }
}

5.注册完成额外功能的 bean

  <bean id="printLog" class="com.xxl.aop.PrintLog" />
  1. 定义切入点
<!--切入点:给哪些方法加入额外功能-->
<aop:config>
  <aop:pointcut id="pc" expression="execution(* * (..))"/>
</aop:config>
  1. 组装切入点和额外功能
<!--切入点:给哪些方法加入额外功能-->
<aop:config>
  <aop:pointcut id="pc" expression="execution(* * (..))"/>
  <aop:advisor advice-ref="printLog" pointcut-ref="pc"/>
</aop:config>

8.测试

@Test
  public void testSpring() {
      // 1、获取工厂
      ApplicationContext act = new ClassPathXmlApplicationContext("/applicationContext.xml");
      // 2、通过工厂类获得对象
      CalculateService calculateService = (CalculateService) act.getBean("calculateService");
      // 3.调用方法
      int result = calculateService.add(1, 2);
      System.out.println("result:" + result);
  }

讲解:

1.上面的例子中我们定义了一个 PrintLog 打印日志的类,并实现了 MethodInterceptor 接口的 invoke 方法。invoke 方法里面实现了在目标方法执行前后打印日志的功能。

2.invoke 方法的返回值就是原始方法的返回值,上个例子中的原始方法就是 add 方法。

3.aop:config 这个标签用来配置切入点和额外功能。上面例子中额外功能就是在要执行的方法前后打印日志,而切入点就是额外功能要作用的位置:比如某些类上或者某些方法上。

4.execution(* * (..)) 是切入点表达式,表示作用在所有类的所有方法上,这个后面会讲。

5.上面的例子表示:你无论执行哪个方法,这个方法的前面和后面都会打印一段日志。

3.5 动态代理实现原理

我们通过 spring 的工厂获取的对象,其实是通过动态代理技术创建的代理类。那这个代理类在哪里?

当程序运行的时候,spring 框架通过动态字节码技术在 JVM 内存中为目标类创建代理类。当程序运行结束的时候,这个代理类就会随之消亡。

所以使用动态代理不需要手动创建多个代理类。

4. AOP

4.1 AOP 概念

AOP: 全称 Producer Oriented Programing,即面向切面编程。

那啥是面向切面编程?其实说白了还是 Spring 的动态代理,通过代理类为原始类增加一些 额外功能(例如打印等)。

那啥是切面?

切面 = 切入点 + 额外功能。

切入点:额外功能作用的位置,在哪些类哪些方法上。

额外功能作用在不同的类上面,我们都知道点连接起来构成面,所以不同的切入点连接起来构成了切面,这个切面就像刀切西瓜一样切在不同的类上面,所以额外功能就对这些类中的方法起了作用。

4.2 AOP 底层实现原理

AOP 的底层还是使用 Spring 的动态代理技术创建代理类对象。

动态代理的方式分为两种:

  • 基于接口实现动态代理:JDK 动态代理
  • 基于继承实现动态代理:Cglib 动态代理

4.2.1 JDK 动态代理

创建代理对象的三个元素:

  • 1.原始对象
  • 2.额外功能
  • 3.原始对象实现的接口

代码格式:

Proxy.newPorxyInstance(classloader,interfaces,invocationHandler)

讲解:

(1)classloader:叫做类加载器,它可以用来创建代理对象。

创建方式:

类.class.getClassLOader()

(2)interfaces:原始对象实现的接口

创建方式

接口.getClass().getInterfaces()

(3)invocationHandler:额外功能

创建方式:

InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("---- 方法执行前打印 ----");
                // 执行原始方法
                Object ret = method.invoke(caculateService, args);
                System.out.println("---- 方法执行后打印 ----");
                return ret;
            }
        };

完整代码:

@Test
public void testJDKProxy() {
    // 1. 原始对象
    CalculateService calculateService = new CalculateServiceImpl();

    // 2. JDK 动态代理:包含额外功能
    InvocationHandler handler = new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("---- 方法执行前打印 ----");
            // 执行原始方法
            Object result = method.invoke(calculateService, args);
            System.out.println("---- 方法执行后打印 ----");
            return result;
        }
    };
    // 3. 代理类
    CalculateService calService = (CalculateService) Proxy.
            newProxyInstance(CalculateService.class.getClassLoader(),
                    calculateService.getClass().getInterfaces(),
                    handler);
    // 4. 执行方法
    int result = calService.add(1, 2);
    System.out.println("result:" + result);
}

测试结果:

4.2.2 Cglib 动态代理

CGlib 创建动态代理的原理:原始类作为父类,代理类作为子类,通过继承关系创建代理类。

代码格式:

Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(classLoader);
enhancer.setSuperclass(calculateService);
enhancer.setCallback(interceptor);

讲解:

(1)classLoader:类加载器(了解即可)

(2)Superclass:父类,就是原始类

(3)interceptor:额外功能

MethodInterceptor interceptor = new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("---- 方法执行前打印 ----");
            // 执行原始方法
            Object result = method.invoke(calculateService, args);
            System.out.println("---- 方法执行后打印 ----");
            return result;
            }
        };

完整代码:

 @Test
    public void testCglibProxy() {
        // 1. 原始对象
        CalculateService calculateService = new CalculateServiceImpl();

        // 2. Cglib 动态代理:包含额外功能
        MethodInterceptor interceptor = new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("---- 方法执行前打印 ----");
                // 执行原始方法
                Object result = method.invoke(calculateService, args);
                System.out.println("---- 方法执行后打印 ----");
                return result;
            }
        };

        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(CalculateService.class.getClassLoader());
        enhancer.setSuperclass(calculateService.getClass());
        enhancer.setCallback(interceptor);

        // 3. 创建代理类
        CalculateService calService = (CalculateService)enhancer.create();
        // 4. 执行方法
        int result = calService.add(3, 4);
        System.out.println("result:" + result);
    }

执行结果:

4.2.3 Spring 如何创建代理对象?

Spring 是如何为原始对象创建目标对象的呢?是通过 BeanPostProcessor。

前面我们讲过 BeanPostProcessor 可以对对象进行二次加工,所以可以用来创建代理对象。

Spring 创建代理对象的流程:

  1. 实现 BeanPostProcessor 接口
/**
 * @Desc: 后置bean创建代理对象
 * @Author: 知否技术
 * @date: 上午11:59 2022/5/5
 */
public class ProxyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        InvocationHandler handler = (proxy, method, args) -> {
            System.out.println("--- 方法执行前打印6666666---");
            Object ret = method.invoke(bean, args);
            System.out.println("--- 方法执行后打印7777777---");
            return ret;
        };
        return Proxy.newProxyInstance(ProxyBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), handler);
    }
}
  1. 注册 bean
 <bean id="calculateService"  class="com.xxl.service.impl.CalculateServiceImpl" />
<bean id="proxyBeanPostProcessor" class="com.xxl.aop.ProxyBeanPostProcessor"/>
  1. 测试
 @Test
    public void testSpring() {
        // 1、获取工厂
        ApplicationContext act = new ClassPathXmlApplicationContext("/applicationContext.xml");
        // 2、通过工厂类获得对象
        CalculateService calculateService = (CalculateService) act.getBean("calculateService");
        // 3.调用方法
        int result = calculateService.sub(7, 2);
        System.out.println("result:" + result);
    }

4.3 基于注解开发 AOP

开发流程:

  1. 开发切面类
@Aspect
public class TestAspect {

    // 前置通知:方法执行前添加额外功能
    @Before("execution(* *(..))")
    public void beforePrint(){
        System.out.println("------before: 方法执行前打印~");
    }
    
    //后置通知: 方法执行后添加额外功能
    @After("execution(* *(..))")
    public void afterPrint(){
        System.out.println("------after: 方法执行前打印~");
    }

    // 环绕通知:方法执行前后添加额外功能
    @Around("execution(* *(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("方法执行前打印~");
        Object result = joinPoint.proceed();
        System.out.println("方法执行后打印~");
        return result;
    }
}
  1. 配置切面类和扫描注解
<bean id="testMyAspect"  class="com.xxl.aop.TestAspect" />
<!-- 扫描 aop 相关注解-->
<aop:aspectj-autoproxy/>
  1. 测试
@Test
  public void testSpring() {
      // 1、获取工厂
      ApplicationContext act = new ClassPathXmlApplicationContext("/applicationContext.xml");
      // 2、通过工厂类获得对象
      CalculateService calculateService = (CalculateService) act.getBean("calculateService");
      // 3.调用方法
      int result = calculateService.add(100, 1);
      System.out.println("result:" + result);
  }

讲解:

1.我们新建了一个 TestMyAspect 类,然后添加 @Aspect 注解,表示这是一个切面类,专门用来完成非业务功能的。

2.在这个类中,我们创建了三个方法,其中 @Before 注解标注的方法表示在目标方法操作前执行。@After 注解标注的方法表示在目标方法操作后执行。@Around 注解标注的方法表示在目标方法操作前后执行。

3.在实际开发中一般使用 @Around 注解标注的方法完成非业务功能。

4.我们新建了这个切面类,但是 spring 不知道啊,所以需要在 Spring 的配置文件中注册一下 bean。

5.现在 Spring 工厂能够管理这个类了,但是 Spring 不知道他是切面类啊!所以需要配置一下扫描注解的标签。

6.然后通过 Spring 获取创建的类,我们获取的其实是 Spring 通过后置 Bean 加工后的代理类。

切入点复用

我们可以在切面类中定义⼀个方法,方法上面标注 @Pointcut 注解。然后就可以重复使用切入点表达式了:

@Aspect
public class TestAspect {

    @Pointcut("execution(* *(..))")
    public void myPointcut() {}
    
    // 环绕通知:方法执行前后添加额外功能
    @Around(value = "myPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("方法执行前打印~");
        Object result = joinPoint.proceed();
        System.out.println("方法执行后打印~");
        return result;
    }
}

4.4 切入点表达式

切入点:额外功能加入的位置。

<aop:pointcut id="pc" expression="execution(* * (..))"/>
  • execution():切入点函数
  • (* * (..)):切入点表达式
public int add(int a, int b)
   *        * (..)

第一个 * 表示方法的修饰符和返回值 第二个 * 是方法名 .. 表示方法中的参数

1.(包.类.方法)切入点:

修饰符-返回值  包.类.方法(参数)

expression="execution(* com.xxl.service.caculateServiceImpl.add(..))"

2.指定切入点为某个包下的所有类中的所有方法:

修饰符-返回值  包.类.方法(参数)

expression="execution(* com.xxl.service.*.*(..))"

3.@annotation

作用:用于匹配当前执行方法持有指定注解的方法,并为之加入额外的功能。

例如我们自定义了一个注解:NoToken

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NoToken {
}

方法中添加自定义注解:

@Override
@NoToken
public int add(int a, int b) {
    int result = a + b;
    System.out.println("加法操作。。。");
    return result;
}

然后我们要为包含 NoToken 注解的方法加入额外功能:

@Aspect
public class TestAspect {
   // 环绕通知:方法执行前后添加额外功能
    @Around("@annotation(com.xxl.annotion.NoToken)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("包含 NoToken 注解--------------");
        Object result = joinPoint.proceed();
        return result;
    }
}

测试:

@Test
public void testSpring() {
    // 1、获取工厂
    ApplicationContext act = new ClassPathXmlApplicationContext("/applicationContext.xml");
    // 2、通过工厂类获得对象
    CalculateService calculateService = (CalculateService) act.getBean("calculateService");
    // 3.调用方法
    int result1 = calculateService.add(99, 1);
    System.out.println("-----------------------");
    int result2 = calculateService.sub(99, 1);
    System.out.println("result1:" + result1);
    System.out.println("result2:" + result2);
}

-END-

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-05-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 知否技术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. Bean 的生命周期
    • 1.1 创建阶段
      • 1.2 初始化阶段
        • 1.3 销毁阶段
        • 2. Bean 的后置处理
        • 3. 代理设计模式
          • 3.1 为啥要用代理设计模式?
            • 3.2 代理设计模式
              • 3.3 静态代理
                • 3.4 动态代理
                  • 3.5 动态代理实现原理
                  • 4. AOP
                    • 4.1 AOP 概念
                      • 4.2 AOP 底层实现原理
                        • 4.2.1 JDK 动态代理
                        • 4.2.2 Cglib 动态代理
                        • 4.2.3 Spring 如何创建代理对象?
                      • 4.3 基于注解开发 AOP
                        • 4.4 切入点表达式
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档