Spring系列之AOP达成的两种办法转载
原创AOP有两种常用的实现方式, 一种是以声明的方式实现它(基于XML)一种是使用注释来实现(基于AspectJ) 。
第一次审查AOP一些更重要的概念:
Joinpoint(连接点): 在程序执行的特定时刻,Spring是方法的执行 。
Pointcut(切割点): 用一种更流行的方式来说,spring中AOP切点是需要增强和代理的方法的集合。它通常根据某些约定的规则来表示,例如正则表达式。切点由一类连接点组成。
Advice(通知): 还是用一种更流行的方式来说,就是在指定切点上要干些什么。
Advisor(通知器): 事实上,它是切入点和通知的结合。 。
一、基于XML配置的Spring AOP
以声明方式实现(在XML配置文件中的配置),一般步骤是:配置文件中配置pointcut, 在java用于写入实际aspect 类, 用于相关业务处理的入口点。
业务接口:
package com.spring.service;
public interface IUserManagerService {
//查找用户
public String findUser();
//添加用户
public void addUser();
}
业务实施:
package com.spring.service.impl;
import com.spring.service.IUserManagerService;
public class UserManagerServiceImpl implements IUserManagerService{
private String name;
public void setName(String name){
this.name=name;
}
public String getName(){
return this.name;
}
public String findUser(){
System.out.println("============执行业务方法findUser,查找的用户是:"+name+"=============");
return name;
}
public void addUser(){
System.out.println("============执行业务方法addUser=============");
//throw new RuntimeException();
}
}
切面类:
package com.spring.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class AopAspect {
/**
* 预先通知:在调用目标方法之前执行的代码
* @param jp
*/
public void doBefore(JoinPoint jp){
System.out.println("===========执行预先通知============");
}
/**
* 返回后通知:目标方法正常结束后执行的代码
* 返回通知是对目标方法的返回值的访问。
* @param jp
* @param result
*/
public void doAfterReturning(JoinPoint jp,String result){
System.out.println("===========执行后通知============");
System.out.println("返回值result==================="+result);
}
/**
* 最终通知:在目标方法调用后执行的代码(无论目标方法是否有异常都执行)
* 无法返回该方法的返回值,因为该方法可能存在异常
* @param jp
*/
public void doAfter(JoinPoint jp){
System.out.println("===========执行最终通知============");
}
/**
*
* 异常通知:目标方法引发异常时执行的代码
* 可以访问异常对象
* @param jp
* @param ex
*/
public void doAfterThrowing(JoinPoint jp,Exception ex){
System.out.println("===========执行异常通知============");
}
/**
* 包装通知:在目标方法调用前后执行的代码,可以在方法调用前后完成自定义行为。
* 封闭连接点(join point)通知的内容。它将在切入点方法执行之前执行,相应的部分将在方法结束时执行。
* 主要是通话proceed()方法将切入点方法作为前后通知方法的分水岭来执行。
*
* 环绕通知类似于动态代理的整个过程:ProceedingJoinPoint类型的参数确定是否执行目标方法。
* 并且周围的通知必须有一个返回值,即目标方法的返回值。
* @param pjp
* @return
* @throws Throwable
*/
public Object doAround(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("======执行包裹通知开始=========");
// 调用方法的参数
Object[] args = pjp.getArgs();
// 调用的方法名称称
String method = pjp.getSignature().getName();
// 获取目标对象
Object target = pjp.getTarget();
// 已执行方法的返回值
// 调用proceed()方法,触发切入点方法以执行
Object result=pjp.proceed();
System.out.println("输出,方法名:" + method + ";目标对象:" + target + ";返回值:" + result);
System.out.println("======执行结束环绕通知=========");
return result;
}
}
Spring配置:
测试类:
package com.spring.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.spring.service.IUserManagerService;
public class TestAop {
public static void main(String[] args) throws Exception{
ApplicationContext act = new ClassPathXmlApplicationContext("applicationContext3.xml");
IUserManagerService userManager = (IUserManagerService)act.getBean("userManager");
userManager.findUser();
System.out.println("
"); userManager.addUser(); } }
测试结果:
2.使用注释配置AOP
采用注释进行操作aop, 主要将写入spring 配置文件中的连接点将写入注释。
业务接口和业务实现同上,具体节类如下:
package com.spring.aop;
import org.aspectj.lang.JoinPoint;
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;
@Aspect
public class AopAspectJ {
/**
* 必须为final String类型的,注释中使用的变量只能是静态常量类型
*/
public static final String EDP="execution(* com.spring.service.impl.UserManagerServiceImpl..*(..))";
/**
* 切平面的前置法 也就是说,在执行方法之前截获的方法。
* 目标方法执行前的通知
* @param jp
*/
@Before(EDP)
public void doBefore(JoinPoint jp){
System.out.println("=========执行预先通知==========");
}
/**
* 在方法正常通过后执行的通知称为返回通知。
* 可以返回给方法的返回值 在注释后添加returning
* @param jp
* @param result
*/
@AfterReturning(value=EDP,returning="result")
public void doAfterReturning(JoinPoint jp,String result){
System.out.println("===========执行后通知============");
}
/**
* 最终通知:在调用目标方法后执行的通知(无论目标方法是否有异常都会执行)
* @param jp
*/
@After(value=EDP)
public void doAfter(JoinPoint jp){
System.out.println("===========执行最终通知============");
}
/**
* 环绕通知:在目标方法调用之前和之后执行的通知。自定义行为可以在方法调用前后完成。
* @param pjp
* @return
* @throws Throwable
*/
@Around(EDP)
public Object doAround(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("======执行包裹通知开始=========");
// 调用方法的参数
Object[] args = pjp.getArgs();
// 调用的方法名称称
String method = pjp.getSignature().getName();
// 获取目标对象
Object target = pjp.getTarget();
// 已执行方法的返回值
// 调用proceed()方法,触发切入点方法以执行
Object result=pjp.proceed();
System.out.println("输出,方法名:" + method + ";目标对象:" + target + ";返回值:" + result);
System.out.println("======执行结束环绕通知=========");
return result;
}
/**
* 目标方法的异常执行完成, 当抛出异常时,此方法将停止。
* @param jp
* @param ex
*/
@AfterThrowing(value=EDP,throwing="ex")
public void doAfterThrowing(JoinPoint jp,Exception ex) {
System.out.println("===========执行异常通知============");
}
}
spring的配置:
测试类:
package com.spring.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.spring.service.IUserManagerService;
public class TestAop1 {
public static void main(String[] args) throws Exception{
ApplicationContext act = new ClassPathXmlApplicationContext("applicationContext4.xml");
IUserManagerService userManager = (IUserManagerService)act.getBean("userManager");
userManager.findUser();
System.out.println("
"); userManager.addUser(); } }
测试结果与上述相同。
注意:
1.围绕方法通知,围绕方法通知注意,必须在调用后给出返回值,否则代理方法将停止调用并返回。null除非你真的想要。
2.只能使用环绕通知JoinPoint的子类ProceedingJoinPoint,每个连接点类型都可以调用代理的方法并获取和更改返回值。
补充:
1.
2.如果在
.
.
.
3.在使用spring框架配置AOP是否通过。XML需要定义配置文件和注释方法。pointcut"切入点"
例如,定义切入点表达式 execution( com.sample.service.impl...(..))
execution()是最常用的正切函数,具有以下语法:
整个表达式可以分为五个部分:
(1)、execution(): 表达式的主体。
(2)、第一个数字:表示返回类型,数字表示所有类型。
(3),package name:表示要拦截的包的名称,后面两个句点表示当前包和当前包的所有子包,com.sample.service.impl包和子包下所有类的方法。
(4)、第二个数字:表示类名,数字表示所有类别。
(5)、(..):最后一个星号表示方法名称,*数字表示所有方法,括号表示方法的参数,两个句点表示任何参数。
版权声明
所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除