Spring框架学习之AOP详解

一、概念

1.面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

2.通俗描述:不通过修改源代码方式,在主干功能里面添加新功能

二、底层原理:动态代理

有两种情况动态代理

2.1 有接口, JDK 动态代理

1.被代理的对象

public class UserDaoImpl implements UserDao {

    @Override
    public int add(int a, int b) {
        System.out.println("add执行");
        return a + b;
    }

    @Override
    public String update(String id) {
        System.out.println("update执行");
        return id;
    }

}

2.代理

package cn.zj.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

import cn.zj.dao.UserDao;
import cn.zj.dao.impl.UserDaoImpl;

public class JDKProxy {

    public static void main(String[] args) {
        // 创建接口实现类代理对象
        Class[] interfaces = { UserDao.class };
        UserDaoImpl userDao = new UserDaoImpl();
        UserDao dao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces,
                new UserDaoProxy(userDao));

        int result = dao.add(1, 2);
        System. out .println( "result:"+result);
    }
}

//创建代理对象代码
class UserDaoProxy implements InvocationHandler {

    private Object target;// 目标类

    public UserDaoProxy(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 方法执行之前
        System.out.println("方法执行之前.." + method.getName() + ";传递的参数:" + Arrays.toString(args));
        // 增强方法
        Object res = method.invoke(target, args);
        // 方法执行之后
        System.out.println("方法执行之后.." + target);
        return res;
    }

}

3.打印

方法执行之前..add;传递的参数:[1, 2]
add执行
方法执行之后..cn.zj.dao.impl.UserDaoImpl@6c629d6e
result:3

2.2 无接口, CGLIB 动态代理

1.被代理的对象

public class Cat {

    public void eat() {
        System.out.println("猫吃鱼");
    }
}

2.代理

public class CglibProxy {

    public static void main(String[] args) {
        Cat c = new CglibProxy().createProxyObject(Cat.class);
        c.eat();
    }

    // JDK代理是对对象做代理,cglib代理是对类做代理
    public Cat createProxyObject(Class clazz) {
        // 1.创建内存中的动态类 Enhance
        // 内存中造出一个没有名称的动态类
        Enhancer enhancer = new Enhancer();
        // 2.现在的类最终完成原始类的功能,同时对其进行功能的增强,必须先具有原始类对应的功能————继承
        enhancer.setSuperclass(clazz);
        // 3.进行功能的增强
        // 设置了方法的调用拦截
        // 设置具体的回调操作
        Callback callback = new MethodInterceptor() {
            // proxy:代理对象
            // method:被拦截的方法对象
            // args:调用参数
            // methodProxy:
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
                    throws Throwable {
                // 做增强
                System.out.println("执行前");
                // Object obj=method.invoke(proxy, args);//这种执行是代理类的方法,而不是目标类的,会造成内存溢出
                // 以下的两种都是执行目标类的方法
                // Object obj = methodProxy.invokeSuper(proxy, args);//执行目标类的方法
                // Object obj=method.invoke(proxy, args);
                Object res = methodProxy.invokeSuper(proxy, args);
                System.out.println("执行后");
                return res;
            }
        };
        enhancer.setCallback(callback);
        // 4.创建内存中的全新的类的对象
        Object proxyObj = enhancer.create();
        return (Cat) proxyObj;
    }

}

三、术语

1.连接点(Joinpoint)

类中的任意方法的运行时表示,可以简单理解为类中的方法,也就是可以被增强的方法

2.切入点(Pointcut)

具有共性功能的方法的运行时表示,可以简单理解为具有共性功能的方法,也就是实际被增强的方法

注意:切入点对应的是被挖去了共性功能后的方法执行时匹配断言(格式)

3.通知/增强(Advice)

共性功能模块化,可以简单理解为将共性功能抽取出来制作成独立的方法,实际增强的逻辑部分

类型:前置,后置,异常,最终,环绕

4.切面(Aspect)

切入点与通知的对应关系,可以简单理解为被抽取的共性功能与共性功能被抽取位置对应的方法之间的关系,把通知应用到切入点的过程

5.目标对象(Target Object)

包含切入点的运行时对象,开发阶段制作的是目标对象对应的类

6.AOP代理(AOP Proxy)

使用AOP的代理机制创建目标对象运行时代理对象,完成原始的功能

注意:原始目标对象已经被挖去了共性功能,此时使用目标对象执行任务无法完成原始任务,使用AOP代理机制,创建一个代理对象来完成原始目标对象的功能

7.织入(Weaving)

是一个将通知功能加入原始字节码的动态过程,将增强添加对目标类具体连接点上的过程

Spring使用的是运行时织入机制

8.引入(Introduction)

一种特殊的机制,可以为一个类的字节码动态的加入变量或方法

四、操作

4.1 Spring 框架一般都是基于 AspectJ 实现 AOP

AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使

用,进行 AOP 操作

4.2 实践

1.jar包引入

<dependencies>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.3.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.3.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.6</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-expression -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>5.3.6</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.3.6</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.6</version>
        </dependency>

        <!-- @Resource注解需要 -->
        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <version>1.3.1</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.3.6</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/net.sourceforge.cglib/com.springsource.net.sf.cglib -->
        <dependency>
            <groupId>net.sourceforge.cglib</groupId>
            <artifactId>com.springsource.net.sf.cglib</artifactId>
            <version>2.2.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.aopalliance/com.springsource.org.aopalliance -->
        <dependency>
            <groupId>org.aopalliance</groupId>
            <artifactId>com.springsource.org.aopalliance</artifactId>
            <version>1.0.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.aspectj/com.springsource.org.aspectj.weaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>com.springsource.org.aspectj.weaver</artifactId>
            <version>1.6.8.RELEASE</version>
        </dependency>

    </dependencies>

2.切入点表达式

作用:知道对哪个类里面的哪个方法进行增强

语法结构: execution([权限修饰符] [返回类型] [类全路径] 方法名称 )

举例 1:对 com.zj.dao.BookDao 类里面的 add 进行增强

execution(* com.zj.dao.BookDao.add(…))

举例 2:对 com.zj.dao.BookDao 类里面的所有的方法进行增强

execution(* com.zj.dao.BookDao.* (…))

举例 3:对 com.zj.dao 包里面所有类,类里面所有方法进行增强

execution(* com.zj.dao.. (…))

3.xml方式

1.创建类

package cn.zj.aop.xml;

public class Book {
    public void buy() {
        System.out.println("buy.............");
    }
}

2.增强类

package cn.zj.aop.xml;

public class BookProxy {
    public void before() {
        System.out.println("before.........");
    }
}

3.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:context="http://www.springframework.org/schema/context"
       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/context http://www.springframework.org/schema/context/spring-context.xsd
                        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--创建对象-->
    <bean id="book" class="cn.zj.aop.xml.Book"></bean>
    <bean id="bookProxy" class="cn.zj.aop.xml.BookProxy"></bean>

    <!--配置aop增强-->
    <aop:config>
        <!--切入点-->
        <aop:pointcut id="p" expression="execution(* cn.zj.aop.xml.Book.buy(..))"/>
        <!--配置切面-->
        <aop:aspect ref="bookProxy">
            <!--增强作用在具体的方法上-->
            <aop:before method="before" pointcut-ref="p"/>
        </aop:aspect>
    </aop:config>
</beans>

4.注解方式

1.创建类

public class User {
    public void add() {
        System.out.println("add.......");
    }

}

2.创建增强类

package cn.zj.aop.an;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
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.stereotype.Component;

//增强的类
@Component
@Aspect  //生成代理对象

public class UserProxy {

    //前置通知
    @Before(value = "execution(* cn.zj.aop.an.User.add(..))")
    public void before() {
        System.out.println("before.........");
    }

    //后置通知(返回通知)
    @AfterReturning(value = "execution(* cn.zj.aop.an.User.add(..))")
    public void afterReturning() {
        System.out.println("afterReturning.........");
    }

    //最终通知
    @After(value = "execution(* cn.zj.aop.an.User.add(..))")
    public void after() {
        System.out.println("after.........");
    }

    //异常通知
    @AfterThrowing(value = "execution(* cn.zj.aop.an.User.add(..))")
    public void afterThrowing() {
        System.out.println("afterThrowing.........");
    }

    //环绕通知
    @Around(value = "execution(* cn.zj.aop.an.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕之前.........");

        //被增强的方法执行
        proceedingJoinPoint.proceed();

        System.out.println("环绕之后.........");
    }
}

3.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:context="http://www.springframework.org/schema/context"
       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/context http://www.springframework.org/schema/context/spring-context.xsd
                        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 开启注解扫描 -->
    <context:component-scan base-package="cn.zj.aop.an"></context:component-scan>

    <!-- 开启Aspect生成代理对象-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

4.测试

ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        User user = context.getBean("user", User.class);
        user.add();

5.相同点抽取

//相同切入点抽取
    @Pointcut(value = "execution(* cn.zj.aop.an.User.add(..))")
    public void pointdemo() {

    }

    //前置通知
    //@Before注解表示作为前置通知
    @Before(value = "pointdemo()")
    public void before() {
        System.out.println("before.........");
    }

6.有多个增强类多同一个方法进行增强,设置增强类优先级

在增强类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高

@Component
@Aspect
@Order(1)
public class PersonProxy {

     //后置通知(返回通知)
    @Before(value = "execution(* cn.zj.aop.an.User.add(..))")
    public void afterReturning() {
        System.out.println("Person Before.........");
    }
}
//增强的类
@Component
@Aspect  //生成代理对象
@Order(2)
public class UserProxy {

到此这篇关于Spring框架学习之AOP详解的文章就介绍到这了,更多相关Spring框架AOP内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Spring AOP日志框架实现过程图解

    AOP日志框架实现 JDK动态代理实现日志框架 首先,在项目包com.ay.test 下创建业务接口类BusinessClassService,具体代码如下: BusinessC lassService 业务接口类可以理解为日常开发业务创建的接口类, 接口中有一个简 单的方法doSomeThing .然后,开发业务类的实现类BusinessClassServiceImpl,具体代码如下: 实现类BusinessClassServicelmpl 实现了BusinessClassServ ice 接

  • Java的Spring框架下的AOP编程模式示例

    Spring框架的关键组件是面向方面编程(AOP)框架.面向方面的编程不仅打破程序逻辑分成不同的部分称为所谓的担忧.跨越多个点的应用程序的功能被称为横切关注点和这些横切关注点是从应用程序的业务逻辑概念上区分开来.还有像日志记录,审计,声明性事务,安全性和高速缓存等方面的各种常见的好例子 模块化的OOP中的关键单元是类,而在AOP中模块化的单元则是切面.依赖注入可以帮助你从对方解耦应用程序对象和AOP可以帮助你从他们影响的对象分离横切关注点. AOP是一样的编程语言如Perl,.NET,Java和

  • 举例讲解Java的Spring框架中AOP程序设计方式的使用

    1.什么是AOP AOP是Aspect Oriented Programming的缩写,意思是面向方面编程,AOP实际是GoF设计模式的延续. 2.关于Spring AOP的一些术语:  A.切面(Aspect):在Spring AOP中,切面可以使用通用类或者在普通类中以@Aspect 注解(@AspectJ风格)来实现 B.连接点(Joinpoint):在Spring AOP中一个连接点代表一个方法的执行 C.通知(Advice):在切面的某个特定的连接点(Joinpoint)上执行的动作.

  • Spring AspectJ AOP框架注解原理解析

    什么是AspectJ AspectJ是一个面向切面的框架,它扩展了Java语言.AspectJ定义了AOP语法所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件. AspectJ是一个基于Java语言的AOP框架 Spring2.0以后新增了对AspectJ切点表达式支持 @AspectJ 是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面 新版本Spring框架,建议使用AspectJ方式来开发AOP AspectJ表达式: 语法:exe

  • 在Spring Boot框架中使用AOP的正确姿势

    前言 Spring Boot是基于Spring的用来开发Web应用的框架,功能与Spring MVC有点类似,但是Spring Boot的一大特点就是需要的配置非常少.Spring Boot推荐convention over configuration,也就是约定大于配置,因此Spring Boot会帮你做许多自动的配置,并且Spring Boot使用的是Java Config,几乎可以做到零XML文件配置. 假设现在有这样一种场景,需要统计某个接口的处理耗时,我们可以使用AOP来实现,AOP为

  • Spring框架基于AOP实现简单日志管理步骤解析

    SPringAOP的使用 技术概述,描述这个技术是做什么?学习该技术的原因,技术的难点在哪里. 为了实现项目管理员端的操作数据库日志,便于方便所以利用Spring框架的AOP机制进行实现,项目的难点在于如果设置切入点,如何获取参数. 技术详述,描述你是如何实现和使用该技术的,要求配合代码和流程图详细描述.可以再细分多个点,分开描述各个部分. 在applicationContext.xml中开启AOP代理 <aop:aspectj-autoproxy /> 自定义一个注解 @Target(Ele

  • 实例讲解Java的Spring框架中的AOP实现

    简介 面向切面编程(AOP)提供另外一种角度来思考程序结构,通过这种方式弥补了面向对象编程(OOP)的不足. 除了类(classes)以外,AOP提供了 切面.切面对关注点进行模块化,例如横切多个类型和对象的事务管理. (这些关注点术语通常称作 横切(crosscutting) 关注点.) Spring的一个关键的组件就是 AOP框架. 尽管如此,Spring IoC容器并不依赖于AOP,这意味着你可以自由选择是否使用AOP,AOP提供强大的中间件解决方案,这使得Spring IoC容器更加完善

  • Spring框架实现AOP添加日志记录功能过程详解

    这篇文章主要介绍了Spring框架实现AOP添加日志记录功能过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 需求,在调用业务方法的时候,在被调用的业务方法的前面和后面添加上日志记录功能 整体架构: 日志处理类: package aop; import java.util.Arrays; import org.apache.log4j.Logger; import org.aspectj.lang.JoinPoint; //日志处理类 增

  • Java的Spring框架中AOP项目的一般配置和部署教程

    0.关于AOP 面向切面编程(也叫面向方面编程):Aspect Oriented Programming(AOP),是软件开发中的一个热点,也是Spring框架中的一个重要内容.利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率. AOP是OOP的延续. 主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等. 主要的意图是:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过

  • Spring框架学习之AOP详解

    一.概念 1.面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率. 2.通俗描述:不通过修改源代码方式,在主干功能里面添加新功能 二.底层原理:动态代理 有两种情况动态代理 2.1 有接口, JDK 动态代理 1.被代理的对象 public class UserDaoImpl implements UserDao { @Override public int add(int a, int b) {

  • Java 自定义Spring框架与核心功能详解

    目录 Spring核心功能结构 核心容器 spring-beans和spring-core模块 spring-context模块 spring-context-support模块 spring-context-indexer模块 spring-expression模块 AOP和设备支持 数据访问与集成 Web组件 通信报文 集成测试 bean概述 在上一讲中,我们对Spring的基本使用进行了一个简单的回顾,接下来,我们就来看一下Spring核心功能结构. Spring核心功能结构 Spring

  • Spring框架中@PostConstruct注解详解

    目录 初始化方式一:@PostConstruct注解 初始化方式二:实现InitializingBean接口 补充:@PostConstruct注释规则 总结 初始化方式一:@PostConstruct注解 假设类UserController有个成员变量UserService被@Autowired修饰,那么UserService的注入是在UserController的构造方法之后执行的. 如果想在UserController对象生成时候完成某些初始化操作,而偏偏这些初始化操作又依赖于依赖注入的对

  • Spring框架学习之Cache抽象详解

    目录 1.简介 cache和buffer 2.缓存抽象 3.spring缓存抽象与多进程 官方文档  8.0 Spring为不同缓存做了一层抽象,这里通过阅读文档以及源码会对使用以及原理做一些学习笔记. 1.简介 从3.1版开始,Spring Framework提供了对现有Spring应用程序透明地添加缓存的支持. 与事务支持类似,缓存抽象允许一致地使用各种缓存解决方案,而对代码的影响最小. 从Spring 4.1开始,通过JSR-107注释和更多自定义选项的支持,缓存抽象得到了显着改进. ca

  • Spring AOP详解面向切面编程思想

    目录 1. 什么是 Spring AOP 2. AOP 的组成 2.1 切面 (Aspect) 2.2 切点 (Pointcur) 2.3 连接点 (Join Point) 2.4 通知 (Advice) 3. Spring AOP 的使用 3.1 添加 AOP 框架 3.2 定义切面和切点 3.3 定义通知 (五种) 4. Spring AOP 实现原理 4.1 织入 (Weaving) 4.2 JDK 和 CGLIB 实现的区别 1. 什么是 Spring AOP AOP (Aspect O

  • Java Spring5学习之JdbcTemplate详解

    一.JdbcTemplate Spring 框架对 JDBC 进行封装,使用 JdbcTemplate 方便实现对数据库操作 二.实战 2.1 引入依赖 <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.24</version> </dependency> <!-

  • spring的IoC和DI详解

    这里先来简单介绍下IoC和DI的区别: IOC:翻译过来是控制反转,将对象的创建权由Spring管理,HelloService不需要自己去创建,Spring可以帮你创建. DI:依赖注入,在我们创建对象的过程中,把对象依赖的属性注入到我们的类中. 我们现在编写的类是没有其它的属性的,如果你学过UML这种设计的话,面向对象中对象中的几种关系 简单来书,IoC更像是一种思想,DI是一种行为. 另一种说法是,ioc是目的,di是手段.ioc是指让生成类的方式由传统方式(new)反过来,既程序员不调用n

  • Spring之WEB模块配置详解

    Spring框架七大模块简单介绍 Spring中MVC模块代码详解 Spring的WEB模块用于整合Web框架,例如Struts1.Struts2.JSF等 整合Struts1 继承方式 Spring框架提供了ActionSupport类支持Struts1的Action.继承了ActionSupport后就能获取Spring的BeanFactory,从而获得各种Spring容器内的各种资源 import org.springframework.web.struts.ActionSupport;

  • SpringBoot Aop 详解和多种使用场景解析

    前言 aop面向切面编程,是编程中一个很重要的思想本篇文章主要介绍的是SpringBoot切面Aop的使用和案例 什么是aop AOP(Aspect OrientedProgramming):面向切面编程,面向切面编程(也叫面向方面编程),是目前软件开发中的一个热点,也是Spring框架中的一个重要内容.利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率. 使用场景 利用AOP可以对我们边缘业务进行隔离,降低无关业务逻辑耦

  • Spring Boot 集成MyBatis 教程详解

    Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置.通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者. 在集成MyBatis前,我们先配置一个druid数据源. Spring Boot 系列 1.Spring Boot 入门 2.Spring Boot 属性配置

随机推荐