在实际开发中,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处的约束:
<?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())
保存商品
删除商品
修改商品
查询所有商品
查询某一个商品
可以很清楚的知道增强的具体是哪个方法。
,