Java_AOP原理
AOP :
面向切面编程
在程序设计中,我们需要满足低耦合高内聚,所以编程需满足六大原则,一个法则.
AOP面向切面编程正是为了满足这些原则的一种编程思想.
一.装饰者模式:
当我们需要给对象增加功能时,为了满足单一职责原则,可利用装饰者模式编程,创建一个类用来装饰原来的类,这个类写需要在原来的功能上增加的功能.
比如:一个类里面有一个增加图书的功能,
@Service
public class BookSericeImpl implements BookSerice {
@Override
public void addOne(BokBean bokBean) {
System.out.println("执行逻辑:插入一本书");
}
@Override
public void deletOne(Long bookId) {
System.out.println("执行逻辑:删除一本书");
}
}
我们需要在这个基础上新增打印日志的功能,
public class BooklogServiceImpl implements BookSerice {
private BookSerice bookSerice;
public BooklogServiceImpl(BookSerice bookSerice) {
this.bookSerice = bookSerice;
}
@Override
public void addOne(BokBean bokBean) {
System.out.println("准备新增一本书");
this.bookSerice.addOne(bokBean);
System.out.println("新增一本书完成");
}
@Override
public void deletOne(Long bookId) {
System.out.println("准备删除一本书");
this.bookSerice.deletOne(323L);
System.out.println("删除一本书完成");
}
}
下面我们调用这个增强过后的的对象
public void test1(){
//Aop :面向切面编程
//使用装饰者模式设计对象
BookSerice bookSerice = new BookSericeImpl();
//把原来功能的对象通过构造方传给新增功能的类,并把新增功能类的对象赋给原来对象
//这里新增功能类和原来的类都是实现了同一个接口.
bookSerice = new BooklogServiceImpl(bookSerice);
//调用新增功能类的方法,在这个方法里让构造方法传过去的对象调用原来的功能
bookSerice.addOne(new BokBean());
}
这样我们就在不改变原来代码的基础上新增了功能,并且也满足单一职责的原则,降低了代码的耦合性.
但是如果接口里面有很多方法,如果每个方法都需要增加日志功能,这样就会出现很多重复代码,并且装饰者模式不能同时为多个没有关系的类同时增强
所以java引入动态代理技术来增加功能.
二.动态代理
在java里动态代理有两个实现方式:
①针对有接口的类的代理,使用jdk中反射包下的动态代理
②针对没有接口的类的代理,使用第三方的jar包Enhancer
如果一个类既没有接口,又是final,那么不能进行增强
1.第一种实现: reflect包下的Proxy
基于接口的动态代理,使用java内部反射包增强
这种方式创建对象是目标对象的兄弟对象.
同样上面是实现了接口的两个功能的类:
@Service
public class BookSericeImpl implements BookSerice {
@Override
public void addOne(BokBean bokBean) {
System.out.println("执行逻辑:插入一本书");
}
@Override
public void deletOne(Long bookId) {
System.out.println("执行逻辑:删除一本书");
}
}
调用通过对象调用上面两个方法:
public void test2(){
//创建需要代理的对象
BookSerice bookSerice = new BookSericeImpl();
//根据对象的类获取类加载器
ClassLoader classLoader = bookSerice.getClass().getClassLoader();
//获取被代理对象说实现的所有接口
Class<?>[] interfaces = bookSerice.getClass().getInterfaces();
//新建代理对象,里面参数需要(类加载器,一个对象所实现的接口,InvocationHandler接口类的对象)
bookSerice = (BookSerice) Proxy.newProxyInstance(classLoader, interfaces, new LogHandler(bookSerice));
bookSerice.addOne(new BokBean());
bookSerice.deletOne(232L);
}
在创建代理对象的时候需要一个InvocationHandler接口类的对象,下面创建一个该类的实现类
public class LogHandler implements InvocationHandler {
//通过构造方法接受一个没有被代理的原来的对象
//通过下面的方法名的反射找到这个对象对应方法
private Object target;
public LogHandler(Object target) {
this.target = target;
}
//当代理对象调用原方法的时候,就会调用这个invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String classname = target.getClass().getName();
String methodName = method.getName();
System.out.println(classname+"."+methodName+"方法开始执行");
//这里实际是Method类通过方法名反射调用了原方法(addone)
Object value = method.invoke(target, args);
System.out.println(classname+"."+methodName+"方法执行完毕");
return value;
}
}
这样实现了动态代理之后,不管实现的接口里有多少个方法,你只需调用该方法,就会增强该方法,而不需要针对每个方法写一遍增强功能,
并且这个增强类LogHandler类和原来的实现类BookSericeImpl类并没有耦合性,这就是说不管你是什么接口类的实现类,只需要对该类的对象进行代理即可,就能对该类的方法添加上这个新增的功能
总的来说,这种动态代理实现方式就是利用反射技术,找到调用的方法名,针对这个方法进行增强.
如果当不需要对某方法增加功能时,就不用不带.
2.第二种实现:cglib包下的Enhancer类
基于类的动态代理,使用cglib框架.
这种方式创建的代理对象是目标对象的子类对象
第二种方式是利用第三方jar包来实现,下载CGLIB包:

利用jar包中的Enhancer类创建增强对象.
创建增强对象需要根据原对象的类名创建类增强器,还需要根据原对象的类型创建子类代理对象
属性通过增强对象set方法赋值,上一种方式是通过调用方法Proxy.newProxyInstance传参.
public void test3(){
//创建需要代理增强的对象
BookSerice bookSerice = new BookSericeImpl();
Enhancer enhancer = new Enhancer();
//用增强器对象创建类增强器
enhancer.setClassLoader(bookSerice.getClass().getClassLoader());
//因为创建的代理对象是目标对象的子类,所以这里填的就是目标对象的类
enhancer.setSuperclass(bookSerice.getClass());
//创建代理对象,这里需要的参数是Callback接口的对象,所以需要创建一个接口的实现类.
enhancer.setCallback(new TimeMethodInterceptor(bookSerice));
//把代理对象赋给原对象
bookSerice = (BookSerice) enhancer.create();
bookSerice.addOne(new BokBean());
bookSerice.deletOne(1l);
}
创建Callback接口的实现类,也就是功能增强部分,
这一部分跟第一种方式的实现是一样的,都是通过反射在添加功能过程中调用原方法.
//Callback接口没有实现方法,所以这里实现的是他的子接口
public class TimeMethodInterceptor implements MethodInterceptor { private Object target; public TimeMethodInterceptor(Object target) {
this.target = target;
} @Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//记录当前系统时间
//这个时间是从1970年1月1日 0点0分到现在的毫秒数
long start = System.currentTimeMillis();
Object value = method.invoke(target, objects);
long time = System.currentTimeMillis() - start;
System.out.println("当前时长"+time+"毫秒");
return null;
}
}
总结:
两种方法的区别:
第一种是用jdk内部方法创建代理对象,由于创建过程中需要一个对象的接口,所以只能针对有接口类的对象进行代理.
第二种是利用第三方jar包中的增强器(Enhancer)创建代理对象,通过set方法给需要的属性赋值.由于没有接口实现,所以创建的是对象的子类代理对象.
Java_AOP原理的更多相关文章
- 奇异值分解(SVD)原理与在降维中的应用
奇异值分解(Singular Value Decomposition,以下简称SVD)是在机器学习领域广泛应用的算法,它不光可以用于降维算法中的特征分解,还可以用于推荐系统,以及自然语言处理等领域.是 ...
- node.js学习(三)简单的node程序&&模块简单使用&&commonJS规范&&深入理解模块原理
一.一个简单的node程序 1.新建一个txt文件 2.修改后缀 修改之后会弹出这个,点击"是" 3.运行test.js 源文件 使用node.js运行之后的. 如果该路径下没有该 ...
- 线性判别分析LDA原理总结
在主成分分析(PCA)原理总结中,我们对降维算法PCA做了总结.这里我们就对另外一种经典的降维方法线性判别分析(Linear Discriminant Analysis, 以下简称LDA)做一个总结. ...
- [原] KVM 虚拟化原理探究(1)— overview
KVM 虚拟化原理探究- overview 标签(空格分隔): KVM 写在前面的话 本文不介绍kvm和qemu的基本安装操作,希望读者具有一定的KVM实践经验.同时希望借此系列博客,能够对KVM底层 ...
- H5单页面手势滑屏切换原理
H5单页面手势滑屏切换是采用HTML5 触摸事件(Touch) 和 CSS3动画(Transform,Transition)来实现的,效果图如下所示,本文简单说一下其实现原理和主要思路. 1.实现原理 ...
- .NET Core中间件的注册和管道的构建(1)---- 注册和构建原理
.NET Core中间件的注册和管道的构建(1)---- 注册和构建原理 0x00 问题的产生 管道是.NET Core中非常关键的一个概念,很多重要的组件都以中间件的形式存在,包括权限管理.会话管理 ...
- python自动化测试(2)-自动化基本技术原理
python自动化测试(2) 自动化基本技术原理 1 概述 在之前的文章里面提到过:做自动化的首要本领就是要会 透过现象看本质 ,落实到实际的IT工作中就是 透过界面看数据. 掌握上面的这样的本领 ...
- CRC、反码求和校验 原理分析
3月份开始从客户端转后台,算是幸运的进入全栈工程师的修炼阶段.这段时间一边是老项目的客户端加服务器两边的维护和交接,一边是新项目加加加班赶工,期间最长经历了连续工作三天只睡了四五个小时的煎熬,人生也算 ...
- 菜鸟学Struts2——Struts工作原理
在完成Struts2的HelloWorld后,对Struts2的工作原理进行学习.Struts2框架可以按照模块来划分为Servlet Filters,Struts核心模块,拦截器和用户实现部分,其中 ...
随机推荐
- thinkphp网站后门-发现后门(Webshell)文件
不知道能不能解决, 1.登录阿里云后台,找到后门文件删除 2.执行 中国镜像 composer config -g repo.packagist composer https://packagist. ...
- nginx 部署ssl证书之后访问用火狐出现SSL_ERROR_RX_RECORD_TOO_LONG此错误,用Google出现ERR_SSL_PROTOCOL_ERROR错误
server { listen ; server_name xxx.com; ssl_certificate ssl/xxx.pem; ssl_certificate_key ssl/xxx.key; ...
- React碰到v-if
最近在重构公司老项目,由于本人以前的技术栈是vue, 换工作后发现现在公司的技术栈是react, 所以重构的过程是及其痛苦.加之项目又是几年前的老项目,不敢大改,比葫芦画瓢比比皆是.本文就介绍下遇到的 ...
- perl学习之裸字
use strict包含3个部分.其中之一(use strict "subs")负责禁止乱用的裸字. 这是什么意思呢? 如果没有这个限制,下面的代码也可以打印出"hell ...
- laravel如何利用数据库的形式发送通知
具体实现需要分几步: 1.修改驱动为database; 2.创建database的queue表 3.创建任务sendMessage 4.创建发送逻辑dispatch 5.启动队列 接下来我们进行实操: ...
- SqlServer 2014该日志未截断,因为其开始处的记录是挂起的复制操作或变更数据捕获
环境:AlwaysOn集群 操作系统:Windows Server 2008 R2 数据库: SQL Server 2014 错误提示:“该日志未截断,因为其开始处的记录是挂起的复制操作或变更数据捕获 ...
- CentOS安装mysql5.6
1. 去官网https://dev.mysql.com/downloads/mysql/5.6.html下载mysql压缩包,选第一个,最大最全的 2. 通过FTP工具比如FileZila存放到目标地 ...
- Python3的基本数据类型及常用的方法
python3的基本数据类型: 在python3当中有这么几种基本的数据类型:int(整形).str(字符串).list(列表).tuple(元组).dict(字典).bool(布尔值)等.数字整体划 ...
- BZOJ 2508: 简单题
题目大意: 加入直线,删除直线,求点到所有直线的距离的平方和. 题解: 把点到直线的距离公式写出来,然后展开.维护六个值,计算一个二元的多项式的最小值. 对x和y分别求导,导数都为零时取到极值.然后解 ...
- ServletResponse使用方法
Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象.和代表响应的response对象 request和response对象即然代表请求和响应,那我们要 ...