Java之Spring5:AOP
AOP面向切面编程
- 介绍
- 底层原理
- 有接口情况,使用JDK动态代理
- JDK动态代理具体实现
- 没有接口情况,使用CGLIB动态代理
- AOP的几个术语
- 基于AspectJ实现注解方式的AOP操作
- 准备
- 实现
- 抽取相同的切入点
- 增强类优先级设置
- 基于AspectJ实现XML方式的AOP操作
- 步骤
- 实现
- 完全注解开发
介绍
AOP:也叫做面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。通俗的描述就是:不通过修改源代码方式,在主干功能里面添加新功能。
用一个登录的功能来举例:假如我们原先有一个登录功能,但现在想添加一个权限判断的功能,原始方法需要修改源代码来实现,而现在可以使用AOP来添加一个权限判断模块,达到不修改源代码就能添加新功能的目的:
底层原理
AOP的底层原理是使用了动态代理,总共有两种情况,分为有接口和没有接口:
有接口情况,使用JDK动态代理
有一个接口UserDao,里面有login()方法;有一个UserDaoImpl类实现了UserDao接口并实现了login()方法,那么JDK动态代理就是创建UserDao接口实现类代理对象。
实现方法:
使用java.lang.reflect中的Proxy类,调用其newProxyInstance方法:
方法有三个参数:
第一参数,类加载器
第二参数,增强方法所在的类,这个类实现的接口,支持多个接口
第三参数,实现这个接口InvocationHandler,创建代理对象,写增强的部分
JDK动态代理具体实现
假设有一个接口User:
package com.jackma.spring5;public interface User {public int add(int a, int b);public String update(String id);
}
和它的一个实现类UserDaoImpl:
package com.jackma.spring5;public class UserDaoImpl implements User {@Overridepublic int add(int a, int b) {System.out.println("add方法执行了");return a+b;}@Overridepublic String update(String id) {System.out.println("update方法执行了");return id ;}
}
接下来要创建接口实现类的代理对象UserDaoProxy:
package com.jackma.spring5;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;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("add result:" + result);}
}// 创建代理对象代码
class UserDaoProxy implements InvocationHandler {// 要把创建的是谁的代理对象,要把它传递过来// 通过有参构造传递Object obj;public UserDaoProxy(Object obj){this.obj = obj;}// 增强的逻辑@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("方法增强之前..." + method.getName() + "传递的参数:" + Arrays.toString(args));// 被增强代码执行Object res = method.invoke(obj, args);if (method.getName() == "add"){System.out.println("这是增强add方法的代码");}if (method.getName() == "upDate"){System.out.println("这是增强upDate方法的代码");}System.out.println("方法增强之后..." + obj);return res;}
}
测试结果:
没有接口情况,使用CGLIB动态代理
有一个类User,里面有有login()方法,若按照往常想要在这个方法基础上新增功能,那么就要创建User类的子类,让它重写login()方法,然后写上增强逻辑。而在Spring的AOP中要使用CGLIB动态代理,创建当前子类的代理对象。
AOP的几个术语
- 连接点:类中哪些方法可以被增强,这些方法称为连接点。
- 切入点:实际被增强的方法,称为切入点。
- 通知(增强):实际增强的逻辑部分称为通知(增强),总共有五种通知:
①:前置通知
②:后置通知
③:环绕通知
④:异常通知
⑤:最终通知 - 切面:把通知应用到切入点的过程(比如:把判断权限的通知应用到Login()切入点的过程)。
基于AspectJ实现注解方式的AOP操作
AspectJ不是Spring组成部分,是独立AOP框架,但是一般把AspectJ和Spirng框架一起使用,进行AOP操作。基于AspectJ实现AOP操作有两种方式:基于 xml 配置文件实现和基于注解方式实现(推荐使用)。
准备
引入AOP相关的jar包:
切入点表达式:
作用:知道对哪个类里面的哪个方法进行增强。
语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称] ([参数列表]) )
举例:
其中*表示全部,返回类型可以省略。
实现
创建类User,对该类的方法进行增强:
// 被增强类
package com.jackma.spring5.aopanno;public class User {public void add(){System.out.println("add.....");}
}
创建增强类,编写增强逻辑,在增强类里面,创建方法,让不同方法代表不同通知类型:
package com.jackma.spring5.aopanno;// 增强类
public class UserPorxy {public void before(){ // 前置通知System.out.println("before......");}
}
进行通知的配置:
- 在 spring 配置文件中,开启注解扫描:
<?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.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 开启注解扫描--><context:component-scan base-package="com.jackma.spring5.aopanno"></context:component-scan>
</beans>
使用注解创建User和UserProxy对象:
在增强类上面添加注解 @Aspect:
在 spring 配置文件中开启生成代理对象:
<?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.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 开启注解扫描--><context:component-scan base-package="com.jackma.spring5.aopanno"></context:component-scan>
<!-- 开启生成aspectj代理对象--><aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
- 配置不同类型的通知(在增强类里,在作为通知的方法上添加通知类型注解,使用切入点表达式配置):
package com.jackma.spring5.aopanno;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;// 增强类
@Component
@Aspect
public class UserPorxy {// 前置通知@Before(value = "execution(* com.jackma.spring5.aopanno.User.add(..))")public void before(){System.out.println("before......");}//后置通知(返回通知)@AfterReturning(value = "execution(* com.jackma.spring5.aopanno.User.add(..))")public void afterReturning() {System.out.println("afterReturning.........");}//最终通知@After(value = "execution(* com.jackma.spring5.aopanno.User.add(..))")public void after() {System.out.println("after.........");}//异常通知@AfterThrowing(value = "execution(* com.jackma.spring5.aopanno.User.add(..))")public void afterThrowing() {System.out.println("afterThrowing.........");}//环绕通知@Around(value = "execution(* com.jackma.spring5.aopanno.User.add(..))")public void around(ProceedingJoinPoint proceedingJoinPoint) throwsThrowable {System.out.println("环绕之前.........");//被增强的方法执行proceedingJoinPoint.proceed();System.out.println("环绕之后.........");}
}
测试:
@Testpublic void test1(){ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");User user = context.getBean("user", User.class);user.add();}
若在add()方法中模拟一个异常:
测试结果:
抽取相同的切入点
从当面的增强类可以看到切入点表达式都是相同的,那么我们可以对其进行抽取:
那么就可以把各种通知的注解改成:
增强类优先级设置
若有多个增强类对同一个方法进行增强,可以设置增强类优先级,方法是在增强类上面添加注解@Order(数字类型值),数字类型值越小优先级越高。
多创建一个增强类PersonPorxy:
package com.jackma.spring5.aopanno;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;// 增强类2
@Component
@Aspect
public class PersonPorxy {// 前置通知@Before(value = "execution(* com.jackma.spring5.aopanno.User.add(..))")public void before(){System.out.println("Person before......");}
}
设置优先级,PersonPorxy设置为1,UserPorxy设置为2:
结果:
基于AspectJ实现XML方式的AOP操作
步骤
- 创建两个类,增强类和被增强类,创建方法
- 在 spring 配置文件中创建两个类对象
- 在 spring 配置文件中配置切入点
实现
新建两个类,一个被增强类Book和一个增强类BookProxy:
package com.jackma.spring5.aopxml;// 被增强类
public class Book {public void Buy(){System.out.println("buy......");}
}
package com.jackma.spring5.aopxml;// 增强类
public class BookProxy {public void before(){System.out.println("before......");}
}
配置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: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 https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--创建对象--><bean id="book" class="com.jackma.spring5.aopxml.Book"></bean><bean id="bookproxy" class="com.jackma.spring5.aopxml.BookProxy"></bean><!-- 配置aop的增强--><aop:config>
<!-- 设置切入点--><aop:pointcut id="p" expression="execution(* com.jackma.spring5.aopxml.Book.Buy(..))"/>
<!-- 配置切面--><aop:aspect ref="bookproxy">
<!-- 将通知配置到具体的方法上--><aop:before method="before" pointcut-ref="p"/></aop:aspect></aop:config>
</beans>
测试:
@Testpublic void test2(){ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");Book book = context.getBean("book", Book.class);book.Buy();}
完全注解开发
创建配置类UserConfig:
package com.jackma.spring5;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration
// 开启扫描
@ComponentScan(basePackages = {"com.jackma"})
// 生成代理对象
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class UserConfig {}
测试:
@Testpublic void test3(){// 完全注解ApplicationContext context = new AnnotationConfigApplicationContext(UserConfig.class);User user = context.getBean(User.class);user.add();}
Java之Spring5:AOP相关推荐
- jdk源码分析书籍 pdf_什么?Spring5 AOP 默认使用Cglib?从现象到源码深度分析
推荐阅读: 阿里工作十年拿下P8,多亏了这些PDF陪我成长(Spring全家桶+源码解析+Redis实战等)zhuanlan.zhihu.com 从入门到熟悉,一步一步带你了解 MySQL 中的「索 ...
- 推荐学java——Spring之AOP
tips:本文首发在公众号逆锋起笔 ,本文源代码在公众号回复aop 即可查看. 什么是AOP? AOP (Aspect Orient Programming),直译过来就是 面向切面编程.AOP 是一 ...
- 什么是 Java 中的 AOP(面向切面编程)?如何使用它来实现横切关注点?
AOP(Aspect-oriented programming,面向切面编程),是一种编程思想和技术,旨在将横切关注点和主业务逻辑分离,使得系统更容易扩展和维护.在 Java 中,AOP 主要通过代理 ...
- Java总结:Spring5框架(1)
Spring5框架(1) 一:什么是Spring? Spring框架是由于软件开发的复杂性而创建的.Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情.然而,Spring的用 ...
- Java之Spring5:IOC容器
IOC容器 什么是IOC容器 IOC容器中的Bean 小试牛刀 搭建Spring5环境 写一个HelloWorld IOC底层原理 IOC的Bean管理 基于 xml 方式创建对象 基于 xml 方式 ...
- Java之Spring5:JDBCTemplate
JDBCTemplate 什么是JDBCTemplate 准备工作 添加操作 修改和删除操作 查询操作 返回某个值 返回对象 返回集合 批量添加 批量修改和删除 完整代码 什么是JDBCTemplat ...
- [Spring5]AOP底层原理
AOP底层原理 1.AOP底层使用动态代理 (1)有两种情况动态代理 第一种 有接口的情况,使用JDK动态代理 a.创建接口实现类代理对象,增强类的方法 第二种 没有接口的情况,使用CGLIB动态代理 ...
- java 自定义注解+AOP实现日志记录
ssm版本: 1.首先自定义一个注解,该注解有两个属性,一个是模块名,一个是操作的内容.该注解是用来修饰Service层中的方法的. 2.创建一个切面类,该切面使用@Aspect和@Component ...
- java @around,Spring AOP基于注解的Around通知
是一种建议类型,可确保方法执行前后的通知可以运行. 以下是通知的语法: 语法 @Pointcut("execution(* com.yiibai.Student.getAge(..))&qu ...
最新文章
- java方法重载编程_学java教程之普通方法重载
- Druid(准)实时分析统计数据库——列存储+高效压缩
- qq飞车服务器维护中是什么,《QQ飞车》服务器对赛车平跑稳定性的影响攻略
- 道哥自述:为什么弹性安全网络将诞生最大的人工智能?
- mime设置 压缩html,MIME设置功能
- STM32 应用程序加密的一种设计方案
- emq auth mysql_EMQ X 认证鉴权(一)——基于 MySQL 的 MQTT 连接认证
- 基于Python+Django+MYSQL的校园食堂点餐管理系统
- IMF:央行须变得更像苹果公司以保证央行数字货币处在技术前沿
- linux vim 常用命令
- [FFmpeg] Ubuntu 16.04 安装 FFmpeg
- java jxdatepicker_在Java Swingx中修剪JXDatePicker
- 树莓派安装python3.8_在树莓派(Raspberry Pi)上编译安装更新版本的Python
- Arrays.sort(arr, (a, b) -> a - b)是对数组进行排序
- 换机潮爆发,5G手机+5G超级SIM卡成趋势
- 6410裸机加载linux内核,KG—Tiny6410裸机环境搭建(补充篇)
- 【WebStorm学生认证】如何用学生邮箱进行JetBrains学生认证
- iOS安装脱壳后的ipa
- 日本关西信息中心:LPWAN技术ZETA、LoRaWAN、SIGFOX测评分析
- linux之getcwd函数解析,Linux 中C语言getcwd()函数的用法