美文网首页
深入理解spring的AOP

深入理解spring的AOP

作者: 每天学点编程 | 来源:发表于2016-08-26 22:36 被阅读0次

    AOP的原理

    AOP的实现有两种方式,一种是使用代理,一种是直接修改.class(即使用CGLib等类库)。

    问题的引入

    <!-- 事物切面配置 -->  
    <tx:advice id="advice" transaction-manager="transactionManager">  
        <tx:attributes>  
            <tx:method name="update*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception"/>  
            <tx:method name="insert" propagation="REQUIRED" read-only="false"/>  
        </tx:attributes>  
    </tx:advice>  
      
    <aop:config>  
        <aop:pointcut id="testService" expression="execution (* com.baobao.service.MyBatisService.*(..))"/>  
        <aop:advisor advice-ref="advice" pointcut-ref="testService"/>  
    </aop:config>  
    

    好神奇!这样就可以了?tx:advice是怎么实现的?如果我想要自己编写advice,那我应该要怎么做呢?

    何谓Advice

    如果要自己实现一个Advice,要怎么做?一个Advice包含了什么东西?怎么样的才算一个Advice?

    来,先看网上一个例子:

    一个需要注入的类:

    package com.mkyong.customer.services;
    
    public class CustomerService {
        private String name;
        private String url;
    
        public void setName(String name) {
            this.name = name;
        }
    
        public void setUrl(String url) {
            this.url = url;
        }
    
        public void printName() {
            System.out.println("Customer name : " + this.name);
        }
    
        public void printURL() {
            System.out.println("Customer website : " + this.url);
        }
    
        public void printThrowException() {
            throw new IllegalArgumentException();
        }
    
    }
    

    before advice

    package com.mkyong.aop;
    
    import java.lang.reflect.Method;
    import org.springframework.aop.MethodBeforeAdvice;
    
    public class HijackBeforeMethod implements MethodBeforeAdvice
    {
        @Override
        public void before(Method method, Object[] args, Object target)
            throws Throwable {
                System.out.println("HijackBeforeMethod : Before method hijacked!");
        }
    }
    

    如何把before advice注入到CustomerService类中:

    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    
        <bean id="customerService" class="com.mkyong.customer.services.CustomerService">
            <property name="name" value="Yong Mook Kim" />
            <property name="url" value="http://www.mkyong.com" />
        </bean>
    
        <bean id="hijackBeforeMethodBean" class="com.mkyong.aop.HijackBeforeMethod" />
    
        <bean id="customerServiceProxy"
                     class="org.springframework.aop.framework.ProxyFactoryBean">
    
            <property name="target" ref="customerService" />
    
            <property name="interceptorNames">
                <list>
                    <value>hijackBeforeMethodBean</value>
                </list>
            </property>
        </bean>
    </beans>
    

    customerServiceProxy就是注入了HijackBeforeMethodCustomerService吗?是的!

    package com.mkyong.common;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import com.mkyong.customer.services.CustomerService;
    
    public class App {
        public static void main(String[] args) {
            ApplicationContext appContext = new ClassPathXmlApplicationContext(
                    new String[] { "Spring-Customer.xml" });
    
            CustomerService cust =
                                    (CustomerService) appContext.getBean("customerServiceProxy");
    
            System.out.println("*************************");
            cust.printName();
            System.out.println("*************************");
            cust.printURL();
            System.out.println("*************************");
            try {
                cust.printThrowException();
            } catch (Exception e) {
    
            }
    
        }
    }
    

    customerServiceProxy怎么会是CustomerServicecustomerServiceProxy什么时候继承CustomerService的?
    这是Spring使用CGLib实现的。

    什么是Advice和Advisor

    从上面的例子中我们可以很清楚地明白什么是Advice:HijackBeforeMethod就是Advice, Advice定义了如何对要被注入的方法加入相关的逻辑,比如是在被注入方法的前面加上逻辑,还是在方法的后面。
    下面的类使用了aspectj注解:

    package test.mine.spring.bean;
    
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    @Aspect
    public class SleepHelper {
    
        @Pointcut("execution(* *.sleep())")
        public void sleeppoint(){}
        
        @Before("sleeppoint()")
        public void beforeSleep(){
            System.out.println("睡觉前要脱衣服!");
        }
        
        @AfterReturning("sleeppoint()")
        public void afterSleep(){
            System.out.println("睡醒了要穿衣服!");
        }    
    }
    

    beforeSleep()@Before是Advice,而sleeppoint()(PointCut)和beforeSleep()(包括@Before)是Advisor。

    再看问题

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
            <property name="dataSource" ref="dataSource" />  
    </bean>  
    <!-- 事物切面配置 -->  
    <tx:advice id="advice" transaction-manager="transactionManager">  
        <tx:attributes>  
            <tx:method name="update*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception"/>  
            <tx:method name="insert" propagation="REQUIRED" read-only="false"/>  
        </tx:attributes>  
    </tx:advice>  
      
    <aop:config>  
        <aop:pointcut id="testService" expression="execution (* com.baobao.service.MyBatisService.*(..))"/>  
        <aop:advisor advice-ref="advice" pointcut-ref="testService"/>  
    </aop:config>  
    

    对于以上的配置,我们可以在看看另一个例子:

    <bean id="customizableTraceInterceptor" class="
      org.springframework.aop.interceptor.CustomizableTraceInterceptor">
      <property name="enterMessage" value="Entering $[methodName]($[arguments])"/>
      <property name="exitMessage" value="Leaving $[methodName](): $[returnValue]"/>
    </bean>
    
    <aop:config>
      <aop:advisor advice-ref="customizableTraceInterceptor"
        pointcut="execution(public * org.springframework.data.jpa.repository.JpaRepository+.*(..))"/>
    </aop:config>
    

    稍微看一些org.springframework.aop.interceptor.CustomizableTraceInterceptor的源码就会知道CustomizableTraceInterceptor实现了MethodInterceptor接口:

    public class CustomizableTraceInterceptor extends AbstractTraceInterceptor
    
    public abstract class AbstractTraceInterceptor implements MethodInterceptor, Serializable
    

    那么我们可不可以自己也写一个DataSourceTransactionManagertx:advice?

    再看注解的AOP

        @Before("sleeppoint()")
        public void beforeSleep(){
            System.out.println("睡觉前要脱衣服!");
        }
    

    只是在一个方法上加上@Before("sleeppoint()"),就等于:

    <bean id="beforeSleepInterceptor" class="BeforeSleepInterceptor">
    </bean>
    
    <aop:config>  
        <aop:pointcut id="sleeppoint" expression="execution(* *.sleep())"/>  
        <aop:advisor advice-ref="beforeSleepInterceptor" pointcut-ref="sleeppoint"/>  
    </aop:config>  
    
    public class BeforeSleepInterceptor implements MethodBeforeAdvice
    {
        @Override
        public void before(Method method, Object[] args, Object target)
            throws Throwable {
                System.out.println("BeforeSleepInterceptor : Before method hijacked!");
        }
    }
    

    相关文章

      网友评论

          本文标题:深入理解spring的AOP

          本文链接:https://www.haomeiwen.com/subject/tvpzsttx.html