1.使用Aspectj进行AOP开发
使用aspectjs的操作步骤:
1)添加类库:aspectjrt.jar和aspectjweaver.jar
2)添加aop schema.
3)定义xml元素:<aop:aspectj-autoproxy>
4)编写java类,并用@Aspect注解成通知
AspectJ支持 5 种类型的通知注解:
@Before: 前置通知, 在方法执行之前执行
@After: 后置通知, 在方法执行之后执行
@AfterReturning: 返回通知, 在方法返回结果之后执行
@AfterThrowing: 异常通知, 在方法抛出异常之后
@Around: 环绕通知, 围绕着方法执行
配置成普通bean元素即可;下面简单介绍这几种通知的术语和使用方式,后面会通过案例分析的方式来剖解aspectjs通知编程。
前置通知:@Before
@Aspect
public class AudienceAdvice {
@Before("execution(* WelcomeService.*(..))")
public void takeSeats(){..}
@Before("execution(* WelcomeService.*(..))")
public void turnOffCellphone(JoinPoint jp){..}
JoinPoint参数可访问连接点细节,切入方法名和参数等.
jp.getTarget()//目标对象
jp.getThis()//当前的代理对象
jp.getArgs();//方法调用参数
jp.getSignature().getName()//方法签名
后置通知:@After
@After("execution(* *..WelcomeService.*(..))")
public void applaud(){..}
后置通知在目标方法执行完成之后执行.一个切面aspect包含很多通知.后置通知表明目标方法执行完之后,不论是否抛异常,都会织入该通知.
返回通知:@AfterReturning
方法返回后通知只在目标方法返回以后执行,若抛异常不执行.
@AfterReturning(pointcut="",returning="res")
public void xxx(Joinput jp,Object res)
在AfterReturning通知中可接收到返回值.res即是用来接收返回值的对象.
环绕通知:@Around
@Around("execution(* *..WelcomeService.*(..))")
public void around(ProceedingPointCut jp){..}
注意:可以控制目标方法是否调用,以及返回完全不同的对象,要慎用.
指定优先级:
@Aspect
@Order(0)
public class xxx{...}
加上@Order注解可以指定加入切面的优先级(先后顺序,值越小,优先级越高)
典型Aspectj切入点表达式定义:
execution(* cn.itcast.WelcomeServiceImpl.*(..))
execution(public * *..WelcomeServiceImpl.*(..))
execution(public void *..WelcomeServiceImpl.*(*))
execution(public void *..*Service.*(double,double))..
切入点表达式运算(&& || !)
@Pointcut("execution(..) || execution(..)")
重用切入点定义
将切入点注解到一个空的方法体上,其它地方引用即可.
//定义切入点
@Pointcut("execution(* *..WelcomeService.*(..))")
public void performPoint(){}
@Before("performPoint()")
@After("performPoint()")
引入通知:
@Aspect
public class MyAspectjIntroduction {
@DeclareParents(value="*..*Service*", defaultImpl=ModifyDateImpl.class)
private ModifyDate md ;
}
value:指定哪些类可以应用该属性
defaultImpl:指定接口的实现类
使用pojo+xml开发aop
基于注解的aspectj声明优先于xml配置.
基于xml的配置是spring专有的.aspectj得到越来越多的支持,
具备更好的重用性.
2.Aspectj案例分析
下面通过两个案例分别来阐释两种不同方法使用aspectj开发的项目;
首先列出这两种方式都用到的目标抽象接口和抽象接口的实现类目标类UserService和UserServiceImpl;以及引用通知需要的接口和实现类
UserService.java
package www.csdn.spring.proxy.advice.aspectjs;
public interface UserService {
public void save(Object entity);
public void update(Object entity);
public void delete(Object entity);
public void getAllObjects();
}
UserServiceImpl.java
package www.csdn.spring.proxy.advice.aspectjs;
public class UserServiceImpl implements UserService {
@Override
public void save(Object entity) {
System.out.println("---保存方法:save()---");
}
@Override
public void update(Object entity) {
System.out.println("---更新方法:update()---");
}
@Override
public void delete(Object entity) {
System.out.println("---删除方法:delete()---");
//int i = 1/0;
}
@Override
public void getAllObjects() {
System.out.println("---查找所有方法:getAllObjects()---");
}
}
下面是引用通知需要的接口和实现类:
Auditable.java
package www.csdn.spring.proxy.advice.aspectjs;
import java.util.Date;
public interface Auditable {
public void setDate(Date date);
public Date getDate();
}
AuditableImpl.java
package www.csdn.spring.proxy.advice.aspectjs;
import java.util.Date;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
public class AuditableImpl extends DelegatingIntroductionInterceptor implements Auditable {
private Date date;
@Override
public void setDate(Date date) {
this.date = date;
}
@Override
public Date getDate() {
return date;
}
}
第一种方式:用的advice通知类:AdviceService.java
package www.csdn.spring.proxy.advice.aspectjs;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.junit.experimental.theories.Theory;
@Aspect
public class AdviceService {
//前置通知
@Before(value = "execution(* UserServiceImpl.save(..))")
public void beforeMethod(){
System.out.println("-------开启事务-------");
}
//后置通知
@After(value = "execution(* www.csdn..UserServiceImpl.save(*))")
public void afterMethod(){
System.out.println("-------结束事务--------");
}
//环绕通知
@Around(value = "execution(* UserServiceImpl.update(*))")
public void aroundMethod(ProceedingJoinPoint pjp){
System.out.println("-=-=-=-=安全处理之前-=-=-=-");
try {
pjp.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("-=-=-=-=-=安全处理之后-=-=-=-=-");
}
//定义一个指定切入点位置的方法
@Pointcut("execution(* *..UserServiceImpl.delete(*))")
public void pointCuts(){}
//下面两个通知引用该切入点方法
//异常通知
/*@AfterThrowing("pointCuts()")
public void throwsMethod(){
System.out.println("异常通知执行");
}*/
@AfterReturning("pointCuts()")
public void afterRuning(){
System.out.println("=========返回值========");
}
}
使用aspectj最重要的一个文件,spring相关的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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,实际是aspectjs自动扫描所有bean,去找有切面的bean -->
<bean id="adviceService" class="www.csdn.spring.proxy.advice.aspectjs.AdviceService" />
<!-- 引入通知切面 -->
<bean id="auditableService" class="www.csdn.spring.proxy.advice.aspectjs.AuditableService" />
<!-- 真实主题 目标对象 -->
<bean id="userServiceImpl" class="www.csdn.spring.proxy.advice.aspectjs.UserServiceImpl" />
<!-- 使用aspectjs配置自动代理 -->
<aop:aspectj-autoproxy />
</beans>
测试类 AdviseTest.java
package www.csdn.spring.proxy.advice.aspectjs;
import java.util.Date;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AdviseTest {
@Test
public void testUser() {
ApplicationContext context = new ClassPathXmlApplicationContext(
"spring-aspectjs.xml");
// 代理主题角色,这里用代理主题和真实主题实现的同样接口类来接收创建的代理主题,就是创建一个目标类
UserService userService = context.getBean("userServiceImpl",
UserService.class);
userService.save(null);
/*userService.update(null);
try{
userService.delete(null);
}catch(Exception e){
}
userService.getAllObjects();*/
//引入通知测试
Auditable auditable = (Auditable) userService;
auditable.setDate(new Date());
System.out.println(auditable.getDate());
}
}
第二种方式 :使用pojo-xml编写aspectj的通知类AdviceService.java
package www.csdn.spring.proxy.advice.aspectjs.pojoxml;
import org.aspectj.lang.ProceedingJoinPoint;
public class AdviceService {
//前置通知
public void beforeMethod(){
System.out.println("-------开启事务-------");
}
//后置通知
public void afterMethod(){
System.out.println("-------结束事务--------");
}
//环绕通知
public void aroundMethod(ProceedingJoinPoint pjp){
System.out.println("-=-=-=-=安全处理之前-=-=-=-");
try {
pjp.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("-=-=-=-=-=安全处理之后-=-=-=-=-");
}
//异常通知
public void throwsMethod(){
System.out.println("异常通知执行");
}
//带返回值的通知
public void afterRuning(){
System.out.println("=========返回值========");
}
}
使用pojo-xml aspectj最重要的一个文件,spring相关的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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,实际是aspectjs自动扫描所有bean,去找有切面的bean -->
<bean id="adviceService"
class="www.csdn.spring.proxy.advice.aspectjs.pojoxml.AdviceService" />
<!-- 真实主题 目标对象 -->
<bean id="userServiceImpl"
class="www.csdn.spring.proxy.advice.aspectjs.pojoxml.UserServiceImpl" />
<!-- 使用pojo-xml aspectjs配置自动代理 -->
<aop:config>
<!-- 配置切面 -->
<aop:aspect ref="adviceService">
<!-- 引入通知 -->
<aop:declare-parents types-matching="*..*Service*"
implement-interface="www.csdn.spring.proxy.advice.aspectjs.pojoxml.Auditable"
default-impl="www.csdn.spring.proxy.advice.aspectjs.pojoxml.AuditableImpl" />
<!-- 切面切入的位置 切入点,可以同时写多个不同的切入点 -->
<aop:pointcut expression="execution(* www.csdn..UserServiceImpl.save(..))"
id="myPcut" />
<aop:pointcut expression="execution(* www.csdn..UserServiceImpl.update(..))"
id="myPcuts" />
<!--织入通知 method:指明方法; pointcut-ref引入切入点 -->
<aop:before method="beforeMethod" pointcut-ref="myPcut" />
<aop:after method="afterMethod" pointcut-ref="myPcut" />
<aop:after method="afterRuning" pointcut-ref="myPcut" />
<aop:around method="aroundMethod" pointcut-ref="myPcuts" />
<aop:after-throwing method="throwsMethod" pointcut-ref="myPcuts"/>
</aop:aspect>
</aop:config>
</beans>
测试类 AdviseTest.java
package www.csdn.spring.proxy.advice.aspectjs.pojoxml;
import java.util.Date;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AdviseTest {
@Test
public void testUser() {
ApplicationContext context = new ClassPathXmlApplicationContext(
"spring-pojo*.xml");
// 代理主题角色,这里用代理主题和真实主题实现的同样接口类来接收创建的代理主题,就是创建一个目标类
UserService userService = context.getBean("userServiceImpl",
UserService.class);
userService.save(null);
userService.update(null);
//引入通知测试
Auditable auditable = (Auditable) userService;
auditable.setDate(new Date());
System.out.println(auditable.getDate());
}
}