SpringBoot学习笔记——第三节,面向切面编程(AOP)

Spring框架有四大原则

       ·使用POJO进行轻量级与最小侵入式开发

       ·通过依赖注入和基于接口编程实现松耦合

       ·通过AOP和默认习惯进行声明式编程

       ·通过AOP和模板减少模式化代码


为什么需要AOP


    • AOP(AspectOrient Programming)也就是面向切面编程,作为面向对象编程的一种补充,已经成为一种比较成熟的编程方式。其实AOP问世的时间并不太长,AOP和OOP互为补充,面向切面编程将程序运行过程分解成各个切面。

    •AOP专门用于处理系统中分布于各个模块(不同方法)中的交叉关注点的问题,在JavaEE应用中,常常通过AOP来处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、对象池管理等,AOP已经成为一种非常常用的解决方案。

    我们知道,面向对象是为了解决面向过程开发中的高冗余代码而出现的。OOP能够自上而下的,让类与类之间建立一种关系从而达到动态添加代码的目的。



而Aop则是以横切的方式,将可以重复性的横切逻辑到一个统一的模块中。


关于面向切面编程的一些术语

切面( Aspect 切面用于组织多个 Advice Advice 放在切面中定义。  :: 切面准确描述了,我们应该在哪切入代码,如何切入。包含了连接点信息,增强处理信息,切入点信息。

连接点 Joinpoint :     程序执行过程中明确的点,如方法的调用,或者异常的抛出。在 Spring AOP 中,连接点总是方法的调用 。::连接点明确指出触发条件。                                

增强处理( Advice : AOP 框架在特定的切入点执行的增强处理。处理有 "around" "before" "after" 类型。::增强处理定义了我们到底在触发点的什么时候执行插入的代码。

切入点( Pointcut 可以插入增强处理的连接点。简而言之,当某个连接点满足指定要求时,该连接点将被添加增强处理,该连接点也就变成了切入点。::切入点定义了在哪里切入。(Spring aop只支持把方法当作切入点)

下面是详细介绍


通知(Advice)

Before :    方法被调用之前调用通知

After :    方法完成之后调用通知,无论方法执行是否成功

AfterReturning :    方法成功执行之后调用通知

AfterThrowing :    方法抛出异常后调用通知

Around :    通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为


切点(PointCut)

    •通知定义了切面的什么何时,切点定义了何处,切点的定义会匹配通知所要织入的一个或多个连接点,我们通常使用明确的类的方法名称来指定这些切点,或是利用正则表达式定义匹配的类和方法名称来指定这些切点。
切点的格式
如下:
    •execution(*com.example.demo.StudentManager.*(..))


连接点(JoinPoint)

定义:连接点是一个应用执行过程中能够插入一个切面的点。

连接点 可以是调用方法时、抛出异常时、甚至修改字段时

切面 代码可以利用这些点插入到应用的正规流程中

程序 执行过程中能够应用通知的所有点。

常用AspectJ指示器:

AspectJ指示器

描述

arg ()

限制连接点的指定参数为指定类型的执行方法

@args ()

限制连接点匹配参数由指定注解标注的执行方法

execution ()

用于匹配连接点的执行方法

this ()

限制连接点匹配 AOP 代理的 Bean 引用为指定类型的类

target ()

限制连接点匹配特定的执行对象,这些对象对应的类要具备指定类型注解

within()

限制连接点匹配指定类型

@within()

限制连接点匹配指定注释所标注的类型(当使用 Spring AOP 时,方法定义在由指定的注解所标注的类里)

@annotation

限制匹配带有指定注释的连接点

下面我们来实战一下

首先我们要使用AOP的话需要添加Spring AOP 和 AspectJ这两个依赖

我们在mvnrepository.com中找到了spring-boot-starter-aop这个依赖同时能够添加Spring AOP 和 AspectJ这两个依赖。


http://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop/2.0.0.RELEASE


首先我们先写两个测试的类,还是沿用上次使用的Student与StudentManager类来做说明。

Student类是个POJO,有两个属性:int -> id , String -> name

package com.example.demo;
import org.springframework.stereotype.Repository;

@Repository("Student")
public class Student {

	private int id;

	private String name;

	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}

}

StudentManager类管理了一个IOC容器中的Student的实例,并提供setStudent ,show 两个方法

setStudent能改变Student实例的属性

show方法能够输出Student实例的 id 和 name

package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service("studentManager")                        //把当前类加入IOC容器进行管理
public class StudentManager {
	@Autowired                                //自动注入一个Student的Bean实例
	Student student;
	
	public void setStudent(Student student) {
		this.student = student;
	}
	
	public void show() {
		System.out.println("Student-Id : "+student.getId());
		System.out.println("Student-name : "+student.getName());
	}
}

以上与两个类与AOP无关hhhhh,仅作为测试使用。

然后我们再写配置类,我们需要在配置类中添加对Aop的支持

使用@EnableAspectJAutoProxy注解
package com.example.demo;

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

@Configuration
@ComponentScan("com.example.demo")
@EnableAspectJAutoProxy                        //添加对AspectJ的支持
@RequestMapping
public class DIConfig {
}

    问题引出

    当我们编写好这两个类的之后过了很长一段时间(假如),我们这个时候在运行这段代码的时候发现这里可能有问题,想在每个方法开始之前输出一句start,方法运行结束以后输出一句end,来表示这个方法被正确执行了。我们假设有许多类都需要做此类操作,我们假如用oop的思想的话,需要在每个类中去添加新的代码,来实现这个输出start,end这种非常简单的功能,是非常繁琐的。特别是项目大了以后,要做修改非常困难。

    我们下面来用AOP实现这样一个简单的功能。

package com.example.demo;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component

public class AOPtest {
	
	@Pointcut("execution(* com.example.demo.StudentManager.*(..))")
	public void studentCut() {
	}
	
	@Before("studentCut()")
	public void start() {
		System.out.println("start");
	}
	
	@After("studentCut()")
	public void end() {
		System.out.println("end");
	}
	
	@Before("execution(* com.example.demo.StudentManager.*(..))")
	public void beforeIt() {
		System.out.println("Before Do It");
	}
}

     我们定义了一个切面叫做AOPtest,实际上它是一个java类,我们使用了@Aspect注解,告诉Spring,这个类是一个切面。

     然后我们使用了@Pointcut注解,定义了一个切点,叫做studentCut,切点后就是这个切点的描述信息。我们来解读一下这段描述:

@Pointcut("execution(* com.example.demo.StudentManager.*(..))")

@Pointcut 声明这是一个切点
execution当方法被执行时调用
第一个*返回值可以为任意类型
com.example.demo.StudentManagercom.example.demo包下的StudentManager类
第二个*StudentManager类的全部方法(这里可以替换为该类的任意方法名)
(..) 可以为任意参数

    我们可以发现,使用这样的切点可以准确定位我们应该在哪里进行拦截操作。有点类似于正则表达式的作用,aop通过这种方式能够一次性匹配一堆类的方法进行拦截。

    下面我们演示一下如何动态的在切点(Pointcut)插入代码

        @Before("studentCut()")
	public void start() {
		System.out.println("start");
	}
	
	@After("studentCut()")
	public void end() {
		System.out.println("end");
	}
我们使用了@Before ,@After 这两个注解分别在studentCut这个切点前后输出了“start”与“end”

@Before在切点前执行该方法
studentCut上面定义好的一个切点
@After在切点后执行该方法

使用了@Before注解的start()方法将会在拦截方法执行前被执行

使用了@After注解的start()方法将会在拦截方法执行后被执行

我们也可以不单独定义Pointcut,直接在通知(Advice)中写明:

@Before("execution(* com.example.demo.StudentManager.*(..))")
	public void beforeIt() {
		System.out.println("Before Do It");
	}

让我们来测试一下


我们可以看到,我们成功的在执行studentManager.show()的时候,动态的加入了一些代码。

总结

    AOP的思维作为oop思想的补充,在处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、对象池管理等,已经成为一种非常常用的解决方案。但是我们必须清楚,aop并不能替代oop的作用,它更多的是起一个辅助者的作用。它为程序提供了一个崭新的编程思考角度,可以重复性的横切逻辑到一个统一的模块中,只要通过纵向抽象和AOP横向抽取,程序员才可以真正解决重复代码问题,提高代码的重用性与复用性。

预告

    下一节我们将简单介绍Spring的常用配置

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值