动态代理到AOP
动态代理
代理(proxy)是一种设计模式,通过了目标对象的另外访问方法,即通过代理对象访问目标对象.动态代理是再程序运行时动态地生成一个代理类代替原本的类.该类会拦截对目标对象的方法调用
为什么使用动态代理
- 动态代理可以帮我们减少许多冗余的代码,当你必须在类中做相同的事时,如日志记录,权限校验.若不想在每个方法里重复写这些代码,动态代理 就是帮你实现这一目标的工具,它通过拦截方法调用,在方法执行前后做一些额外的事情比如记录日志,验证权限
动态代理的实现
动态代理分别分为:JDK动态代理和CGLIB动态代理
JDK动态代理
- 适用于代理接口类型的对象
//业务接口
public interface UserService {
void addUser();
}
//实现类
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
System.out.println("添加用户业务逻辑...");
}
}
public class LoggingInvocationHandler implements InvocationHandler {
private Object target;
//封装一个绑定方法获取
public Object newProxyInsatance(Object target) {
this.target = target;
//Proxy.newProxyInstance()方法用于在运行时动态生成代理类和实例
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),//被代理类类加载器
target.getClass().getInterfaces(),//被代理类需要实现的接口列表
this//方法调用处理器
);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法执行之前执行" + method.getName());
Object result = method.invoke(target, args);
System.out.println("方法执行之后执行" + method.getName());
return result;
}
}
public class Test01 {
public static void main(String[] args) {
LoggingInvocationHandler handler = new LoggingInvocationHandler();
UserService userService = new UserServiceImpl();
UserService insatance = (UserService) handler.newProxyInsatance(userService);
insatance.addUser();
}
}
//output
//方法执行之前执行addUser
//添加用户业务逻辑...
//方法执行之后执行addUser
Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)这是JDK动态代理和核心方法,用于在运行时动态生成代理类和实例,其需要传入三个参数,分别为
ClassLoader loader被代理类的类加载器,Class<?>[] interfaces代理类需要实现的接口列表,InvocationHandler h,方法调用处理器
CGLIB动态代理
CGLIB是通过生成目标类的子类来实现代理,Spring在目标类没有实现的接口时,就会采用CGLIB动态代理,CGLIB生成的代理类是目标类的子类,并覆盖目标类方法,因此其能代理没有接口的类
导入CGLIB依赖
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
一个没有接口的UserService
public class UserService {
public void addUser() {
System.out.println("添加逻辑...");
}
}
//实现MethodInterceptor类
public class LoggingMethodInterceptor implements MethodInterceptor {
private Object target;
public Object newProxyInstance(Object target) {
this.target = target;
//创建CGLIB字节增强对象,用于生成目标类的子类代理
Enhancer enhancer = new Enhancer();
//设置代理类的父类
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
methodProxy.invokeSuper(o, objects);
return null;
}
}
public class Test {
public static void main(String[] args) {
UserService userService = new UserService();
LoggingMethodInterceptor interceptor = new LoggingMethodInterceptor();
UserService instance = (UserService) interceptor.newProxyInstance(userService);
instance.addUser();
}
}
AOP(Aspect Oriented Programming)
动态代理是AOP的底层实现,其作用是把横切关注点(如日志,事务)从核心业务逻辑中分离出,形成一个独立的模块.这就可以做到在不修改源代码的情况下,给程序动态统一地添加功能
作用:减少代码冗余,提高开发效率,易于维护
进一步理解
在Spring中,AOP基于动态代理实现,其会为目标对象创建一个代理对象,在目标方法执行之前,后,或异常时执行你的配置的增强
- 如图:原有四个接口要验证参数,日志.我们发现这些代码太冗余.比较繁琐.使用切面后将验证参数,日志等功能提取出来,四个接口我们只需要通过配置就可以直接调用对应功能,让我们更加专注于实际业务逻辑

AOP 的关键术语
- 目标对象(Target):
- 目标对象指的是业务逻辑的实现类,这些实现类是切面增强的对象.
- 作用:目标对象是我们通常定义的业务类,切面提供了切点的功能如(日志记录,业务管理等等)
- 假设有一个
UserService业务类,其包含了一个addUser()方法.UserService类就是目标对象,AOP会在addUser()中增强功能
- 织入(Weaving):
- 织入是将切面(通知和切入点)用于到目标对象的过程.
- 作用:织入的过程是把定义好的增强逻辑插入到目标对象中去.这是AOP实现的关键步骤,Spring AOP通过动态代理实现
- 切面(Aspect):
- 切面是对横切关注点的模块化,其包含了切入点和通知,切面提取出业务中一些相同的功能进行统一管理
- 作用:切面将横切关注点上的功能封装在一起,方便重用.一个切面可以有多个增强方法(通知),这些通知可以应用到目标对象的多个方法上
- 例如一个
LogAspect就可以包含多个通知,用于不同的方法前执行插入日志记录功能等等
- 通知(Advice):
- 通知是指在切点执行时,执行增强的代码,通知可以在方法执行的不同阶段执行,包括方法执行前,执行后,围绕方法执行的前后
- 常见分为
- 前置通知(Before):在目标方法执行之前执行
- 后置通知(After):在目标方法执行之后执行,无论是否抛出异常
- 后置返回通知(AfterReturning):在目标方法执行之后执行,有异常时不执行
- 后置异常通知(AfterThrowing):在目标方法抛出异常时执行
- 环绕通知(Around):最为常用的通知,在目标方法执行前后执行
- 切入点(PointCut):
- 切入点指通知应用的具体位置条件,其决定了哪些连接点可以被增强,通常使用切入点达式来定义
- 作用:切入点是AOP的核心,其决定了哪些方法可以被增强.通过切入点表达式,开发者可以灵活选择要增强的方法
- 通过
execution(* com.company.service.UserService.*(..))切入点表达式,可以表示UserService类中的所有方法都被增强,下面的内容会展开讲解
- 连接点(JoinPoint):
- 连接点是程序执行的一个具体位置,其指的是可以被切面拦截的方法.连接点是AOP的关键概念.所有的通知都会在某个连接点处执行.在Spring AOP中通常指的是方法调用
- 作用:连接点定义了切面可以织入的程序位置,通常是目标方法执行的位置.在 Spring AOP中,连接点仅限于方法的执行.
AOP的使用
导入依赖
- 对于普通的Spring项目导入的AOP依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
- 对于SpringBoot项目导入的AOP依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
AOP的实现分为XML文件配置和注解配置,XML文件配置都是老掉骨头的东西我们就不学了,注解配置避免了繁琐的XM 配置,使得开发更加灵活,易于理解.我们着重对注解配置进行掌握即可
使用注解实现AOP
配置类
- 配置类是用于启动Spring AOP的注解和扫描指定位置的包.我们创建一个
AppConfig类,使用@Comfiguration,@ComponentScan和EnableAspectAutoProxy来启动Spring AOP
@Configuration
@ComponentScan({"com.mashang.service", "com.mashang.aspect"})
@EnableAspectJAutoProxy
public class APPConfig {
}
@Comfiguration:指明当前类为一个配置类,类似于XML配置文件@EnableAspectAutoProxy:启用Spring AOP注解支持,允许在上下文中应用@Aspect注解标记切面@ComponentScan({"com.mashang.service", "com.mashang.aspect"}):扫描指定包中的组件
- 配置类是用于启动Spring AOP的注解和扫描指定位置的包.我们创建一个
切面类
- 接着创建切面类,使用
@Aspect注解来标记该类是切面类,切面类中可以通过注解标记不同的增强方法(如@Before,@After,@Around)
@Component
//配置切面类得注解
@Aspect
@Order(1)
public class LogAspect { //定义切点,拦截service包下所有方法
@Pointcut("execution(* com.mashang.service.*.*(..))")
public void serviceMethod() { } //前置通知
@Before("serviceMethod()")
public void before() {
System.out.println("[前置通知] 方法执行");
} //环绕通知,通常用于记录性能耗时
@Around("serviceMethod()")
public Object around(ProceedingJoinPoint pjp) throws Throwable { Long startTime = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) { }
System.out.println("[环绕通知]开始执行方法 " + pjp.getSignature().getName()); Object proceed = pjp.proceed(); Long endTime = System.currentTimeMillis();
System.out.println("方法耗时: " + (endTime - startTime) + "ms"); return proceed;
} //后置通知
@After("serviceMethod()")
public void after() {
System.out.println("[后置通知] 方法执行");
} //异常通知
@AfterThrowing(pointcut = "serviceMethod()", throwing = "e")
public void afterThrowing(Exception e) {
System.out.println("[异常通知]" + e.getMessage());
}
}@Aspect表示该类为切面类@Order(int):控制切面类的顺序@Component标记交为Spring容器管理,允许注入容器中@Before,@After,@Around,@AroundThrowing表明不同类型的通知- 切入点表达式
切入点表达式是AOP的核心语法,用于定义哪些方法需要被增强,与
@PointCut注解一同使用核心语法结构
execution([修饰符] 返回类型 [包名.类名.方法名(参数列表) [throws 异常类型]])
- 可以使用通配符
*表示匹配任意字符除了...表示任意子包或任意参数
- 如匹配com.mashang.service下包的所有方法
@PointCut("excution(* com.mashang.service.*.*(..))")
- 可以使用通配符
- 接着创建切面类,使用
测试
public class Test {
public static void main(String[] args) {
//加载Spring配置
AnnotationConfigApplicationContext cxt
= new AnnotationConfigApplicationContext(APPConfig.class); //获取Userservice实例
UserService userService = cxt.getBean(UserService.class); System.out.println("=======测试正常方法=========");
userService.getUser("张三"); System.out.println("=======测试异常方法========="); try {
userService.throwException();
} catch (Exception e) {
e.printStackTrace();
} cxt.close();
}
}
动态代理到AOP的更多相关文章
- 动态代理到基于动态代理的AOP
动态代理,是java支持的一种程序设计方法. 动态代理实现中有两个重要的接口和类,分别是InvocationHandler(interface),Proxy(class). 要实现动态代理,必须要定义 ...
- 动态代理实现AOP【转】
http://blog.csdn.net/beijiguangyong/article/details/8624016 根据前面介绍的Proxy和InvocationHandler,实在很难看出这种动 ...
- .Net 动态代理,AOP
.Net 动态代理,AOP 直接上代码了. /***************************************** * author:jinshuai * * E-mail:redfox ...
- 动态代理的两种方式,以及区别(静态代理、JDK与CGLIB动态代理、AOP+IoC)
Spring学习总结(二)——静态代理.JDK与CGLIB动态代理.AOP+IoC 目录 一.为什么需要代理模式 二.静态代理 三.动态代理,使用JDK内置的Proxy实现 四.动态代理,使用cg ...
- Java动态代理-->Spring AOP
引述要学习Spring框架的技术内幕,必须事先掌握一些基本的Java知识,正所谓“登高必自卑,涉远必自迩”.以下几项Java知识和Spring框架息息相关,不可不学(我将通过一个系列分别介绍这些Jav ...
- .Net 框架实现AOP(动态代理实现AOP,本文为翻译)
在上一节,我们将静态实现AOP,但是对于一个大型项目,要想为每个类,每个方法都去实现AOP ,进行日志记录和权限验证似乎是不可能的. 即使可能对于成百上千个类维护,也是很难维护.所以今天的主题就是如标 ...
- Java使用动态代理实现AOP
参考资料: http://www.importnew.com/15420.htmlhttp://www.cnblogs.com/techyc/p/3455950.html Spring是借助了动态代理 ...
- DispatchProxy实现动态代理及AOP
DispatchProxy类是DotnetCore下的动态代理的类,源码地址:Github,官方文档:MSDN.主要是Activator以及AssemblyBuilder来实现的(请看源码分析),园子 ...
- Java 动态代理与AOP
动态代理与AOP 代理模式 代理模式给某一个目标对象(target)提供代理对象(proxy),并由代理对象控制对target对象的引用. 模式图: 代理模式中的角色有: 抽象对象角色(Abstrac ...
- Java 动态代理及AOP实现机制
AOP实现机制http://www.iteye.com/topic/1116696 AOP: (Aspect Oriented Programming) 面向切面编程AOP包括切面(aspect).通 ...
随机推荐
- MySql 9 in Docker 利用克隆插件搭建主从
环境说明 Docker Windows 11 MySql 9.1.0 搭建步骤 1. 准备主库 准备一个主库的配置文件 master.cnf [mysqld] server-id=1 log-bin= ...
- Fiddler模拟网络超时
前情 最近在优化接口请求错误的报错提示,希望尽可能的能从提示语知道当前错误大致原因,于是我需要模拟各种错误请求的状况. 问题 网络超时是很常见的接口请求错误情况,在没有服务端配合的情况下,我需要怎样来 ...
- 你应该了解的hooks式接口编程 - useSWR
什么是 useSWR ? 听名字我们都知道是一个 React 的 hooks,SWR 是stale-while-revalidate的缩写, stale 的意思是陈旧的, revalidate 的意思 ...
- 在 .NET 环境下访问 SOAP 服务
在 .NET 环境下访问 SOAP 服务 SOAP 服务有着悠久的历史,目前仍然存在大量的 SOAP 服务,它是基于 HTTP 协议和 XML 技术的简单对象访问协议. 在 .NET Framewor ...
- The Financial-Grade Digital Infrastructure
01 Product Introduction The Financial-Grade Digital Infrastructure is a digitally-enabled foun ...
- 大咖论道|金融AI下一阶段的发展思考
回顾过去十年,人工智能(AI)技术的发展速度让人惊叹,金融行业是现今AI应用最具潜力和最为活跃的领域之一.通过多年渗透,AI不间断从技术驱动迈向场景驱动,已广泛与金融业务深度融合,衍生出众多新业态.新 ...
- debian 安装后需做的几件事
debian 安装后需做的几件事 安装环境:X86 >> Debian 9 Linux/GNU apt源更新 注意连上有线网络刚安装好的debian系统中,/etc/apt/sources ...
- Qt音视频开发2-vlc回调处理
一.前言 用句柄来显示视频,方便是很方便,但是有个缺点就是不能拿到实时视频的每张图片的数据,这个就比较讨厌,大部分的监控行业的应用,除了截图以外,很可能需要拿到图片自己做一些处理的,而且拿到图片自己绘 ...
- 网络编程入门从未如此简单(三):什么是IPv6?漫画式图文,一篇即懂!
本文由小枣君分享,文案:小枣君.漫画:杨洋,来自鲜枣课堂,有少许改动,原文链接见文末. 1.引言 网络编程能力对于即时通讯技术开发者来说是基本功,而计算机网络又是网络编程的理论根基,因而深刻准确地理解 ...
- Centos-基础配置
切换下载源到阿里云 备份 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup 下载新的 Cent ...