Spring学习笔记【十一】静态代理和动态代理

静态代理

01 代理设计模式

  1. 通过代理类,为目标类添加额外的功能
  2. 好处:利于目标类的维护

02 名词解释

  1. 目标类:业务类
  2. 目标方法:目标类中的方法,就是目标方法
  3. 额外功能(附加功能):一般是指 日志,事务,性能

03 代理开发的核心要素

  1. 代理类 = 目标类 + 额外功能 + 目标类实现相同的接口

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 存在的问题

  1. 静态类的文件数量过多,不利于项目管理
  • 通过上面可以发现,一个Service类需要一个代理类,当Service类增加的时候,代理类的数量也随之增加,类的数量成倍增加
  1. 代理类中, 额外功能维护性较差

动态代理

01 基础信息

  1. 概念:通过代理类为原始类(目标类)添加额外功能
  2. 好处:利用原始类的维护
  3. 开发步骤
    • 创建目标对象
    • 添加额外功能
    • 定义切入点
    • 进行组装

02 搭建开发环境

  1. 添加依赖
<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>
  1. 创建原始类
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>
  1. 额外功能
  • 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>
  1. 定义切入点
  2. 切入点:额外功能加入的位置
  3. 目的:程序员根据自己的需求,决定额外功能所要加给哪一个方法
<aop:config>
    <aop:pointcut id="pc" expression="execution(* *(..))"></aop:pointcut>
</aop:config>
  1. 组装
  • 将步骤 三 和 步骤 四 进行整合
<aop:config>
     <aop:pointcut id="pc" expression="execution(* *(..))"></aop:pointcut>
     <aop:advisor advice-ref="before" pointcut-ref="pc"></aop:advisor>
</aop:config>
  1. 调用
  2. 目的:获得Spring工厂所创建的动态代理对象,并进行调用
  3. Spring工厂通过原始对象的** id值 获得的是代理对象**
  4. 获得代理对象之后,可以通过声明接口类型,进行对象的存储

03 动态代理细节分析


Spring创建的动态代理类到底在哪里

  1. Spring框架在运行时, 通过动态字节码技术, 在JVM创建的, 运行在JVM内部, 等程序结束后, 会和JVM一起消失
    • 动态字节码:通过第三方动态字节码框架,在JVM中创建对应类的字节码,进而创建对象,当虚拟机结束之后,动态字节码跟着消失
  2. 动态代理不需要定义类文件,都是JVM在运行过程中动态创建的,所以说不会造成静态代理,类文件数量过多,影响项目管理的问题
  3. 在额外功能不改变的前提之下,创建其他目标类(原始类)的代理对象,只需要指定原始(目标)对象即可
  4. 动态代理额外功能的维护性大大增强

04 动态代理详解

4.1 MethodBeforeAdvice

  1. 作用:额外功能运行在原始方法执行之前, 进行额外功能操作
  2. 需要把运行在原始方法执行之前运行的额外功能,书写在before方法中
  3. 参数
    • Method:额外功能所增加给的那个原始方法
    • Object[] args:额外功能所增加给的那个原始方法的参数
    • Object target:额外功能所增加给的那个原始对象

4.2 MethodInterceptor

额外功能 可以根据需求运行在原始方法执行之前,之后,或者是前后

MethodBeforeAdvice提供的功能相对于比较单一,只能添加功能到原始方法之前

实现MethodInterceptor接口

  • 参数:methodInvocation:额外功能所增加给的那个原始方法
  • 返回值:Object原始方法的返回值
    • 将原始方法的返回值,直接作为invoke方法的返回值返回,MethodeInterceptor不会影响原始方法的返回值
    • 在实现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

  1. 最为重要的切入点函数, 功能最全
  2. 弊端:书写麻烦

6.2 args

  1. 作用:主要用于函数方法的匹配
  2. 格式:args(String,String)

6.3 within

  1. 作用:主要用于进行类,包切入点表达式的匹配
  2. 案例一
切入点: UserServiceImpl 这个类

 # 使用execution
execution(* *..UserServiceImpl.*(..))

# 使用within
within(*..UseserviceImpl)

  1. 案例二
切入点 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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

佩奇inging

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值