zoukankan      html  css  js  c++  java
  • 【一步一步学习spring】spring传统aop

    提前说明一下,实际开发中一般不会使用传统aop进行开发,而是会使用aspectJ。但是这个是aspectJ的基础,有助于理解。

    1. 通知类型

    • AOP联盟为通知advice定义了org.aopalliance.aop.Interface.Advice,即接口规范
    • Spring按照通知Advice在目标类方法的连接点位置,可以分为5类:
      • 前置通知 org.springframework.aop.MethodBeforeAdvice
        • 在目标方法执行前实施增强
      • 后置通知 org.springframework.aop.AfterReturningAdvice
        • 在目标方法执行后实施增强
      • 环绕通知 org.aopalliance.intercept.MethodInterceptor
        • 在目标方法执行前后实施增强
      • 异常抛出通知 org.springframework.aop.ThrowsAdvice
        • 在方法抛出异常后实施增强

    2. 切面类型

    • Advisor:代表一般切面,Advice本身就是一个切面,对目标类所有方法进行拦截
    • PointcutAdvisor:代表具有切点的切面,可以指定拦截目标类哪些方法

    3. 具体使用方式

    3.1 准备工作

    • pom.xml中引入传统aop依赖包
    <dependency>
        <groupId>aopalliance</groupId>
        <artifactId>aopalliance</artifactId>
        <version>1.0</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>4.3.18.RELEASE</version>
    </dependency>
    
    • 创建接口类
    package com.aop.traditional;
    
    public interface StudentDao {
    	public void find();
    	public void save();
    	public void update();
    	public void delete();
    }
    
    • 创建接口实现类
    package com.aop.traditional;
    
    public class StudentDaoImpl implements StudentDao {
    
    	public void find() {
    		System.out.println("查询");
    	}
    
    	public void save() {
    		System.out.println("保存");
    	}
    
    	public void update() {
    		System.out.println("更新");
    	}
    
    	public void delete() {
    		System.out.println("删除");
    	}
    
    }
    
    • 创建配置文件
    <?xml version="1.0" encoding="UTF-8"?>
    <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.xsd">
    
    	<bean id="studentDao" class="com.aop.traditional.StudentDaoImpl"></bean>
    
    </beans>
    

    3.2 普通切面增强整个目标类

    我们要告诉spring,我们要在哪对谁进行增强啥。

    • 创建前置通知类
    package com.aop.traditional;
    
    import java.lang.reflect.Method;
    
    import org.springframework.aop.MethodBeforeAdvice;
    
    public class BeforeAdvice implements MethodBeforeAdvice {
    
    	public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
    		System.out.println("前置通知=========");
    	}
    
    }
    
    • 配置文件注册bean
    <!-- 前置通知类型 -->
    <bean id="beforeAdvice" class="com.aop.traditional.BeforeAdvice"/>
    
    • spring aop产生代理对象,借助ProxyFactoryBean产生代理对象。重要的属性如下:
    <!-- spring aop产生代理对象 -->
    <bean id="studentDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 目标类名称 -->
        <property name="target" ref="studentDao"/>
        <!-- 目标类实现的接口类名称 -->
        <property name="proxyInterfaces" value="com.aop.traditional.StudentDao"/>
        <!-- 拦截器名称,需要织入目标的advice -->
        <property name="interceptorNames" value="beforeAdvice"/>
        <!-- 是否是对类代理而不是接口,设置为true时,使用cglib代理 -->
        <property name="proxyTargetClass" value="false"/>
        <!-- 返回代理是否为单例 -->
        <property name="singleton" value="true"/>
        <!-- 是否强制使用cglib -->
        <property name="optimize" value="false"/>
    </bean>
    
    • 调用
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:application-context-aop.xml")
    public class SpringDemo {
    	
    	@Resource(name="studentDaoProxy")
    	private StudentDao studentDao;
    	
    	@Test
    	public void demo1() {
    		studentDao.save(); 
    		studentDao.find(); 
    		studentDao.update(); 
    		studentDao.delete(); 
    	}
    }
    

    3.3 PointcutAdvisor 带切点的切面增强类方法

    • 使用普通advice作为切面,将对目标类所有方法进行拦截,不够灵活,在实际开发中常采用带有切点的切面。
    • 常用的PointcutAdvisor实现类
      • DefaultPointcutAdvisor
      • RegexpMethodPointcut 正则表达式切点,推荐用这个

    3.2中的栗子使用RegexpMethodPointcut 实现,只需要修改xml中的配置项即可,有一次显示了aop的灵活性。

    对delete和save方法进行前置通知增强。

    <!-- 配置目标类 -->
    <bean id="studentDao" class="com.aop.traditional.StudentDaoImpl" />
    <!-- 前置通知类型 -->
    <bean id="beforeAdvice" class="com.aop.traditional.BeforeAdvice" />
    
    <bean id="myadvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="patterns" value="com.aop.traditional.StudentDaoImpl.delete,.*save.*" />
        <property name="advice" ref="beforeAdvice" />
    </bean>
    <!-- spring aop产生代理对象 -->
    <bean id="regStudentDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!-- 目标类名称 -->
        <property name="target" ref="studentDao" />
        <!-- 是否是对类代理而不是接口,设置为true时,使用cglib代理 -->
        <property name="proxyTargetClass" value="true" />
        <!-- 拦截器名称,需要织入目标的advice -->
        <property name="interceptorNames" value="myadvisor" />
        <!-- 返回代理是否为单例 -->
        <property name="singleton" value="true" />
        <!-- 是否强制使用cglib -->
        <property name="optimize" value="false" />
    </bean>
    

    3.4 自动创建代理

    • 前面的案例中,每个代理都是通过ProxyFactoryBean织入切面代理,在实际开发中,非常多的Bean每个都配置ProxyFactoryBean开发维护量都巨大。
    • 解决方案:自动创建代理
      • BeanNameAutoProxyCreator 根据Bean名称创建代理,比如:所有后边带dao的bean都代理
      • DefaultAdvisorAutoProxyCreator 根据advisor中的信息创建代理
      • AnnotationAwareAspectJAutoProxyCreator 基于Bean中的AspectJ注解进行自动代理,这个在后边还会讲,此处略过。

    3.4.1 BeanNameAutoProxyCreator 举例

    对所有以dao结尾的bean的所有方法使用代理,注意该方式又只能是所有方法!!!!

    <!-- 配置目标类 -->
    <bean id="studentDao" class="com.aop.traditional.StudentDaoImpl" />
    <!-- 前置通知类型 -->
    <bean id="beforeAdvice" class="com.aop.traditional.BeforeAdvice" />
    <!-- 环绕通知类型 -->
    <bean id="aroundAdvice" class="com.aop.traditional.AroundAdvice"/>
    
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <property name="beanNames" value="*Dao"/>
        <property name="interceptorNames" value="beforeAdvice, aroundAdvice"/>
    </bean>
    

    细心的话,可以看到,BeanNameAutoProxyCreator是没有设置id的,说明我们不会主动注入。这个其实是在bean的postProcessAfterInitialization的声明周期阶段执行的,即是在bean初始化阶段就将增强注入进去了。这个跟其他的方式不同,其他的是先创建bean,在对bean进行代理。

    3.4.2 DefaultAdvisorAutoProxyCreator 举例

    <bean id="myadvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <property name="patterns" value="com.aop.traditional.StudentDaoImpl.delete,.*save.*" />
        <property name="advice" ref="beforeAdvice" />
    </bean>
    
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>
    

    这个DefaultAdvisorAutoProxyCreator将扫描上下文,寻找所有的Advistor(一个Advisor是一个切入点和一个通知的组成),将这些Advisor应用到所有符合切入点的Bean中 。

  • 相关阅读:
    canvas实现文字分行
    二维码扫描添加闪光灯的功能
    修改二维码扫描框的大小位置
    Android中实现EditText圆角的方法
    利用FrameLayout实现遮罩层
    EditText中的灰色提示
    实现等待动画
    发送短信
    自定义EditText动态在EditText中添加图片
    xml解析
  • 原文地址:https://www.cnblogs.com/xxxuwentao/p/9598614.html
Copyright © 2011-2022 走看看