Spring学习总结(2)- AOP

一,什么是AOP

AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

在学习AOP时,先要了解什么是代理模式,可以参考: 代理模式

二,使用Spring实现AOP

横切关注点:跨越应用程序多个模块的方法或功能。(软件系统,可以看做由一组关注点即业务或功能或方法组成。其中,直接的业务关注点是直切关注点,而为直切关注点服务的,就是横切关注点。)即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。


切面(ASPECT)


横切关注点被模块化的特殊对象。即,它是一个类。


通知(Advice)


切面必须要完成的工作。即,它是类中的一个方法。


目标(Target)


被通知对象。


代理(Proxy)


向目标对象应用通知之后创建的对象。


切入点(PointCut)


切面通知执行的"地点"的定义。


连接点(JointPoint)


与切入点匹配的执行点。

下面示意图:

SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:

定义通知类,如下:

前置通知BeforeAdvice类:

// 前置通知
public class BeforeAdvice implements MethodBeforeAdvice{
    /**
     *
     * @方法名: before
     * @描述: 前置通知调用方法
     * @param arg0 方法信息
     * @param arg1 参数列表
     * @param arg2 被代理的目标对象
     * @throws Throwable
     * @创建人:Zender
     */
    @Override
    public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
        System.out.println("-----------------前置通知-----------------");
    }
}

后置通知AfterAdvice类:

//后置通知
public class AfterAdvice implements AfterReturningAdvice {
    /**
     *
     * @方法名: afterReturning
     * @描述:后置通知调用的方法
     * @param arg0 返回值
     * @param arg1 被调用的方法
     * @param arg2 方法参数列表
     * @param arg3 被代理对象
     * @throws Throwable
     * @创建人:Zender
     */
    @Override
    public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
        System.out.println("-----------------后置通知-----------------");
    }
}

环绕通知SurroundAdvice类:

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
 
//环绕通知
public class SurroundAdvice implements MethodInterceptor {
    /**
     *
     * @方法名: invoke
     * @描述:环绕通知调用的方法
     * @param arg0 方法信息对象
     * @return
     * @throws Throwable
     * @创建人:Zender
     */
    @Override
    public Object invoke(MethodInvocation arg0) throws Throwable {
        //前置横切逻辑
        System.out.println("方法:" + arg0.getMethod() + " 被调用在对象:" + arg0.getThis() + "上,参数:" + arg0.getArguments());
        //方法调用
        Object ret = arg0.proceed();
        //后置横切逻辑
        System.out.println("返回值:"+ ret);
        return ret;
    }
}

抽象主题角色(subject):

/**
 *
 * @类名称:Subject
 * @类描述:抽象主题角色(subject)
 * @创建人:zender
 */
public interface Subject {
    public String sailBook();
}

具体主题角色(RealSubject):

/**
 *
 * @类名称:RealSubject
 * @类描述:具体主题角色(RealSubject)
 * @创建人:zender
 */
public class RealSubject implements Subject {
    @Override
    public String sailBook() {
        System.out.println("买书:Spring!");
        return "Spring";
    }
} 

DynamicProxy类:

import org.springframework.aop.framework.ProxyFactory;
 
import com.zender.aop.advice.AfterAdvice;
import com.zender.aop.advice.BeforeAdvice;
import com.zender.aop.advice.SurroundAdvice;
 
//获得代理对象
public class DynamicProxy {
    /**
     *
     * @方法名: getProxy
     * @描述: 获得任意的代理对象
     * @param object
     * @return
     * @创建人 Zender
     */
    public static Object getProxy(Object object){
        //实例化Spring代理工厂
        ProxyFactory factory=new ProxyFactory();
        //设置被代理的对象
        factory.setTarget(object);
        //添加通知,横切逻辑
        factory.addAdvice(new BeforeAdvice());
        factory.addAdvice(new AfterAdvice());
        factory.addAdvice(new SurroundAdvice());
        return factory.getProxy();
    }
}

测试类:

import com.zender.aop.DynamicProxy;
import com.zender.aop.RealSubject;
import com.zender.aop.Subject;
 
public class SpringAOPTest {
 
    public static void main(String[] args) {
        //从代理工厂中获得代理对象
        Subject rs = (Subject) DynamicProxy.getProxy(new RealSubject());
        rs.sailBook();
    }
}

结果:

二,使用IOC配置的方式实现AOP

创建IOC的配置文件beans.xml,内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 被代理的目标对象 -->
    <bean id="realSubject" class="com.zender.aop.RealSubject"></bean>
    <!-- 后置通知 -->
    <bean id="afterAdvice" class="com.zender.aop.advice.AfterAdvice"></bean>
    <!-- 前置通知  -->
    <bean id="beforeAdvice" class="com.zender.aop.advice.BeforeAdvice"></bean>
    <!--
    代理对象
    interceptorNames 通知数组
    target 被代理的目标对象
    proxyTargetClass 被代理对象是否为一个类,如果是则使用cglib,否则使用jdk动态代理  -->
    <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="proxyTargetClass" value="true" />
        <property name="target" ref="realSubject" />
        <property name="interceptorNames">
            <list>
                <value>afterAdvice</value>
                <value>beforeAdvice</value>
            </list>
        </property>
    </bean>
</beans>

测试类:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
import com.zender.aop.Subject;
 
public class SpringAOPTest {
 
    public static void main(String[] args) {
        //容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
        Subject s = (Subject) ctx.getBean("proxy");
        s.sailBook();
    }
}

运行结果:

三,使用XML配置Spring AOP切面

这个时候需要引用一个新的jar包:aspectjweaver.jar,该包是AspectJ的组成部分。pom.xml内容如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>Zender</groupId>
    <artifactId>SpringAOP</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.version>4.3.0.RELEASE</spring.version>
    </properties>
    <dependencies>
        <!-- Spring Core -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!-- Spring Context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.9</version>
        </dependency>
    </dependencies>
</project>

定义通知通知类:

//通知类
public class SubjectAdvices {
    //前置通知
    public void before(JoinPoint jp)
    {
        System.out.println("--------------------前置通知--------------------");
        System.out.println("方法名:" + jp.getSignature().getName() + ",参数:" + jp.getArgs().length + ",被代理对象:" + jp.getTarget().getClass().getName());
    }
    //后置通知
    public void after(JoinPoint jp){
        System.out.println("--------------------后置通知--------------------");
    }
    //环绕通知
    public Object around(ProceedingJoinPoint pjd) throws Throwable{
        System.out.println("--------------------环绕通知开始--------------------");
        Object object = pjd.proceed();
        System.out.println("--------------------环绕通知结束--------------------");
        return object;
    }

    //异常后通知
    public void afterThrowing(JoinPoint jp,Exception exp)
    {
        System.out.println("--------------------异常后通知,发生了异常:" + exp.getMessage() + "--------------------");
    }

    //返回结果后通知
    public void afterReturning(JoinPoint joinPoint, Object result)
    {
        System.out.println("结果是:" + result);
    }
}

配置IOC容器依赖的XML文件IOCBeans.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
 
    <!--被代理的目标对象 -->
    <bean id="realSubject" class="com.zender.aop.RealSubject"></bean>
    <!-- 通知类 -->
    <bean id="advice" class="com.zender.aop.advice.SubjectAdvices"></bean>
    <!-- AOP配置 -->
    <!-- proxy-target-class属性表示被代理的类是否为一个没有实现接口的类,Spring会依据实现了接口则使用JDK内置的动态代理,如果未实现接口则使用cblib -->
    <aop:config proxy-target-class="true">
        <!-- 切面配置 -->
        <!--ref表示通知对象的引用 -->
        <aop:aspect ref="advice">
            <!-- 配置切入点(横切逻辑将注入的精确位置) -->
            <aop:pointcut expression="execution(* com.zender.aop.RealSubject.s*(..))" id="pointcut1"/>
            <!--声明通知,method指定通知类型(即通知类的对应的方法名),pointcut指定切点,就是该通知应该注入那些方法中 -->
            <aop:before method="before" pointcut-ref="pointcut1"/>
            <aop:after method="after" pointcut-ref="pointcut1"/>
            <aop:around method="around" pointcut="execution(* com.zender.aop.RealSubject.s*(..))"/>
            <!-- throwing里面的内容与SubjectAdvices类的afterThrowing的参数名相同 -->
            <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut1" throwing="exp" />
             <!-- returning里面的内容与SubjectAdvices类的afterReturning的参数名相同 -->
            <aop:after-returning method="afterReturning" pointcut-ref="pointcut1" returning="result" />
        </aop:aspect>
    </aop:config>
</beans>

aop:after-throwing需要指定通知中参数的名称throwing="exp",则方法中定义应该是这样:afterThrowing(JoinPoint jp,Exception exp);aop:after-returning同样需要设置returning指定方法参数的名称。

测试代码:

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
import com.zender.aop.Subject;
 
public class SpringAOPTest {
 
    public static void main(String[] args) {
        //容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("IOCBeans.xml");
        Subject s = (Subject) ctx.getBean("realSubject");
        s.sailBook();
    }
}

运行结果如下:

四,使用注解配置Spring AOP切面

1,基于上一个示例,修改被代理的类RealSubject,为了实现IOC扫描在RealSubject类上注解了@Service并命名bean为realSubject。相当于上一个示例中在xml配置文件中增加了一个bean:

<!--被代理的目标对象 -->

<bean id="realSubject" class="com.zender.aop.RealSubject"></bean>

修改后的RealSubject类的代码如下:

/**
 *
 * @类名称:RealSubject
 * @类描述:具体主题角色(RealSubject)
 * @创建人:zender
 */
@Service("realSubject")
public class RealSubject implements Subject {
    @Override
    public String sailBook() {
        System.out.println("买书:Spring!");
        return "Spring";
    }
}

2,修改通知类SubjectAdvices,如下:

//通知类
@Component
@Aspect
public class SubjectAdvices {
    //前置通知
    @Before("execution(* com.zender.aop.RealSubject.s*(..))")
    public void before(JoinPoint jp)
    {
        System.out.println("--------------------前置通知--------------------");
        System.out.println("方法名:" + jp.getSignature().getName() + ",参数:" + jp.getArgs().length + ",被代理对象:" + jp.getTarget().getClass().getName());
    }
    //后置通知
    @After("execution(* com.zender.aop.RealSubject.s*(..))")
    public void after(JoinPoint jp){
        System.out.println("--------------------后置通知--------------------");
    }
    //环绕通知
    @Around("execution(* com.zender.aop.RealSubject.s*(..))")
    public Object around(ProceedingJoinPoint pjd) throws Throwable{
        System.out.println("--------------------环绕通知开始--------------------");
        Object object = pjd.proceed();
        System.out.println("--------------------环绕通知结束--------------------");
        return object;
    }

    //异常后通知
    @AfterThrowing(pointcut="execution(* com.zender.aop.RealSubject.s*(..))",throwing="exp")
    public void afterThrowing(JoinPoint jp,Exception exp)
    {
        System.out.println("--------------------异常后通知,发生了异常:" + exp.getMessage() + "--------------------");
    }

    //返回结果后通知
    @AfterReturning(pointcut="execution(* com.zender.aop.RealSubject.s*(..))",returning="result")
    public void afterReturning(JoinPoint joinPoint, Object result)
    {
        System.out.println("结果是:" + result);
    }
}

@Component表示该类的实例会被Spring IOC容器管理;@Aspect表示声明一个切面;@Before表示before为前置通知,通过参数execution声明一个切点

3,新增配置文件IOCBeans2.xml,如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
 
    <context:component-scan base-package="com.zender.aop" />
    <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>

在配置IOC的基础上增加了aop:aspectj-autoproxy节点,Spring框架会自动为与AspectJ切面配置的Bean创建代理,属性proxy-target-class="true"表示被代理的目标对象是一个类,而非实现了接口的类。

4,测试代码如下:

public class SpringAOPTest {
 
    public static void main(String[] args) {
        //容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("IOCBeans2.xml");
        RealSubject s = ctx.getBean("realSubject" , RealSubject.class);
        s.sailBook();
    }
}

5,运行结果:

时间: 2024-10-19 08:21:05

Spring学习总结(2)- AOP的相关文章

Spring学习篇:AOP知识整理

AOP知识整理 AOP(Aspect-Oriented Programming):面向切面的编程.OOP(Object-Oriented Programming)面向对象的编程.对于OOP我们已经再熟悉不过了,对于AOP,可能我们会觉得是一种新特性,其实AOP是对OOP的一种补充,OOP面向的是纵向编程,继承.封装.多态是其三大特性,而AOP是面向横向的编程. 面向切面编程(AOP)通过提供另外一种思考程序结构的途经来弥补面向对象编程(OOP)的不足.在OOP中模块化的关键单元是类(classe

Spring学习六、AOP与整合Mybatis

十一.AOP AOP(Aspect Oriented Programming) 意为:面向切面编程 是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术. AOP是OOP的一种延续,是软件开发的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型. 利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各个部分之间的耦合度降低,提高程序的可重用性,同时提高开发效率. AOP的作用及优势 作用 程序运行期间,不修改源码对已有方法进行增强 优势 减少重复代码

spring 学习之二 AOP编程

AOP概念 AOP, aspect oriented programing,翻译过来就是面向切面编程的意思,那什么叫面向切面编程呢?相对于之前传统的纵向继承方式来对原有功能进行功能扩展, 面向切面编程的好处就是不需要增加源代码就可以进行功能扩展. AOP术语 --joinpint(连接点): 指那些被拦截到的点,在spring中,这些点指的是方法 --pointcut(切入点) : 指要对哪些连接点进行拦截定义,即对哪些方法做扩展处理 --advice(通知/增强) : 通知是指拦截到joinp

spring学习 十三 注解AOP

spring 不会自动去寻找注解,必须告诉 spring 哪些包下的类中可能有注解,也就是要开启注解扫描,注解的包是spring-context.jar,所以在配置文件中还要引入context约束,也就是引入 xmlns:context命名空间 第一步开始注解,base-package的属性值指定哪些包下可能有注解,多个包用逗号隔开,也可以采用通配符进行配置 <!-- 开启注解 --> <context:component-scan base-package="com.airp

spring学习笔记(19)mysql读写分离后端AOP控制实例

在这里,我们接上一篇文章,利用JNDI访问应用服务器配置的两个数据源来模拟同时操作不同的数据库如同时操作mysql和oracle等.实际上,上个例子可能用来模拟mysql数据库主从配置读写分离更贴切些.既然如此,在本例中,我们就完成读写分离的模拟在web端的配置实例. 续上次的例子,关于JNDI数据源的配置和spring datasource的配置这里不再重复.下面着重加入AOP实现DAO层动态分库调用.可先看上篇文章<spring学习笔记(18)使用JNDI模拟访问应用服务器多数据源实例 >

Spring学习之第一个AOP程序

IOC和AOP是Spring的两大基石,AOP(面向方面编程),也可称为面向切面编程,是一种编程范式,提供从另一个角度来考虑程序结构从而完善面向对象编程(OOP). 在进行 OOP 开发时,都是基于对组件(比如类)进行开发,然后对组件进行组合,OOP 最大问题就是无法解耦组件进行开发,比如我们上边举例,而 AOP 就是为了克服这个问题而出现的,它来进行这种耦合的分离.AOP 为开发者提供一种进行横切关注点(比如日志关注点)分离并织入的机制,把横切关注点分离,然后通过某种技术织入到系统中,从而无耦

Spring学习总结(三)——Spring实现AOP的多种方式

AOP(Aspect Oriented Programming)面向切面编程,通过预编译方式和运行期动态代理实现程序功能的横向多模块统一控制的一种技术.AOP是OOP的补充,是Spring框架中的一个重要内容.利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率.AOP可以分为静态织入与动态织入,静态织入即在编译前将需织入内容写入目标模块中,这样成本非常高.动态织入则不需要改变目标模块.Spring框架实现了AOP,使用注解

Spring入门IOC和AOP学习笔记

Spring入门IOC和AOP学习笔记 概述 Spring框架的核心有两个: Spring容器作为超级大工厂,负责管理.创建所有的Java对象,这些Java对象被称为Bean. Spring容器管理容器中Bean之间的依赖关系,使用一种叫做"依赖注入"的方式来管理bean之间的依赖关系. Spring有两个核心接口:BeanFactory和ApplicationContext,ApplicationContext是BeanFactory的子接口.它们都可以代表Spring容器,Spri

spring学习(二) ———— AOP之AspectJ框架的使用

前面讲解了spring的特性之一,IOC(控制反转),因为有了IOC,所以我们都不需要自己new对象了,想要什么,spring就给什么.而今天要学习spring的第二个重点,AOP.一篇讲解不完,所以这篇文章主要介绍一下什么是AOP,如何去理解AOP.理解完之后,在spring中如何使用AspectJ AOP框架的.看得懂,写的出spring配置的那么就学的差不多了.加油.建议都自己手动实现一遍,这样才能更好的理解. --WH 一.什么是AOP? AOP:面向切面编程,采用横向抽取机制,取代了传

spring学习5:基于注解实现spring的aop

目录 spring学习5:基于注解实现spring的aop 一.基于注解+xml实现 1.1 在配置文件中开启spring对注解aop的支持 1.2 把通知类用注解配置到容器中,并用注解声明为切面 1.3 定义切入点表达式 1.4 定义通知 二.基于纯注解实现 三.多个aop的执行顺序 1.xml配置 2.注解配置 3.注意 spring学习5:基于注解实现spring的aop 上一节学习了spring aop的基本概念和如何基于xml配置来实现aop功能.这一节来学习下如何用注解实现aop 一