来学习采用Spring框架如何进行面向切面(AOP)编程

使用Spring进行面向切面(AOP)编程
-------------------------------------------------------------
要进行AOP编程,首先我们要在spring的配置文件中引入aop命名空间:


Xml代码


 

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
</beans>



如果要使用切面编程(AOP),还需要下列jar文件

lib/aspectj/aspectjweaver.jar和aspectjrt.jar 
lib/cglib/cglib-nodep-2.1_3.jar




我们使用Spring框架进行AOP编程的时候呢,Spring提供了两种切面声明方式,实际工作中我们可以选用其中一种:
1.基于XML配置方式进行AOP开发。
2.基于注解方式进行AOP开发。

对这两种方式都进行下介绍,先学习基于注解方式进行AOP开发
先是beans.xml里,要引入AOP的命名空间,打开配置项


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-2.5.xsd
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
        <aop:aspectj-autoproxy/>
		<!--
			打开配置项,这个配置项是对@Aspectj这个注解进行支持
			前面已经说过了,注解本身是不能干活的,注解之所以能干活是因为后面有处理器对其进行处理
			这个配置相当于为我们将要使用的@Aspectj注解提供了解析的功能
		-->
</beans>



接下来的工作是,定义一个切面,在切面里面定义切入点,所谓切入点就是说我们要对业务bean里面的哪些方法进行拦截,我们还要定义通知,所谓通知,就是说我们拦截到方法后我们需要做的工作。

新建业务bean用作测试,PersonService.java


Java代码


package cn.itcast.service;
public interface PersonService {
	public void save(String name);
	public void update(String name, Integer id);
	public String getPersonName(Integer id);
}

PersonServiceBean.java

Java代码

package cn.itcast.service.impl;

import cn.itcast.service.PersonService;

public class PersonServiceBean implements PersonService {

 public String getPersonName(Integer id) {
  return "xxx";
 }

 public void save(String name) {
  System.out.println("我是save()方法");
 }

 public void update(String name, Integer id) {
  System.out.println("我是update()方法");
 }

}


用作测试用的业务bean已经开发好了,接着就要进行AOP的开发,因为我们采用的是基于注解的方式来实现AOP功能,先了解一些概念

基于注解方式声明切面
------------------------------------------------------------------------------


Java代码


@Aspect
public class LogPrint {
	@Pointcut("execution(* cn.itcast.service..*.*(..))")
	private void anyMethod() {}//声明一个切入点	
	@Before("anyMethod() && args(userName)")//定义前置通知
	public void doAccessCheck(String userName) {
	}	
	@AfterReturning(pointcut="anyMethod()",returning="revalue")//定义后置通知
	public void doReturnCheck(String revalue) {
	}
	@AfterThrowing(pointcut="anyMethod()", throwing="ex")//定义例外通知
    public void doExceptionAction(Exception ex) {
	}
	@After("anyMethod()")//定义最终通知
	public void doReleaseAction() {
	}
	@Around("anyMethod()")//环绕通知
	public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
		return pjp.proceed();
	}
}

在这里@Pointcut("execution(* cn.itcast.service..*.*(..))")


private void anyMethod() {}//声明一个切入点
切入点用来定义我们要拦截的方法,因为Spring只支持方法的拦截,所以这里说的是拦截方法
切入点的定义里面,使用了一个AOP的表达式语言@Pointcut("execution(* cn.itcast.service..*.*(..))"),这个语言是比较灵活的,功能也是比较强大的,在实际使用中呢几乎80%多我们使用的表达式都是类似这种写法,给大家介绍下切入点的表达式的含义。
execution代表执行,执行业务方法的时候我要进行拦截;
第一个*号代表的是返回值的类型,通配符*号表示的话就代表任何的返回值类型;
cn.itcast.service代表的是包名,也就是说你要对哪些包底下的类进行拦截;
然后是两个点..,这两个点代表对cn.itcast.service的子包底下的类也要进行拦截,如果不定义这两个点的话,那么它只会对service包底下的类进行拦截,如果定义了两个点,那么即会对service包底下的类进行拦截,也会对service子包底下的类进行拦截;
第二个*号代表的是类,你要对哪个类进行拦截,*号代表所有类,也就是说service子包底下的所有类进行拦截
第三个*号代表方法,代表所有的方法;
最后的括号里面有两个点(..),这两个点代表方法的参数可以任意,有可以,没有也可以。一个也可以,多个也可以,代表随意
MyInterceptor.java


Java代码


package cn.itcast.service;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
/**
 * 切面
 */
@Aspect //用来指明这个类是一个切面
public class MyInterceptor {
	@Pointcut("execution (* cn.itcast.service.impl.PersonServiceBean.*(..))")
	//切入点的表达式写完了,接下来要为切入点定义一个名称,这个名称的定义有点怪怪的
	//采用的是方法定义的形式来定义它的名称的
	private void anyMethod() {}//声明一个切入点,anyMethod()就是切入点的名称,括号也带上

	@Before("anyMethod()")  //里面应该填入切入点的名称
	public void doAccessCheck() {
		System.out.println("前置通知");
	}
}

这样切面就定义好了,在切面里面有我们关注的切入点,并且有我们关注的通知,我们的代码很简洁,当我们定义好切面之后就要注意了,要把MyInterceptor这个切面交给Spring容器管理,如果你不交给Spring管理的话,你定义完了它也不会起效果。 
  要交给Spring管理的话,有两种方式,一种方式是在配置文件里面通过<bean>元素把它交给Spring管理,还有种方式是通过扫描的方式,加一个注解@Component在public class MyInterceptor{上面的方式把这个bean交给Spring管理。现在并没有开启自动扫描的方式,所以这里就采用基于XML配置的方式把bean交给Spring管理


beans.xml


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-2.5.xsd
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
        <aop:aspectj-autoproxy/>
		<!--
			打开配置项,这个配置项是对@Aspectj这个注解进行支持
			前面已经说过了,注解本身是不能干活的,注解之所以能干活是因为后面有处理器对其进行处理
			这个配置相当于为我们将要使用的@Aspectj注解提供了解析的功能
		-->
        <bean id="myInterceptor" class="cn.itcast.service.MyInterceptor"/>
        <bean id="personService" class="cn.itcast.service.impl.PersonServiceBean"></bean>
</beans>

现在就来开发客户端,在客户端里面我们调用PersonServiceBean这个业务bean的save方法,看一下MyInterceptor这个切面能否帮我们拦截到这个方法,然后在拦截到之前先执行前置通知代码,然后再执行业务方法save() 

新建一个单元测试


SpringAOPTest.java


Java代码



package junit.test;

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

import cn.itcast.service.PersonService;

public class SpringAOPTest {

	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
	}

	@Test public void interceptorTest(){
		ApplicationContext cxt = new ClassPathXmlApplicationContext("beans.xml");
		PersonService personService = (PersonService)cxt.getBean("personService");
		personService.save("xx");
	}
}


运行单元测试代码,控制台输出
前置通知
我是save()方法
说明,先执行前置通知,在执行业务方法,前置通知起效果了。