spring aop原理详解

2018年02月18日 14:21 | 2702次浏览

spring 的aop :面向切面编程。有什么特点呢?和面对对象有什么不同呢?顾名思义,切面编程就是在一个切面上编程,这样解释有点笨。就那日志管理来看,用户登入前,登出后进行日志的记录。那么登入前和登入后便是切面了。


为什么要用aop呢?

1.如果你在用户每个逻辑都写上代码,会显得很冗余。通过aop切面编程将事务放在一个地方,这样便于管理和维护。

2.使业务代码跟干净,便于管理和维护。

之前听大牛说aop是一把双刃剑,但对于初学的我来说,貌似没有被割伤。


aop术语

下图是在网上的图,做的很好:

切面(Aspect): 横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象

通知(Advice): 切面必须要完成的工作

目标(Target): 被通知的对象

代理(Proxy): 向目标对象应用通知之后创建的对象

连接点(Joinpoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。


切点(pointcut):每个类都拥有多个连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。


使用spring4 aop

第一步:导包,可以参考:spring4 项目搭建 

还需要添加AspectJ(Java中最流行的aop框架):

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-aspects</artifactId>
	<version>4.2.2.RELEASE</version>
</dependency>

第二步:配置bean文件

配置aop可以通过注解,也可以通过配置文件,项目中建议有配置文件,这样便于修改。我先讲解注解方法

配置自动扫描和AspectJ框架

<!-- 配置自动扫描的aop包 -->
	<context:component-scan base-package="com.spring.aop"/>
	
	<!-- 配置 AspectJ -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

如果aop显示不出来就先xml头文件中导入(我相信大家会导入的,就不多说了)

xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd

第三步:业务代码,模拟用户的操作

业务代码AopService

package com.spring.aop;

import org.springframework.stereotype.Service;

@Service
public class AopService {
	
	// 用户登入
	public void login(){
		System.out.println("登入成功");
	}
	
	// 用户退出
	public void loginOut(){
		System.out.println("用户退出系统");
	}
	
	// 用户操作
	public void writeABlog(){
		System.out.println("用户编写博客");
	}
	
	// 用户操作
	public void deleteABlog(){
		System.out.println("用户删除博客");
	}

}

切面代码:

package com.spring.aop;

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

@Component
@Aspect
public class LogAspect {
	
	@After("execution(public void com.spring.aop.AopService.*(..))")
	public void afterMethod(JoinPoint joinPoint) {
		String opreate = joinPoint.getSignature().getName();
		System.out.println("ITDragon opreate " + opreate);
	}

}

测试类:

package com.spring.aop;

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

public class AopTest {
	
	private ApplicationContext ctx = null;
	
	{
		ctx = new ClassPathXmlApplicationContext("beans.xml");
	}
	
	@Test
	public void aopTest(){
		AopService aopService = (AopService) ctx.getBean("aopService");
		aopService.login();
		aopService.writeABlog();
		aopService.deleteABlog();
		aopService.loginOut();
	}

}

测试结果:

登入成功
ITDragon opreate login
用户编写博客
ITDragon opreate writeABlog
用户删除博客
ITDragon opreate deleteABlog
用户退出系统
ITDragon opreate loginOut

@Before: 前置通知, 在方法执行之前执行

@After: 后置通知, 在方法执行之后执行 

@AfterRunning: 返回通知, 在方法返回结果之后执行,方法必须有返回值。需要添加returning = "result",其中result就是返回结构,

@AfterReturning(pointcut="execution(public void com.spring.aop.AopService.*(..))",returning="result")

@AfterThrowing: 异常通知, 在方法抛出异常之后,有异常的时候才执行

@AfterThrowing(pointcut="execution(public void com.spring.aop.AopService.*(..))",throwing="e")

@Around: 环绕通知, 围绕着方法执行

@Component:标识该类受spring管理

@Aspect: 标识该类是一个切面

execution(public void com.spring.aop.AopService.*(..)) : 切入点签名表达式,用*号表示所有,也可以指定,括号内两点表示多个变量,也可以指定,还可以用 && , || , !;以每个execution为一个单位

JoinPoint:连接点,该参数可以访问到更多的数据,还有很多方法可以自己试试。

@Order(n): 切面执行的优先级,n值越小,越优先执行

还有重用切面,等知识我就不过多描述了,毕竟是入门,讲一些常用的就可以了。


使用xml配置文件

package com.spring.aop;

import org.aspectj.lang.JoinPoint;

public class LogAspect {
	
	public String afterMethod(JoinPoint joinPoint) {
		String opreate = joinPoint.getSignature().getName();
		System.out.println("ITDragon opreate " + opreate);
		return "";
	}

}

配置文件:

<!-- 扫描切面类 -->
	<bean class="com.spring.aop.LogAspect" id="logAspect"></bean>
	
	<!-- aop配置 -->
	<aop:config>
		<!-- 切点 -->
		<aop:pointcut expression="execution(public void com.spring.aop.AopService.*(..))" id="aop"/>
		<!-- 切面 : ref 的值是 切面类的id-->
		<aop:aspect id="aspect" ref="logAspect">
			<!-- 前置方法 : pointcut-ref 的值是 切点的id -->
			<aop:before method="afterMethod" pointcut-ref="aop"/>
		</aop:aspect>
	</aop:config>

还是一样的测试方法,其结果为:

ITDragon opreate login
登入成功
ITDragon opreate writeABlog
用户编写博客
ITDragon opreate deleteABlog
用户删除博客
ITDragon opreate loginOut
用户退出系统


小说《我是全球混乱的源头》

感觉本站内容不错,读后有收获?小额赞助,鼓励网站分享出更好的教程