目录
(一)AOP
1.1、概述
Spring中的代理机制是什么?
在Spring框架中,代理机制是一种实现AOP(面向切面编程)的方式。它允许在目标对象的方法执行前后添加额外的逻辑,而不需要修改目标对象的代码。
Spring中的代理机制主要有两种类型:JDK动态代理和CGLIB代理。
(一)JDK动态代理:基于接口的代理。当目标对象实现了接口时,Spring会使用JDK动态代理来创建代理对象。JDK动态代理通过反射机制,在运行时动态地创建代理对象,并将方法的调用转发给目标对象。JDK动态代理要求目标对象实现接口,因此它只能代理接口中定义的方法。
(二)CGLIB代理:基于类的代理。当目标对象没有实现接口时,Spring会使用CGLIB代理来创建代理对象。CGLIB代理通过继承目标对象的子类,并重写其中的方法来实现代理。CGLIB代理不要求目标对象实现接口,因此它可以代理任意的类。
Spring框架会根据配置和条件自动选择使用JDK动态代理还是CGLIB代理。通常情况下,如果目标对象实现了接口,则使用JDK动态代理;如果目标对象没有实现接口,则使用CGLIB代理。
代理机制在Spring中广泛应用于事务管理、日志记录、性能监控等方面,它可以将这些横切关注点与业务逻辑分离,提高代码的可维护性和可扩展性。
1.2、Aop的术语
1.切点(pointcut):需要处理的连接点,表示一组 joint point,它决定处理相应的 Advice (如权限校验、日志记录等)在何处切入业务代码中(即织入切面)。切点分为excution方式和annotation方式。
2.连接点(Joinpoint):执行过程中某个阶段点,包括方法调用、执行或者异常的处理和对类成员的访问等等。在 Spring AOP 中,一个连接点总是代表一个方法执行。
3.切面(Aspect):即Pointcut Advice。封装的用于横向插入系统的功能(事务/日志)Aspect 声明类似于 Java 中的类声明。
》Advice(通知/增强处理): AOP框架在特定的切入点(pointcut)执行的增强处理,包括处理时机和处理内容。处理内容就是要做什么事,比如校验权限和记录日志。处理时机就是在什么时机执行处理内容,它通过 before、after 和 around 来区别是在每个连接点(Joinpoint) 之前、之后还是代替执行的代码。即在定义好的切入点处所要执行的程序代码。可以将其理解为切面类中的方法。
》Target Obiect (目标对象): 指所有被通知的对象,也被称为增强对象。如果AOP框架采用的是动态的AOP实现,那么该对象就是一个被代理对象
》Proxy(代理):将通知应用到目标对象之后,被动态创建的对象。(媒婆,是一种思想)
》Weaving(织入):将切面代码插入到目标对象上来创建代理对象的过程。
Spring 的 通 知 类 型 | |
---|---|
前置通知(Before) | 在目标方法被调用之前调用通知功能 |
后置通知(After) | 在目标方法完成之后调用通知,此时不会关心方法的输出是什么 |
返回通知(After-returning) | 在目标方法成功执行之后调用通知 |
异常通知(After-throwing) | 在目标方法抛出异常后调用通知 |
环绕通知(Around) | 通知包裹了被通知的方法,在被通知的方法调用之前和之后执行自定义的行为 |
简单地去理解,其实AOP要做三类事:
-
在哪里切入,也就是权限校验等非业务操作在哪些业务代码中执行。
-
在什么时候切入,是业务代码执行前还是执行后。
-
切入后做什么事,比如做权限校验、日志记录等。
(二)代理模式
为什么要学习代理模式?
因为这就是springAOP的底层!【springAOP和springMVC】
1、代理模式?
为某一个对象(委托类)提供一个代理(代理类),类似于媒婆,用来控制对这个对象的访问。委托类和代理类有一个共同的父类或父接口。代理类会对请求做预处理、过滤,将请求分配给指定对象。即在原始类的基础上进行增强
2、分类
按照代理创建的时期进行分类,可以分为两类:静态代理、动态代理。
静态代理的代理类=原始类+增强(额外功能)+和原始类实现同一个接口,即基于接口的代理。
动态代理 又分为jdk动态代理,其也是基于接口的代理;cglib的动态代理。
2.1、静态代理
2.1.1、角色分析:
-
抽象角色:一般使用继承接口或者实现抽象类来解决,目标对象和代理对象共同的父接口(租房接口)
-
真实(目标)角色:被代理的角色,是最终要引用的目标对象(房东)。
-
代理角色:内部含有对真实对象的引用,代理真实角色,一般会做一些附属操作(中介)。
-
客户:访问代理对象的人!
2.1.2、静态代理
静态代理:在不修改原始类的基础上在定义方法的内部增强(额外功能)。
原始类:
//原始类:
//查询用户的接口
interface UserService{
//抽象方法 查找用户
public abstract void findName();
}
/**
* 业务逻辑:需要实现类来实现接口
* Alt+回车 实现方法
*要查找用户,得实现查找用户的接口
*/
class UserServiceImpl implements UserService{
@Override
public void findName() {
System.out.println("正在从数据库中查询所有的用户名。。。");
}
}
//现在staticApp要查找用户,直接找UserServiceImpl
public class staticApp {
public static void main(String[] args) {
//1、普通。接口不能new ,通过它的实现类new
UserService userService = new UserServiceImpl();
userService.findName();
}
}
正在从数据库中查询所有的用户名。。。
以上是原始类,现在需求增强,需要在findName() 执行之前进行增强(额外功能)。
class User_ServiceImpl implements User_Service{
@Override
public void findName() {
//增强(额外功能):增强查询的内容
System.out.println("正在从数据库中查询所有的用户名。。。");
}
}
静态代理:在不修改原始类的基础上在定义方法的内部增强(额外功能)。
实现步骤:
-
定义⼀个接口及其实现类;
-
创建⼀个代理类同样 实现这个接口;
-
将目标对象注入进代理类,然后在代理类的对应方法调用目标类中的对应方法。
-
本质:重写。
这时就需要一个代理(中介)来—代理原来的方法
//查询用户的接口
interface User_Service{
public abstract void findName();
}
//业务逻辑:需要实现类来实现接口
class User_ServiceImpl implements User_Service{
@Override
public void findName() {
System.out.println("正在从数据库中查询所有的用户名。。。");
}
}
/**
* 静态方法:在不修改原来的基础上,制定方法内容 进行增强,
* 这时就需要一个代理来—代理原来的方法—
* 代理需要实现业务类相同的接口 - (增强业务,需要实现这个方法Alt+回车)
* * *静态方法:在代理时就确认了 需要要代理的—UserServiceImpl—类,通过这个类来调用方法:
* findName() {System.out.println("正在从数据库中查询所有的用户名。。。");
* *:此时就不能使用User_ServiceImpl而应该使用-代理User_ServiceImplProxy实现类,但它们调用的都是一样的方法
* 代理:在原来的基础上进行增强。
*/
class User_ServiceImplProxy implements User_Service{
//静态代理对象:要代理的User_ServiceImpl类,通过类来调用方法
private User_ServiceImpl user_service = new User_ServiceImpl();
@Override
public void findName() {
System.out.println("此时是增强的方法");
//通知被代理对象(真实角色)进行业务处理
user_service.findName();
}
}
public class static_App {
public static void main(String[] args) {
// 1、普通。接口不能new ,通过它的实现类new
// User_Service user_service = new User_ServiceImpl();
// user_service.findName();
/**
* 2、静态方法
**:此时就不能使用User_ServiceImpl而应该使用-代理User_ServiceImplProxy实现类,但它们调用的都是一样的方法
*/
User_Service user_service = new User_ServiceImplProxy();
user_service.findName();
}
}
此时是增强的方法
正在从数据库中查询所有的用户名。。。
上面就是所谓的静态代理。
缺点:原始的类(User_ServiceImpl)如果有多个方法,那么我们需要实现更多方法。
interface User_Service{
public abstract void findName();
public abstract void delName();
}
class User_ServiceImpl implements User_Service{
@Override
public void findName() {
System.out.println("正在从数据库中查询所有的用户名。。。");
}
@Override
public void delName() {
System.out.println("删除用户");
}
}
class User_ServiceImplProxy implements User_Service{
private User_ServiceImpl user_service = new User_ServiceImpl();
@Override
public void findName() {
System.out.println("此时是增强的方法");
//通知被代理角色(真实角色)进行业务处理
user_service.findName();
}
@Override
public void delName() {
System.out.println("此时是增强的方法");
//通知被代理角色(真实角色)进行业务处理
user_service.delName();
}
}
public class static_App {
public static void main(String[] args) {
User_Service user_service = new User_ServiceImplProxy();
user_service.findName();
user_service.delName();
}
}
2.2、动态代理:
在程序执行过程中,使用jdk的反射机制,创建代理类对象,并动态的指定要代理的目标类。换句话说:动态代理是一种创建java对象的能力,让你不用创建代理类,就能创建代理类的对象。
2.2.1、 代理的对象:
静态代理:在编译器编译,写代码时就确定好了需要要代理的对象(编写了代理类)。
动态代理:在运行期间才确定的代理对象(通过反射自动生成代理类)
2.2.2、JDK动态代理:
在 Java 动态代理机制中 InvocationHandler 接口 和 Proxy 类 是核心。
一、JDK 动态代理类步骤:
1、 定义⼀个接口及其实现类。
2、自定义 InvocationHandler 并重写 invoke 方法 ,在 invoke 方法中会调用原始方法(被代理类的方法)并自定义⼀些处理逻辑。
3、通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法创建一个代理对象的类;
//查询用户的接口
interface User_Service{
public abstract void findName();
}
//业务逻辑:需要实现类来实现接口
class User_ServiceImpl implements User_Service{
@Override
public void findName() {
System.out.println("正在从数据库中查询所有的用户名(----真实类执行逻辑----)");
}
}
//自动生成代理类!
//InvocationHandler接口中声明了一个 invoke方法,invoke方法是实际进行业务增强处理。
class User_ServiceImplProxy_demo implements InvocationHandler{
//动态代理对象:写代理的时候并不知道要代理哪个对象object,从外界调用时进行获取get()
// **获取原始对象,产生代理对象**
//声明 被代理对象/被代理的接口 (谁被代理)object:Impl
private Object object;
//生成得到 动态代理类-中介-
public Object get(Object o){
this.object = o;
//Proxy类newProxyInstance方法生成动态代理对象(中介)/动态生成代理类。
//newProxyInstance(传统构造器 接口(JDK依靠接口动态代理))
//创建一个代理类(中介):通过 被代理对象(impl)的类加载器(getClassLoader: 类加载器)、被代理(impl)实现的接口(getInterfaces: 共同的接口)、方法调用-处理器—来创建。
return Proxy.newProxyInstance(
this.object.getClass().getClassLoader(),
this.object.getClass().getInterfaces(),this);
}
/**
* 通过invoke方法进行业务增强操作。
* invoke方法:代理执行逻辑
* @param proxy:获取动态生成的代理对象(中介)的实例。被代理的类: —— 真实类(房东) ——
* @param method:被代理对象(--真实类--)当前正在执行的业务方法
* @param Object[] args:method参数
* @throws Throwable
* invoke:(代理对象,外界传过来的对象 来调用方法,代理的参数)
*/
//处理代理实例,返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("这是增强之后的代码");
//通知被代理角色(真实角色)进行业务处理
return method.invoke(this.object,args);
}
}
public class static_App {
public static void main(String[] args) {
//3、动态
//创建被代理对象(真实角色)/原始代理的对象。user_service
User_Service user_service = new User_ServiceImpl();
User_ServiceImplProxy_demo Proxy_demo = new User_ServiceImplProxy_demo();
//*获取动态生成的代理对象*Proxy(代理:现在没有) 利用(impl)接口去接代理对象Proxy:get(user_service)
User_Service user_serviceImplProxy = (User_Service)
Proxy_demo.get(user_service);
//用代理Proxy:Impl 去调用接口里的方法findName()--->invoke方法
user_serviceImplProxy.findName();
}
}
这是增强之后的代码
正在从数据库中查询所有的用户名。。。(----真实类执行逻辑----)
二、了解
1、反射包:java.lang.reflect
三个类:
-
Proxy:动态生成的代理类。
-
InvocationHandler:调用处理程序,是由代理实例的调用处理程序实现的接口。
-
Method:方法,类中的方法。
2、两个方法:
(1)newProxyInstance()返回指定接口的代理类的实例,该接口将方法调用分派给指定的调用处理程序。
newProxyInstance 方法有三个参数:
loader: 用哪个类加载器去加载代理对象/类加载器来定义代理类
interfaces/:动态代理类需要实现的接口/代理类实现的接口列表
h:动态代理方法在执行时,会调用h里面的invoke方法去执行/调度方法调用的调用处理函数
-
ClassLoader loader 类加载器,用于加载代理对象的字节码
-
Class<?>[] interfaces 字节码数组,用于让代理对象(中介)和被代理对象(房东)有相同的方法
-
InvocationHandler h 处理方法,用于方法增强
要实现动态代理的话,还必须需要实现InvocationHandler
来自定义处理逻辑。当我们的动态代理对象调用一个方法时候,这个方法的调用就会被转发到实现InvocationHandler
接口类的 invoke
方法来调用。
(2)invoke() 方法有下面三个参数:
-
1、proxy :动态生成的代理类
-
2、method : 与代理类对象调用的方法相对应
-
3、args : 当前 method 方法的参数
通过Proxy 类的 newProxyInstance() 创建的代理对象在调用方法的时候,实际会调用到实现InvocationHandler 接口的类的 invoke()方法。 你可以在 invoke() 方法中自定义处理逻辑,比如在方法执行前后做什么事情。
三、动态代理的好处:
-
可以使真实角色的操作更加纯粹!不用去关注一些公共的业务!
-
公共也就就交给代理角色!实现了业务的分工!
-
公共业务发生扩展的时候,方便集中管理!
-
一个动态代理类代理的是一个接口,一般就是对应的一类业务!
-
一个动态代理类可以代理多个类,只要是实现了同一个接口即可!