浅析Spring AOP(面向方面编程)

来源:互联网 发布:linux开启telnet命令 编辑:程序博客网 时间:2024/05/04 20:17

SpringAOP提供的优势
1、允许开发者声明企业级服务,比如:事务服务、安全性服务。EJB组件能够使用J2EE容器提供声明式服务。但是需要借助于EJB组件,而SpringAOP却不需要EJB容器,即借助于Spring的事务抽象框架能够在EJB容器外部使用企业级、声明式服务。
2、开发者可以开发满足业务需求的自定义方面。类似于JBOSS服务器中拦截器开发一样,如果标准的J2EE安全性不能满足业务需求,则必须开发拦截器。
3、开发SpringAOP advice很方便,这些AOP Advice不仅仅POJO类,借助于Spring提供的ProxyFactoryBean,能够迅速的搭建Spring AOP Advice
Spring AOP的装备
1、before装备:在执行目标之前执行的装备
使用接口org.springframework.aop.MethodBeforeAdvice
源码如下:

public class LoggingBeforeAdvice implements MethodBeforeAdvice{    protected static final Log log = LogFactory.getLog(LoggingBeforeAdvice.class);    public void before(Method arg0,Object[] arg1,Object arg2){        // before do something    }}该方法在

调用目标操作之前调用,这很适用于那些有安全性要求的方法,即在调用目标操作之前检查客户身份。开发者还需要提供application.xml文件。

<beans><bean id="helloworldbean" class="org.springframework.aop.framework.ProxyFactoryBean">    <property name="proxyInterfaces">        <value>com.openv.spring.IHelloWorld</value>    </property>    <property name="target">        <ref local="helloworldbeanTarget"/>    </property>    <property name="interceptorNames">        <list>            <value>loggingBeforeAdvisor</value>        </list>    </property></bean><bean id="helloworldbeanTarget" class="com.openv.spring.HelloWorld" /><bean id="loggingBeforeAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">    <property name="advice">        <ref local="loggingBeforeAdvice" />    </property>    <property name="pattern">        <value>.*</value>    </property></bean><bean id="loggingBeforeAdvice" class="com.openv.spring.LoggingBeforeAdvice" /></beans>

其中借助了RegexpMethodPointcutAdvisor类实现了对LoggingBeforeAdvice装备的集成,以完成pointcut和拦截器的定义。
2、Throws装备:如果目标操作在执行过程中抛出了异常,该装备会执行,可以采用Java捕捉异常而不用对异常信息或throwable进行造型
使用接口org.springframework.aop.ThrowsAdvice
对于处理事务或者特定业务需求很有帮助,源码如下:

public interface IHelloWorld{    public String getContext(String helloworld) throws Exception;}public class HelloWorld implements IHelloWorld{    protected static final Log log = LogFactory.getLog(HelloWorld.class);    public String getContext(String helloworld) throws Exception{        // do something        throw new Exception();    }}

LoggingThrowsAdvice的装备代码如下:

public class LoggingThrowsAdvice implements ThrowsAdvice{    protected Log log = LogFactory.getLog(LoggingThrowsAdvice.class);    public void afterThrowing(Method method,Object[] args,Object target,Throwable subclass){        // throw some Exception    }}

其实现了afterThrowing方法,当异常抛出时,该装备即被激活,具体的Spring配置文件如下:

<bean id="helloworldbean" class="org.springframework.aop.framework.ProxyFactoryBean">    <property name="proxyInterfaces">        <value>com.openv.spring.IHelloWorld</value>    </property>    <property name="target">        <ref local="helloworldbeanTarget" />    </property>    <property name="interceptorNames">        <list>            <value>loggingThrowsAdvisor</value>        </list>    </property></bean><bean id="helloworldbeanTarget" class="com.openv.spring.HelloWorld" /><bean id="loggingThrowsAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">    <property name="advice">        <ref local="loggingThrowsAdvice">    </property>    <property name="pattern">        <value>.*</value>    </property></bean><bean id="loggingThrowsAdvice" class="com.openv.spring.LoggingThrowsAdvice" />

应用抛出Exception后,LoggingThrowsAdvice的AfterThrowing即被激活

3、After装备:在执行目标之后执行的装备
After状态在执行密保操作之后执行装备中的afterReturnning方法。具体的LoggingAfterAdvice实现如下:

public class LoggingAfterAdvice implements AfterRuturningAdvice{    protected static final Log log = LogFactory.getLog(LoggingAfterReturnningAdvice.class);    public void afterReturnning(Object object,Method m,Object[] args,Object target) throws Throwable(        // after do something    )}
配置文件:<bean id="helloworldbean" class="org.springframework.aop.framework.ProxyFactoryBean">    <property name="proxyInterfaces">        <value>com.openv.spring.IHelloWorld</value>    </property>    <property name="target">        <ref local="helloworldbeanTarget" />    </property>    <property name="interceptorNames">        <list>            <value>loggingAfterAdvisor</value>        </list>    </property></bean><bean id="helloworldbeanTarget" class="com.openv.spring.HelloWorld"/><bean id="loggingAfterAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">    <property name="advisor">        <ref local="loggingAfterAdvice" />    </property>    <property name="pattern">        <value>.*</value>    </property></bean><bean id="loggingAfterAdvice" class="com.openv.spring.LoggingAfterAdvice" />

使用接口org.springframework.aop.AfterReturningAdvice
对于代理Java接口的场景,Spring默认时是采用动态代理实现的,对于代理Java类的场景Spring采用动态字节码(byte-code)生成技术,比如使用了CGLIB库。
4、Around装备:在调用方法前后执行的装备。功能最强大,能够在目标操作执行前后实现特定的行为,使用灵活。
使用接口org.springframework.aop.MethodInterceptor
功能最强大,灵活性最好,能够在执行目标前后执行,这对一些需要做资源初始化和释放操作的应用特别有用,具体代码分析如下:

public class LoggingAroundAdvice implements MethodInterceptor{    protected static final Log log = LogFactory.getLog(LoggingAroundAdvice.class);    public Object invoke(MethodInvocation invocation) throws Throwable{        log.info("before:The Invocation of getContent()");        invocation.getArguments()[0] = "jader";        invocation.proceed();        log.info("after:The Inovation of getContent()");        return null;    }}
配置文件如下:<bean id="helloworldbean" class="org.springframework.aop.framework.ProxyFactoryBean" >    <property name="proxyInterfaces">        <value>com.openv.spring.IHelloWorld</value>    </property>    <property name="target">        <ref local="helloworldbeanTarget" />    </property>    <property name="interceptorNames">        <list>            <value>loggingAroundAdvisor</value>        </list>    </property></bean><bean id="loggingAroundAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">     <property name="advice">        <ref local="loggingAroundAdvice">     </property>     <property name="pattern">        <value>.*</value>    </property></bean><bean id="loggingAroundAdvice" class="com.openv.spring.LoggingAroundAdvice" />

5、Introduction装备:能够为类新增方法,最复杂
这几种接口之间的关系:
这里写图片描述
ProxyFactoryBean
ProxyFactoryBean引入了间接层。通过名字或者id(helloworldbean)获得的引用对象并不是ProxyFactoryBean实例本身,而是ProxyFactoryBean中getObject方法实现返回的对象。其中getObject方法将创建AOP代理,并将目标对象包裹(wrapper)在其中。那么ProxyFactoryBean到底是什么?
ProxyFactoryBean实现了org.springframework.beans.factory.FactoryBean接口,本身也是JavaBean,下面是其类图:
这里写图片描述
主要有以下几个属性:
proxyInterfaces:接口构成字符串列表,即接口集合
proxyTargetClass:是否使用CGLIB代理目标类的标志位动态代理能够对接口指定,如果目标是类则无能无力,因此需要借助于CGLIB库,实现类的子类,从而起到代理类的作用。
interceptorNames:拦截器名构成的字符串列表,拦截器集合
target:执行目标类
SingleTon:单实例的标志位,每次调用ProxyFactoryBean的getObject方法时是返回同一个对象还是返回不同的对象。
下面看下具体的代码:

public class HelloClient{    protected static final Log log = LogFactory.getLog(HelloClient.class);    public static void main(String[] args){        // 创建LoggingAroundAdvice装备        Advice advice = new LoggingAroundAdvice();        // 创建ProxyFactory,从而不需要借助Spring IOC容器提供反转功能        ProxyFactory factory = new ProxyFactory(new HelloWorld());        factory.addAdviced(advice);        IHelloWorld hw = (IHelloWorld)factory.getProxy();        log.info(hw.getContext("jader"));    }}

通过手工创建AOP代理能够摆脱Spring IOC容器的依赖。
Spring框架开发team推荐:借助于Spring IOC框架自动创建AOP代理,并将有关AOP代理的Java代理通过Spring配置文件配置。
FactoryBean在Spring框架起了很重要的作用,ProxyFactoryBean实现了FactoryBean接口,借助于ProxyFactoryBean能够实现各种业务需求,但要求去额外开发很多辅助业务操作,比如事务、数据库连接。
对象池
先研究下application.xml

// 借助于spring框架对commons pool的有效支持,能够实现对helloworldbeanTarget的有效池化
// 同时还能设置对象池的最大数量

<bean id="poolTargetSource" class="org.springframework.aop.target.CommonsPoolTargetSource">    <property name="targetBeanName">        <value>helloworldbeanTarget</value>    </property>    <property name="maxSize">        <value>25</value>    </property></bean>

// 通过ProxyFactoryBean中指定targetSource属性,便能够使用到poolTargetSource提供的强大功能

<bean id="helloworldbean" class="org.springframework.aop.framework.ProxyFactoryBean">    <property name="targetSource">        <ref local="poolTargetSource" />    </property>    <property name="interceptorNames">        <list>            <value>loggingAroundAdvisor</value>        </list>    </property></bean>
2 0
原创粉丝点击