Spring中使用AOP

AOP概念

面向切面编程(也叫面向方面编程):Aspect Oriented Programming(AOP),是软件开发中的一个热点,也是Spring框架中的一个重要内容。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

AOP是OOP的延续。

主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等。

主要的意图是:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。

可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,提高代码的灵活性和可扩展性,AOP可以说也是这种目标的一种实现。

在Spring中提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。

1.简介

AOP主要包含了通知、切点和连接点等术语,介绍如下

  • 通知(Advice)
    通知定义了切面是什么以及何时调用,何时调用包含以下几种

    Before 在方法被调用之前调用通知
    After 在方法完成之后调用通知,无论方法执行是否成功
    After-returning 在方法成功执行之后调用通知
    After-throwing 在方法抛出异常后调用通知
    Around 通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为

  • 切点(PointCut)
    通知定义了切面的什么何时,切点定义了何处,切点的定义会匹配通知所要织入的一个或多个连接点,我们通常使用明确的类的方法名称来指定这些切点,或是利用正则表达式定义匹配的类和方法名称来指定这些切点。
    切点的格式如下

    execution(* com.ganji.demo.service.user.UserService.GetDemoUser (..) )

    aop切点定位表达式

  • 连接点(JoinPoint)
    连接点是在应用执行过程中能够插入切面的一个点,这个点可以是调用方法时,抛出异常时,甚至是修改一个字段时,切面代码可以利用这些连接点插入到应用的正常流程中,并添加新的行为,如日志、安全、事务、缓存等。

现阶段的AOP框架
AOP框架除了Spring AOP之外,还包括AspectJJBoss AOP
上述框架的区别是Spring AOP只支持到方法连接点,另外两个还支持字段和构造器连接点。


2.应用

需要spring的核心包外,还需要aspectjrt.jar、aspectjweaver.ja、cglib-nodep.jar几个包


第一种实现的方式:通过Spring的API实现AOP。

第一步:
    
    
  1. public interface UserService {
  2. public void add();
  3. public void update(int a);
  4. public void delete();
  5. public void search();
  6. }
第二步:
    
    
  1. public class UserServiceImpl implements UserService {
  2. @Override
  3. public void add() {
  4. System.out.println("增加用户");
  5. }
  6. @Override
  7. public void update(int a) {
  8. System.out.println("修改用户");
  9. }
  10. @Override
  11. public void delete() {
  12. System.out.println("删除用户");
  13. }
  14. @Override
  15. public void search() {
  16. System.out.println("查询用户");
  17. }
第三步:实现MethodBeforeAdvice的接口,Spring框架当中为我们提供了很多中通知。
    
    
  1. public class Log implements MethodBeforeAdvice{
  2. /**
  3. * @param method 被调用方法对象
  4. * @param args 被调用的方法的参数
  5. * @param target 被调用的方法的目标对象
  6. * */
  7. @Override
  8. public void before(Method method, Object[] args, Object target)
  9. throws Throwable {
  10. System.out.println(target.getClass().getName()+"的"+method.getName()+"方法被执行");
  11. }
  12. }
第四步:配置beans.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. xmlns:aop="http://www.springframework.org/schema/aop"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/aop
  8. http://www.springframework.org/schema/aop/spring-aop.xsd">
  9. <bean id="userService" class="com.spring.service.impl.UserServiceImpl"/>
  10. <!-- 这个切面也要配置成bean-->
  11. <bean id="log" class="com.spring.advice.Log"/>
  12. <aop:config>
  13. <!--切入点,需要告诉方法在什么去执行
  14. expression="execution(* com.spring.service.impl.*.*(..))"
  15. 第一个* 表示所有的返回值,然后就是包名
  16. 第二个*表示所有的类对象
  17. 第三个*表示类对象所有的方法
  18. 第四个..表示所有方法下面的带参数的方法或者是不带参数的方法
  19. -->
  20. <aop:pointcut expression="execution(* com.spring.service.impl.*.*(..))" id="pointcut"/>
  21. <!-- 在所有的方法中都切入前置通知-->
  22. <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
  23. </aop:config>
  24. </beans>
第五步:测试:
    
    
  1. package com.spring.test;
  2. import org.springframework.context.ApplicationContext;
  3. import org.springframework.context.support.ClassPathXmlApplicationContext;
  4. import com.spring.service.UserService;
  5. public class Test {
  6. public static void main(String[] args) {
  7. ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
  8. UserService userService = (UserService)ac.getBean("userService");
  9. userService.update(2);
  10. userService.add();
  11. }
  12. }
运行结果:
    
    
  1. 三月 12, 2017 2:22:44 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
  2. 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@439f5b3d: startup date [Sun Mar 12 14:22:44 GMT+08:00 2017]; root of context hierarchy
  3. 三月 12, 2017 2:22:44 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
  4. 信息: Loading XML bean definitions from class path resource [beans.xml]
  5. com.spring.service.impl.UserServiceImplupdate方法被执行
  6. 修改用户
  7. com.spring.service.impl.UserServiceImpladd方法被执行
  8. 增加用户
故前置通知可以在spring当中被执行,接下可以完善通知;
    
    
  1. public class AfterLog implements AfterReturningAdvice{
  2. /**
  3. * 目标方法执行后执行的通知
  4. * returnValue--返回值
  5. * method 被调用的方法对象
  6. * args 被调用的方法对象的参数
  7. * target 被调用的方法对象的目标对象
  8. * */
  9. @Override
  10. public void afterReturning(Object returnValue, Method method,
  11. Object[] args, Object target) throws Throwable {
  12. System.out.println(target.getClass().getName()+"的"+method.getName()+"被成功执行,返回值是:"+returnValue);
  13. }
  14. }

    
    
  1. import java.lang.reflect.Method;
  2. import org.springframework.aop.ThrowsAdvice;
  3. public class ExceptionLog implements ThrowsAdvice {
  4. public void afterThrowing(Method method,Exception ex) throws Throwable {
  5. }
  6. }
重新配置:
    
    
  1. <!-- 这个切面也要配置成bean-->
  2. <bean id="log" class="com.spring.advice.Log"/>
  3. <bean id="afterLog" class="com.spring.advice.AfterLog"></bean>
  4. <aop:config>
  5. <!--切入点,需要告诉方法在什么去执行
  6. expression="execution(* com.spring.service.impl.*.*(..))"
  7. 第一个* 表示所有的返回值,然后就是包名
  8. 第二个*表示所有的类对象
  9. 第三个*表示类对象所有的方法
  10. 第四个..表示所有方法下面的带参数的方法或者是不带参数的方法
  11. -->
  12. <aop:pointcut expression="execution(* com.spring.service.impl.*.*(..))" id="pointcut"/>
  13. <!-- 在所有的方法中都切入前置通知-->
  14. <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
  15. <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
  16. </aop:config>
测试运行结果:
      
      
  1. 三月 12, 2017 2:28:19 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
  2. 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@439f5b3d: startup date [Sun Mar 12 14:28:19 GMT+08:00 2017]; root of context hierarchy
  3. 三月 12, 2017 2:28:19 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
  4. 信息: Loading XML bean definitions from class path resource [beans.xml]
  5. com.spring.service.impl.UserServiceImpl的update方法被执行
  6. 修改用户
  7. com.spring.service.impl.UserServiceImpl的update被成功执行,返回值是:null
  8. com.spring.service.impl.UserServiceImpl的add方法被执行
  9. 增加用户
  10. com.spring.service.impl.UserServiceImpl的add被成功执行,返回值是:null
总结:AOP的重要性,非常重要 
Spring的AOP就是将公共的业务(如日志,安全等)和业务类结合。当执行业务的时候将会把公共业务加进来。实现公共业务的重复利用。我们自己的业务就会变得更加的纯粹,我们就可以关注我们的自己的业务,本质就是动态代理。


第二种方式:自定义类来实现AOP,不实现spring的自带的通知
第一步:重新通知:
    
    
  1. public class Log {
  2. public void before(){
  3. System.out.println("方法执行前");
  4. }
  5. public void after(){
  6. System.out.println("方法执行后");
  7. }
  8. }
第二步:重新写配置文件
    
    
  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. xmlns:aop="http://www.springframework.org/schema/aop"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/aop
  8. http://www.springframework.org/schema/aop/spring-aop.xsd">
  9. <bean id="userService" class="com.spring.service.impl.UserServiceImpl"/>
  10. <!-- 这个切面也要配置成bean-->
  11. <bean id="log" class="com.spring.advice.Log"/>
  12. <aop:config>
  13. <!--切入点,需要告诉方法在什么去执行
  14. expression="execution(* com.spring.service.impl.*.*(..))"
  15. 第一个* 表示所有的返回值,然后就是包名
  16. 第二个*表示所有的类对象
  17. 第三个*表示类对象所有的方法
  18. 第四个..表示所有方法下面的带参数的方法或者是不带参数的方法
  19. -->
  20. <aop:aspect ref="log">
  21. <aop:pointcut expression="execution(* com.spring.service.impl.*.*(..))" id="pointcut"/>
  22. <aop:before method="before" pointcut-ref="pointcut"/>
  23. <aop:after method="after" pointcut-ref="pointcut"/>
  24. </aop:aspect>
  25. </aop:config>
  26. </beans>

第三种方式:通过注解实现AOP
第一步:修改log
    
    
  1. package com.spring.advice;
  2. import java.lang.reflect.Method;
  3. import org.aspectj.lang.ProceedingJoinPoint;
  4. import org.aspectj.lang.annotation.After;
  5. import org.aspectj.lang.annotation.Around;
  6. import org.aspectj.lang.annotation.Aspect;
  7. import org.aspectj.lang.annotation.Before;
  8. import org.springframework.aop.MethodBeforeAdvice;
  9. @Aspect
  10. public class Log {
  11. @Before("execution(* com.spring.service.impl.*.*(..))")
  12. public void before(){
  13. System.out.println("方法执行前");
  14. }
  15. @After("execution(* com.spring.service.impl.*.*(..))")
  16. public void after(){
  17. System.out.println("方法执行后");
  18. }
  19. @Around("execution(* com.spring.service.impl.*.*(..))")
  20. public Object around(ProceedingJoinPoint jp) throws Throwable{
  21. System.out.println("环绕前");
  22. System.out.println("方法"+jp.getSignature());
  23. Object result=jp.proceed();
  24. System.out.println("环绕后");
  25. return result;
  26. }
  27. }
第二步:修改beans.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. xmlns:aop="http://www.springframework.org/schema/aop"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/aop
  8. http://www.springframework.org/schema/aop/spring-aop.xsd">
  9. <bean id="userService" class="com.spring.service.impl.UserServiceImpl"/>
  10. <!-- 这个切面也要配置成bean-->
  11. <bean id="log" class="com.spring.advice.Log"/>
  12. <aop:aspectj-autoproxy/>
  13. </beans>
第三步:运行:
  
  
  1. 三月 12, 2017 3:00:02 下午 org.springframework.context.support.AbstractApplicationContext prepareRefresh
  2. 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@439f5b3d: startup date [Sun Mar 12 15:00:02 GMT+08:00 2017]; root of context hierarchy
  3. 三月 12, 2017 3:00:02 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
  4. 信息: Loading XML bean definitions from class path resource [beans.xml]
  5. 环绕前
  6. 方法void com.spring.service.UserService.update(int)
  7. 方法执行前
  8. 修改用户
  9. 环绕后
  10. 方法执行后


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值