Spring框架学习笔记(五)Spring Aop学习笔记(基于注解的Aop)

1.Aop:  面向切面编程 

具体的视图可以这样理解:

即通过切面来使代码不冗余,同样也便于修改。

2. Aop术语

  1. 切面(Aspect):  横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象
  2. 通知(Advice):  切面必须要完成的工作 目标(Target): 被通知的对象
  3. 代理(Proxy): 向目标对象应用通知之后创建的对象
  4. 连接点(Joinpoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。例如 ArithmethicCalculator#add() 方法执行前的连接点,执行点为 ArithmethicCalculator#add(); 方位为该方法执行前的位置
  5. 切点(pointcut):每个类都拥有多个连接点:例如 ArithmethicCalculator 的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。

3. 用 AspectJ 注解声明切面

具体步骤: 

  • 要在 Spring 中声明 AspectJ 切面, 只需要在 IOC 容器中将切面声明为 Bean 实例.
  • 在 AspectJ 注解中, 切面只是一个带有 @Aspect 注解的 Java 类. 
  • 通知是标注有某种注解的简单的 Java 方法.

AspectJ 支持 5 种类型的通知注解:

@Before: 前置通知, 在方法执行之前执行
@After: 后置通知, 在方法执行之后执行 
@AfterRunning: 返回通知, 在方法返回结果之后执行
@AfterThrowing: 异常通知, 在方法抛出异常之后
@Around: 环绕通知, 围绕着方法执行

首先写一个接口类 一个实现类,用于实现代码

接口类:

package SpringAop;

public interface Userformula {
	
	public int add(int i,int j);
	public int sub(int i,int j);
	public int mul(int i,int j);
	public int div(int i,int j);

}

实现类 (注意添加注解  @Component(value="userformula") )

package SpringAop;

import org.springframework.stereotype.Component;

@Component(value="userformula")
public class formulaimpl implements Userformula {
	@Override
	public int add(int i, int j) {
		// TODO 自动生成的方法存根
		int result=i+j;	
		return result;
	}
	@Override
	public int sub(int i, int j) {
		// TODO 自动生成的方法存根
        int result=i-j;	
		return result;	
		}
	@Override
	public int mul(int i, int j) {
		// TODO 自动生成的方法存根
        int result=i*j;	
		return result;
	}
	@Override
	public int div(int i, int j) {
		// TODO 自动生成的方法存根
        int result=i/j;	
		return result;
	}
}

创建切面类:

需要添加   @Component     @Aspect  两个注解

① : 前置通知

创建方法, 添加前置通知

如果要获取切入点的数据或者值,可以通过 JoinPoint 来获取

@Before("execution(public int SpringAop.*.*(int, int))")
	public void beformethod(JoinPoint joinPoint) {
		//切入点方法名
		String methodName = joinPoint.getSignature().getName();
		//将切入点获取的值传入集合
		Object [] args = joinPoint.getArgs();
		
		System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
	}

②:后置通知 (同理与前置)

@After("execution(public int SpringAop.*.*(int, int))")
	public void afterMethod(JoinPoint joinPoint){
		String methodName = joinPoint.getSignature().getName();
		System.out.println("The method " + methodName + " ends");
	}

③:返回通知 

//返回通知 可以通过returning="result" 以及方法名中加Object result 来获取返回值
	@AfterReturning(value="execution(public int SpringAop.*.*(int, int))",
returning="result")
	public void afterreturnning(JoinPoint joinPoint,Object result) {
		String methodName=joinPoint.getSignature().getName();
		System.out.println("The method"+methodName+"result:"+result);
	}

 ④:异常通知

//异常通知  同返回通知一样
	@AfterThrowing(value="execution(public int SpringAop.*.*(int, int))",throwing="ex")
	public void afterthrowig(JoinPoint joinPoint,Exception ex) {
		String methodname=joinPoint.getSignature().getName();
		System.out.println("The method"+methodname+"exception:"+ex);
	}

⑤:环绕通知(不常用)

将通知全部围绕在一个里面

@Around(value="execution(public int SpringAop.*.*(int, int))")
	public Object around(ProceedingJoinPoint pjd) {
		Object result = null;
		String methodName = pjd.getSignature().getName();
		
		try {
			//前置通知
			System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
			//执行目标方法
			result = pjd.proceed();
			//返回通知
			System.out.println("The method " + methodName + " ends with " + result);
		} catch (Throwable e) {
			//异常通知
			System.out.println("The method " + methodName + " occurs exception:" + e);
			throw new RuntimeException(e);
		}
		//后置通知
		System.out.println("The method " + methodName + " ends");
		
		return result;
	}

从上面可以看到每一个注解后面都需要添加exetution() 如果需要修改则需要修改多处,不符合现代代码风格,违背设计模式。

我们需要通过一种方式来封装起来。

利用   @Pointcut("execution(public int SpringAop.*.*(int, int))") 然后后面添加一个空方法,下面则只需要调用空方法就可以实现。

@Pointcut("execution(public int SpringAop.*.*(int, int))")
	public void point() {
		
	}
	

调用切入点方法实现前置通知。

@Before("point()")
	public void beformethod(JoinPoint joinPoint) {
		//切入点方法名
		String methodName = joinPoint.getSignature().getName();
		//将切入点获取的值传入集合
		Object [] args = joinPoint.getArgs();
		
		System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
	}

配置文件中添加Context 和 Aop

<context:component-scan base-package="SpringAop"></context:component-scan>
    <!-- 起到切面作用 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

写一个主函数:

package SpringAop;

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

public class main {

	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		
		ApplicationContext ctx=new ClassPathXmlApplicationContext("Applicationcontext.xml");
		
		Userformula user=(Userformula) ctx.getBean("userformula");
		
		int r=user.add(3, 6);
		
		System.out.println("r的值为: "+r);
		
		int s=user.div(100, 10);
		System.out.println("s的值为"+s);	
	}

}

运行即可完成。

运行结果:

分别为 前置通知    后置通知   返回通知  因为无错所以没有报异常通知

发布了25 篇原创文章 · 获赞 14 · 访问量 7197

猜你喜欢

转载自blog.csdn.net/sdau_20171819/article/details/104174388