Java Spring AOP 从入门到精通 -2

Java Spring AOP 从入门到精通 -2

示例环境

这里面的示例用gradle,初学者可以参考以下build.gradle配置,熟悉者略过。
以下build.gradle文件是通过spring initializer (https://start.spring.io/, 强烈推荐大家使用,特别是新建一个工程时,减少了很多繁琐的创建class / config / test 结构的过程) 快速生成的一个模板工程,然后加进了spring-boot-starter-aop的依赖。
注意Spring AOP 是一个保证了Spring 与 AOP的良好集成的实现,并非AspectJ, 后者是一门独立的语言,实现更全面。虽然两者有很多类似的概念。

plugins {
	id 'org.springframework.boot' version '2.2.0.RELEASE'
	id 'io.spring.dependency-management' version '1.0.8.RELEASE'
	id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

repositories {
	mavenCentral()
}

dependencies {
	compile 'org.springframework.boot:spring-boot-starter-aop'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	//classpath "gradle.plugin.aspectj:gradle-aspectj:0.1.6"
	testImplementation('org.springframework.boot:spring-boot-starter-test') {
		exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
	}
}

test {
	useJUnitPlatform()
}

程序示例

  • Horseman 类
    骑士可以冲刺rush(),可以攻击chop()。
    exercise()是可以通过锻炼增加经验值。
    printLatestStrength() 是打印当前经验值。
package com.example.demo;

public class Horseman {
    public int strength;
    Horseman () {
        strength = 5;
    }

    public void rush(String enemy) {
        System.out.println(this.getClass().getSimpleName() + "rush" + enemy);
    }

    public int exercise(int experience) {strength += experience; return strength;}

    public void chop(String enemy) {
        System.out.println(this.getClass().getSimpleName() + "attack" + enemy);
    }
    public void printLatestStrength() {System.out.println("Latest Strength: " + strength);}
}
  • Swordman 类
    剑客可以阻拦 block(),可以攻击chop().
package com.example.demo;

public class Swordman {
    public int strength;
    Swordman () {
        strength = 3;
    }
    public void block(String enemy) {
        System.out.println(this.getClass().getSimpleName() + "hinder" + enemy);
    }

    public void chop(String enemy) {
        System.out.println(this.getClass().getSimpleName() + "attack" + enemy);
    }
    public int exercise(int experience) {strength += experience; return strength;}
}
  • StorageAdvisor 类
    这是定义Aspect的地方。就是你想让程序在调用一个方法之前(或之中,之后)做什么。
    @Before(“execution(* chop(…))”) 标记表明我们希望在执行任何 chop() 方法前注入逻辑。其中 *指任何返回类型, chop匹配方法名, …指任何参数。
 package com.example.demo;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class StorageAdvisor {

    @Before("execution(* chop(..))")
    public void beforeAttack(JoinPoint point) {
        System.out.println("Advice: " + point.getTarget().getClass().getSimpleName() + " prepare ");

    }

    @Around("execution(* exercise(..))")
    public Object process(ProceedingJoinPoint point) throws Throwable {
        System.out.println("@Around:before executing target method...");
        //访问目标方法的参数:
        Object[] args = point.getArgs();
        if (args != null && args.length > 0 && args[0].getClass() == Integer.class) {
            args[0] = 10;
        }
        //用改变后的参数执行目标方法
        Object returnValue = point.proceed(args);
        System.out.println("@Around:after executing target method...");
        System.out.println("@Around:wove target object: " + point.getTarget());
        return returnValue;
    }
}
  • AspectTest类
    这个类负责测试。第一个是对调用前进行测试。
    第二个演示了调用中如何获取原来的参数并注入逻辑。角色的exercise() 方法是简化版的通过锻炼获得更多能量值。不同的角色获得能量值的速度可能不一样,所以可能有很多个实现。那么可能发布一个版本后,后续需要对获得能量值的方式进行某些调整,但是又不想跑到每个方法里去改动。这样就可以通过一个aspect编程实现。注意对原来的角色类code没有做任何改动,hm.printLatestStrength()打印出了经过注入逻辑修改后的能量值。
package com.example.demo;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AspectTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Horseman hm = (Horseman)context.getBean("horseman");
        hm.rush("Ghoul");
        hm.chop("Ghoul");
        Swordman sm = (Swordman)context.getBean("swordman");
        sm.block("Ghoul");
        sm.chop("Ghoul");
        hm.exercise(8);
        hm.printLatestStrength();
    }

}
  • ApplicationContext.xml
    这是用的xml式的配置方式,定义哪些类(bean)需要注入这个aspect. 如果使用Spring boot的话,文件放在能被找到的目录下即可。比如workdir/, resources/。
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/aop
  http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <aop:aspectj-autoproxy />
    <bean id="horseman" class="com.example.demo.Horseman" />
    <bean id="swordman" class="com.example.demo.Swordman" />
    <bean class="com.example.demo.StorageAdvisor" />
</beans>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值