AOP使用
标签:Spring

Spring-AOP使用

1. AOP增强类型

AOP联盟为通知Advice定义了org.aopalliance.aop.Interface.Advice

Spring按照通知(代码增强)Advice在目标类方法的连接点位置,可以分为5类

2. AOP切面类型

Advisor就是对PointCut应用Advice(通常所说Advisor指一个PointCut和一个Advice)

2.1 不带切点的切面

使用普通Advisor,使用Advice作为一个切面,不定义切点,拦截目标类所有方法。

ProxyFactoryBean常用可配置属性

<list>
 <value></value>
   ....
</list>


操作步骤:

  1. 导入jar包
    导入 aop联盟的规范 : com.springsource.org.aopalliance-1.0.0.jar
    导入 spring aop实现 : spring-aop-3.2.0.RELEASE.jar

  2. 编写被代理 接口和实现类
    CustomerDAO

    /**
     * 客户管理接口
     */
    public interface CustomerDAO {
        public void add();
    
        public void search();
    
        public void update();
    
        public void delete();
    }
    

CustomerDAOImpl

    public class CustomerDAOImpl implements CustomerDAO {
        @Override
        public void add() {
            System.out.println("add");
        }
    
        @Override
        public void search() {
            System.out.println("search");
        }
    
        @Override
        public void update() {
            System.out.println("update");
        }
    
        @Override
        public void delete() {
            System.out.println("delete");
        }
    }
  1. 编写前置通知 (在目标方法前执行...)

    MyMethodBeforeAdvice

    import org.springframework.aop.MethodBeforeAdvice;
    
    import java.lang.reflect.Method;
    
    /**
     * Created By liuyao on 2018/5/11 19:30.
     */
    public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
        /**
         * @param method 目标方法
         * @param args   参数
         * @param target 目标对象
         * @throws Throwable
         */
        @Override
        public void before(Method method, Object[] args, Object target) throws Throwable {
            System.out.println("=====before=====");
        }
    }
    
  2. 为目标对象创建 , 配置applicationContext.xml,使用ProxyFactoryBean 为目标对象创建代理

 <!--不带有切点的切面-->
    <!--被代理对象-->
    <bean id="customerDAO" class="com.liuyao.advisor.CustomerDAOImpl"></bean>

    <!--增强-->
    <bean class="com.liuyao.advisor.MyMethodBeforeAdvice" id="myMethodBeforeAdvice"></bean>

    <!--创建代理-->
    <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="customerDAOProxy">
        <!--目标-->
        <property name="target" ref="customerDAO"></property>
        <!--针对接口代理-->
        <property name="proxyInterfaces" value="com.liuyao.advisor.CustomerDAO"></property>
        <!--增强 可以运用多个Advice 必须写value -->
        <property name="interceptorNames" value="myMethodBeforeAdvice"></property>
    </bean>
  1. 测试

    Test:

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = "classpath:ApplicationContext.xml")
    public class CustomerDAOTest {
        @Autowired
        //注意事项: 在编程时,应该使用 ProxyFactoryBean 创建后代理对象(CustomerDAOProxy ), 不要引入原来Bean (CustomerDAO)
        @Qualifier("customerDAOProxy")
        private CustomerDAO customerDAO;
    
        @Test
        public void test1() {
            this.customerDAO.add();
            this.customerDAO.delete();
            this.customerDAO.search();
            this.customerDAO.update();
        }
    }
    

运行结果:

2.2 带有切点的切面

使用普通Advice作为切面,将对目标类所有方法进行拦截,不够灵活,在实际开发中常采用 带有切点的切面

常用PointcutAdvisor 实现类

操作步骤:

  1. 创建被代理对象,没有接口的类

    OrderDAO:

/**
 * 订单操作 (被代理对象)
 */
public class OrderDAO {
    public void add() {
        System.out.println("add");
    }

    public void search() {
        System.out.println("search");
    }

    public void update() {
        System.out.println("update");
    }

    public void delete() {
        System.out.println("delete");
    }

}

  1. 增强 (编写环绕通知)

MyMethodInterceptor:

package com.liuyao.pointcutadvisor;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

/**
 * Created By liuyao on 2018/5/12 15:09.
 */
public class MyMethodInterceptor implements MethodInterceptor {
    @Override
//    进行增强方法增强
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("方法前增强");
        Object res = invocation.proceed();
        System.out.println("方法后增强");
        return res;
    }
}

  1. 通过配置 ProxyFactory 为目标对象创建代理
    <!--带有切点的切面-->
    <!--被代理对象-->
    <bean id="orderDAO" class="com.liuyao.pointcutadvisor.OrderDAO"></bean>

    <!--增强-->
    <bean id="myMethodInterceptor" class="com.liuyao.pointcutadvisor.MyMethodInterceptor"></bean>

    <!--定义切面-->
    <bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <!--正则表达式规则-->
        <property name="pattern" value="com\.liuyao\.pointcutadvisor\.OrderDAO\.add.*"></property>
        <property name="advice" ref="myMethodInterceptor"></property>
    </bean>

    <!--创建代理-->
    <bean id="orderDAOProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--目标-->
        <property name="target" ref="orderDAO"></property>
        <!--针对类代理-->
        <property name="proxyTargetClass" value="true"></property>
        <!--增强-->
        <property name="interceptorNames" value="myAdvisor"></property>
    </bean>
  1. 测试
package com.liuyao.pointcutadvisor;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * Created By liuyao on 2018/5/12 15:19.
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:ApplicationContext.xml")
public class MyMethodInterceptorTest {
    @Autowired
    @Qualifier("orderDAOProxy")
    private OrderDAO orderDAO;

    @Test
    public void test() {
        this.orderDAO.add();
        this.orderDAO.delete();
        this.orderDAO.search();
        this.orderDAO.update();
    }
}

测试结果:

正则表达式写法:

3. 自动代理

每个代理都是通过ProxyFactoryBean织入切面代理,在实际开发中,非常多的Bean每个都配置ProxyFactoryBean开发维护量巨大 。

自动代理原理: 根据xml中配置advisor的规则,得知切面对哪个类的哪个方法进行代理 (切面中本身就包含 被代理对象信息) ,就不需要ProxyFactoryBean ,使用BeanPostProcessor 完成自动代理

自动代理和ProxyFactoryBean 本质区别 :

3.1 BeanNameAutoProxyCreator 根据Bean名称创建代理

<!--被代理对象-->
    <bean id="customerDAO" class="com.liuyao.advisor.CustomerDAOImpl"></bean>
    <bean id="orderDAO" class="com.liuyao.pointcutadvisor.OrderDAO"></bean>

    <!--增强-->
    <bean class="com.liuyao.advisor.MyMethodBeforeAdvice" id="myMethodBeforeAdvice"></bean>
    <bean id="myMethodInterceptor" class="com.liuyao.pointcutadvisor.MyMethodInterceptor"></bean>

    <!--第一种,BeanName自动代理-->
    <!--后处理Bean不需要id-->
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <!--对所有以DAO结尾的代理-->
        <property name="beanNames" value="*DAO"></property>
        <!--增强-->
        <property name="interceptorNames" value="myMethodInterceptor"></property>
    </bean>

3.2 DefaultAdvisorAutoProxyCreator 根据Advisor本身包含信息创建代理

<!--被代理对象-->
    <bean id="customerDAO" class="com.liuyao.advisor.CustomerDAOImpl"></bean>
    <bean id="orderDAO" class="com.liuyao.pointcutadvisor.OrderDAO"></bean>

    <!--增强-->
    <bean class="com.liuyao.advisor.MyMethodBeforeAdvice" id="myMethodBeforeAdvice"></bean>
    <bean id="myMethodInterceptor" class="com.liuyao.pointcutadvisor.MyMethodInterceptor"></bean>

    <!--切面-->
    <bean id="myAdvice" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <!--切点拦截信息-->
        <property name="pattern" value="com\.liuyao\.pointcutadvisor\.OrderDAO.add.*"></property>
        <!--增强-->
        <property name="advice" ref="myMethodInterceptor"></property>
    </bean>
    <!--第二种基于切面信息自动代理-->
	<!--只需要配置这个就行了,会自动读取切面信息-->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>

  • 8 min read

CONTRIBUTORS


  • 8 min read