文章目录
静态代理
01 代理设计模式
- 通过代理类,为目标类添加额外的功能
- 好处:利于目标类的维护
02 名词解释
- 目标类:业务类
- 目标方法:目标类中的方法,就是目标方法
- 额外功能(附加功能):一般是指 日志,事务,性能
03 代理开发的核心要素
- 代理类 = 目标类 + 额外功能 + 目标类实现相同的接口
04 静态代理
有一个原始类,就有一个代理类,这里的代理类是手动编写的
4.1 编码
创建Service接口
package com.spring.proxy;
public interface UserService {
boolean login(User user);
boolean register(User user);
}
创建Service接口的实现类ServiceImpl
package com.spring.proxy;
public class UserServiceImpl implements UserService {
@Override
public boolean login(User user) {
System.out.println("UserServiceImpl.login");
return true;
}
@Override
public boolean register(User user) {
System.out.println("UserServiceImpl.register");
return true;
}
}
创建代理类
package com.spring.proxy;
public class UserServiceProxy implements UserService{
private UserServiceImpl userService = new UserServiceImpl();
@Override
public boolean login(User user) {
System.out.println("--------日志--------------");
return userService.login(user);
}
@Override
public boolean register(User user) {
System.out.println("--------日志--------------");
return userService.register(user);
}
}
创建测试类
- 这里new的是代理类
UserService userService = new UserServiceProxy();
User user = new User();
userService.login(user);
userService.register(user);
4.2 存在的问题
- 静态类的文件数量过多,不利于项目管理
- 通过上面可以发现,一个Service类需要一个代理类,当Service类增加的时候,代理类的数量也随之增加,类的数量成倍增加
- 代理类中, 额外功能维护性较差
动态代理
01 基础信息
- 概念:通过代理类为原始类(目标类)添加额外功能
- 好处:利用原始类的维护
- 开发步骤
- 创建目标对象
- 添加额外功能
- 定义切入点
- 进行组装
02 搭建开发环境
- 添加依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.17.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.8.M1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.5.3</version>
</dependency>
- 创建原始类
public interface OrderService {
void order(Order order);
}
public class OrderServiceImpl implements OrderService {
@Override
public void order(Order order) {
System.out.println("OrderServiceImpl.order");
}
}
<bean id="orderService" class="com.spring.proxy.OrderServiceImpl"></bean>
- 额外功能
- MethodeBeforeAdvice 接口:额外功能书写在接口的实现之中
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class Before implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("--------------methode before advice 提供日志---------------------");
}
}
- 在Spring的配置文件进行注册
<bean id="before" class="com.spring.proxy.Before"></bean>
- 定义切入点
- 切入点:额外功能加入的位置
- 目的:程序员根据自己的需求,决定额外功能所要加给哪一个方法
<aop:config>
<aop:pointcut id="pc" expression="execution(* *(..))"></aop:pointcut>
</aop:config>
- 组装
- 将步骤 三 和 步骤 四 进行整合
<aop:config>
<aop:pointcut id="pc" expression="execution(* *(..))"></aop:pointcut>
<aop:advisor advice-ref="before" pointcut-ref="pc"></aop:advisor>
</aop:config>
- 调用
- 目的:获得Spring工厂所创建的动态代理对象,并进行调用
- Spring工厂通过原始对象的** id值 获得的是代理对象**
- 获得代理对象之后,可以通过声明接口类型,进行对象的存储
03 动态代理细节分析
Spring创建的动态代理类到底在哪里
- Spring框架在运行时, 通过动态字节码技术, 在JVM创建的, 运行在JVM内部, 等程序结束后, 会和JVM一起消失
- 动态字节码:通过第三方动态字节码框架,在JVM中创建对应类的字节码,进而创建对象,当虚拟机结束之后,动态字节码跟着消失
- 动态代理不需要定义类文件,都是JVM在运行过程中动态创建的,所以说不会造成静态代理,类文件数量过多,影响项目管理的问题
- 在额外功能不改变的前提之下,创建其他目标类(原始类)的代理对象,只需要指定原始(目标)对象即可
- 动态代理额外功能的维护性大大增强
04 动态代理详解
4.1 MethodBeforeAdvice
- 作用:额外功能运行在原始方法执行之前, 进行额外功能操作
- 需要把运行在原始方法执行之前运行的额外功能,书写在before方法中
- 参数
Method
:额外功能所增加给的那个原始方法Object[] args
:额外功能所增加给的那个原始方法的参数Object target
:额外功能所增加给的那个原始对象
4.2 MethodInterceptor
额外功能 可以根据需求运行在原始方法执行之前,之后,或者是前后
MethodBeforeAdvice提供的功能相对于比较单一,只能添加功能到原始方法之前
实现MethodInterceptor接口
- 参数:
methodInvocation
:额外功能所增加给的那个原始方法 - 返回值:
Object
原始方法的返回值- 将原始方法的返回值,直接作为invoke方法的返回值返回,
MethodeInterceptor
不会影响原始方法的返回值 - 在实现
invoke方法
不要用原始方法的返回值即可,就可以改变
- 将原始方法的返回值,直接作为invoke方法的返回值返回,
methodeInvocation.proceed()
:原始方法运行
public class Around implements MethodInterceptor {
/*
* 参数:额外功能所增加给的哪个原始方法
* 返回值:原始方法的返回值
* */
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("--------额外功能------");
// 原始方法运行
Object object = methodInvocation.proceed();
System.out.println("--------额外功能------");
return object;
}
}
05 切入点表达式
切入点额外功能加入的位置
5.1 通配符
符号 | 意义 |
---|---|
* | 0至多个任意字符 |
… | 用在方法参数中,表示任意多个参数,用在包名之后,表示当前包以及子包路径 |
+ | 用在类名后,表示当前类以及子类,用在接口之后,表示当前接口以及实现类 |
5.1 方法切入点
execution( [访问权限 返回值] [方法名(参数列表)] 异常类型)
execution()
:切入点函数- 函数中所写的就是切入点表达式
- 访问权限和异常类型一般省略不写
案例一:定义login方法作为切入点
execution(* login(..))
案例二:定义两个String类型的login方法作为切入点
execution(* login(String,String))
案例三:定义一个方法是User类型的login方法作为切入点
execution(* login(com.spring.proxy.User))
案例四:定义一个login方法,参数列表第一个是String的方法作为切入点
execution(* login(String,..))
案例五:指定切入点是任何一个以set开头的方法
execution(* set*(..))
案例六:指定切入点为任何一个公共方法
execution(public * *(..))
案例七:精准定位
修饰符 返回值 包.类.方法(参数)
execution(* com.spring.proxy.UserService.login(com.spring.proxy.User))
5.2 类切入点
指定特定类作为切入点,自然这个类中的所有方法,都会加上额外功能
案例一:给**UserserviceImpl**
类中所有方法都加入额外功能
execution(* com.spring.proxy.UserServiceImpl.*(..))
5.3 包切入点
指定包作为额外功能的加入位置, 自然包中的所有类以及方法都会加上额外功能
案例一:切入点包中所有的类,必须在proxy包中,但是不能再proxy包的子包中
execution(* com.spring.proxy.*.*(..))
案例二:切入点包以及子包都生效
execution(* com.spring.proxy..*.*(..))
06 切入点函数
作用:用于执行切入定表达式
6.1 execution
- 最为重要的切入点函数, 功能最全
- 弊端:书写麻烦
6.2 args
- 作用:主要用于函数方法的匹配
- 格式:
args(String,String)
6.3 within
- 作用:主要用于进行类,包切入点表达式的匹配
- 案例一
切入点: UserServiceImpl 这个类
# 使用execution
execution(* *..UserServiceImpl.*(..))
# 使用within
within(*..UseserviceImpl)
- 案例二
切入点 com.spring.proxy 这个包
# 使用executino
execution(* com.spring.proxy..*.**(..))
# 使用within
within(com.spring.proxy..*)
6.4 @annotation
定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
}
在指定方法上加上注解
@Override
@Log
public boolean login(String username, String password) {
System.out.println("UserServiceImpl.login");
return true;
}
切入点表达式
<aop:config>
<aop:pointcut id="pc" expression="@annotation(com.spring.proxy.Log)"/>
<aop:advisor advice-ref="around" pointcut-ref="pc"/>
</aop:config>
6.5 切入点操作的逻辑运算
6.5.1 and操作
execution(* login(..)) and args(String,String)
6.5.2 or操作
execution(* login(..)) or args(String,String)