Spring Aop两种实现
标签(空格分隔): spring
Spring Aop(Aspct-Orinted Programming 面向切面编程):是对传统OOP(Object-Orinted Programming,面向对象编程)的补充。
AOP 的主要编程对象是切面(aspect),而切面模块化横切关注点。
在应用Aop编程是任需要定义公共功能,但可以明确定义功能在哪里,以什么方式应用,并且不必修改送影响类,这样一来横切关注点就被模块化到特殊的对象(切面)里
AOP 的好处:
每个事物逻辑位于一个位置, 代码不分散, 便于维护和升级
业务模块更简洁, 只包含核心业务代码.
- 切面(Aspect) :横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象
- 通知(Advide):切面必须完成的工作
- 目标(Target):被通知的对象
- 代理(Proxy):向目标对象应用通知之后创建的对象
- 连接对象(JoinPoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。例如 ArithmethicCalculator#add() 方法执行前的连接点,执行点为 ArithmethicCalculator#add(); 方位为该方法执行前的位置
- 切点(pointcut):每个类都拥有多个连接点:例如 ArithmethicCalculator 的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。
Aop基于注解使用
bean.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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.zx.aop"/>
<!-- 使 AspectJ 的注解起作用 默认使用JDK动态代理 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<!-- 使用 CGLib动态代理 -->
<!--<aop:aspectj-autoproxy proxy-target-class="true"/>-->
</beans>
dao层
@Repository
public interface ArithmethicCalculatorDao {
int add(int i,int j);
int sub(int i,int j);
int mul(int i,int j);
int div(int i,int j);
}
service层:
//将AirthmeticDaoImp添加到Spring容器里面
@Service
public class ArithmethicCalculatorDaoImp implements ArithmethicCalculatorDao {
@Override
public int add(int i, int j) {
System.out.println("执行中" );
return i+j;
}
@Override
public int sub(int i, int j) {
System.out.println("执行中" );
return i-j;
}
@Override
public int mul(int i, int j) {
System.out.println("执行中" );
return i*j;
}
@Override
public int div(int i, int j) {
System.out.println("执行中" );
return i/j;
}
}
定义切面:
@Order(2)//切面的优先级,值越低优先级越高
@Aspect
@Component
public class LoggingAspect {
/**
* 定义一个方法,同于声明切入点表达式,一般地,该方法中再不需要添入其他的代码
* 使用@Pointcut 来声明切入点表达式
* 后面的其他通知直接使用方法名来引用当前的切入点表达式
* 1. 在相同的包,其他切面引用时,需要加入的类名
* 2. 在不同的包,其他切面引用时,需要加入包名和类名
*
*/
@Pointcut("execution(public int com.zx.aop.ArithmethicCalculatorDaoImp.*(int, int))")
public void declareJointPointExpression(){
}
@Before("declareJointPointExpression()")
public void beforeMethod(JoinPoint joinPoint){
//获取切入点方法名称
String methodName = joinPoint.getSignature().getName();
System.out.println(methodName+Arrays.asList(joinPoint.getArgs()));
System.out.println("正在使用前置通知:"+methodName);
}
/* @Before("execution(public int com.zx.aop.AirthmeticDaoImp.*(int, int))")
public void beforeMethod(JoinPoint joinPoint){
//获取切入点方法名称
String methodName = joinPoint.getSignature().getName();
System.out.println("正在使用前置通知:"+methodName);
}*/
//在目标方法执行后(无论是否发生异常),执行通知
//后置通知不能还不能访问目标执行结果
@After("execution(public int com.zx.aop.ArithmethicCalculatorDaoImp.*(int, int))")
public void afterMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("正在使用后置通知:"+methodName);
}
/**
* 返回通知:返回通知可以获得切入点方法的返回结果
* 1.当程序正常执行结束才会执行返回通知
* @param joinPoint
* @param result 返回通知返回的值
*/
@AfterReturning(value = "execution(public int com.zx.aop.ArithmethicCalculatorDaoImp.*(int, int)))",
returning = "result")
public void afterReturing(JoinPoint joinPoint,Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("正在使用返回通知,返回值为:"+result);
}
/**
*当切入点方法发次指定的异常时才执行,异常通知
* public void afterThrowing(JoinPoint joinPoint,NullPointerException ex) 只有发生NullPointerException时才会执行异常通知
* @param joinPoint
* @param ex 指定发生那种异常时,才执行异常通知
*/
@AfterThrowing(value = "execution(public int com.zx.aop.ArithmethicCalculatorDaoImp.*(int, int))",
throwing = "ex")
public void afterThrowing(JoinPoint joinPoint,Exception ex){
String methodName = joinPoint.getSignature().getName();
System.out.println(methodName+"正以使用异常通知:"+ ex);
}
/**
* 环绕通知需要携带ProceedingJoinPoint 类型的参数
* 环绕通知类似于动态代理的全过程;ProceedingJoinPoint类型的参数可以决定是否执行目标方法。
* 企鹅环绕通知必须有返回值,返回值即为目标方法的返回值
* @param proceedingJoinPoint 环绕通知的必须参数,决定环绕通知是否执行
* @return
*/
/* @Around("execution(public int com.zx.aop.ArithmethicCalculatorDaoImp.*(int, int))")
public Object aroundMethod(ProceedingJoinPoint proceedingJoinPoint){
Object result = null;
String methodName = proceedingJoinPoint.getSignature().getName();
try {
//前置通知
//Arrays.asList(proceedingJoinPoint.getArgs())获取通知的的参数
System.out.println("The method"+methodName+" begins with"+ Arrays.asList(proceedingJoinPoint.getArgs()));
result = proceedingJoinPoint.proceed();//环绕通知开启
System.out.println("返回通知:"+result);
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("异常通知"+throwable);
}
System.out.println("后置通知");
return result;
}*/
}
测试:
public class testAop {
public static void main(String[] args) {
BeanFactory beanFactory = new ClassPathXmlApplicationContext("bean.xml");
ArithmethicCalculatorDaoImp airthmeticDaoImp = beanFactory.getBean("arithmethicCalculatorDaoImp", ArithmethicCalculatorDaoImp.class);
airthmeticDaoImp.add(1,1);
airthmeticDaoImp.sub(4,2);
airthmeticDaoImp.mul(2,2);
airthmeticDaoImp.div(1000,100);
}
}
AOP XML配置文件实现
<bean id="airthmeticDaoImpXml" class="com.zx.aop.ArithmethicCalculatorDaoImpXml"></bean>
<!-- 配置切面的bean -->
<bean id="loginAspectXml" class="com.zx.aop.LoggingAspectXml"></bean>
<bean id="vlidationAspect" class="com.zx.aop.VlidationAspect"></bean>
<!-- 配置Aop -->
<aop:config>
<!-- 配置切点表达式 -->
<aop:pointcut id="point" expression="execution(* com.zx.aop.ArithmethicCalculatorDaoImpXml.*(int,int))"/>
<!--配置切面及通知-->
<aop:aspect ref="loginAspectXml" order="2">
<aop:before method="benforeK" pointcut-ref="point"/>
<aop:after method="afterK" pointcut-ref="point"/>
<aop:after-returning method="afterReturingK" pointcut-ref="point" returning="result"/>
</aop:aspect>
<!-- 配置第二个切面 和通知-->
<aop:aspect ref="vlidationAspect" order="1">
<aop:before method="vlidationK" pointcut-ref="point"></aop:before>
</aop:aspect>
</aop:config>
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
LoggingAspectXml切面设置
public class LoggingAspectXml {
public void vaildateArgs(JoinPoint joinPoint){
System.out.println("-->vaildateArgs"+ Arrays.asList(joinPoint.getArgs()));
}
public void benforeK(JoinPoint joinPoint){
//获取方法名称
String methodName = joinPoint.getSignature().getName();
System.out.println(methodName+"正在执行前置通知");
}
public void afterK(JoinPoint joinPoint){
//获取方法名称
String methodName = joinPoint.getSignature().getName();
System.out.println(methodName+"正在执行后置通知");
}
public void afterReturingK(JoinPoint joinPoint,Object result){
String methodName = joinPoint.getSignature().getName();
System.out.println("正在使用返回通知,返回值为:"+result);
}
}
测试:
public class TestAopXml {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean-xml.xml");
ArithmethicCalculatorDaoImpXml airthmeticDaoImpXml = applicationContext.getBean("airthmeticDaoImpXml", ArithmethicCalculatorDaoImpXml.class);
airthmeticDaoImpXml.add(1,1);
airthmeticDaoImpXml.sub(100,10);
}
}