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分支下载。

目录
相关文章
|
5天前
|
Java Spring
【Spring】方法注解@Bean,配置类扫描路径
@Bean方法注解,如何在同一个类下面定义多个Bean对象,配置扫描路径
115 73
|
5天前
|
Java Spring
【Spring配置相关】启动类为Current File,如何更改
问题场景:当我们切换类的界面的时候,重新启动的按钮是灰色的,不能使用,并且只有一个Current File 项目,下面介绍两种方法来解决这个问题。
|
5天前
|
Java Spring
【Spring配置】idea编码格式导致注解汉字无法保存
问题一:对于同一个项目,我们在使用idea的过程中,使用汉字注解完后,再打开该项目,汉字变成乱码问题二:本来a项目中,汉字注解调试好了,没有乱码了,但是创建出来的新的项目,写的注解又成乱码了。
|
5天前
|
Java Spring
【Spring配置】创建yml文件和properties或yml文件没有绿叶
本文主要针对,一个项目中怎么创建yml和properties两种不同文件,进行配置,和启动类没有绿叶标识进行解决。
|
13天前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
60 14
|
10天前
|
XML Java 数据格式
Spring容器Bean之XML配置方式
通过对以上内容的掌握,开发人员可以灵活地使用Spring的XML配置方式来管理应用程序的Bean,提高代码的模块化和可维护性。
45 6
|
12天前
|
XML Java 数据格式
🌱 深入Spring的心脏:Bean配置的艺术与实践 🌟
本文深入探讨了Spring框架中Bean配置的奥秘,从基本概念到XML配置文件的使用,再到静态工厂方式实例化Bean的详细步骤,通过实际代码示例帮助读者更好地理解和应用Spring的Bean配置。希望对你的Spring开发之旅有所助益。
67 3
|
30天前
|
监控 安全 Java
什么是AOP?如何与Spring Boot一起使用?
什么是AOP?如何与Spring Boot一起使用?
59 5
|
1月前
|
Java 开发者 Spring
深入解析:Spring AOP的底层实现机制
在现代软件开发中,Spring框架的AOP(面向切面编程)功能因其能够有效分离横切关注点(如日志记录、事务管理等)而备受青睐。本文将深入探讨Spring AOP的底层原理,揭示其如何通过动态代理技术实现方法的增强。
68 8
|
1月前
|
Java 开发者 Spring
Spring AOP 底层原理技术分享
Spring AOP(面向切面编程)是Spring框架中一个强大的功能,它允许开发者在不修改业务逻辑代码的情况下,增加额外的功能,如日志记录、事务管理等。本文将深入探讨Spring AOP的底层原理,包括其核心概念、实现方式以及如何与Spring框架协同工作。