参考博客:
https://blog.youkuaiyun.com/hejingyuan6/article/details/36203505 >JAVA学习篇–静态代理VS动态代理
https://www.jianshu.com/p/774c65290218 >关于动态代理invoke()方法的理解
https://www.cnblogs.com/biaogejiushibiao/p/9466097.html >java动态代理中的invoke方法是如何被自动调用的
https://blog.youkuaiyun.com/moreevan/article/details/11977115/ >AOP 实现原理
https://baijiahao.baidu.com/s?id=1661692768282961545&wfr=spider&for=pc >深入理解SpringAOP
https://segmentfault.com/a/1190000007469968 >SpringAOP理论篇
https://blog.youkuaiyun.com/qq_41981107/article/details/87920537 >Spring AOP——简单粗暴,小白教学
代理模式
静态代理中,一个代理只能代理一种类型,且在编译期就已经被确认的对象。
动态代理是在运行期间,通过反射机制实现动态代理创建对象,并且可以代理各种类型的对象。
静态代理
角色分析:
- 抽象方法- Subject类,定义真实角色和代理角色的公共方法即共同接口
- 真实对象- RealSubject,被代理的角色
- 代理角色- Proxy,代理真实的角色,帮助真实对象完成一些工作,减少其负担
- 客户端- Client, 访问代理角色的人
- 举例:租客(客户端)通过中介(代理角色)找房东(真实角色)完成租赁房屋(抽象方法)这个动作/事件
想访问一个对象,不让直接访问,会找一个代理,去访问这个代理。在客户端和要访问的对象之间多加了一层,代理类。
下面的例子没有写Dao层,按理来说用户calls Service层,Service层calls Dao层。因为想学习客户端怎么calls Service层。
1.创建Servcie接口
// 公共接口
public interface UserService {
public void insert();
public void delete();
public void update();
public void select();
}
2.创建Service实现类
注意:这里的打印代表的是某种业务逻辑代码,而不是打印日志
// 真实对象
public class UserServiceImpl implements UserService {
public void insert() {
// do something
System.out.println("insert用户的业务逻辑代码正在执行中...");
}
public void delete() {
System.out.println("delete用户的业务逻辑代码正在执行中...");
}
public void update() {
System.out.println("update用户的业务逻辑代码正在执行中...");
}
public void select() {
System.out.println("select用户的业务逻辑代码正在执行中...");
}
}
3.测试(模拟客户端Client)
public class Test06 {
//模拟客户端Client
public static void main(String[] args) {
// Client calls service directly
UserService us = new UserServiceImpl();
us.insert();
}
}
可以发现如果给每个方法增加一个打日志的功能的话,现在这种情况就需要给Service实现类里的每个方法都添加一个功能,这样很繁琐很复杂。所以需要添加一个代理角色去完成,不能让真实对象去做这个事情。(代理真实的角色,帮助真实对象完成一些工作,减少其负担)
4.创建Proxy代理类
代理类代理的是Service实现类,不是Servcie接口!!!
代理角色和真实对象都需要实现一个公共的接口:Servcie接口
代理角色和真实对象需要保持一致,不能保持一致就不能帮真实对象完成工作。
public class UserServiceImplProxy implements UserService {
// 引入真实对象(因为代理他所以要先引入,跟他发生关系)
private UserServiceImpl userServiceImpl;
public UserServiceImplProxy(){}
//通过构造器去注入对象
public UserServiceImplProxy(UserServiceImpl userServiceImpl){
this.userServiceImpl = userServiceImpl;
}
//set方式注入对象
public void setUserServiceImpl(UserServiceImpl userServiceImpl){
this.userServiceImpl = userServiceImpl;
}
public void insert() {
log("insert");
userServiceImpl.insert();
}
public void delete() {
log("delete");
userServiceImpl.delete();
}
public void update() {
log("update");
userServiceImpl.update();
}
public void select() {
log("select");
userServiceImpl.select();
}
// 打印日志的公共方法
public void log(String methodName){
System.out.println("[Debug]:"+ methodName+"方法执行");
}
}
4.新的测试类(模拟客户端Client)
- 两个方法选择其中一个来使用
- 尽量使用构造器来创建,构造器优于 set 。
- https://blog.youkuaiyun.com/qq_27093465/article/details/92763548 >Java 代码优化:使用构造函数和使用一个个setter的效率差别
public class Test06 {
//模拟客户端Client
public static void main(String[] args) {
// Client calls service directly
// UserService us = new UserServiceImpl();
// us.insert();
// 代理对象和真实对象绑定
// 方式一: 通过构造器
UserServiceImplProxy proxy = new UserServiceImplProxy(new UserServiceImpl());
proxy.insert();
// 方式二: 通过set方式注入
UserServiceImpl u = new UserServiceImpl();
UserServiceImplProxy proxy2 = new UserServiceImplProxy();
proxy2.setUserServiceImpl(u);
}
}
静态代理类的逻辑:

实现场景: 为CRUD 操作 新增打印日志的功能
开闭原则: 对拓展开放,对修改关闭
- 代理类和被代理类(真实对象)实现了同样的接口,继而实现了相同的方法
- 优点:避免修改原有的实现类(真实对象),符合开闭原则: 对拓展开放,对修改关闭
- 缺点:被代理类中若新增方法,代理类要同步更新;一个代理类只能服务于一个被代理类—— 增加了代码维护的复杂度
动态代理
静态代理中,一个代理只能代理一种类型,且是在编译期就已经被确认的对象。
动态代理是在运行期间,通过反射机制实现动态代理创建对象,并且可以代理各种类型的对象。
- 动态代理类只能代理接口(不支持抽象类)(不要理解成为接口服务,动态代理是为真实对象服务的)
- 代理类都需要实现InvocationHandler类
- 实现invoke方法:
- 该invoke方法就是调用被代理接口的所有方法时需要调用的
- 该invoke方法返回的值是被代理接口的一个实现类
JAVA实现动态代理两种方式:
- JDK动态代理 (下面的例子使用的是这种方法):利用反射机制生成一个实现代理接口的匿名类,在具体方法前调用
InvokeHandler来处理。 - CGLib动态代理(Code Generation Library):,利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
在Java中要想实现动态代理机制,需要java.lang.reflect.InvocationHandler接口和java.lang.reflect.Proxy类的支持
invoke方法说明
invoke()就是将参数args传入目标方法target当中,触发方法并获取返回值(所以invocationHandle就是调用处理程序并返回结果)- 第一个参数
proxy表示调用该方法的代理实例,提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。 - 第二个参数
method表示原对象被调用的方法,对应于在代理实例上调用的接口方法的方法实例。 方法对象的声明类将是声明方法的接口,它可以是代理接口继承方法的代理接口的超接口。 - 第三个参数
args表示方法的参数,包含代理实例上方法调用中传递的参数值的对象数组,如果接口方法不带参数,null。 原始类型的参数包含在适当的原始包装类的实例中,例如java.lang.Integer或java.lang.Boolean 。 - 被代理对象
target通过参数传递进来,我们通过target.getClass().getClassLoader()获取ClassLoader对象,然后通过target.getClass().getInterfaces()获取它实现的所有接口,然后将target包装到实现了InvocationHandler接口的LogHandler对象中。通过newProxyInstance函数我们就获得了一个动态代理对象。
java.lang.reflect.InvocationHandler接口的定义如下:
//Object proxy:被代理的对象
//Method method:要调用的方法
//Object[] args:方法调用时所需要参数
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
使用的例子:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
return method.invoke(target,args);
}
getProxy方法说明
- 该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
- 第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
- 第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口
- 第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法
- 根据传入的目标返回一个代理对象
java.lang.reflect.Proxy类的定义如下:
//CLassLoader loader:类的加载器
//Class<?> interfaces:得到全部的接口
//InvocationHandler h:得到InvocationHandler接口的子类的实例
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
throws IllegalArgumentException
如下查看各个参数含义:
public Object getProxy(){
System.out.println("被代理的类是: "+target.getClass());
System.out.println("被代理的父类是: "+target.getClass().getSuperclass());
System.out.println("类加载器: "+target.getClass().getClassLoader());
Class<?>[] interfaces = target.getClass().getInterfaces();
for(Class<?> c : interfaces){
System.out.println("被代理的类所对应的接口是: "+c);
}
System.out.println(this instanceof DynamicProxyTool);
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
打印结果:
被代理的类是: class com.xsz.service.UserServiceImpl
被代理的父类是: class java.lang.Object
类加载器: sun.misc.Launcher$AppClassLoader@18b4aac2
被代理的类所对应的接口是: interface com.xsz.service.UserService
true
1.首先创建动态代理工具类
如果想要做动态代理,首先第一件事就要去实现InvocationHandler接口。
如果说你想要去使用某个方法,那就把这个方法作为invoke函数的某一个参数把它传进来,然后再点invoke,就可以使用了。
是一个纯粹的工具类,没有和任何一个类绑定关系。
public class DynamicProxyTool implements InvocationHandler {
// 静态代理只能为某一个对象创建一个代理,只针对某一个类型,现在要为所有类型都可以去创建,那么只能用Object。
// 把所有代理对象设置为Object类型
private Object target;
// 通过set方法去注入对象
public void setTarget(Object target){
this.target = target;
}
// 通过构造器去注入对象
public DynamicProxyTool(Object target){
this.target = target;
}
// 打印日志的公共方法
public void log(String methodName){
System.out.println("[Debug]:"+ methodName+"方法执行");
}
// target: 目标对象- 真实对象- 被代理对象(这三个名字都可以)
public Object getProxy(){
// getClassLoader()是获取类加载器
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
return method.invoke(target,args);
}
}
2.创建接口和实现类
使用上面静态代理的接口类和实现类,使用现成的。
3.测试类(模拟客户端Client)
想使用上面的工具类,只需要在客户端创建这个工具类就可以了,把你所需要生成的代理类的参数传进去就可以了,传的是被代理的对象,你想代理谁,就把它作为参数传给这个工具。工具会帮你自动生成代理
public class Test {
//模拟客户端Client
public static void main(String[] args) {
// Client calls service directly
/**
* new UserServiceImpl() - 真实对象
* ||
* || 传参,把要设置的真实对象传给工具类
* \||/
* \/
* new DynamicProxyTool() - 创建代理类的工具
*
* getProxy -返回代理实力- (代理对象)- 中介
*
* 创建出来的代理,要实现UserService接口
*/
DynamicProxyTool proxyTool = new DynamicProxyTool(new UserServiceImpl());
// call的接口而不是接口的实现类
UserService userService = (UserService) proxyTool.getProxy();
userService.update();
}
}
到这里可以发现,静态代理和动态代理之间的差别只在于代理类的getProxy方法和invoke方法,还有模拟客户端测试类的时候call的接口而不是接口的实现类。
动态代理优点
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。而且动态代理的应用使我们的类职责更加单一,复用性更强
- 可以使得真实对象的操作更加纯粹,不需要关注公共业务(如:打印日志,性能监测等)
- 公共业务需要拓展时,方便集中管理
- 一个动态代理的工具类,可以创建多个代理
总结
代理对象就是把被代理对象包装一层,在其内部做一些额外的工作,比如用户需要上facebook,而普通网络无法直接访问,网络代理帮助用户先翻墙,然后再访问facebook。这就是代理的作用了。
静态代理与动态代理,它们都能实现相同的功能,而我们看从静态代理到动态代理的这个过程,我们会发现其实动态代理只是对类做了进一步抽象和封装,使其复用性和易用性得到进一步提升而这不仅仅符合了面向对象的设计理念,其中还有AOP的身影,这也提供给我们对类抽象的一种参考。关于动态代理与AOP的关系,个人觉得AOP是一种思想,而动态代理是一种AOP思想的实现!
AOP概念
AOP(Aspect-OrientedProgramming)面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP可以说是OOP(Object-Oriented Programing 面向对象编程)的补充和完善,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合降低,提高程序的可重用性,同时提高了开发的效率。
传统OOP是自上而下的逻辑开发,AOP是一种面向切面的编程思想。这些横切性问题,把它们抽象为一个切面,关注点在切面的编程,这就是所谓的AOP。
实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。
AOP实现
先了解SpringAOP是用什么技术实现,有两种方式:JDK动态代理和CGLIB动态代理实现。
由此可见,SpringAOP是实现通过了创建目标对象的代理类,然后对目标进行拦截来实现的这样一种设计,底层实现,主要有重点掌握几个,如以下ProxyConfig类、AdvisedSupport类、ProxyCreatorSupport类、ProxyFactoryBean类、ProxyFactory、AspectJProxyFactory类。
它们的具体作用是:
- ProxyConfig类主要为各种AOP代理工厂提供属性配置。
- ProxyCreatorSupport类是AdvisedSupport类的子类,主要辅助不同子类的通用操作封装到一个叫ProxyCreatorSupport类里面。
- AdvisedSupport类是ProxyConfig类的子类,主要封装AOP的通知和通知器。
- AspectJProxyFactory类主要是创建基于AspectJ的代理对象。
- ProxyFactoryBean类主要作用是创建声明代理对象。
- ProxyFactory类主要的作用就是创建编程式的代理对象。
横切关注点
就上方动态代理的例子中log打印日志就是横切关注点
跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …
Aspect(切面)
就上方动态代理的例子放在这不太对,因为把log打印日志这个方法放在了别的类中,正常应该单独用一个工具类来专门存放打印日志等功能来使用,切面就是把模块化的工具类,这个类就是切面
横切关注点 被模块化 的特殊对象。即,它是一个类。
aspect 由 pointcount 和 advice 组成, 它既包含了横切逻辑的定义, 也包括了连接点的定义。Spring AOP就是负责实施切面的框架, 它将切面所定义的横切逻辑织入到切面所指定的连接点中。
AOP的工作重心在于如何将增强织入目标对象的连接点上,这里包含两个工作:
- 如何通过 pointcut 和 advice 定位到特定的 joinpoint 上
- 如何在 advice 中编写切面代码.
可以简单地认为, 使用 @Aspect 注解的类就是切面。
Advice(通知)
切面是个类,这个类中有很多的方法,方法就是切面
切面必须要完成的工作。即,它是类中的一个方法。
由 aspect 添加到特定的 join point(即满足 point cut 规则的 join point)的一段代码。
许多 AOP框架, 包括 Spring AOP,会将 advice 模拟为一个拦截器(interceptor),并且在 join point 上维护多个 advice, 进行层层拦截。
增强,包括处理时机和处理内容。
处理内容就是要做什么事,比如校验权限和记录日志。
处理时机就是在什么时机执行处理内容,分为前置处理(即业务代码前)、后置处理(业务代码执行后)等。
Spring中支持五种类型的Advice
参考博客:
https://blog.youkuaiyun.com/yangshangwei/article/details/77151974 >Spring-AOP 增强(Advice)5种类型和创建增强类
按照增强在目标类方法连接点的位置可以将增强划分为以下五类:
- 前置增强 (org.springframework.aop.BeforeAdvice)
表示在目标方法执行前来实施增强 - 后置增强(org.springframework.aop.AfterReturningAdvice)
表示在目标方法执行后来实施增强 - 环绕增强(org.aopalliance.intercept.MethodInterceptor)
表示在目标方法执行前后同时实施增强 - 异常抛出增强(org.springframework.aop.ThrowsAdvice)
表示在目标方法抛出异常后来实施增强 - 引介增强(org.springframework.aop.introductioninterceptor)
表示在目标类中添加一些新的方法和属性(使用不多,可以只当作一个学习)
before advice, 在 join point 前被执行的 advice. 虽然 before advice 是在 join point 前被执行, 但是它并不能够阻止 join point 的执行, 除非发生了异常(即我们在 before advice 代码中, 不能人为地决定是否继续执行 join point 中的代码)
after return advice,在一个 join point 正常返回后执行的 advice。
after throwing advice,当一个 join point 抛出异常后执行的 advice。
after(final) advice,无论一个 join point 是正常退出还是发生了异常,都会被执行的 advice。
around advice,在 join point 前和 joint point 退出后都执行的 advice。这个是最常用的 advice。
Target(目标对象)
上方的例子里就是Service实现类
被通知的对象(目标对象or被代理对象)。
织入 advice 的目标对象。目标对象也被称为 advised object。
因为 Spring AOP 使用运行时代理的方式来实现 aspect,因此 adviced object 总是一个代理对象(proxied object)。
注意,adviced object 指的不是原来的类,而是织入 advice 后所产生的代理类。
Proxy(代理)
被创建出来的代理对象
向目标对象应用通知之后创建的对象(代理对象)。
一个类被 AOP 织入 advice,就会产生一个结果类,它是融合了原类和增强逻辑的代理类。
在 Spring AOP 中,一个 AOP 代理是一个 JDK 动态代理对象或 CGLIB 代理对象。
Point cut(切入点)
上方的例子中执行log打印日志方法的地方,就是切入点
决定处理如权限校验、日志记录等在何处切入业务代码中(即织入切面)。
切点分为execution方式和annotation方式。
前者可以用路径表达式指定哪些类织入切面,后者可以指定被哪些注解修饰的代码织入切面。
切面通知 执行的 “地点”的定义。
匹配 join point 的谓词(a predicate that matches join points)。
Advice 是和特定的 point cut 关联的, 并且在 point cut 相匹配的 join point 中执行。
每个程序的连接点有多个,如何定位到某个感兴趣的连接点,就需要通过切点来定位。比如,连接点–数据库的记录,切点–查询条件。
切点用于来限定Spring-AOP启动的范围,通常我们采用表达式的方式来设置,所以关键词是范围。
Join point(连接点)
与切入点匹配的执行点。
参考博客:
https://blog.youkuaiyun.com/weixin_42649617/article/details/109997718 >切入点和连接点的区别
连接点是一个虚拟的概念,可以理解为所有满足切点扫描条件的所有的时机
具体举个例子:比如开车经过一条高速公路,这条高速公路上有很多个出口(连接点),但是我们不会每个出口都会出去,只会选择我们需要的那个出口(切点)开出去。
(👆这个例子太形象了,简直无敌)
程序运行中的一些时间点,例如一个方法的执行,或者是一个异常的处理。
在 Spring AOP 中,join point 总是方法的执行点,即只有方法连接点。
程序执行的某个特定位置,如某个方法调用前,调用后,方法抛出异常后,这些代码中的特定点称为连接点。简单来说,就是在哪加入你的逻辑增强。
连接点表示具体要拦截的方法,上面切点是定义一个范围,而连接点是具体到某个方法。
Weaving(织入)
就是通过动态代理,在目标对象方法中执行处理内容的过程。
将 aspect 和其他对象连接起来,并创建 adviced object 的过程。
根据不同的实现技术,AOP织入有三种方式:
编译器织入,这要求有特殊的Java编译器。
类装载期织入,这需要有特殊的类装载器。
动态代理织入,在运行期为目标类添加增强(Advice)生成子类的方式。
Spring 采用动态代理织入,而AspectJ采用编译器织入和类装载期织入。
关于Join point 和 Point cut 的区别
在 Spring AOP 中,所有的方法执行都是 join point。而 point cut 是一个描述信息,它修饰的是 join point,通过 point cut,我们就可以确定哪些 join point 可以被织入 Advice。因此 join point 和 point cut 本质上就是两个不同纬度上的东西。
advice 是在 join point 上执行的,而 point cut 规定了哪些 join point 可以执行哪些 advice。
连接点:连接点是一个虚拟的概念,可以理解为所有满足切点扫描条件的所有的时机。
具体举个例子:比如开车经过一条高速公路,这条高速公路上有很多个出口(连接点),但是我们不会每个出口都会出去,只会选择我们需要的那个出口(切点)开出去。
简单可以理解为,每个出口都是连接点,但是我们使用的那个出口才是切点。每个应用有多个位置适合织入通知,这些位置都是连接点。但是只有我们选择的那个具体的位置才是切点。
使用Spring实现AOP
可以通过配置文件或者编程的方式来使用Spring AOP。
配置可以通过xml文件来进行,大概有四种方式:
- 配置
ProxyFactoryBean,显式地设置advisors, advice, target等 - 配置
AutoProxyCreator,这种方式下,还是如以前一样使用定义的bean,但是从容器中获得的其实已经是代理对象 - 通过
<aop:config>来配置 - 通过
<aop: aspectj-autoproxy>来配置,使用AspectJ的注解来标识通知及切入点
也可以直接使用ProxyFactory来以编程的方式使用Spring AOP,通过ProxyFactory提供的方法可以设置target对象, advisor等相关配置,最终通过 getProxy()方法来获取代理对象
导入依赖包
https://mvnrepository.com/ >依赖包下载库
找到下面最新的依赖:

找到最新的1.9.8:
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.8</version>
<scope>runtime</scope>
</dependency>
1.创建Servcie接口和实现类
// 公共接口
public interface UserService {
public void insert();
public void delete();
public void update();
public void select();
}
// 真实对象
public class UserServiceImpl implements UserService {
public void insert() {
// do something
System.out.println("insert用户的业务逻辑代码正在执行中...");
}
public void delete() {
System.out.println("delete用户的业务逻辑代码正在执行中...");
}
public void update() {
System.out.println("update用户的业务逻辑代码正在执行中...");
}
public void select() {
System.out.println("select用户的业务逻辑代码正在执行中...");
}
}
2.创建增强类
增强就是在原有的方法上去新加一些功能:日志、事务、安全等····
我们编写两个:一个前置增强、一个后置增强
前置增强
public class LogBeforeService implements MethodBeforeAdvice {
/**
* @param method :要执行目标方法的对象(被调用的方法)
* @param args :被调用的方法的参数
* @param target :被调用的目标对象
* @throws Throwable
*/
// 前置增强
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
// 模拟打印前置日志的功能
System.out.println("[Debug]:" + target.getClass().getName() + "的" + method.getName() + "方法被执行了");
}
}
后置增强
public class LogAfterService implements AfterReturningAdvice {
/**
* @param returnValue :方法的返回值
* @param method :被调用的方法
* @param args :被调用的方法的参数
* @param target :被调用的目标对象
* @throws Throwable
*/
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
// 模拟打印前置日志的功能
System.out.println("[Debug]:" + target.getClass().getName() + "的" + method.getName() + "方法被执行了 返回值是:" + returnValue);
}
}
创建测试类
public class Test08 {
//模拟客户端Client
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Bean.xml");
UserService userService = context.getBean("userServiceImpl", UserService.class);
userService.select();
}
}
创建Bean.xml
先导入命名空间:
<?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>
< aop:aspect >与 < aop:advisor > 的区别
参考博客:
https://blog.youkuaiyun.com/u011983531/article/details/70504281
在开发过程中,不少有Spring Aop的使用,在面向切面编程时,我们会使用 < aop:aspect >;
在进行事务管理时,我们会使用 < aop:advisor >。
那么,对于 < aop:aspect > 与 < aop:advisor > 的区别,具体是怎样的呢?
至于两者的区别,网上有很多资料,但是似乎都不能说清楚。
首先,我们需要明确两者的概念。
< aop:aspect >:定义切面(切面包括通知和切点)< aop:advisor >:定义通知器(通知器跟切面一样,也包括通知和切点)
< aop:advisor > 大多用于事务管理。
< aop:aspect > 大多用于日志,缓存。
< aop:advisor > 和 < aop:aspect > 实现逻辑是一样的。
< aop:advisor > 和 < aop:aspect > 都是将通知和切面进行了封装,原理基本上是一样的,只是使用的方式不同而已。
expression表达式解析
参考博客:
https://blog.youkuaiyun.com/qq525099302/article/details/53996344
任意公共方法的执行:
execution(public * *(..))
任何一个以“set”开始的方法的执行:
execution(* set*(..))
AccountService 接口的任意方法的执行:
execution(* com.xyz.service.AccountService.*(..))
定义在service包里的任意方法的执行:
execution(* com.xyz.service.*.*(..))
定义在service包和所有子包里的任意类的任意方法的执行:
execution(* com.xyz.service..*.*(..))
定义在pointcutexp包和所有子包里的JoinPointObjP2类的任意方法的执行:
execution(* com.test.spring.aop.pointcutexp..JoinPointObjP2.*(..))")
***> 最靠近(..)的为方法名,靠近.*(..))的为类名或者接口名,如上例的JoinPointObjP2.*(..))
pointcutexp包里的任意类.
within(com.test.spring.aop.pointcutexp.*)
pointcutexp包和所有子包里的任意类.
within(com.test.spring.aop.pointcutexp..*)
实现了MyInterface接口的所有类,如果MyInterface不是接口,限定MyInterface单个类.
this(com.test.spring.aop.pointcutexp.MyInterface)
***> 当一个实现了接口的类被AOP的时候,用getBean方法必须cast为接口类型,不能为该类的类型.
带有@MyTypeAnnotation标注的所有类的任意方法.
@within(com.elong.annotation.MyTypeAnnotation)
@target(com.elong.annotation.MyTypeAnnotation)
带有@MyTypeAnnotation标注的任意方法.
@annotation(com.elong.annotation.MyTypeAnnotation)
***> @within和@target针对类的注解,@annotation是针对方法的注解
参数带有@MyMethodAnnotation标注的方法.
@args(com.elong.annotation.MyMethodAnnotation)
参数为String类型(运行是决定)的方法.
args(String)
方法一:通过Spring API实现
下面代码的意思是:以UserServiceImpl作为切入点去执行logBeforeService和logAfterService
<!--**方法一:通过 Spring AOP API 实现**-->
<aop:config>
<!--先连接切入点,后续需要围绕真实对象去实现。* 后面要有空格,是特殊规定。UserServiceImpl.*意思是类中所有方法,(..)不区分任何参数-->
<aop:pointcut id="pointcut" expression="execution(* com.fullstack.service.UserServiceImpl.*(..))"/>
<!--执行环绕: advice-ref是执行方法 ,pointcut-ref是切入点 -->
<aop:advisor advice-ref="logBeforeService" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="logAfterService" pointcut-ref="pointcut"/>
</aop:config>
方法二:自定义切面实现
先创建一个自定义的切面:
public class DiyAspect {
public void before(){
System.out.println("--[debug]方法执行前--");
}
public void after(){
System.out.println("--[debug]方法执行后--");
}
}
<!--**方法二:通过 自定义切面实现**-->
<!--新建的类,所以需要先注入进来-->
<bean id="diy" class="com.fullstack.aspect.DiyAspect"/>
<aop:config>
<aop:aspect ref="diy">
<aop:pointcut id="pointcut" expression="execution(* com.fullstack.service.UserServiceImpl.*(..))"/>
<aop:before method="before" pointcut-ref="pointcut" />
<aop:after method="after" pointcut-ref="pointcut" />
</aop:aspect>
</aop:config>
方法三:使用注解实现
注解的方式是创建一个类,以注解的形式把bean的功能实现,所以需要在Bean.xml中添加支持。
执行逻辑顺序是 :环绕前 > 运行 joinPoint.proceed() > 运行 @Before > 运行业务逻辑代码 > 运行 @After > 环绕后
@Aspect
public class AnnotationAspect {
// 前置增强
@Before("execution(* com.fullstack.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("------------[debug]方法执行前---------------");
}
// 后置增强
@After("execution(* com.fullstack.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("------------[debug]方法执行后---------------");
}
@Around("execution(* com.fullstack.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("-----------环绕前-------------");
System.out.println("[" + joinPoint.getSignature().getName() + "]的方法执行,参数是:" + Arrays.asList(joinPoint.getArgs()));
joinPoint.proceed();// 业务代码
System.out.println("-----------环绕后-------------");
}
}
<!--**方法三:使用注解实现实现**-->
<bean id="annotationAspect" class="com.fullstack.aspect.AnnotationAspect"/>
<aop:aspectj-autoproxy/>
AOP总结
- Aop的重要性:很重要。一定要理解其中的思路,主要是思想的理解这一块。
- Spring的Aop就是将公共的业务(日志 , 安全等)和领域业务结合起来,当执行领域业务时,将会把公共业务加进来。
- 实现公共业务的重复利用。领域业务更纯粹,程序猿专注领域业务,其本质还是动态代理。
791

被折叠的 条评论
为什么被折叠?



