代理模式

  • 为什么要学习代理模式,aop的底层机制就是动态代理。
  • 代理模式

    • 静态代理
    • 动态代理

      静态代理举例

  • 创建一个抽象角色,例如用户业务,crud

    1. //抽象角色:增删改查业务
    2. public interface UserService {
    3. void add();
    4. void delete();
    5. void update();
    6. void query();
    7. }
  • 需要一个真实对象来完成这些增删改查操作

    1. //真实对象,完成增删改查操作的人
    2. public class UserServiceImpl implements UserService {
    3. public void add() {
    4. System.out.println("增加了一个用户");
    5. }
    6. public void delete() {
    7. System.out.println("删除了一个用户");
    8. }
    9. public void update() {
    10. System.out.println("更新了一个用户");
    11. }
    12. public void query() {
    13. System.out.println("查询了一个用户");
    14. }
    15. }
  • 需求来了,需要增加一个日志功能,

    • 思路1 实现类中添加代码,
    • 思路2 使用代理来做,不改变原代码的情况下实现
  • 设置一个代理类来处理日志,代理角色 ```java //代理角色,在这里面增加日志的实现 // userServiceProxy 为 userService 接口的一个实现 public class UserServiceProxy implements UserService { // 将 原实现类作为属性引入进当前实现类 private UserServiceImpl userService; // set方法,提供一个对其进行动态赋值的入口 public void setUserService(UserServiceImpl userService) {

    1. this.userService = userService;

    }

    public void add() {

    1. log("add"); // 代理的新增方法
    2. userService.add(); // 原实现类方法

    }

    public void delete() {

    1. log("delete");
    2. userService.delete();

    }

    public void update() {

    1. log("update");
    2. userService.update();

    }

    public void query() {

    1. log("query");
    2. userService.query();

    }

    public void log(String msg){

    1. System.out.println("执行了"+msg+"方法");

    }

}

  1. 如上,在不改变原生代码的情况下对其进行了拓展<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/1608527/1616416691161-a1d3450c-0686-4247-a86e-ada26827c3c7.png#align=left&display=inline&height=191&margin=%5Bobject%20Object%5D&name=image.png&originHeight=381&originWidth=1038&size=115817&status=done&style=none&width=519)
  2. <a name="3YdX6"></a>
  3. # 动态代理
  4. - 动态代理的角色和静态代理一样
  5. - 动态代理的代理类是动态生成的,静态代理的代理类是提前写好的
  6. - 动态代理分为两类,一类基于接口的动态代理,以类是基于类的动态代理
  7. - 基于接口的动态代理 == jdk动态代理
  8. - 基于类的动态代理 ==cglib
  9. - 现在常见的是javasist 来生成动态代理,
  10. - 下面使用JDK的原生代码来实现,其余道理一样。
  11. <a name="AInjK"></a>
  12. # 动态代理的好处
  13. - 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .<br />
  14. - 公共的业务由代理来完成 . 实现了业务的分工 ,<br />
  15. - 公共业务发生扩展时变得更加集中和方便 .<br />
  16. - 一个动态代理 , 一般代理某一类业务<br />
  17. - 一个动态代理可以代理多个类,代理的是接口!
  18. <a name="mr1Ub"></a>
  19. # 什么是AOP
  20. - AOPAspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOPOOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
  21. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/1608527/1616417760508-901ff4ba-d266-44fd-bef9-eec67771ce80.png#align=left&display=inline&height=276&margin=%5Bobject%20Object%5D&name=image.png&originHeight=551&originWidth=952&size=197183&status=done&style=none&width=476)
  22. <a name="iHVLg"></a>
  23. # AOP在Spring中的作用
  24. - 提供声明式事务:允许用户自定义切面
  25. 以下名次需要了解:
  26. - 横切关注点:跨越应用程序多个模块的方法或者功能,即是,与我们业务逻辑无关,但是需要关注的部分,就是横切关注点,如,日志,安全,缓存,事务等等。
  27. - 切面 ASPECT 横切关注点,被模块化的特殊对象,即,它是一个类
  28. - 通知 Adivice 切面必须完成的工作,即,它是类中一个方法
  29. - 目标 Target 被通知对象
  30. - 代理 Proxy 想目标对象应用通知之后创建的对象。
  31. - 切入点 PointCut 切面通知 执行的 ‘地点’ 定义
  32. - 链接点 JoinPoint 与切入点匹配的执行点
  33. - ![image.png](https://cdn.nlark.com/yuque/0/2021/png/1608527/1616418451912-ffec2733-d6bd-4f37-80d7-4b3a6e48116d.png#align=left&display=inline&height=236&margin=%5Bobject%20Object%5D&name=image.png&originHeight=471&originWidth=744&size=209292&status=done&style=none&width=372)
  34. - SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:
  35. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/1608527/1616418472232-76627357-672d-4304-887a-8f27977ffb84.png#align=left&display=inline&height=237&margin=%5Bobject%20Object%5D&name=image.png&originHeight=474&originWidth=762&size=154796&status=done&style=none&width=381)
  36. - 即是,AOP在不改变原有代码的情况下,去增加新的功能。
  37. <a name="mxgB7"></a>
  38. # Spring 实现aop
  39. - 导入依赖
  40. ```java
  41. <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
  42. <dependency>
  43. <groupId>org.aspectj</groupId>
  44. <artifactId>aspectjweaver</artifactId>
  45. <version>1.9.4</version>
  46. </dependency>

通过Spring API实现

  • 首先,业务接口,实现类 ```java public interface UserService {

    public void add();

    public void delete();

    public void update();

    public void search();

}

  1. ```java
  2. public class UserServiceImpl implements UserService{
  3. @Override
  4. public void add() {
  5. System.out.println("增加用户");
  6. }
  7. @Override
  8. public void delete() {
  9. System.out.println("删除用户");
  10. }
  11. @Override
  12. public void update() {
  13. System.out.println("更新用户");
  14. }
  15. @Override
  16. public void search() {
  17. System.out.println("查询用户");
  18. }
  19. }
  • 之后是增强类,一个是前置增强,一个后置增强。

    1. public class Log implements MethodBeforeAdvice {
    2. //method : 要执行的目标对象的方法
    3. //objects : 被调用的方法的参数
    4. //Object : 目标对象
    5. @Override
    6. public void before(Method method, Object[] objects, Object o) throws Throwable {
    7. System.out.println( o.getClass().getName() + "的" + method.getName() + "方法被执行了");
    8. }
    9. }
    1. public class AfterLog implements AfterReturningAdvice {
    2. //returnValue 返回值
    3. //method被调用的方法
    4. //args 被调用的方法的对象的参数
    5. //target 被调用的目标对象
    6. @Override
    7. public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
    8. System.out.println("执行了" + target.getClass().getName()
    9. +"的"+method.getName()+"方法,"
    10. +"返回值:"+returnValue);
    11. }
    12. }
  • 最后去spring的文件中注册 , 并实现aop切入实现 , 注意导入约束

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <beans xmlns="http://www.springframework.org/schema/beans"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xmlns:aop="http://www.springframework.org/schema/aop"
    5. xsi:schemaLocation="http://www.springframework.org/schema/beans
    6. http://www.springframework.org/schema/beans/spring-beans.xsd
    7. http://www.springframework.org/schema/aop
    8. http://www.springframework.org/schema/aop/spring-aop.xsd">
    9. <!--注册bean-->
    10. <bean id="userService" class="com.kuang.service.UserServiceImpl"/>
    11. <bean id="log" class="com.kuang.log.Log"/> //前置方法所在类的bean
    12. <bean id="afterLog" class="com.kuang.log.AfterLog"/>
    13. // 后置方法所在类的bean
    14. <!--aop的配置-->
    15. <aop:config>
    16. <!--切入点 expression:表达式匹配要执行的方法-->
    17. <aop:pointcut id="pointcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
    18. <!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
    19. <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
    20. <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    21. </aop:config>
    22. </beans>
  • 测试

    1. public class MyTest {
    2. @Test
    3. public void test(){
    4. ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    5. UserService userService = (UserService) context.getBean("userService");
    6. userService.search();
    7. }
    8. }
  • AOP重要性: 非常重要,要理解思想。

    • Spring AOp就是将公共的业务(日志,安全等)和领域业务结合起来,当执行领域业务时,将会吧公公业务加起来,实现公公业务的重复利用,领域业务更加纯粹,专注于业务代码,本质还是动态代理。

      自定义类实现AOP

  • 目标待增强业务类依然是 userServiceImpl

  • 写一个自定义切入类 ```java public class DiyPointcut {

    public void before(){

    1. System.out.println("---------方法执行前---------");

    } public void after(){

    1. System.out.println("---------方法执行后---------");

    }

}

  1. - xml文件中进行配置
  2. ```java
  3. <!--第二种方式自定义实现-->
  4. <!--注册bean-->
  5. <bean id="diy" class="com.kuang.config.DiyPointcut"/>
  6. <!--aop的配置-->
  7. <aop:config>
  8. <!--第二种方式:使用AOP的标签实现-->
  9. <aop:aspect ref="diy">
  10. <aop:pointcut id="diyPonitcut" expression="execution(* com.kuang.service.UserServiceImpl.*(..))"/>
  11. <aop:before pointcut-ref="diyPonitcut" method="before"/>
  12. <aop:after pointcut-ref="diyPonitcut" method="after"/>
  13. </aop:aspect>
  14. </aop:config>
  • 测试

    1. public class MyTest {
    2. @Test
    3. public void test(){
    4. ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    5. UserService userService = (UserService) context.getBean("userService");
    6. userService.add();
    7. }
    8. }

    注解实现AOP

  • 编写一个注解实现的增强类 ```java package com.kuang.config;

import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before;

@Aspect public class AnnotationPointcut { @Before(“execution( com.kuang.service.UserServiceImpl.(..))”) public void before(){ System.out.println(“————-方法执行前————-“); }

  1. @After("execution(* com.kuang.service.UserServiceImpl.*(..))")
  2. public void after(){
  3. System.out.println("---------方法执行后---------");
  4. }
  5. @Around("execution(* com.kuang.service.UserServiceImpl.*(..))")
  6. public void around(ProceedingJoinPoint jp) throws Throwable {
  7. System.out.println("环绕前");
  8. System.out.println("签名:"+jp.getSignature());
  9. //执行目标方法proceed
  10. Object proceed = jp.proceed();
  11. System.out.println("环绕后");
  12. System.out.println(proceed);
  13. }

}

  1. - spring配置文件中,注册bean,并增加支持注解的配置。
  2. ```java
  3. <!--第三种方式:注解实现-->
  4. <bean id="annotationPointcut" class="com.kuang.config.AnnotationPointcut"/>
  5. <aop:aspectj-autoproxy/>
  • aop:aspectj-autoproxy 说明 ```java 通过aop命名空间的声明自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面。当然,spring 在内部依旧采用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,但具体实现的细节已经被隐藏起来了

有一个proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强,当配为时,表示使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。 ```