在实际开发中,Spring最常用到的是基于AspectJ的AOP开发方式。而AspectJ的使用通常有两种方式:注解方式和xml配置方式。

AspectJ简述

AspectJ是一个基于Java语言的AOP框架,在此之前都是使用传统的AOP开发,AspectJ是一个独立的AOP框架,由于它的方便效率,Spring2.0以后新增了对AspectJ切点表达式支持。

@AspectJ是AspectJ1.5新增的功能,通过JDK5注解的方式,允许直接在Bean类中定义切面。新版本的Spring框架建议使用AspectJ方式来进行开发aop。

使用AspectJ需要导入Spring AOP和AspectJ相关的jar包,如spring-aop、aopalliance、aspects以及aspectj.weaver等。

基于注解方式的AspectJ开发AOP

第一步:新建一个webapp,名称为spring_aspectJ,接着在里面新建一个java源文件。

第二步:导入相应的包。首先在pom.xml文件中引入spring的基本开发包以及AspectJ开发包:

<!--Spring开发的基本包--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>4.2.4.RELEASE</version> </dependency> <!--传统的AOP开发需要引入的两个包--> <dependency> <groupId>aopalliance</groupId> <artifactId>aopalliance</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>4.2.4.RELEASE</version> </dependency> <!--AspectJ AOP开发需要引入的两个包--> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.10</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>4.2.4.RELEASE</version> </dependency> <!--测试包--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>4.2.4.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> </dependency>

这里使用Spring进行开发,那么spring-core、spring-beans、spring-context、spring-context这四个核心包不能缺少。与此同时进行AOP开发,aop联盟的包和Spring的aop包也要引入:aopalliance和spring-aop。由于使用aspectJ,故需引入aspectJ框架的包aspectjweaver和Spring的aspects包spring-aspects。为了便于测试需要引入spring-test和junit包(注意如果想使用SpringJUnit4ClassRunner,则junit的版本必须是4.12及以上)。特别注意spring四个核心包及spring-test包的版本必须保持一致。

第三步:配置applicationContext文件。在Resources下面新建一个applicationContext.xml文件。既然进行aop开发,因此配置文件中需要有aop相关的约束,新创建的applicationContext.xml文件是没有的,因此需要到spring-framework-4.2.0.RELEASE-dist\spring-framework-4.2.0.RELEASE\docs\spring-framework-reference\html\xsd-configuration.html页面中进行查找(此处是Spring的源码文件),找到40.2.7 the aop schema处的约束:

spring中aop的设计模式(Spring学习5)(1)

<?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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here --> </beans>

修改配置文件中的代码为:

<?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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here --> <!--开启AspectJ的注解开发,自动代理--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>

这样就搭建完基于AspectJ的AOP开发环境了。

@AspectJ提供了不同类型的增强处理如下所示:@Before:前置通知,相当于BeforeAdvice;@AfterReturning后置通知,相当于AfterReturningAdvice;@Around环绕通知,相当于MethodInterceptor;@AfterThrowing异常抛出通知,相当于ThrowAdvice,@After最终通知,不管是否异常,该通知都会执行。还有一个较为复杂的引介通知@DeclareParents,它其实就相当于IntroductionInterceptor。

切入点表达式定义

你可以在通知中通过value属性来定义切点,此处是使用execution函数来定义切点。语法为:execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)。这个注解定义了增强处理的类型,还定义了作用到哪个类的哪个方法上,结合(*)的使用可以使其变得很灵活。

举几个例子,如匹配所有类public方法,此时为execution(public * *(..));匹配指定包下所有类的方法,此时为execution(* com.envy.dao.*(..))注意是不包含子包的,如果想表示包及子孙包下所有的类此时应当execution(*com.envy.dao..*(..));匹配指定类所有方法,此时为execution(*com.envy.service.UserService.*(..));匹配实现特定接口所有类方法,此时为execution(*com.envy.dao.UserDao .*(..));匹配所有save开头的方法,此时为execution(*save*(..))。

接下来进入正式的开发。首先要有目标类,这里提供一个ProductDao类(为了简单此处并没有实现接口):

public class ProductDao { public void save(){ System.out.println("保存商品"); } public void update(){ System.out.println("修改商品"); } public void delete(){ System.out.println("删除商品"); } public void findOne(){ System.out.println("查询某一个商品"); } public void findAll(){ System.out.println("查询所有商品"); } }

然后就是去applicationContext.xml文件中对Bean进行管理,此处使用xml配置,当然使用注解也是可以的:

<!--目标类--> <bean id="productDao" class="com.envy.aspectJ.demo1.ProductDao"/>

接着就是需要去定义一个切面类,名称为AspectJByAnno(使用@AspectJ来定义切面类),里面的代码为:

import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; /** * 切面类 **/ @Aspect public class AspectJByAnno { //前置通知类型 @Before(value="execution(* com.envy.aspectJ.demo1.ProductDao.*(..))") public void before(){ System.out.println("前置通知"); } }

当然别忘了去applicationContext.xml文件中对AspectJByAnno进行管理:

<!--切面类--> <bean class="com.envy.aspectJ.demo1.AspectJByAnno"/>

接着创建一个名为SpringTest的类,里面的代码为:

import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import javax.annotation.Resource; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:applicationContext.xml") public class SpringTest { @Resource(name="productDao") private ProductDao productDao; @Test public void testOne(){ productDao.save(); productDao.delete(); productDao.update(); productDao.findAll(); productDao.findOne(); } }

运行结果为:

前置通知 保存商品 前置通知 删除商品 前置通知 修改商品 前置通知 查询所有商品 前置通知 查询某一个商品

所有方法均前置通知了,若只需前置特定方法只需修改@Before(value="execution(* com.envy.aspectJ.demo1.ProductDao.*(..))")中的代码。

前置通知

@Before注解,可以在注解的方法中传入JointPoint对象,该对象用于注解目标类中的方法信息,即连接点。

import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; /** * 切面类 **/ @Aspect public class AspectJByAnno { //前置通知类型 @Before(value="execution(* com.envy.aspectJ.demo1.ProductDao.save(..))") public void before(JoinPoint joinPoint){ System.out.println("前置通知" joinPoint); } }

运行结果:

前置通知execution(void com.envy.aspectJ.demo1.ProductDao.save()) 保存商品 删除商品 修改商品 查询所有商品 查询某一个商品

可以很清楚的知道增强的具体是哪个方法。

spring中aop的设计模式(Spring学习5)(2)

,