详述Spring AOP

一、为什么需要AOP

情景实例:假设你现在有一个计算类ComputerService实现了一些简单的加减乘除功能,你想在返回结果前输出一些提示性语句,因此你写了如下的代码:

ComputerService.java

public class ComputerService implements IComputerService {

    public int add(int a, int b) {
	System.out.println(this.getClass().getName()+":The add method begins.");
	System.out.println(this.getClass().getName()+":Parameters of the add method: ["+a+","+b+"]");
	return a+b;
    }

    public int div(int a, int b) {
	System.out.println(this.getClass().getName()+":The div method begins.");
	System.out.println(this.getClass().getName()+":Parameters of the div method: ["+a+","+b+"]");
	return a/b;
    }
}

你稍加思考似乎这些提示性语句太过繁琐,似乎有种方法可以简化ComputerService中的代码,这时你希望有个“人”如果能帮你先输出这些提示性语句就方便多了。你开始意识到,你需要使用AOP技术获得一个代理来帮你执行这些操作。

二、什么是AOP?

介绍:AOP(Aspect Oriented Programming 面向切面编程)是一种通过运行期动态代理实现代码复用的机制,是对传统OOP(Object Oriented Programming,面向对象编程 )的补充。目前,Aspect J是Java社区里最完整最流行的AOP框架,在Spring 2.0以上版本中可以通过Aspect J注解或基于XML配置AOP。

原理图

三、使用Aspect J注解实现AOP(自动代理)

1、添加所需jar类库

2、修改Spring配置文件

(1)向xml文件中添加<aop:aspectj-autoproxy></aop:aspectj-autoproxy>配置文件,其作用为如果创建目标对象的目标类中的方法与AspectJ切面中切入点表达式(execution注释)匹配,则自动为该目标对象生成动态代理对象。

(2)向xml文件中添加添加<context:component-scan base-package="com.test"></context:component-scan>配置文件,其作用为扫描指定包及其子包中有特定注释的类并为此类创建对象(详见博客——详述context:component-scan作用)。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
	
	<context:component-scan base-package="com.jd"></context:component-scan>
	
	<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>

3、自定义一个@Aspect修饰的切面类并将其创建的对象保存于Spring IOC容器

ComputerAOP.java

@Aspect
@Component
public class ComputerAOP {
}

4、自定义增强方法(也称为通知方法),指有@Before、@AfterRunning、@AfterThrowing、@After或@Around注解修饰的Java方法

(1)@Before:目标方法执行前执行的方法

ComputerAOP.java

@Aspect
@Component
public class ComputerAOP {
        //时机:目标方法执行前
        @Before("execution(public int com.jd.computer.service.ComputerService.*(..))")
        public void before(JoinPoint jp) {
        Object [] args = jp.getArgs();//获取目标类中符合execution限定方法的参数
		
        Signature signature = jp.getSignature();//获取方法名
        String name = signature.getName();

        System.out.println(this.getClass().getName()+":The "+name+" method begins.");
        System.out.println(this.getClass().getName()+":Parameters of the "+name+" method: ["+args[0]+","+args[1]+"]");
    }
}

(2)@After:目标方法执行完成后执行的方法

ComputerAOP.java

@Aspect
@Component
public class ComputerAOP {
   //时机:目标方法执行完成;无论目标方法是否出现异常,都会执行
    @After("execution(public int com.jd.computer.service.ComputerService.*(..))")
    public void after(JoinPoint jp) {
        Signature signature = jp.getSignature();//获取方法名
        String name = signature.getName();
        System.out.println(this.getClass().getName()+":The "+name+" method ends.");
    }
}

(3)@AfterReturing:目标方法返回结果后执行

ComputerAOP.java

@Aspect
@Component
public class ComputerAOP {
   //时机:目标方法返回结果;如果目标方法出现异常无返回数据,则该方法不执行
    @AfterReturning(value = "execution(public int com.jd.computer.service.ComputerService.*(..))",returning="result")//returning指定获取目标方法return返回值的变量名
    public void afterReturning(JoinPoint jp,Object result) {
        Signature signature = jp.getSignature();//获取方法名
	String name = signature.getName();
	System.out.println(this.getClass().getName()+":Result of the "+name+" method:"+result);
    }
}

(4)@AfterThrowing:目标方法返回异常后执行

ComputerAOP.java

@Aspect
@Component
public class ComputerAOP {
   //时机:目标方法返回异常;
   @AfterThrowing(value = "execution(public int com.jd.computer.service.ComputerService.*(..))",throwing="e")//returning指定获取目标方法return返回值的变量名
    public void afterThrowing(JoinPoint jp,Exception e) {
	System.out.println(e.getMessage());
    }
}

(5)@Around:在@Around修饰的方法中可以实现@Before,@After,@AfterReturning和@AfterThrowing增强效果,可以实现动态代理全过程

ComputerAOP.java

@Aspect
@Component
public class ComputerAOP {
   @Around("execution(public int com.jd.computer.service.ComputerService.*(..))")
    public Object around(ProceedingJoinPoint pjp) {
	Object [] args = pjp.getArgs();//传入目标方法参数
		
	Signature signature = pjp.getSignature();//获取方法名
	String name = signature.getName();
		
	//目标方法执行前
	System.out.println(this.getClass().getName()+":The "+name+" method begins.");
	System.out.println(this.getClass().getName()+":Parameters of the "+name+" method: ["+args[0]+","+args[1]+"]");
		
	try {
	    Object result = null;
	    try {
		Object object = pjp.getTarget();//创建目标类对象
		System.out.println(object.getClass().getName());
		result = pjp.proceed();//调用目标方法并返回目标方法结果(没有此行代码将不会调用目标方法)
	    }finally {
		//目标方法执行完成
		System.out.println(this.getClass().getName()+":The "+name+" method ends.");
	    }
	    //目标方法返回结果
	    System.out.println(this.getClass().getName()+":Result of the "+name+" method:"+result);
	    return result;
	} catch (Throwable e) {
	    System.out.println(e.getMessage());
	}
	return -1;
    }
}

5、测试

我们在按上述方法创建Aspect切面类后,原来情景实例中的代码可简化为如下:

ComputerService.java

public class ComputerService implements IComputerService {

    public int add(int a, int b) {
	return a+b;
    }

    public int div(int a, int b) {
	return a/b;
    }
}

此时可以创建Test.java类来测试

Test.java

public class Test {

    public static void main(String[] args) {
	ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
	IComputerService computerService = applicationContext.getBean(IComputerService.class);
	System.out.println(computerService.getClass().getName());	
	System.out.println(computerService.div(1, 1));
        applicationContext.close();	
    }
}

五、Pointcut注解(配置切点)

通过单独自定义一个@Pointcut注解修饰的空方法,通过该方法可以简化@Before,@After,@AfterReturning、@AfterThrowing和@Around注解中的切入点表达式

@Aspect
@Component
public class ComputerAOP {
        
        @Pointcut("execution(public int com.jd.computer.service.ComputerService.*(..))")
	public void t() {
	}
        
        @Before("t()")
        public void before(JoinPoint jp) {
        Object [] args = jp.getArgs();
        Signature signature = jp.getSignature();
        String name = signature.getName();
        System.out.println(this.getClass().getName()+":The "+name+" method begins.");
        System.out.println(this.getClass().getName()+":Parameters of the "+name+" method: ["+args[0]+","+args[1]+"]");
    }
}

六、使用XML配置实现AOP(手动代理)

使用Spring框架的XML配置AOP,则不需要在AOP切面类添加注解。

MethodAOP.java

public class MethodAOP {

	public void before(JoinPoint jp) {
                //获取参数列表
		Object [] args = jp.getArgs();
                //获取方法名
		Signature signature = jp.getSignature();
		String name = signature.getName();
		System.out.println("The "+name+" method begins.");
		System.out.println("The div method params ["+args[0]+","+args[1]+"].");
	}
}

application.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
	
	<context:component-scan base-package="com.jd"></context:component-scan>
	
        <!-- 装配AOP切面类Bean对象 -->
	<bean id="method" class="com.jd.aop.MethodAOP"/>

	<aop:config proxy-target-class="false">
                <!-- 配置切点表达式 -->
		<aop:pointcut expression="execution(public int com.jd.computer.service.ComputerService.*(..))" id="t"/>
                <!-- 配置切面及增强类型:可以有多个切面,每个切面又可以配置多个增强类型-->
		<aop:aspect ref="method">
			<aop:before method="before" pointcut-ref="t"/>
		</aop:aspect>
	</aop:config>
</beans>

注意<aop:config>标签中有一属性proxy-target-class="false",该属性值为false时代表使用JDK代理,而当值为true代表使用CGLib代理,两者代理区分详见博客:详述JDK代理与CGLib代理区别


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值