简单实现 AOP

AOP 面向切面编程

  1. 新建项目Spring Starter Project。
  2. 自定义注解,用于标记需要 动态插入切面 的 位置,在这里我定义了3个。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 自定义注解,标记需要检查健康码的位置
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface HealthCode {

}

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

// 自定义注解,标记需要进行性能分析的位置
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Timing {

}

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

/**
 * 自定义注解,标记需要用户认证的位置
 *	其中包含一个属性:用户名,默认为User
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Auth {
	String value() default "user";
}

  1. 为每一个注解增加一个类,标记为组件 @Component 和切面 @Aspect,还可以标记其优先级 @Order,当一个方法被多个注解标记时,可以根据优先级判断切入顺序。

@Pointcut() 标记切入点 :这里是被注解标记的方法
@Before() 指定前置通知的切入点
@After() 指定后置通知的切入点
JoinPoint 连接点:指切面切入的具体方法,通过连接点可以获取:
- 切入点 方法的名字 getSignature().getName()
-切入点方法的参数 getArgs()
- 切入点方法标注的注解对象,进而可以获取注解的信息
- …

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component // 标记为组件
@Aspect   // 标记为切面
@Order(2) // 优先级
public class HealthCodeAspect {

	// 通知(特定位置):织入目标方法的位置
//	@Before				前置
//	@After				后置
//	@Around				环绕
//	@AfterReturning		方法返回后
//	@AfterThrowing		异常抛出后
	
	// 把重复的部分提取出来,用注解的方式标记接入点 @Pointcut,不必每次都重复写
	@Pointcut("@annotation(com.newer.aop.HealthCode)")
	public void x() {
	}
	
	// 指定前置通知的切入点,这里的 切入点 是被 注解标记的方法
	// 传入 注解的 包命+类名
	//@Before("@annotation(com.newer.aop.HealthCode)")
	@Before("x()")
	public void checkCode(JoinPoint joinpoint) {
		
		// 获得连接点,即切面切入的具体方法
		String name = joinpoint.getSignature().getName();
		System.out.printf("%s ::前置通知:检查健康码\n",name);
	}
	
	//@After("@annotation(com.newer.aop.HealthCode)")
	@After("x()")
	public void after(JoinPoint joinPoint) {
		System.out.printf("%s ::后置通知\n",joinPoint.getSignature().getName());
	}
}

@Around() :指定环绕通知的切入点
环绕通知 = 前置 + 切面切入方法的执行 + 后置
proceed 方法就是用于启动目标方法的执行,通过Proceedingjoinpoint 对象调用

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

// 标记为 组件,切面
@Component
@Order(1)
@Aspect
public class TimingAspect {

	@Pointcut("@annotation(com.newer.aop.Timing)")
	public void y() {
		
	}
	
	// 环绕通知,切入点
	@Around("y()")
	public void aroung(ProceedingJoinPoint point) {
		System.out.printf("-----------------------%s性能分析开始\n",point.getSignature().getName());
		long time = System.currentTimeMillis();
		
		// 被切入方法的执行
		Object result = null;
		try {
			result =  point.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
		}
		
		time = System.currentTimeMillis() - time;
		System.out.printf("=========%s 性能分析,耗时 %d\n",
				point.getSignature().getName(),
				time);
	
	}
}

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

// 组件、切面
@Component
@Aspect
@Order(0)
public class AuthAspect {

	@Before("@annotation(com.newer.aop.Auth)")
	public void beforeAuth(JoinPoint joinPoint) {
		// 切入点的方法名
		String name = joinPoint.getSignature().getName();
		System.out.println("方法名:"+name);
		// 切入点方法中的参数
		Object[] args = joinPoint.getArgs();
		if(args!=null) {
			for(Object obj:args) {
				System.out.println("参数:"+obj);
			}
		}
		
		// 获得注解的值,进一步拓展可做权限认证等...
		MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
		Method targetMethod = methodSignature.getMethod();
		Auth ano = targetMethod.getAnnotation(Auth.class);
		System.out.println("注解标记的权限值为:"+ano.value());
		System.out.println("*********************************************************");
	}
	
}

  1. 给需要动态加入公共需求的方法标记上相对应的注解。
import org.springframework.stereotype.Component;

@Component
public class Life {

	// 织入(一些公共的需求,可动态加入)
	// 织入多个切面,可以定义切面的优先级 @order()
	@HealthCode
	@Timing
	public void shopping() {
		// 用户认证
		// 权限的鉴定
		// 日志
		// 开启事务
		// ...
		System.out.println("购物...");
		
		int t = (int) (Math.random()*1000+500);
		try {
			Thread.sleep(t);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		// 提交事务
		// 性能分析
	}
	
	@HealthCode
	@Timing
	public void subway() {
		// 日志
		// 开启事务
		System.out.println("乘地铁...");
		
		int t = (int) (Math.random()*1000+500);
		try {
			Thread.sleep(t);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		// 提交事务
		// 性能分析
	}
	
	@Timing
	public void eat() {
		System.out.println("吃饭...");
		
		int t = (int) (Math.random()*1000+500);
		try {
			Thread.sleep(t);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	// 权限验证
	@Auth(value = "admin")
	public void checkIn(String name) {
		//System.out.println("checkIn:"+name);
	}
	
	@Auth(value = "member")
	public void addOrder(int a,int b) {
	}
}

  1. Spring Main 调用测试
import org.springframework.beans.factory.BeanFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class Aop0810Application {

	public static void main(String[] args) {
		SpringApplication.run(Aop0810Application.class, args);
	}

	@Bean
	public CommandLineRunner operation(BeanFactory factory) {
		
		return new CommandLineRunner() {
			
			@Override
			public void run(String... args) throws Exception {
				// 从组件工厂获得实例
				Life life = factory.getBean(Life.class);
				life.subway();
				System.out.println();
				life.shopping();
				System.out.println();
				life.eat();
				
				life.checkIn("jack");
				life.addOrder(7, 9);
			}
		};
	}
}

  1. 查看控制台输出
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值