spring学习笔记(13)基于Schema配置AOP详解

简介: <div class="markdown_views"><h1 id="基于schema配置入门实例">基于Schema配置入门实例</h1><p>除了基于@AspectJ注解的形式来实现AOP外,我们还可以在IOC容器中配置。先来看看一个常见的应用场景,在我们的web项目中,我们需要为service层配置事务,传统的做法是在每个业务逻辑方法重复下面配置中:</p>

基于Schema配置入门实例

除了基于@AspectJ注解的形式来实现AOP外,我们还可以在IOC容器中配置。先来看看一个常见的应用场景,在我们的web项目中,我们需要为service层配置事务,传统的做法是在每个业务逻辑方法重复下面配置中:

Created with Raphaël 2.1.0程序开始1. 获取DAO层封装好的数据库查询API,如HIbernate中的SessionFactory/Session和mybatis中的xxxMapper2. 开启事务3. 根据入参查询数据库完成相应的业务逻辑操作4. 关闭事务5. 如果有必要则关闭连接。程序结束

第1步可以通过我们SpringIOC注入完成,但2,4步很多时候则显得非常冗杂,我们需要在每个方法中都开启关闭事务,于是我们利用AOP的横切逻辑来实现事务配置:

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
    <property name="dataSource" ref="dataSource" /> </bean>
<!-- 拦截器方式配置事物 -->
<tx:advice id="transactionAdvice" transaction-manager="transactionManager"><!--事务的增强配置-->
    <tx:attributes>
        <tx:method name="add*" propagation="REQUIRED" />
        <!--name="add*"相当于我们的函数切点表达式,匹配以add开头的方法,使用REQUIRED的事务传播行为-->
        <tx:method name="save*" propagation="REQUIRED" />
        <tx:method name="modify*" propagation="REQUIRED" />
        <tx:method name="delete*" propagation="REQUIRED" />

        <tx:method name="get*" propagation="SUPPORTS" />
        <tx:method name="search*" propagation="SUPPORTS" />
        <tx:method name="*" propagation="SUPPORTS" />
    </tx:attributes>
</tx:advice>
<aop:config><!--aop命名空间配置的开始-->
    <aop:pointcut id="transactionPointcut"
<!--这里也是切点,和前面的<tx:method name="add*>取交来定位连接点-->
    <aop:advisor pointcut-ref="transactionPointcut"
        advice-ref="transactionAdvice" />
        <!--通过整合切点和增强来配置我们的切面(advisor),从配置中我们看到,这种切面的增强和切点都是唯一的-->
</aop:config>

以上实例就是我们基于Schema的方式来配置切面,这样,我们com.yc.service包下,以Impl结尾的类中所有的方法都会根据方法名织入相应的事务,就不用之前繁琐地硬编码式为每个方法配置事务了。

配置详解

下面我们通过一个比较全面的例子来认识Schema特色配置

1. 定义目标对象

    package test.aop3;

    public class UserController {
        public void login(String name){
            System.out.println("I'm "+name+" ,I'm logining");
        }

        //模拟非法注销
        public void logout() {
            throw new RuntimeException("illegal logout");
        }
    }

2. 配置增强方法所在类

    package test.aop3;

    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.AfterReturning;
    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.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.springframework.core.Ordered;

    public class MyAdvice {

        public void AfterReturning(Object retInfo) throws Throwable{
            System.out.println("MyAdvice 实施AfterReturning,目标对象方法返回值为:"+retInfo);

        }
        public void after(String name) throws Throwable{
            System.out.println("MyAdvice 实施after,目标对象方法入参为:"+name);
        }
        public void before(String name) throws Throwable{
            System.out.println("MyAdvice 实施@before,目标对象方法入参为:"+name);
        }
        public void around(ProceedingJoinPoint joinPoint) throws Throwable{
            System.out.println("MyAdvice 实施around前,目标对象方法入参为:"+joinPoint.getArgs()[0]);
            joinPoint.proceed();
            System.out.println("MyAdvice 实施around后,目标对象方法入参为:"+joinPoint.getArgs()[0]);
        }
        public void afterThrowing(RuntimeException re) throws Throwable{
            System.out.println("MyAdvice 实施afterThrowing,目标对象方法抛出异常:"+ re.getMessage());
        }

    }

3. 配置xml文件

    <bean  id="myAdvice" class="test.aop3.MyAdvice" /><!-- 注册advice -->
    <bean id="userController" class="test.aop3.UserController" />
    <!-- 注册目标对象类,方便测试使用 -->

<!-- 基于Schema配置必须以<aop:config>开始 ,可配置是否暴露AOP代理,或是否使用CGLib代理-->
        <aop:pointcut expression="target(test.aop3.UserController) and args(name) " id="pointcut"/>
        <!-- 配置独立切点,方便在后面的切面配置中服用 -->
<!-- 配置切面,通过ref引入增强类,通过order配置织入顺序 -->
        <aop:before method="before" pointcut-ref="pointcut" arg-names="name"/>
        <!-- 通过arg-names绑定目标对象的方法入参 名字需和增强的方法入参一致-->
        <!-- 除了通过pointcut-ref属性引用独立命名切点,还可以通过pointcut属性声明并引用匿名切点 -->
        <aop:around method="around" pointcut="execution( * test.aop3.UserController.login(..))"/>
            <aop:after method="after" pointcut-ref="pointcut" arg-names="name"/>
<!--绑定目标对象方法的返回值到增强方法入参,名字需一致  -->
            <aop:after-throwing method="afterThrowing" 
            pointcut="target(test.aop3.UserController)" throwing="re"/>
        </aop:aspect>
    </aop:config>

4. 运行测试方法

public static void main(String args[]){
    ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:test/aop3/aop.xml");
    UserController userController = (UserController) ac.getBean("userController");
    userController.login("zenghao");
    userController.logout();
}

5. 测试结果分析

控制台打印信息:
MyAdvice 实施@before,目标对象方法入参为:zenghao
MyAdvice 实施around前,目标对象方法入参为:zenghao
I’m zenghao ,I’m logining
MyAdvice 实施around后,目标对象方法入参为:zenghao
MyAdvice 实施after,目标对象方法入参为:zenghao
MyAdvice 实施AfterReturning,目标对象方法返回值为:null
MyAdvice 实施afterThrowing,目标对象方法抛出异常:illegal logout
Exception in thread “main” java.lang.RuntimeException: illegal logout
at test.aop3.UserController.logout(UserController.java:10)
at test.aop3.UserController$$FastClassBySpringCGLIB$$d89843a8.invoke()
……(忽略下面异常信息)……

结合上面实例,我们分析:
1. aop切点、切面等配置必须被<aop:config>标签包括
2. 一个<aop:config>可以配置多个切点、切面,一个切面可以配置多个增强
3. <aop:pointcut>可以配置在<aop:config>和<aop:aspect>中,配置在<aop:config>中对所 有<aop:aspect>可见,配置在特定的<aop:aspect>则对其他的<aop:aspect>不可见。
4. 在<aop:config>中,各标签配置顺序先后依序必须为:<aop:pointcut>、<aop:advisor>、<aop:aspect>,若<aop:pointcut>配置在<aop:aspect>中,则无顺序要求(即在<aop:after>等标签前后都可以,即使<aop:after>标签中引用了该<aop:pointcut>标签)
5. 在xml文件中使用逻辑运算符&&会报错,必须使用and,||和!则无此限制

引介增强

在前面的例子中,我们没有提到引介增强,实际上,引介增强的配置和其他增强的差异是挺大的,它没有了method,pointcut,pointcut-ref属性,但多了以下四个属性:
1. implement-interface
2. default-impl
3. implement-interface
4. delegate-ref
关于引介增强的各种配置和现实应用场景分析,请移步至我的另外一篇博文《spring学习笔记(12)引介增强详解:定时器实例:无侵入式动态增强类功能》查看。

advisor配置

前面我们在谈等几个标签的配置顺序中提到,它的使用格式为:
<aop:advisor advice-ref="实现了特定的增强接口的增强类" order="1" pointcut="使用切点表达式函数定义切点"/>
其中,实现了特定增强的接口实现类配置可参考我前面的文章:spring学习笔记(6)AOP增强(advice)配置与应用 ,它的配置形式如:

public class BeforeAdvice implements MethodBeforeAdvice{

    @Override
    public void before(Method method, Object[] args, Object target)
            throws Throwable {
        System.out.println("前置日志记录:"+target+ "调用了"+method.getName() + "方法,传入的第一个参数为:"+args[0]);
    }

}

源码下载

本博文提到的示例源码请到我的github仓库https://github.com/jeanhao/spring的aop分支下载。

目录
相关文章
|
12天前
|
SQL Java 数据库连接
(自用)Spring常用配置
(自用)Spring常用配置
15 0
|
1月前
|
监控 Java 开发者
Spring AOP动态代理
Spring AOP动态代理
43 1
|
1月前
|
Java Spring 容器
Spring的AOP失效场景详解
Spring的AOP失效场景详解
102 0
|
28天前
|
设计模式 Java Maven
Spring Aop 底层责任链思路实现-springaopdi-ceng-ze-ren-lian-si-lu-shi-xian
Spring Aop 底层责任链思路实现-springaopdi-ceng-ze-ren-lian-si-lu-shi-xian
34 1
|
1月前
|
SQL Java 数据库连接
挺详细的spring+springmvc+mybatis配置整合|含源代码
挺详细的spring+springmvc+mybatis配置整合|含源代码
41 1
|
5天前
|
存储 安全 Java
第2章 Spring Security 的环境设置与基础配置(2024 最新版)(下)
第2章 Spring Security 的环境设置与基础配置(2024 最新版)(下)
15 0
|
5天前
|
安全 Java 数据库
第2章 Spring Security 的环境设置与基础配置(2024 最新版)(上)
第2章 Spring Security 的环境设置与基础配置(2024 最新版)
28 0
|
6天前
|
安全 Java Spring
Spring Security 5.7 最新配置细节(直接就能用),WebSecurityConfigurerAdapter 已废弃
Spring Security 5.7 最新配置细节(直接就能用),WebSecurityConfigurerAdapter 已废弃
18 0
|
6天前
|
安全 Java 应用服务中间件
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
24 0
江帅帅:Spring Boot 底层级探索系列 03 - 简单配置
|
12天前
|
JSON Java 数据库连接
属性注入掌握:Spring Boot配置属性的高级技巧与最佳实践
属性注入掌握:Spring Boot配置属性的高级技巧与最佳实践
21 1