AOP面向切面编程

〇、前言

AOP是一种思想,一种编程方式。他不同于面向对象变成,他是面向切片编程。他可以编写一段代码在合适的时机找到切入点然后执行。不直接修改原来的代码,而是在原代码执行的前后执行一段额外的代码。

主要作用:在不侵入原有程序的基础上实现对原有功能的增强

优点:
1.解耦合,系统应求追高内聚低耦合,增强的逻辑独立存在,即插即用,不需要移除掉切点即可,对原有业务无影响(或影响极小)。
2.符合开闭原则,对扩展开放,对修改关闭,不修改原有代码。改代码的代价有些时候比较大。
3.代码复用,可以在不侵入当前代码的情况下复用代码或引入第三方功能从而扩展系统功能

一、基础概念

  • 1、连接点JointPoint
    在类当中哪些方法可以增强,这些方法就是连接点(基本每个方法的前、后(两者都有也行),或抛出异常是时都可以是连接点,spring只支持方法连接点)
  • 2、切入点Pointcut
    实际被增强的方法就是切入点(一个类里,假如有15个方法,那就有十几个连接点了,但是你并不想在所有方法附件都织入,你只是想让其中几个,在调用这几个方法之前、之后或者抛出异常时干点什么,那么就用切入点来定义这几个方法。)
  • 3、增强(通知)Advisor
    通知和切入点的结合
    实际增强的逻辑代码。比如在login中添加的权限判断代码。
  • 4、切面@Aspect
    是一个动作,将增强(通知)应用到切入点的一个过程

二、JoinPoint与ProceedingJoinPoint

JointPoint
JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象
JointPoint常用方法总结

// 获取传入目标方法的参数,返回一个数组
		Object [] args = joinPoint.getArgs();
		
		// 获取目标方法名
		String name=joinPoint.getSignature().getName();
		
		// 获取目标方法所属类的简单类名
		String name1 = joinPoint.getSignature().getDeclaringType().getSimpleName(); 
		
		// 获取目标方法所属类的类名
	    String name2 = joinPoint.getSignature().getDeclaringTypeName(); 
	    
		// 获取目标方法声明类型(public、private、protected)
	    Object object1 = joinPoint.getSignature().getModifiers(); 
	    
		// 获取被代理的对象
		Object object2 = joinPoint.getTarget(); 
		
		// 获取代理对象自己
		Object object3 = joinPoint.getThis(); 

ProceedingJoinPoint
ProceedingJoinPoint对象是JoinPoint的子接口,该对象只用在@Around的切面方法中, 添加了两个方法.

方法名功能
Object proceed() throws Throwable执行目标方法
Object proceed(Object[] var1) throws Throwable传入的新的参数去执行目标方法

三、5种增强方式

介绍增强方法之前先看一些注解的关键参数
注解中通常是两种比较常用的表达式,即execution和@annotation

1、execution

execution,它的作用,就是指定你要切入的一个地方,比如具体的方法,或者是接口

2、@annotation

项目中的方法,凡是带有被这个annotation修饰的注解,然后这个注解所修饰的方法或是接口都会被拦截

(一)前置通知

前置增强(又称前置通知):在目标方法执行之前执行

	@Before("execution(int mul(..))")
	public void before(JoinPoint joinPoint) {
	Object object = joinPoint.getTarget();
    
    Object [] args = joinPoint.getArgs();
    
    String name=jp.getSignature().getName();
		
		System.out.println(this.getClass().getName()+":The "+name +" method begins.");
		System.out.println(this.getClass().getName()+":Parameters of the mul method: ["+args[0]+","+args[1]+"]");
	}
(二)后置增强

后置增强(又称后置通知):在目标方法执行后执行,无论目标方法运行期间是否出现异常。

    @After("execution(int mul(int,int))")
	public void after(JoinPoint jp) {
		Object object = jp.getTarget();
		String name= jp.getSignature().getName();
		System.out.println(this.getClass().getName()+":The "+name+"method ends.");
	}

或者说
通知: 就是我们编写的希望Aop时执行的那个方法。我们通过Aop希望我们编写的方法在目标方法执行前执行,或者执行后执行。
切点:切点就是我们配置的满足我们条件的目标方法。比如我们规定:名字前面是select开头的才执行我们自定义的通知方法。那么这些select开头的方法就是切点。
连接点:连接点可以说是切点的全集。切点是连接点的子集。也可以理解为,连接点是我们没有定义那个select开头规则时,满足条件的全部的方法。
切面:切面是切点和通知的组合称谓,就是变相给组合起了个名字。

(三)返回增强

返回增强(又称返回通知):在目标方法正常结束后执行,可以获取目标方法的执行结果

	@AfterReturning(value="execution(int mul(..))",returning="a")
	public void afterReturning(JoinPoint jp,Object a) {
		Object object = jp.getTarget();
		String name= jp.getSignature().getName();
		System.out.println(this.getClass().getName()+":Result of the "+name +" method:"+a);
	}

执行顺序: 前置增强---->目标函数----->后置增强------>返回增强

(四)异常增强

异常增强(又称异常通知):目标方法抛出异常之后执行,可以访问到异常对象,且可以指定在出现哪种异常时才执行增强代码

	@AfterThrowing(value="execution(int mul(..))", throwing="e")
	public void afterThrowing(JoinPoint jp,Exception e) {
		Object object=jp.getTarget();
		String name= jp.getSignature().getName();
		System.out.println(this.getClass().getName()+":Result of the "+name +" method:"+e);
	}

执行顺序: 前置增强---->目标函数----->后置增强------>异常增强

(五)环绕增强

目标方法执行前后都可以织入增强处理,可以实现以上四种增强

      @Around("execution(int mul(int,int))")
	public Object around(ProceedingJoinPoint jp) {
		Object result=null;
		Object object = jp.getTarget(); //产生代理对象
		Object [] args = jp.getArgs();
		String name= jp.getSignature().getName();
		try {
			try {
				//前置增强
				System.out.println(this.getClass().getName() + ":The " + name + " method begins.");
				System.out.println(this.getClass().getName() + ":Parameters of the "+name +" method: [" + args[0] + "," + args[1] + "]");
				result = jp.proceed();//执行目标对象内的方法
			} finally {
				//后置增强
				System.out.println(this.getClass().getName() + ":The " + name + "method ends.");
			} 
			//返回增强
			System.out.println(this.getClass().getName()+":Result of the "+name +" method:"+result);
		} catch (Throwable e) {
			//异常增强
			System.out.println(this.getClass().getName()+":Result of the "+name +" method:"+e);
		}
		return result;
	}

@Around修饰的方法必须有返回值

四、通过自定义注解的方式实现增强

通过自定义注解的方式实现用户登录权限管理

自定义注解语法

// @Target 说明了Annotation所修饰的对象范围,即METHOD:用于描述方法,即使用时把他放在方法上面。
@Target(ElementType.METHOD)  
// 注解的注解, 即元注解。此注解用于表示当前注解的生命周期,说人话就是这个注解作用会保留到什么时候,如@Retention(RetentionPolicy.RUNTIME)就表示在程序运行期间依然有效,此时就可以通过反射拿到注解的信息
@Retention(RetentionPolicy.RUNTIME)  
public @interface Filter {  
}

定义切面和切点

// 表示该类为切面类
@Aspect  
@Component  
public class LoginFilterAspect {  

	
    @Autowired  
    private RedisTemplate redisTemplate;  

	// 定义切点
    @Pointcut("@annotation(com.lxw.test1.annotation.Filter)")  
    public void FilterCut() {  
    }

	// 环绕注解
    @Around("FilterCut()")  
    public Object Filter(ProceedingJoinPoint joinPoint) {  
        Object[] arg1 = joinPoint.getArgs();  
        Object token = redisTemplate.opsForValue().get(arg1[0].toString());   
        Object result = null;  
        if (arg1[1].toString().equals(token)) {  
            System.out.println("验证成功");  
            try {  
	            // 执行被增强的方法
                result = joinPoint.proceed();  
            } catch (Throwable e) {  
                ResultJson.failure(ResultCode.UNAUTHORIZED);  
            }  
            return result;  
        } else {  
            return ResultJson.failure(ResultCode.UNAUTHORIZED);  
        }  
    }

}

注:需自行配置RedisConfig配置文件,否则上传至redis的数据会存在问题。

接口

@RestController  
@RequestMapping ("/test")  
public class TestController {  
  
    @Autowired  
    private RedisTemplate redisTemplate;  
  
	// 登录成功时向redis注入数据
    @PostMapping("login")  
    public ResultJson login(@RequestBody One one){  
        One two = new One();  
        two.setAccount(123456);  
        two.setPassword("123");  
        String token = UUID.randomUUID() + "";  
        if(Objects.equals(one.getAccount(), two.getAccount()) && one.getPassword().equals(two.getPassword())){  
             redisTemplate.opsForValue().set(one.getAccount().toString(),token);  
  
            return ResultJson.ok(ResultCode.SUCCESS);  
        }else{  
            return ResultJson.failure(ResultCode.LOGIN_ERROR);  
        }  
    }  

	// 访问内部页面,此时加上了@Filter注解,会进行权限验证
    @Filter  
    @GetMapping("test1")  
    public ResultJson test(Integer account,String token){  
        System.out.println("success");  
        return ResultJson.ok(ResultCode.SUCCESS);  
    }  
}

五、参考

基础概念部分:
https://blog.csdn.net/q982151756/article/details/80513340
execution和@annotation:
https://blog.csdn.net/weixin_50968313/article/details/122482395

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的AOP面向切面编程的测试代码示例,使用Spring框架实现: 首先,创建一个切面类 `LoggingAspect`,用于定义切面逻辑: ```java import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Aspect @Component public class LoggingAspect { @Before("execution(* com.example.service.*.*(..))") public void beforeAdvice(JoinPoint joinPoint) { System.out.println("Before method: " + joinPoint.getSignature().getName()); } @After("execution(* com.example.service.*.*(..))") public void afterAdvice(JoinPoint joinPoint) { System.out.println("After method: " + joinPoint.getSignature().getName()); } } ``` 然后,创建一个测试服务类 `UserService`,用于演示AOP的应用: ```java import org.springframework.stereotype.Service; @Service public class UserService { public void createUser(String username) { System.out.println("Creating user: " + username); } public void deleteUser(String username) { System.out.println("Deleting user: " + username); } } ``` 最后,创建一个Spring Boot应用程序,并在启动类中进行配置: ```java import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.EnableAspectJAutoProxy; @SpringBootApplication @EnableAspectJAutoProxy public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); UserService userService = context.getBean(UserService.class); userService.createUser("John"); userService.deleteUser("John"); } } ``` 在上述示例中,`LoggingAspect` 切面类使用 `@Before` 和 `@After` 注解分别定义了在目标方法执行前和执行后的逻辑。切面逻辑会应用于 `UserService` 类中的所有方法。 当运行应用程序时,可以看到切面逻辑在方法执行前和执行后打印了相应的日志消息。 这是一个简单的AOP面向切面编程的示例,你可以根据实际需求进行更复杂的切面逻辑定义和应用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值