Spring之面向切面编程AOP(三)

上两节提到Spring的装配bean还有高级装配,这一节就是Spring的另一个核心内容-AOP

AOP的基本概念

AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等通用的辅助模块,在系统的很多模块都要添加。

日志,事务,权限等就是切面,使用这种面向切面编程时,我们仍然可以在一个地方声明通用功能(切面),然后声明这个功能在何时通过何种方式使用,而无需修改应用这个切面的模块。这样做的好处是我们的关注点可以集中在模块,而不是分散的通用功能,其次,模块的代码因此会变得整洁。

AOP的组成

(1)Aspect(切面):通常是一个类,里面可以定义切入点和通知

(2)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用

(3)Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around

(4)Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式

(5)AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类

例如:

@Aspect  
@Component  
public class Audience {  
    @Pointcut("execution(* com.hsb.beans.Perform.show(..))")  
    public void performance() {  
    }  

    @Before("performance()")  
    public void beforeShow() {  
        System.out.println("message from beforeShow");  
    }   

Audience就是一个切面
@Pointcut("execution(* com.hsb.beans.Perform.show(..))") 是切点
beforeShow()是连接点
@Before(“performance()”) 就是通知

下面是两种常用的aop方法:

1.在XML中声明切面

  • a.编写一个原始类
    package com.hsb.beans;  

    import org.springframework.stereotype.Repository;  

    @Repository  
    public class Perform {  
        public void show(){  
            System.out.println("message from Perform.show()");  
        }  
    }  
  • b.编写一个切面
    package com.hsb.aop;  

    import org.aspectj.lang.ProceedingJoinPoint;  

    public class Audience {  
        public void beforeShow(){  
            System.out.println("message from beforeShow");  
        }  
        public void afterShow(){  
            System.out.println("message from afterShow");  
        }  
        public void around(ProceedingJoinPoint joinpoint){  
            System.out.println("message from Start around");  
            long start = System.currentTimeMillis();  
            try {  
                joinpoint.proceed();  
            } catch (Throwable e) {  
                e.printStackTrace();  
            }  
            long end = System.currentTimeMillis();  
            System.out.println("message from End around total : "+(end-start)+" ms");  
        }  
    }  

代码中的joinpoint.proceed(),这句话就是实际调用切点方法,也就是本例中的show()。如果切面类中定义了around通知,通知一定要加上这句话,否则要切点方法不会被调用!
- c.配置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:aop="http://www.springframework.org/schema/aop"  
        xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"  
        xsi:schemaLocation="http://www.springframework.org/schema/beans     
                            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd    
                            http://www.springframework.org/schema/aop     
                            http://www.springframework.org/schema/aop/spring-aop-3.0.xsd    
                            http://www.springframework.org/schema/tx     
                            http://www.springframework.org/schema/tx/spring-tx-3.0.xsd    
                            http://www.springframework.org/schema/context     
                            http://www.springframework.org/schema/context/spring-context-3.0.xsd"  
        default-lazy-init="true">  
        <context:component-scan base-package="com.hsb.beans" />  
        <bean id="audience" class="com.hsb.aop.Audience" />  
        <aop:config>  
            <aop:aspect ref="audience">  
                <aop:pointcut expression="execution(* com.hsb.beans.Perform.show(..))"  
                    id="performance" />  
                <aop:before method="beforeShow" pointcut-ref="performance" />  
                <aop:after method="afterShow" pointcut-ref="performance" />  
                <aop:around method="around" pointcut-ref="performance" />  
            </aop:aspect>  
        </aop:config>  
    </beans>  

首先将audience声明为一个bean,
<aop:aspect ref="audience">定义切面。
<aop:pointcut>定义切点。
<aop:before>等定义通知。

这样就实现了在xml中实现aop,原理是,一个代理类封装了需要切面的这个模块,并拦截了被通知方法的调用,再把调用方法转发给真正的bean。
当拦截到方法调用时,先执行切面逻辑,再调用目标方法

2.注解切面

  • a.编写一个原始类
    package com.hsb.beans;  

    import org.springframework.stereotype.Repository;  

    @Repository  
    public class Perform {  
        public void show(){  
            System.out.println("message from Perform.show()");  
        }  
    }  
  • b.编写一个切面
    package com.hsb.aop;  

    import org.aspectj.lang.ProceedingJoinPoint;  
    import org.aspectj.lang.annotation.After;  
    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.springframework.stereotype.Component;  

    @Aspect  
    @Component  
    public class Audience {  
        @Pointcut("execution(* com.hsb.beans.Perform.show(..))")  
        public void performance() {  
        }  

        @Before("performance()")  
        public void beforeShow() {  
            System.out.println("message from beforeShow");  
        }  

        @After("performance()")  
        public void afterShow() {  
            System.out.println("message from afterShow");  
        }  

        @Around("performance()")  
        public void around(ProceedingJoinPoint joinpoint) {  
            System.out.println("message from Start around");  
            long start = System.currentTimeMillis();  
            try {  
                joinpoint.proceed();  
            } catch (Throwable e) {  
                e.printStackTrace();  
            }  
            long end = System.currentTimeMillis();  
            System.out.println("message from End around total : " + (end - start)  
                    + " ms");  
        }  
    }  

@Aspect将此类声明为切面类,@Component将此类声明为bean放到spring容器中,@Pointcut将下面的performance声明为一个切点,并将Perform中的show()方法进行了关联。execution(* com.hsb.beans.Perform.show(..))的意思是,执行此方法时,忽略返回值,参数类型和个数忽略。还可以更加简写,用于匹配合适的方法,譬如对Perform类的全部方法进行匹配就是com.hsb.beans.Perform.*。

  • c.配置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:aop="http://www.springframework.org/schema/aop"  
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans     
                        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd    
                        http://www.springframework.org/schema/aop     
                        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd    
                        http://www.springframework.org/schema/tx     
                        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd    
                        http://www.springframework.org/schema/context     
                        http://www.springframework.org/schema/context/spring-context-3.0.xsd"  
    default-lazy-init="true">  
    <context:component-scan base-package="com.hsb.*" />  
    <aop:aspectj-autoproxy proxy-target-class="true"/>  
</beans>

可以看见此处就没有使用<aop:config />声明,而是使用<aop:aspectj-autoproxy proxy-target-class="true" />这句话的意思是自动在spring上下文中创建一个AnnotationAwareAspectJAutoProxyCreator类,它会自动代理一些bean,这些bean的方法需要与使用@Aspect注解的bean中所定义的切点相匹配,而这些切点又是使用@Pointcut注解定义出来的。proxy-target-class=”true”的意思是使用基于类的代理使用cglib库,如果为false则使用jdk自带的基于接口的代理

如上所示,完全是一样的效果。可以看出使用注解减少了很多工作,不容易出错,各个组件间的耦合度降低了。试想,如果一个大型项目中有很多切面,切点,如果全部去xml中配置,将会是一项极为艰苦的工作,但是如果使用注解就可以做很少的工作完成这一切。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值