SpringAOP

AOP

面向切面(儿)编程: 面向程序的横截面(儿)编程,将软件横向切开,在原有软件不变的情况下扩展横向的功能。

AOP:

在这里插入图片描述

如何没有AOP将会是这样的:

在这里插入图片描述

Hello World

AOP的演示案例:

在这里插入图片描述

步骤:

  1. 导入包:

     <dependency>
     	<groupId>org.springframework</groupId>
     	<artifactId>spring-aop</artifactId>
     	<version>3.2.8.RELEASE</version>
     </dependency>
     <dependency>
     	<groupId>org.aspectj</groupId>
     	<artifactId>aspectjweaver</artifactId>
     	<version>1.8.0</version>
     </dependency>
     <dependency>
     	<groupId>org.aspectj</groupId>
     	<artifactId>aspectjtools</artifactId>
     	<version>1.8.0</version>
     </dependency>
    
  2. 开发了 AOP Bean 组件

     @Component //将DemoAspect交给Spring管理
     @Aspect    //声明当前Bean组件是一个切面组件
     public class DemoAspect {
     	/**
     	 * @Before("bean(userService)") 注解的意义:
     	 * 在userService bean的全部方法之前执行test 方法,
     	 * 这个注解声明会被Spring 自动处理,并且执行
     	 */
     	@Before("bean(userService)")
     	public void test(){
     		System.out.println("Hello World!");
     	}
     }
    
  3. 配置:

     <!-- spring-aop.xml -->
     <context:component-scan 
     	base-package="cn.tedu.store.aspect" />
     
     <!-- 让  aspectj 带来的 注解生效-->
     <aop:aspectj-autoproxy/>
    

通知

用于声明 AOP 方法在目标业务方法执行时机。

常用的有5种通知:

@Before
@After	
@AfterThrowing
@AfterReturning
@Around

执行原理如下:

在这里插入图片描述

@Before

在目标方法之前执行, 案例:

/**
 * @Before("bean(userService)") 注解的意义:
 * 在userService bean的全部方法之前执行test 方法,
 * 这个注解声明会被Spring 自动处理,并且执行
 *  Before 之前
 */
@Before("bean(userService)")
public void test(){
	System.out.println("Hello World!");
}

@After

在目标方法执行之后执行,无论目标业务方法是否出现异常@After修饰的方法都会执行:

/**
 * 在 userService 的全部方法之后(After)执行
 */
@After("bean(userService)")
public void test2(){
	System.out.println("Hello @After!");
}

@AfterReturning

在目标方法正常执行之后,如果没有异常发生,则执行@AfterReturning修饰的方法:

/**
 * 在目标方法没有异常情况下执行
 */
@AfterReturning("bean(userService)")
public void test3(){
	System.out.println("Hello @AfterReturning");
}

@AfterThrowing

在目标方法执行出现异常以后会执行 @AfterThrowing 修饰的方法。

/**
 * 在目标方法有异常情况下执行
 */
@AfterThrowing("bean(userService)")
public void test4(){
	System.out.println("Hello @AfterThrowing");
}

@Around 通知

这个是一个万能通知,可以替代其他几个通知,但是使用繁琐:

案例:

/**
 * Around 通知: 对应的AOP方法:
 * 	1. 必须有 参数 ProceedingJoinPoint 
 *  2. 必须有返回值 Object
 *  3. 必须抛出异常 Throwable
 * @param jp
 * @return
 * @throws Throwable
 */
@Around("bean(userService)")
public Object test5(ProceedingJoinPoint jp)
		throws Throwable{
	//Proceeding 进行,处理
	//Join 连接
	//Point 点 
	// 处理过程的连接点
	System.out.println("Around Before");
	//jp.proceed() 调用了目标业务方法,其返回值
	//就是业务方法返回的业务处理结果
	Object obj = jp.proceed();
	//jp 对象中包含被调用目标方法的全部信息
	//其中 getSignature 返回方法的签名,包括:
	//方法和方法的参数类型列表
	Signature method= jp.getSignature();
	System.out.println(method);
	
	System.out.println("Around After:"+obj);
	return obj;
}

切入点表达式PointCut

切入点用于声明对那些 类,对象,方法进行AOP切入

Bean组件切入点

语法:

@Before("bean(userService)") //切入到userService的全部方法
@Before("bean(userService) || bean(dictService)")

案例:

/**
 * Bean组件切入点,对两个Bean组件进行切入
 * 如下程序将在 userService 和 dictService
 * 全部方法之前执行 test() 方法
 */
//@Before("bean(userService) || bean(dictService)")
public void test(){
	System.out.println("Point Cut Test!");
}
/**
 * 切入点表达式 bean(*Service) 将会拦截全部的
 * 业务层方法,每个业务层方法之前都会执行 test1()
 * 方法。使用这种方式进行切入时候,建议有统一的
 * 命名规范。
 */
//@Before("bean(*Service)")
public void test1(){
	System.out.println("Point Cut Test1!");
}

类切入点

按照具体的类名,切到类的全部方法

语法:

//切入到类 UserServiceImpl 中声明的全部的方法
@Before("within(cn.tedu.store.service.UserServiceImpl)")
@Before("within(cn.tedu.store.service.*ServiceImpl)")
@Before("within(cn.tedu.store..*Impl)")

案例:

/**
 * 类的切入点:如下切入点表达式将test2()方法切入到
 * UserServiceImpl 的全部方法之前执行。
 */
@Before("within(cn.tedu.store.service.UserServiceImpl)")
public void test2(){
	System.out.println("Point Cut Test2!");
}

@Before("within(cn.tedu.store.service.*ServiceImpl)")
public void test3(){
	System.out.println("Point Cut Test3!");
}

方法切入点

语法:

//execution 执行
@Before("execution(修饰词 类名.方法名(参数类型))")
@Before("execution(* cn.tedu.store.service.UserService.login(..))")
@Before("execution(* cn.tedu.store.service.*Service.get*(..))")
@Before("execution(* cn.tedu.store..*Service.get*(..))")

案例:

/**
 * 方法切入点:只切入到login方法
 */
@Before("execution(* cn.tedu.store.service.*Service.login(..))")
public void test4(){
	System.out.println("Point Cut Test4!");
} 

测试业务层的所有方法的性能

方案:

@Component
@Aspect
public class TestAspect {
	
	@Around("bean(*Service)")
	public Object test(ProceedingJoinPoint jp)
		throws Throwable{
		try {
			long t1 = System.currentTimeMillis();
			Object val=jp.proceed();
			long t2 = System.currentTimeMillis();
			Signature m = jp.getSignature();
			System.out.println((t2-t1)+":"+m); 
			return val;
		} catch (Throwable e) {
			//继续抛出业务异常
			throw e;
		}
	}
}

AOP 的工作原理

Spting 底层是 AspectJ 再底层利用了 Java的反射和动态代理。其中反射用于解析注解,动态代理用于生成代理对象。

Spring AOP 的底层代理方式有两种,一种是基于Java 动态代理对象,一种是基于 CGlib 的动态代理。 当被代理对象有接口时候优先使用 JAVA 动态代理,如果被代理对象没有接口时候,会自动使用CGLIB。 推荐使用 JAVA 动态代理.

在这里插入图片描述

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值