Spring_AOP的实现机制-动态代理
Posted 奔跑的路奇
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring_AOP的实现机制-动态代理相关的知识,希望对你有一定的参考价值。
Spring_AOP的实现机制-动态代理
1 Spring核心之AOP
1.1 AOP的概念
AOP是Aspect-Oriented Programming,即面向切面编程。
AOP的作用:不修改源码的情况下,程序运行期间对方法进行功能增强
开发中:各自做自己擅长的事情,运行的时候将服务性代码织入到核心业务中。
通过spring工厂自动实现将服务性代码以切面的方式加入到核心业务代码中。
1.2 AOP的相关术语
Target(目标对象)
要被增强的对象,一般是业务逻辑类的对象。
代理(Proxy)
一个类被 AOP 织入增强后,就产生一个结果代理类。
切面(Aspect)
表示增强的功能,就是一些代码完成的某个功能,非业务功能。是切入点和通知的结合。
连接点(Joinpoint)
所谓连接点是指那些被拦截到的点。在Spring中,这些点指的是方法(一般是类中的业务方法),因为Spring只支持方法类型的连接点。
切入点(Pointcut)
切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。
Advice(通知/增强)
所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执等。通知类型不同,切入时间不同。
Weaving(织入)
是指把增强应用到目标对象来创建新的代理对象的过程。 spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。
2 AspectJ 对 AOP 的实现
2.1 AspectJ的通知类型
AspectJ 中常用的通知有5种类型::
前置通知(Before):在目标方法被调用之前调用通知功能;
后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
返回通知(After-returning):在目标方法成功执行之后调用通知;
异常通知(After-throwing):在目标方法抛出异常后调用通知;
环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。
2.2 AspectJ的切入点表达式
以上表达式共 4 个部分。
execution(访问权限 方法返回值 方法声明(参数) 异常类型)。
切入点表达式要匹配的对象就是目标方法的方法名。所以,execution 表达式中就是方法的签名。
示例
execution(* com.kkb.service.*.*(..))
指定切入点为:定义在 service 包里的任意类的任意方法。
execution(* com.kkb.service..*.*(..))
指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“..”出现在类名中时,后面必须跟 “*”,表示包、子包下的所有类。
execution(* com.kkb.service.IUserService+.*(..))
指定切入点为:IUserService 若为接口,则为接口中的任意方法及其所有实现类中的任意方法;若为类, 则为该类及其子类中的任意方法。
2.3 代码示例
1 目标类
//目标类
public class SomeServiceImpl{
public void doThird() {
System.out.println("执行业务方法doThird()");
}
}
2 切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码
/**
* @Aspect : 是aspectj框架中的注解
* 作用:表示当前类是切面类
* 切面类:是用来给业务方法增加功能的类,在这个类中有切面的功能代码
*/
@Aspect
public class MyAspect {
@After(value = "myPointcut()")
public void myAfter(){
System.out.println("执行最终通知,总是会被执行的代码");
}
@Before(value = "myPointcut()")
public void myBefore(){
System.out.println("执行前置通知,在目标方法执行之前执行");
}
/**
* @Pointcut : 定义和管理切入点,如果项目中有多个切入点表达式是重复的,可以复用
*
* 特点:使用@Pointcut定义在方法上面,这个方法的名称就是切入点表达式的别名其它的通知中,value属性就可以使用这个方法名称,代替切入点表达式了
* 在
*/
@Pointcut(value = "execution(* *..SomeServiceImpl.doThird(..))")
public void myPointcut(){
//无需代码
}
}
3 静态代理
/**
静态代理类优缺点
优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点。
缺点:
1)代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
2)如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
*/
1:下面是一个去电影院看电影的例子
public interface Movie {
//看电影
void play();
}
2、RealMovie 是实现类
/**
* 核心业务,只看电影
*/
public class RealMovie implements Movie {
@Override
public void play() {
System.out.println("您正在观看电影 《肖申克的救赎》");
}
}
3:Cinema是代理类
/**
* 代理类
*/
public class Cinema implements Movie {
private RealMovie movie;
//代理角色,代理
public Cinema(RealMovie movie) {
super();
this.movie = movie;
}
@Override
public void play() {
guanggao(true);(服务型业务)
//你看电影(核心业务是看电影)
movie.play();(服务型业务)
guanggao(false);
}
//中介(电影院)的附属操作
public void guanggao(boolean isStart){
if ( isStart ) {
System.out.println("电影马上开始了,爆米花、可乐、口香糖9.8折,快来买啊!");
} else {
System.out.println("电影马上结束了,爆米花、可乐、口香糖8.8折,买回家吃吧!");
}
}
}
4:测试类
public class ProxyTest {
public static void main(String[] args) {
RealMovie realmovie = new RealMovie();
//代理,中介还会添加一些附属操作
Movie movie = new Cinema(realmovie);
movie.play();
}
}
5:控制台输出
电影马上开始了,爆米花、可乐、口香糖9.8折,快来买啊!
您正在观看电影 《肖申克的救赎》
电影马上结束了,爆米花、可乐、口香糖8.8折,买回家吃吧!
4 AOP的实现机制-动态代理
4.1 JDK动态代理
/**
* 基于接口的动态代理:实现InvocationHandler接口->调用处理程序Proxy->代理
*
* 具体步骤是:
* a. 实现InvocationHandler接口创建自己的调用处理器
* b. 给Proxy类提供ClassLoader和代理接口类型数组创建动态代理类
* c. 以调用处理器类型为参数,利用反射机制得到动态代理类的构造函数
* d. 以调用处理器对象为参数,利用动态代理类的构造函数创建动态代理类对象
*
* 好处:一个动态代理类可以代理多个类,只要是实现了同一个接口即可;复用性高
*
*/
JDK 动态代理主要涉及到 java.lang.reflect 包中的两个类:Proxy 和 InvocationHandler。InvocationHandler是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起。
Proxy 利用 InvocationHandler 动态创建一个符合某一接口的实例,生成目标类的代理对象。
4.2 Cglib动态代理
Cglib代理,也叫做子类代理。在内存中构建一个子类对象从而实现对目标对象功能的扩展。
DK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的类,就可以使用CGLIB实现。
5 静态代理和动态代理的区别
1 静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。
2 静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。
3 静态代理,在程序运行前,代理类的.class文件就已经存在了;动态代理,在程序运行时,运用反射机制动态创建而成。
6 两种动态代理方式区别
1 JDK动态代理只能对实现了接口的类生成代理,而不能针对类;Cglib是针 对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以该类或方法最好不要声明成final 。
2 Cglib一个目标类方法会生成两个代理方法,一个重写目标方法,并实现代理逻辑,还有一个直接调用目标类方法。
最后,如果有问题,希望指正,一起进步。
以上是关于Spring_AOP的实现机制-动态代理的主要内容,如果未能解决你的问题,请参考以下文章