SpringAop两种配置:xml配置和注解方式

一,什么是SpringAop?

        所谓的springaop就是面向切面编程,就是在你的原有项目功能上,通过aop去添加新的功能,这些功能是建立在原有的功能基础上的,而且不会修改原来的动能代码以及功能逻辑。例如你用银行卡购物,购物付款,这是一个功能。付款后,银行向你的手机发送一条取钱信息,这就是新加的功能。也就是实现了增强

二,Springaop的实现机制是什么?

        横向抽取机制,那么什么是横向抽取机制呢?所谓的横向抽取机制就是使用动态的代理的方式(cglib代理和jdk代理)来实现对象的代理,实际上我们操作的是假对象。既然有横向抽取机制,那么有没有纵向代理模式呢 ?答案是有的。那么什么是纵向抽取呢?纵向抽取就是把公共的方法写在父类里,所有的类都继承父类,这样就是能调用父类的方法。例如,你购物付款是一个子类的功能,你可能还会取款,这也是一个功能,而在他们结束之后,银行都会发送一个信息给你,这又是一个功能,这个银行给你发送信息是个公共的方法,所以这个发信息的功能就是属于父类的。子类继承父类并调用父类的方法就是纵向抽取。

三,Springaop的使用场景

    一般来说:打印日志,还有短信通知啊,权限验证等,都可以使用aop来实现。

四,sringaop的两种实现方式

          (1)xml文件配置方式

          (2)注解的方式实现

  方式一:xml文件方式配置

 导入AOPjar包

 创建如图所示项目结构

 

创建核心类的接口

package com.su.service;

public interface BookService {
//    添加
    int save(int n);
//    删除
    int del();
//    修改
    int update();
//    查询
    void find();
}

  创建核心类(被加强类)

package com.su.service.impl;

import com.su.service.BookService;

public class BookServiceImpl implements BookService {
    @Override
    public int save(int n) {
        System.out.println("添加");
        return 1;
    }

    @Override
    public int del() {
        System.out.println("删除");
        return 1;
    }

    @Override
    public int update() {
        System.out.println("修改");
        return 1;
    }

    @Override
    public void find() {
        System.out.println("查询");
    }
}

 创建增强类

package com.su.advice;

import org.aspectj.lang.ProceedingJoinPoint;

public class Loger {
    public void check(){
        System.out.println("前置通知/增强:执行系统的权限验证");
    }
    public void logPrint(){
        System.out.println("后置通知/增强:执行日志的打印");
    }
    public void exception(){
        System.out.println("异常通知/增强:做出异常处理");
    }
    public void distory(){

        System.out.println("最终通知/增强:资源释放");
    }
    public Object around(ProceedingJoinPoint pjp) {
        try {
            //前置增强
            System.out.println("环绕通知---前置增强");
            //通过ProceedingJoinPoint 完成代理对象的方法调用
            Object result = null;//定义返回值变量
            Object[] args = pjp.getArgs();//获取参数列表
            result = pjp.proceed(args);
            //后置增强
            System.out.println("环绕通知---后置增强");
            return result;
        } catch (Throwable e) {
            //异常通知
            System.out.println("环绕通知----异常增强");
            throw new RuntimeException(e);
        } finally {
            //最终增强
            System.out.println("环绕通知----最终增强");
        }
    }
}

 配置切点和切面

<?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">

    <!--1.把所有类的对象交给IOC容器进行管理-->
    <bean id="loger" class="com.su.advice.Loger"/>
    <bean id="bookService" class="com.su.service.impl.BookServiceImpl"/>
    <!--2.AOP的配置:让增强类 的 哪个方法  动态进行何种增强   核心类 的 哪个方法-->
    <aop:config>
    <!--配置 增强类的哪个方法  对  核心类的哪个方法  进行  何种增强-->
        <aop:aspect id="log" ref="loger">
<!--            <aop:before method="check" pointcut="execution(* *..BookServiceImpl.*(..))"/>-->
<!--            <aop:after-returning method="logPrint" pointcut="execution(* *..BookServiceImpl.*(..))"/>-->
<!--            <aop:after-throwing method="exception" pointcut="execution(* *..BookServiceImpl.*(..))"/>-->
<!--            <aop:after method="distory" pointcut="execution(* *..BookServiceImpl.*(..))"/>-->
<!--环绕增强-->
            <aop:around method="around" pointcut="execution(* *..BookServiceImpl.*(..))"/>
        </aop:aspect>
    </aop:config>

</beans>

 创建测试类

package com.su.servlet;

import com.su.service.BookService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test01 {
   @Test
   public void test01(){
       ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("spring.xml");
       BookService bookService=context.getBean(BookService.class);
       bookService.save(5);
   }
}

运行结果


 由上面的配置文件可以看出,配置相当复杂,接下来使用spring注解的方式。


方式二:通过springAop注解实现

导入AOPjar包

创建如图所示项目结构

创建核心类的接口

package com.su.service;

public interface BookService {
//    添加
    int save(int n);
//    删除
    int del();
//    修改
    int update();
//    查询
    void find();
}

创建核心类(被加强类)

package com.su.service.impl;

import com.su.service.BookService;

public class BookServiceImpl implements BookService {
    @Override
    public int save(int n) {
        System.out.println("添加");
        return 1;
    }

    @Override
    public int del() {
        System.out.println("删除");
        return 1;
    }

    @Override
    public int update() {
        System.out.println("修改");
        return 1;
    }

    @Override
    public void find() {
        System.out.println("查询");
    }
}

2创建增强类并打上注解

@Aspec:增强

@Component:定义Spring管理Bean(也就是将标注@Component注解的类交由spring管理)

@Before:前置通知
@AfterReturning:后置通知
@AfterThrowing:异常通知

@After:最终通知

@Around:环绕通知

package com.su.advice;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * 注解
 */
@Component
//告诉系统loger是增强类
@Aspect
public class Loger {
    //@Before:前置通知
    @Before("execution(* *..BookServiceImpl.*(..))")
    public void check(){
        System.out.println("前置通知/增强:执行系统的权限验证");
    }

    //@AfterReturning:后置通知
    @AfterReturning("execution(* *..BookServiceImpl.*(..))")
    public void logPrint(){ System.out.println("后置通知/增强:执行日志的打印"); }

    //@AfterThrowing:异常通知
    @AfterThrowing("execution(* *..BookServiceImpl.*(..))")
    public void exception(){ System.out.println("异常通知/增强:做出异常处理"); }

    //@After:最终通知
    @After("execution(* *..BookServiceImpl.*(..))")
    public void distory(){ System.out.println("最终通知/增强:资源释放"); }

    //@Around:环绕通知
    @Around("execution(* *..BookServiceImpl.*(..))")
    public Object around(ProceedingJoinPoint pjp) {
        try {
            //前置增强
            System.out.println("环绕通知---前置增强");
            //通过ProceedingJoinPoint 完成代理对象的方法调用
            Object result = null;//定义返回值变量
            Object[] args = pjp.getArgs();//获取参数列表
            result = pjp.proceed(args);
            //后置增强
            System.out.println("环绕通知---后置增强");
            return result;
        } catch (Throwable e) {
            //异常通知
            System.out.println("环绕通知----异常增强");
            throw new RuntimeException(e);
        } finally {
            //最终增强
            System.out.println("环绕通知----最终增强");
        }
    }
}

 配置切点和切面

<?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">

    <!--1.扫描component及同名注解-->
   <context:component-scan base-package="com.su"/>
    <!--2.开启AOP注解支持-->
    <aop:aspectj-autoproxy/>
</beans>

  创建测试类

package com.su.servlet;

import com.su.service.BookService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test01 {
   @Test
   public void test01(){
       ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("spring.xml");
       BookService bookService=context.getBean(BookService.class);
       bookService.save(5);
   }
}

 运行结果

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring1.2或之前的版本中,实现AOP的传统方式就是通过实现SpringAOP API来定义Advice,并设置代理对象。Spring根据Adivce加入到业务流程的时机的不同,提供了四种不同的Advice:Before Advice、After Advice、Around Advice、Throw Advice。 1、Before Advice 顾名思义,Before Advice会在目标对象的方法执行之前被调用,您可以通过实现org.springframework.aop.MethodBeforeAdvice接口来实现Before Advice的逻辑,接口定义如下: java 代码 1. package org.springframework.aop; 2. 3. public interface MethodBeforeAdvice extends BeforeAdvice { 4. void before(Method method, Object[] args, Object target) throws Throwable; 5. } 其中BeforeAdvice继承自Adivce接口,这两者都是标签接口,并没有定义任何具体的方法。before方法会在目标对象的指定方法执行之前被执行,在before方法种,你可以取得指定方法的Method实例、参数列表和目标对象,在before方法执行完后,目标对象上的方法将会执行,除非在before方法种抛出异常。 下面通过例子来说明Before Advice的使用方法。首先定义目标对象所要实现的接口: java 代码 1. package com.savage.aop 2. 3. public interface MessageSender { 4. void send(String message); 5. } 接着实现MessageSender接口: java 代码 1. package com.savage.aop; 2. 3. public class HttpMessageSender implements MessageSender { 4. public void send(String message) { 5. System.out.println("Send Message[" + message + "] by http."); 6. } 7. } OK,我们的业务代码实现完了,现在如果要在不改变我们的业务代码的前提下,在执行业务代码前要记录一些日志,这时就可以通过实现MethodBeforeAdvice接口来实现,如: java 代码 1. package com.savage.aop; 2. 3. import java.lang.reflect.Method; 4. 5. import org.springframework.aop.framework.MethodBeforeAdvice; 6. 7. public class LogBeforeAdvice implements MethodAdvice { 8. public void before(Method method, Object[] args, Object target) throws Throwable { 9. System.out.println("Log before " + method + " by LogBeforeAdvice."); 10. } 11. } 然后再在XML进行如下定义: xml 代码 1. <?xml version="1.0" encoding="UTF-8"?> 2. <beans xmlns="http://www.springframework.org/schema/beans" 3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4. xsi:schemaLocation="http://www.springframework.org/schema/beans 5. http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"> 6. 7. <bean id="messageSenderImpl" class="com.savage.aop.HttpMessageSender"></bean> 8. 9. <bean id="logBeforeAdvice" class="com.savage.aop.LogBeforeAdvice"></bean> 10. 11. <bean id="messageSender" class="org.springframework.aop.framework.ProxyFactoryBean"> 12. <property name="proxyInterfaces" value="com.savage.aop.MessageSender"/> 13. <property name="target" ref="messageSenderImpl"/> 14. <property name="interceptorNames"> 15. <list> 16. <value>logBeforeAdvice</value> 17. </list> 18. </property> 19. </bean> 20. </beans> 这样我们就为MessageSender对象指定了Before Advice对象。在这里,我们分别定义了一个MessageSender对象(messageSenderImpl)和一个Before Advice对象(logBeforeAdvice),并定义了一个 org.springframework.aop.framework.ProxyFactoryBean对象(messageSender),FactoryBean或ApplicationContext将使用ProxyFactoryBean来建立代理对象,在这里就是messageSenderImpl建立代理对象。在ProxyFactoryBean的定义中,proxyInterfaces属性指定了要代理的接口;target指定了要建立代理的目标对象;interceptorNames则指定了应用与指定接口上的Advices对象列表,spring将根据列表中定义的顺序在执行目标对象的方法前、后执行Advice中定义的方法。 现在我们写一个程序来验证下: java 代码 1. package com.savage.aop; 2. 3. import org.springframework.context.ApplicationContext; 4. import org.springframework.context.support.ClassPathXmlApplication; 5. 6. public class AdviceDemo { 7. public void main(String[] args) { 8. ApplicationContext context = new ClassPathXmlApplicationContext("beans-config.xml"); 9. MessageSender sender = (MessageSender)context.getBean("messageSender"); 10. sender.send("message"); 11. } 12. } 执行结果: Log before public abstract void com.savage.simplespring.bean.MessageSender.send(java.lang.String) by LogBeforeAdvice. Send Message[message] by http. 正如你所看到的,在执行MessageSender的send方法前先执行了LogBeforeAdvice的方法!在这个例子中,记录日志的代码并没有横切到我们的业务代码中,LogBeforeAdvice和HttpMessageSender彼此不知道对方的存在,而且我们的应用程序AdviceDemo对LogBeforeAdvice的存在也是一无所知。假如有一天我们的应用程序不需要再业务代码执行前记录日志了,只需要修改XML文件中的定义,而不用更改AdviceDemo的代码: xml 代码 1. <bean id="messageSender" class="com.savage.aop.HttpMessageSender">bean> 2、After Advice After Advice会在目标对象的方法执行完后执行,你可以通过实现org.springframework.aop.AfterReturingAdvice接口来实现After Advice的逻辑,AfterReturingAdvice接口定义如下: java 代码 1. package org.springframework.aop; 2. 3. public interface AfterReturningAdvice { 4. void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable; 5. } 在afterReturning方法中,你可以获得目标方法执行后的返回值、目标方法对象、目标方法的参数以及目标对象。 继续以上面的例子为例,如果要在MessageSender的send方法执行完后,要再记录日志,那么我们可以先实现AfterReturningAdvice接口: java 代码 1. package com.savage.aop; 2. 3. import org.springframework.aop; 4. 5. public LogAfterAdvice implements AfterReturningAdvice { 6. public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { 7. System.out.println("Log after " + method + " by LogAfterAdvice."); 8. } 9. } 然后在XML文件中指定LogAfterAdvice的实例: xml 代码 1. <?xml version="1.0" encoding="UTF-8"?> 2. <beans xmlns="http://www.springframework.org/schema/beans" 3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4. xsi:schemaLocation="http://www.springframework.org/schema/beans 5. http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"> 6. 7. <bean id="messageSenderImpl" class="com.savage.aop.HttpMessageSender"></bean> 8. 9. <bean id="logBeforeAdvice" class="com.savage.aop.LogBeforeAdvice"></bean> 10. 11. <bean id="messageSender" class="org.springframework.aop.framework.ProxyFactoryBean"> 12. <property name="proxyInterfaces" value="com.savage.aop.MessageSender"/> 13. <property name="target" ref="messageSenderImpl"/> 14. <property name="interceptorNames"> 15. <list> 16. <value>logAfterAdvice</value> 17. </list> 18. </property> 19. </bean> 20. </beans> 在前面Before Advice的基础上,我们为MessageSender再指定了一个LogAfterAdvice的服务。运行前面的AdviceDemo,结果如下: Send Message[message] by http. Log after public abstract void com.savage.simplespring.bean.MessageSender.send(java.lang.String) by LogAfterAdvice. 3、Around Advice 在上面的LogAfterAdvice例子中,我们通过指定BeforeAdvice和AfterReturingAdvice,在MessageSender的send方法前后执行额外的业务。实际上,如果需要在业务代码执行前后增加额外的服务,你可以直接通过实现org.aopalliance.intercept.MethodInterceptor接口来达到这一目的,MethodInterceptor定义如下: java 代码 1. package org.aopalliance.intercept; 2. 3. public interface MethodInterceptor { 4. public Object invoke(MethodInvocation methodInvocation) throws Throwable; 5. } 例如: java 代码 1. package com.savage.aop; 2. 3. import org.aopalliance.intercept.MethodInterceptor; 4. import org.aopalliance.intercept.MethodInvocation; 5. 6. public class LogAdvice implements MethodInterceptor { 7. public Object invoke(MethodInvocation methodInvocation) throws Throwable { 8. System.out.println("Log before " + methodInvocation.getMethod() + " by LogAdvice."); 9. Object retValue = methodInvocation.proceed(); 10. System.out.println("Log after " + methodInvocation.getMethod() + " by LogAdvice."); 11. return retValue; 12. } 13. } 正如上面所示,在MethodInterceptor中你得自行决定是否调用MethodInvocation的proceed()方法来执行目标对象上的方法,proceed()方法在执行完后会返回目标对象上方法的执行结果。 MethodInterceptor在XML文件中的定义如下: xml 代码 1. <?xml version="1.0" encoding="UTF-8"?> 2. <beans xmlns="http://www.springframework.org/schema/beans" 3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4. xsi:schemaLocation="http://www.springframework.org/schema/beans 5. http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"> 6. 7. <bean id="messageSenderImpl" class="com.savage.aop.HttpMessageSender"></bean> 8. 9. <bean id="logAdvice" class="com.savage.aop.LogAdvice"></bean> 10. 11. <bean id="messageSender" class="org.springframework.aop.framework.ProxyFactoryBean"> 12. <property id="proxyInterfaces" value="com.savage.aop.MessageSender"/> 13. <property id="target" ref="messageSenderImpl"/> 14. <property id="interceptorNames"> 15. <list> 16. <value>logAdvice</value> 17. </list> 18. </property> 19. </bean> 20. </beans> Spring在真正执行目标对象的方法前,会执行interceptorNames中执行的Advice,每个Advice在执行完自己的业务后,会调用MethodInvocation的proceed()方法,将执行的主动权移交给下一个Advice,直到没有下一个Advice为止,在执行完目标对象的方法后,Spring会再以相反的顺序一层层的返回。例如: xml 代码 1. <bean id="messageSender" class="org.springframework.aop.framework.ProxyFactoryBean"> 2. <property id="proxyInterfaces" value="com.savage.aop.MessageSender"/> 3. <property id="target" ref="messageSenderImpl"/> 4. <property id="interceptorNames"> 5. <list> 6. <value>logBeforeAdvice</value> 7. <value>logAdvice</value> 8. <value>logAfterAdvice</value> 9. </list> 10. </property> 11. </bean> 象上面这个例子,logBeforeAdvice先会被执行,然后执行logAdvice,接着执行logAfterAdvice,最后又返回到了logAdvice。 现在我们把LogAdvice作一下简单的修改,增加一个id属性,用以在后面查看Advice的调用顺序: java 代码 1. package com.savage.aop; 2. 3. import org.aopalliance.intercept.MethodInterceptor; 4. import org.aopalliance.intercept.MethodInvocation; 5. 6. public class LogAdvice implements MethodInterceptor { 7. private static int INSTANCE_NUM = 0; 8. 9. private int id; 10. 11. public LogAdvice() { 12. id = ++INSTANCE_NUM; 13. } 14. 15. public Object invoke(MethodInvocation methodInvocation) throws Throwable { 16. System.out.println("Log before " + methodInvocation.getMethod() + " by LogAdvice[" + id + "]."); 17. Object retValue = methodInvocation.proceed(); 18. System.out.println("Log after " + methodInvocation.getMethod() + " by LogAdvice[" + id + "]."); 19. return retValue; 20. } 21. } 同时把XML中的定义改为: xml 代码 1. <?xml version="1.0" encoding="UTF-8"?> 2. <beans xmlns="http://www.springframework.org/schema/beans" 3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4. xsi:schemaLocation="http://www.springframework.org/schema/beans 5. http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"> 6. 7. <bean id="messageSenderImpl" class="com.savage.aop.HttpMessageSender"></bean> 8. 9. <bean id="logBeforeAdvice" class="com.savage.aop.LogBeforeAdvice"></bean> 10. <bean id="logAfterAdvice" class="com.savage.aop.LogAfterAdvice"></bean> 11. <bean id="logAdvice1" class="com.savage.aop.LogAdvice"></bean> 12. <bean id="logAdvice2" class="com.savage.aop.LogAdvice"></bean> 13. 14. <bean id="messageSender" class="org.springframework.aop.framework.ProxyFactoryBean"> 15. <property id="proxyInterfaces" value="com.savage.aop.MessageSender"/> 16. <property id="target" ref="messageSenderImpl"/> 17. <property id="interceptorNames"> 18. <list> 19. <value>logBeforeAdvice</value> 20. <value>logAdvice1</value> 21. <value>logAfterAdvice</value> 22. <value>logAdvice2</value> 23. </list> 24. </property> 25. </bean> 26. </beans>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值