Spring系列之aAOP AOP是什么?+xml方式实现aop+注解方式实现aop
Spring系列之aop aop是什么?+xml方式实现aop+注解方式实现aop
什么是AOP?
AOP为Aspect Oriented Programming 的缩写,意识为面向切面的编程,是通过预编译和运行期动态代理实现程序功能的统一维护的一种技术
AOP是OOP(Object Oriented Programmin 面向对象编程)的延续,是软件开发中的一个热点,也是框架中的一个重要内容,是函数式编程的一种衍生范型,利用AOP可以对业务逻辑的各个部分进行隔离,也使业务逻辑各部分的耦合性降低,提高程序的可重用性,同时提高了开发的效率
我先来讲讲什么是切面
把一块蛋糕切成两块,这个切口就是切面,;炒饭的时候,锅和锅铲就是切面;web层级设计中,web层->网关层->服务层->数据层,每一层之间也是一个切面。编程中,对与对象之间,方法与方法之间,模块与模块之间都是一个个切面。
我们使用一个银行管理系统来说说为什么要使用面向切面编程。
如图银行的取款业务和查询余额业务有交叉的业务逻辑(所谓交叉业务逻辑是与主业务无关的代码,比如安全检查,事务,日志等等),这里指的是验证用户的业务。这会导致代码纠缠,交叉业务逻辑与主业务逻辑混合在一起,这会导致业务逻辑的混合不清,这时候就要用到AOP
使用AOP可以帮助我们简化代码,我们在写代码的时候可不写这个验证用户的业务,可以在另一个地方写好验证用户的代码,然后告诉Spring那几个地方需要这些代码,让Spring加过去即可,如果有多个控制流的话,会大大的减少时间,而AOP不会把代码加入到源文件中但是他会正确的影响最后的机器代码
上面那个 验证用户 的方框,我们可以把它当成一块板子,在这块板子上插入一些控制流程,这块板子就可以当成是 AOP 中的一个切面。所以 AOP 的本质是在一系列的纵向的控制流程中,把那些相同的子流程提取成一个横向的面,把纵向流程画成一条直线,而 AOP 相当于把相同的地方连起来了(这幅图是真的形象,好好体会一下应该不难),这个验证用户的子流程 就成了一条直线,也可以理解成一个切面,这里只插了三个流程,如果其他流程也需要这个子流程,也可以插到其他地方去。
AOP的优势与作用
作用:在不修改源码的情况下对方法进行增强
优势:提高代码的可复用性,提高开发效率,便于维护
AOP的底层实现
AOP的底层是通过Spring动态代理技术实现的,在运行期间通过动态代理,获取代理对象,代理方法执行时增强功能介入,在去调用目标对象的方法,从而完成功能增强。
AOP的动态代理技术
jdk代理
jdk代理:基于接口的动态代理技术
cglib代理:基于父类的动态代理技术
我们来逐一讲解这两个代理方式的差别
jdk代理
demo内容:user类实现一个userImp接口,对user类进行动态代理
user类代码
package com.pjh.user;
public interface user {
public void save();
}
userImp代码
package com.pjh.user.Imp;
import com.pjh.user.user;
public class userImp implements user {
public void save() {
System.out.println("save run....");
}
}
对save方法进行增强
这里使用两种方式
方式一匿名内部类:即InvocationHandler直接使用匿名内部类的方式来创建
package com.pjh.test;
import com.pjh.user.Imp.userImp;
import com.pjh.user.user;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class main {
public static void main(String[] args) {
//创建目标对象
final userImp userImp=new userImp();
//调用proxy类的静态方法来创建代理对象
//Proxy.newProxyInstance(类加载器,获取目标对象的接口,实现动态代理接口)
user userproxy = (user)Proxy.newProxyInstance(userImp.class.getClassLoader(),userImp.getClass().getInterfaces(), new InvocationHandler() {
//invoke(代理类,被代理的方法,参数)
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前置增强代码");
//当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
Object invoke = method.invoke(userImp);
System.out.println("后置增强代码");
return invoke;
}
});
userproxy.save();
}
}
运行结果
成功对方法进行了增强
方法二使用一个类继承自InvocationHandler来实现
编写InvocationHandler实现类
package com.pjh.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class InvocationHandlerImp implements InvocationHandler {
//所有类均继承自object类
private Object object;
//写一个带参构造的方法,来引入目标对象
public InvocationHandlerImp(Object object) {
this.object = object;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("增强前");
Object invoke = method.invoke(object, args);
System.out.println("执行后的方法");
return invoke;
}
}
编写测试类
package com.pjh.test;
import com.pjh.proxy.InvocationHandlerImp;
import com.pjh.user.Imp.userImp;
import com.pjh.user.user;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
//创建目标对象,即代理的真实对象
userImp person = new userImp();
//获取处理器实现类InvocationHandlerImp
InvocationHandlerImp invocationHandlerImp = new InvocationHandlerImp(person);
//获取代理对象
user o = (user)Proxy.newProxyInstance(userImp.class.getClassLoader(),
person.getClass().getInterfaces(),
invocationHandlerImp);
//调用方法
o.save();
}
}
运行结果
放这张表情包的目的是想提醒大家休息一下想必大家都看了很久的电脑了,可以开窗看看外面,休息休息
Cglib的动态代理
这里就简单的讲一下流程
目标类
这里仅仅是一个类没有实现任何接口
package com.pjh.user;
public class person {
public void save(){
System.out.println("save");
}
}
主函数
package com.pjh.test;
import com.pjh.user.person;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class test2 {
public static void main(String[] args) {
//设置目标对象
final person one = new person();
//创建增强器
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(person.class);
//设置回调
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("前置增强代码");
Object invoke = method.invoke(one, objects);
System.out.println("后置增强");
return invoke;
}
});
//获取代理对象
person oneproxy = (person)enhancer.create();
//调用增强后的方法
oneproxy.save();
}
}
AOP相关概念
String 的AOP实现底层就是对上面的动态代理进行了封装,封装后我们只需要对关注的部分进行代码进行编写,并通过配置的方式完成对指定目标的方法增强
AOP的部分术语
Target(目标对象):代理的目标对象
Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
Joinpoint(连接点):所谓连接点指那些被拦截到的点,在spring中这些点指的是方法,因为spring是只支持方法类型的连接点
Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义,即被增强的方法
Jointpoint不一定是Pointcut但是Pointcut一定是Joinpoint
Advice(通知/增强):拦截到jointpoint之后要做的事情就是通知,封装增强业务逻辑的方法
Aspect(切面):是切入点和通知的结合
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程,spring采用动态织入代理,而Aspect采用编译织入和类装载期织入,切点与通知结合的过程
AOP的实现内容
Spring框架监控切入点方法的执行,只要检测到切入点被执行,就会使用代理机制,创建代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行
AOP底层使用什么代理机制
在spring中,框架会根据是否实现了接口来选择使用那种动态代理方式
基于XML的AOP开发
快速入门
1.导入AOP的相关配置坐标
2.创建目标接口和目标类(内有切入点)
3.创建切面类(内部有增强方法)
4.将目标类和切面类的对象创建权交给spring
5.在applicationContext.xml中配置织入关系
6.测试代码
1.导入AOP的相关坐标
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
</dependencies>
创建接口与实现类
接口
package com.pjh.user;
public interface userInterface {
public void save();
}
实现类
package com.pjh.user;
public class user implements userInterface{
public void save() {
System.out.println("save run...");
}
}
创建切面类
package com.pjh.enhance;
public class enhance {
public void enhance(){
System.out.println("这是增强代码!!!!");
}
}
将目标类和切面类的对象创建权交给spring
<bean id="daoImp" class="com.pjh.dao.Imp.daoImp"/>
<bean id="aspect" class="com.pjh.aspect.aspect"/>
引入命名空间与约束路径
<?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 http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
配置切点表达式和前置增强之间的关系
切点表达式的配置语法
excution(【修饰符】返回值类型 包名.类名.方法名(参数))
通知的配置语法
<aop:通知类型 method=“切面类中方法名” pointcut=“切点表达式"></aop:通知类型>
这里先写个简单的格式后面再给大家深入讲
<aop:config >
<!--要切入的类-->
<aop:aspect ref="enhance">
<!--切入后的增强方法-->
<!--这是一个前置增强-->
<!--method切入后的增强方法-->
<!--pointcut对什么类方法执行的时候进行增强-->
<aop:before
method="enhance" pointcut="execution(public void com.pjh.user.user.save())"></aop:before>
</aop:aspect>
</aop:config>
测试代码类
import com.pjh.user.userInterface;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class test {
/*如果是继承自接口的一定要使用接口进行定义否则会报错*/
@Autowired
private userInterface user;
@Test
public void test1(){
user.save();
}
}
结果
**
切点表达式的花样写法
**
表达式语法
excution(【修饰符】 返回值类型 包名.类名.方法名(参数))
返回值的修饰符可省略
返回值的类名,包名,方法名可以使用“ * ”星号代表任意
包名与类名之间的一个点" . "代表当前包下的所有类,两个点“ .. ”代表当前包及其子包下的所有类
参数列表可以使用两个点 " . . " 表示任意个数,任意类型的参数列表
//user类下的save方法增强
execution(public void com.pjh.one.user.save())
//对user类下的所有放回值为void的方法进行增强
execution(public void com.pjh.one.user.*(..))
//one包下所有类的所有方法进行增强
execution(* com.pjh.*.*.*(..))
//one包包括其子包下所有类的所有方法进行增强
execution(* com.pjh..*.*.*(..))
//任何包任何类任何方法
execution(* *.*..*.*. * (..))
切点表达式的抽取
当多个增强切点表达式相同时,可以将切点表达式进行抽取。在增强中使用pointcut-ref属性替代pointcut属性来引用切点表达式
<aop:config >
<aop:aspect ref="enhance">
<aop:pointcut id="myPointcut" expression="execution(public void com.pjh.user.user.save())"/>
<aop:before
method="enhance" pointcut-ref="myPointcut"></aop:before>
</aop:aspect>
</aop:config>
**
通知的类型
**
通知的配置语法
<aop:通知的类型 method=“切面中的方法名” pointcut=“切点表达式”/>
简单的小总结
<aop:config >
<aop:aspect ref="切入类的名称>
<aop:before
method="切入方法的名称e" pointcut="切点表达式"></aop:before>
</aop:aspect>
</aop:config>
通知的类型:前置通知、后置通知、环绕通知、异常抛出通知、最终通知
切点表达式的写法:
excution(【修饰符】返回值类型 包名.类名.方法名(参数))
下面我们再来讲讲更加简单的方法,即使用注解的方式
基于注解的AOP开发
注解aop的开发步骤
1.使用@Aspect标注切面类
2.使用@通知注解标注通知方法
3.在配置文件中配置aop自动代理<aop:aspectj-autoproxy>
标注为一个切面类@Aspect
@Aspect
public class enhance {
}
使用注解来抽取切点表达式
@Pointcut(”注解表达式“)
/切点表达式方法的抽取,抽取方法是在切点内定义方法,
在方法内使用 @Pointcut注解切点表达式,然后在增强注解中进行引用/
@Pointcut("execution(public void com.pjh.user.user.save())")
public void mypoint(){}
以上就是Spring的AOP的概念及其使用方法,我会不断的学习,也会不断更新我的学习文章,主要有java和数据结构两个方面,有想要一起学习的伙伴可以私信或则关注我,共勉
Spring系列之aAOP AOP是什么?+xml方式实现aop+注解方式实现aop的更多相关文章
- Spring学习笔记之 Spring IOC容器(二) 之注入参数值,自动组件扫描方式,控制Bean实例化方式,使用注解方式
本节主要内容: 1. 给MessageBean注入参数值 2. 测试Spring自动组件扫描方式 3. 如何控制ExampleBean实例化方式 4. 使用注解方式重构Jdb ...
- Spring 通过XML配置文件以及通过注解形式来AOP 来实现前置,环绕,异常通知,返回后通知,后通知
本节主要内容: 一.Spring 通过XML配置文件形式来AOP 来实现前置,环绕,异常通知 1. Spring AOP 前置通知 XML配置使用案例 2. Spring AOP ...
- Spring 中使用XML配置方式和使用注解方式实现DI
Spring容器给我们提供了很好的环境,我们只关注主要业务即可,其他的无需关注太多.今天刚学的DI DI(Dependency Injection):依赖注入 使用XML配置文件完成依赖注入 1.1普 ...
- Spring——AOP(面向切面编程)@AspectJ注解方式
一.什么是AOP? AOP: (Aspect Oriented Programming)即面向切面编程. 试想这样的场景:项目中需要在业务方法执行完打印日志记录.最笨的办法就是在每个方法核心业务执行完 ...
- Spring配置文件中bean标签中init-method和destroy-method和用注解方式配置
Person类: public class Person { private int i = 0; public Person(){ System.o ...
- 【初识Spring】对象(Bean)实例化及属性注入(注解方式)
通过xml的方式进行对象的实列化或属性注入或许有一些繁琐,所以在开发中常用的方式更多是通过注解的方式实现对象实例化和属性注入的. 开始之前 1.导入相关的包(除了导入基本的包还要导入aop的包): 创 ...
- Spring Aop实例之xml配置
AOP的配置方式有2种方式:xml配置和AspectJ注解方式.今天我们就来实践一下xml配置方式. 我采用的jdk代理,所以首先将接口和实现类代码附上 package com.tgb.aop; pu ...
- Spring学习之旅(八)Spring 基于AspectJ注解配置的AOP编程工作原理初探
由小编的上篇博文可以一窥基于AspectJ注解配置的AOP编程实现. 本文一下未贴出的相关代码示例请关注小编的上篇博文<Spring学习之旅(七)基于XML配置与基于AspectJ注解配置的AO ...
- java框架之Spring(2)-注解配置IOC&AOP配置
注解配置IoC 准备 1.要使用注解方式配置 IoC,除了之前引入的基础 jar 包,还需要引入 spring-aop 支持包,如下: 2.在 applicationContext.xml 中引入 c ...
随机推荐
- 弱校验之@NotNull@NotEmpty@NotBlank
@NotNull 适用于非空判断 The annotated element must not be {@code null}. CharSequence, Collection, Map 和 Arr ...
- 表单和 v-model
思维导图 form 做表单一定要用 form+button组合 <template> <div id="app"> 登录 <form @submit. ...
- 为何要做seo关键词排名
http://www.wocaoseo.com/thread-229-1-1.html 武汉seo百度指数在150左右,做seo的同仁们都知道这样的一件事情. 真正搜索武汉seo关键词能作为潜在客户的 ...
- Qt 绘图(QBitmap,QPixmap,QImage,QPicture)
QPainter绘图绘图设备(QPixmap,QImage,QBitmap,QPicture) 重写绘图事件,虚函数 如果窗口绘图,必须放在绘图事件里实现 绘图事件内部自动调用,窗口需要重绘的时候,状 ...
- 揭秘 Kubernetes attach/detach controller 逻辑漏洞致使 pod 启动失败
前言 本文主要通过深入学习k8s attach/detach controller源码,了解现网案例发现的attach/detach controller bug发生的原委,并给出解决方案. 看完本文 ...
- Android开发之制作圆形头像自定义View,直接引用工具类,加快开发速度。带有源代码学习
作者:程序员小冰,CSDN博客:http://blog.csdn.net/qq_21376985 QQ986945193 博客园主页:http://www.cnblogs.com/mcxiaobing ...
- eslint 的 使用常见问题(一)
在source tree 远程拉去一个项目,然后无缘无故 代码各处飘红 ,然后看了很是烦躁.碰见一下几个问题,后续持更 一.升级es6 出现这个问题的原因:let是EcmaScript 6 里面才有的 ...
- Macbook Pro HDMI 无信号解决办法
因为CS:GO无法启动的问题,使用过了下面的命令 sudo pmset -a GPUSwitch 0 导致HDMI显示器无信号 输入下面的代码 sudo pmset -a GPUSwitch 1 可以 ...
- add forward
adb forward tcp:34999 localabstract:Unity-com.kingsgroup.rob adb forward --list adb forward --remove ...
- Unity游戏资源反解工具
https://github.com/ata4/disunity http://devxdevelopment.com/UnityUnpacker 链接:https://pan.baidu.com/s ...