spring AOP简介

一、动态代理
在讨论AOP之前,首先要了解常用的设计模式之一:代理模式,就是给某一个对象创建一个代理对象,由这个代理对象控制对原对象的引用,并可以添加一些额外的操作;根据创建代理类的时间点,分为静态代理和动态代理;具体介绍参考前文:常用设计模式(一)

二、AOP和OOP
Java是面向对象编程,但实际有一些需求并不是OOP可以解决的,这个时候就需要面向切面编程的AOP,例如常用的数据库事务,一个事务可能会涉及到多个操作,通常会要求这些操作要么全部成功要么全部失败,就可能会涉及到在同一个事务中管理多个对象,但这个事务就是一个切面 ;除此之外,AOP还可以拦截一些方法,通过advice插入操作前后的额外需求;还可以简化开发,集中在业务处理上(用到动态代理);下图为AOP框架约定的SQL流程示例:
在这里插入图片描述

以上例子为同时失败或同时成功,AOP也支持部分失败,部分成功的情况;总之,AOP是通过动态代理,来掌控各个对象操作的切面环境,管理包括日志、数据库事务等操作,让我们可以在原始方法之前之后等插入自己的逻辑代码,有时候甚至取代原始方法;对于有些常用流程,如数据库事务,AOP还提供了默认的实现逻辑;

AOP同样基于Proxy和InvocationHandler;先介绍一下AOP相关的术语:
-连接点join point:对应spring中特定的方法,如method();
-切点point cut:通过正则和指示器规则定义,从而适配连接点即什么时候启动AOP;
-通知advice:切面开启后,切面的方法,分为前置通知before advice、后置通知after advice、环绕通知around advice、事后返回通知afterReturning advice和异常通知afterThrowing advice;
-目标对象target:被代理对象;
-引入introduction:引入新的类及其方法,增强现有Bean功能;
-织入weaving:通过动态代理技术,为原有服务对象生成代理对象,然后将其与切点定义匹配的连接点拦截,并按约定将各类通知织入约定流程的过程;
-切面aspect:在一个怎么样的环境中工作,可以定义各类通知、切点、引入,可以理解成一个拦截器;
如下为AOP流程图:
在这里插入图片描述

三、Spring AOP
spring作为支持AOP编程的框架之一,是基于方法拦截的,有如下4种方式实现:
(1)使用ProxyFactoryBean和对应的接口实现AOP;
(2)使用XML配置;
(3)使用@AspectJ注解驱动切面:最常用;
(4)使用AspectJ注入切面;

接下来将使用@AspectJ介绍Spring AOP的开发实例:
(1)选择连接点:随意定一个接口及其实现

public interface RoleService {
	public void printRole(Role role);
}
@Component //把普通pojo实例化到spring容器中
public class RoleServiceImpl implements RoleService{
	@Override
	public void printRole(Role role) {
		System.out.println("ID:"+role.getId()+",Name:"+role.getName()+",Note:"+role.getNote());
	}
}
//引入:增强Bean,如下为验证role是否空
public interface RoleVerifier {
	public boolean verify(Role role);
}
public class RoleVerifierImpl implements RoleVerifier{
	@Override
	public boolean verify(Role role) {
		System.out.println("验证Role是否为空");
		return role!=null;
	}
}

(2)创建切面:使用@Aspect注解一个类,Spring IoC就会认为这是一个切面了;

//方式一:
@Aspect
public class RoleAspect {
	@DeclareParents(value="cn.infocore.aop.RoleServiceImpl+",
	defaultImpl=RoleVerifierImpl.class)
	public RoleVerifier verifier;
	
	@Before("execution(* cn.infocore.aop.RoleServiceImpl.printRole(..))")
	public void before(){
		System.out.println("前置通知:在被代理对象的方法前先调用");
	}
	
	@After("execution(* cn.infocore.aop.RoleServiceImpl.printRole(..))")
	public void after(){
		System.out.println("后置通知:在被代理对象的方法后调用");
	}
	
	@AfterReturning("execution(* cn.infocore.aop.RoleServiceImpl.printRole(..))")
	public void afterReturning(){
		System.out.println("返回通知:在被代理对象的方法正常返回后调用");
	}
	
	@AfterThrowing("execution(* cn.infocore.aop.RoleServiceImpl.printRole(..))")
	public void afterThrowing(){
		System.out.println("异常通知:在被代理对象的方法抛出异常后调用");
	}
	
	@Around("execution(* cn.infocore.aop.RoleServiceImpl.printRole(..))")
	public void around(ProceedingJoinPoint jp){
		System.out.println("around before......");
		try {
			jp.proceed();
			System.out.println("环绕通知:将被代理对象的方法封装起来,并用环绕通知取代它,允许通过反射调用原有方法");
		} catch (Throwable e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("around after......");
	}
}
//方式二:借助@Pointcut使用简易表达式取代多次书写的重复表达式
@Aspect
public class RoleAspect {
        //value表示对RoleServiceImpl类进行增强,引入了一个新的接口,defaultImpl代表其默认实现类
	@DeclareParents(value="cn.infocore.aop.RoleServiceImpl+",
	defaultImpl=RoleVerifierImpl.class)
	public RoleVerifier verifier;
	
	@Pointcut("execution(* cn.infocore.aop.RoleServiceImpl.printRole(..))")
	public void print(){}
	
	@Before("print()")
	public void before(){
		System.out.println("前置通知:在被代理对象的方法前先调用");
	}
	
	@After("print()")
	public void after(){
		System.out.println("后置通知:在被代理对象的方法后调用");
	}
	
	@AfterReturning("print()")
	public void afterReturning(){
		System.out.println("返回通知:在被代理对象的方法正常返回后调用");
	}
	
	@AfterThrowing("print()")
	public void afterThrowing(){
		System.out.println("异常通知:在被代理对象的方法抛出异常后调用");
	}
	
	@Around("print()") //通过ProceedingJoinPoint可以反射连接点方法
	public void around(ProceedingJoinPoint jp){
		System.out.println("around before......");
		try {
			jp.proceed();
			System.out.println("环绕通知:将被代理对象的方法封装起来,并用环绕通知取代它,允许通过反射调用原有方法");
		} catch (Throwable e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("around after......");
	}
}

(3)定义切点:
切面定义中的正则表达式:execution(*cn.infocore.aop.RoleServiceImpl.printRole(…));
execution:代表执行方法的时候会触发;
*:代表任意返回类型的方法;
cn.infocore.aop.RoleServiceImpl:代表类的全限定名;
printRole:被拦截方法名;
(…):任意的参数;
这样,方法printRole就按照AOP通知规则被织入流程了;如下为spring支持的AspectJ指示器列表:
在这里插入图片描述
(4)配置Spring Bean

@Configuration
@ComponentScan("cn.infocore.aop")
@EnableAspectJAutoProxy  //启用AspectJ框架的自动代理
public class AopConfig {
	@Bean
	public RoleAspect getRoleAspect(){
		return new RoleAspect();  //生成一个切面实例
	}
}

(5)测试

public class Test {
	public static void main(String[] args) {
		//根据配置生成IoC容器
		ApplicationContext ctx=new AnnotationConfigApplicationContext(AopConfig.class); 
		//从容器中获取bean对象
		RoleService service=ctx.getBean(RoleService.class);
		//强制转换:代理对象挂在多个接口下,那么对应的Bean就可以互相转换
		RoleVerifier verifier=(RoleVerifier)service;
		Role role=new Role();
		role.setId("1");
		role.setName("name1");
		role.setNote("note1");
		service.printRole(role);
	}
}

结果:
验证Role是否为空
around before…
前置通知:在被代理对象的方法前先调用
ID:1,Name:name1,Note:note1
环绕通知:将被代理对象的方法封装起来,并用环绕通知取代它,允许通过反射调用原有方法
around after…
后置通知:在被代理对象的方法后调用
返回通知:在被代理对象的方法正常返回后调用

上述例子中通知中除了环绕以外,未传递参数,因此我们可以使用args(xx,xx…)来给通知传递参数;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值