关于AOP思想,建议你看看这份五年开发总结的笔记,写的太详细了
前言
OOP(Object Oriented Programing)面向对象编程
以对象为基本单位进行程序开发,通过对象间的彼此协同,相互协调,完成程序的构建
POP(Producer Oriented Programing)面向过程(方法,函数)编程
以过程为基本单位的程序开发,通过彼此间协同,相互调用,完成程序的构建
静态代理存在的问题
//实现相同的接口
public class UserServiceProxy implements UserService {
//创建原始对象
private UserServiceImpl userService = new UserServiceImpl();
public void register(User user) {
//实现额外功能
System.out.println("-------------------");
userService.register(user);
}
public boolean login(String name, String password) {
System.out.println("____________________");
return userService.login(name,password);
}
}
每一个原始类都会手工编写一个代理类
- 静态类文件数目过多,不利于项目管理
- 代码可维护性差
概述

上图展示了一个被划分的典型应用,每个模块的核心功能都是为特定业务领域提供服务,但是这些模块都需要类似的辅助功能,例如安全和事务管理。
如果要重用通用功能的话,最常见的面向对象技术是继承或者委托。但是,如果在整个应用中都使用相同的基类,继承往往会导致一个脆弱的对象体系,而使用委托可能需要对委托对象进行复杂的调用。
切面提供了取代继承和委托的另一种可选方案,而且在很多场景下更清晰简洁,在使用面向切面编程时,我们仍然在一个地方定义通用功能,但是可以通过声明的方式定义这个功能要以何种方式在何处应用,而无需修改受影响的类,横切关注点模块化为特殊的类,这些类被称为切面(aspect)。这样做有两个好处:首先,现在每个关注点都集中在一个地方,而不是分散到多处代码;其次,服务模块更简洁,因为它们只包含主要关注点(或者核心功能)的代码,而次要关注点的代码被移转到切面中了。
AOP不可能取代 OOP,它只是OOP的有意补充
Spring中的AOP
AOP:本质上就是 Spring动态代理开发,有益于原始类的维护
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。
如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的,诸如private的方法也是不可以作为切面的。
JDK动态代理的实现
代理创建三要素:1 原始对象 2 额外功能 3 代理对象实现相同的接口
JDK动态代理的核心是InvocationHandler接口和Proxy类。
public static void main(String[] args) {
//创建原始对象
UserService userService = new UserServiceImpl();
//JDK创建动态代理
Proxy.newProxyInstance(ClassLoader ,interfaces, invocationHandler)
}
public interface InvocationHandler {
//用于书写额外功能 额外功能:原始方法执行前后 抛出异常
// 参数:Proxy 忽略掉,表示的是代理对象
//method 额外功能所增加给的那个原始方法
//Object[] args 原始方法的参数
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
interfaces:原始对象所实现的接口
userService.getClass().getInterfaces()
类加载器的作用:
- 通过类加载器把对应类的字节码加载到JVM中
- 通过类加载器创建class对象,进而创建这个类的对象
如何获得类加载器:每个类的.class文件 自动分配与之对应的ClassLoder
在动态代理创建的过程中,需要ClassLoader创建代理类的Class对象,可是动态代理类没有对应的.class文件,JVM也不会为其分配ClassLoader,但是又需要怎么办?(借用一个ClossLoader)
ClassLoader:完成代理类的创建
创建代理类的class对象,进而完成代理类的创建
//注意:类加载器是借用来的 可以随表找一个借用
// 在JDK8.0之前 内部变量访问外部变量需要加final
public class JDKProxy {
public static void main(String[] args) {
final UserService userService = new UserServiceImpl();
InvocationHandler handler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-----login-------");
//原方法运行
Object obj = method.invoke(userService, args);
return obj;
}
};
UserService service = (UserService) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), userService.getClass().getInterfaces(), handler);
service.login("gewei","hello");
service.register(new User());
}
}
CGlib动态代理的实现
对于一些没有实现接口的方法
public class UserServiceImpl(){
login();
register();
}
代理类 继承你要代理的类
public clss Proxy extends UserServiceImpl(){
login(){
额外功能
super.login();
}
}
public class TestCglib {
public static void main(final String[] args) {
//创建原始对象
final UserService userService = new UserService();
/*
通过cglib方式创建动态代理对象
Proxy.newProxyInstance(ClassLoader ,interfaces, invocationHandler)
cglib同样也需要做这些:
enhancer.setClassLoader();
enhancer.setSuperclass();
enhancer.setCallback(); -->MethodInterceptor(cglib包下)
*/
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(TestCglib.class.getClassLoader());
enhancer.setSuperclass(userService.getClass());
MethodInterceptor interceptor = new MethodInterceptor() {
//等同于 InvocationHandler -- invoke
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("hello world");
Object invoke = method.invoke(userService, args);
return invoke;
}
};
enhancer.setCallback(interceptor);
UserService userServiceProxy = (UserService) enhancer.create();
userServiceProxy.login();
userServiceProxy.register();
}
}
Spring中基于AspectJ注解的AOP编程
环境搭建
<!-- 1.配置扫描的基包 -->
<context:component-scan base-package="com.itheima"/>
<bean id="Aspectj" class="cn.gewei.factory.Aspectj"></bean>
//打开注解开发
<aop:aspectj-autoproxy/>
环绕通知
/**
* 次要的业务:切面类
*/
@Component //把这个类加到Spring容器中
@Aspect //这个类是切面类
public class LogAspect {
@Pointcut("execution(* com.itheima.service..*.*(..))") //切面表达式,这是一个空方法,作用:创建切面表达式的
public void pt() {
}
/**
* 环绕通知
*/
@Around("pt()")
public Object around(ProceedingJoinPoint joinPoint) {
Object result = null;
try {
System.out.println("前置通知");
//调用目标方法
result = joinPoint.proceed();
System.out.println("后置通知");
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("异常通知");
} finally {
System.out.println("最终通知");
}
//返回方法的返回值
return result;
}
}
前置,后置,异常,最终通知
/**
* 次要的业务:切面类
*/
@Component //把这个类加到Spring容器中
@Aspect //这个类是切面类
public class LogAspect {
@Pointcut("execution(* com.itheima.service..*.*(..))") //切面表达式,这是一个空方法,作用:创建切面表达式的
public void pt() {
}
@Before("pt()")
public void before() {
System.out.println("前置通知");
}
@AfterReturning("pt()")
public void afterReturn() {
System.out.println("后置通知");
}
@AfterThrowing("pt()")
public void afterThrowing() {
System.out.println("异常通知");
}
@After("pt()")
public void after() {
System.out.println("最终通知");
}
}
切入点复用
@Pointcut("execution(* *(..))")
public void pointcut(){}
@Around(value = "pointcut()")
默认情况下Spring底层是JDK动态代理实现的
//将proxy-target-class设为true就是基于Cglib的开发模式
<aop:aspectj-autoproxy proxy-target-class="true"/>
小结
AOP是面向对象编程的一个强大补充。通过Spring的动态代理,我们现在可以把之前分散在应用各处的行为放入可重用的模块中。我们显示地声明在何处如何应用该行为。这有效减少了代码冗余,并让我们的类关注自身的主要功能。
AOP编程概念(Spring动态代理开发),通过代理类为原始类增加额外的功能,好处:利于原始类的维护。
最后
感谢你看到这里,看完有什么的不懂的可以在评论区问我,觉得文章对你有帮助的话记得给我点个赞,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!
关于AOP思想,建议你看看这份五年开发总结的笔记,写的太详细了的更多相关文章
- 还不懂java类加载机制的,建议看下这份阿里技术官总结的笔记!
类加载机制 把class文件加载到内存,并对数据进行校验,准备,解析,初始化,形成可以被虚拟机直接使用的字节码 类加载的时机(触发类的初始化) 使用new关键字实例化对象 读取一个类的静态代码块 使用 ...
- Unity应用架构设计(12)——AOP思想的实践
想象一下,当程序所有的业务逻辑都完成的时候,你可能还来不及喘口气,紧张的测试即将来临.你的Boss告诉你,虽然程序没问题,但某些方法为什么执行这么慢,性能堪忧.领会了Boss的意图之后,漫长的排查问题 ...
- Spring框架学习之注解配置与AOP思想
上篇我们介绍了Spring中有关高级依赖关系配置的内容,也可以调用任意方法的返回值作为属性注入的值,它解决了Spring配置文件的动态性不足的缺点.而本篇,我们将介绍Spring的又一大核心 ...
- SpringBoot源码解析:AOP思想以及相应的应用
spring中拦截器和过滤器都是基于AOP思想实现的,过滤器只作用于servlet,表现在请求的前后过程中:拦截器属于spring的一个组件,由spring管理, 可以作用于spring任何资源,对象 ...
- Spring框架系列之AOP思想
微信公众号:compassblog 欢迎关注.转发,互相学习,共同进步! 有任何问题,请后台留言联系! 1.AOP概述 (1).什么是 AOP AOP 为 Aspect Oriented Progra ...
- [ SSH框架 ] Spring框架学习之二(Bean的管理和AOP思想)
一.Spring的Bean管理(注解方式) 1.1 什么是注解 要使用注解方式实现Spring的Bean管理,首先要明白什么是注解.通俗地讲,注解就是代码里的特殊标记,使用注解可以完成相应功能. 注解 ...
- 第一节: Timer的定时任务的复习、Quartz.Net的入门使用、Aop思想的体现
一. 前奏-Timer类实现定时任务 在没有引入第三方开源的定时调度框架之前,我们处理一些简单的定时任务同时都是使用Timer类, DotNet中的Timer类有三个,分别位于不同的命名空间下,分别是 ...
- 第四节:MVC中AOP思想的体现(四种过滤器)并结合项目案例说明过滤器的实际用法
一. 简介 MVC中的过滤器可以说是MVC框架中的一种灵魂所在,它是MVC框架中AOP思想的具体体现,所以它以面向切面的形式无侵入式的作用于代码的业务逻辑,与业务逻辑代码分离,一经推出,广受开发者的喜 ...
- spring框架学习(四)AOP思想
什么是AOP 为什么需要AOP 从Spring的角度看,AOP最大的用途就在于提供了事务管理的能力.事务管理就是一个关注点,你的正事就是去访问数据库,而你不想管事务(太烦),所以,Spring在你访问 ...
随机推荐
- Linux文件系统和管理-2文件操作命令(中)
创建空文件和刷新时间 touch touch命令可以用来创建空文件或刷新文件的时间 touch 存在的文件刷新时间,不存在的文件创建空文件 生成指定日期的日志文件 [root@C8-1 ~]# dat ...
- maven 获取pom.xml的依赖---即仓库搜索服务
常用仓库地址: http://repository.sonatype.org/ (https://repository.sonatype.org/)如下图: http://www.mvnrepo ...
- drf (学习第三部)
目录 视图 视图额基类 视图类扩展 GenericAPIView的视图子类 视图集ViewSet 路由Routers 视图 Django REST framework 提供的视图的主要作用: 控制序列 ...
- 5. Bean Validation声明式验证四大级别:字段、属性、容器元素、类
1024,代码改变世界.本文已被 https://www.yourbatman.cn 收录,里面一并有Spring技术栈.MyBatis.JVM.中间件等小而美的专栏供以免费学习.关注公众号[BAT的 ...
- 前端在开发过程中怎么提高网站的seo?
前端开发过程中怎么提高网站的seo? 到写这篇博客为止,我还没有做过需要做seo的项目,也不知道怎么前后端合作(我才毕业工作一年左右,往后会多去学习这方面知识的),通过一些配置来提高网站的seo性能. ...
- 码农会锁,synchronized 对象头结构(mark-word、Klass Pointer)、指针压缩、锁竞争,源码解毒、深度分析!
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 感觉什么都不会,从哪开始呀! 这是最近我总能被问到的问题,也确实是.一个初入编程职场 ...
- 用 Java 训练深度学习模型,原来可以这么简单!
本文适合有 Java 基础的人群 作者:DJL-Keerthan&Lanking HelloGitHub 推出的<讲解开源项目> 系列.这一期是由亚马逊工程师:Keerthan V ...
- 4G DTU比GPRS/3G DTU的优势
4G DTU一般来说是采用电脑和数据线连接来进行参数设置的,为了适应不同的工作环境,提高工作的效率,成都远向电子4G DTU还支持远程参数配置和远程固件升级,只需一部手机即可轻松完成操作.今天我们就来 ...
- python实现银行系统模拟程序
银行系统模拟程序 关注公众号"轻松学编程"了解更多. 1.概述 使用面向对象思想模拟一个简单的银行系统,具备的功能:管理员登录/注销.用户开户.登录.找回密码.挂失.改密.查询 ...
- 神州笔记本电脑【K670D】安装 Ubuntu18.04 系列操作
一.使用U盘安装 Ubuntu 前的处理如下: 进入BIOS将SSD设为启动首选项 -> F10保存退出 -> 用方向键高亮ubuntu启动项 -> 按e键进入编辑状态 -> ...