第一章、静态代理设计模式

1. 为什么需要代理设计模式

1.1 问题
  • 在JavaEE分层开发开发中,那个层次对于我们来讲最重要 ```markdown DAO —-> Service —> Controller

JavaEE分层开发中,最为重要的是Service层

  1. - Service层中包含了哪些代码?
  2. ```markdown
  3. Service层中 = 核心功能(几十行 上百代码) + 额外功能(附加功能)
  4. 1. 核心功能
  5. 业务运算
  6. DAO调用
  7. 2. 额外功能
  8. 1. 不属于业务
  9. 2. 可有可无
  10. 3. 代码量很小
  11. 事务、日志、性能...
  • 额外功能书写在Service层中好不好?

    1. Service层的调用者的角度(Controller):需要在Service层书写额外功能。
    2. 软件设计者:Service层不需要额外功能
  • 现实生活中的解决方式

image.png

2. 代理设计模式

1.1 概念
  1. 通过代理类,为原始类(目标)增加额外的功能
  2. 好处:利于原始类(目标)的维护

1.2名词解释
  1. 1. 目标类 原始类
  2. 指的是 业务类 (核心功能 --> 业务运算 DAO调用)
  3. 2. 目标方法,原始方法
  4. 目标类(原始类)中的方法 就是目标方法(原始方法)
  5. 3. 额外功能 (附加功能)
  6. 日志,事务,性能

1.3 代理开发的核心要素
  1. 代理类 = 目标类(原始类) + 额外功能 + 原始类(目标类)实现相同的接口
  2. 房东 ---> public interface UserService{
  3. m1
  4. m2
  5. }
  6. UserServiceImpl implements UserService{
  7. m1 ---> 业务运算 DAO调用
  8. m2
  9. }
  10. UserServiceProxy implements UserService
  11. m1
  12. m2

1.4 编码

静态代理:为每一个原始类,手工编写一个代理类 (.java .class)
image-20200422114654195.png

1.5 静态代理存在的问题
  1. 1. 静态类文件数量过多,不利于项目管理
  2. UserServiceImpl UserServiceProxy
  3. OrderServiceImpl OrderServiceProxy
  4. 2. 额外功能维护性差
  5. 代理类中 额外功能修改复杂(麻烦)

第二章、Spring的动态代理开发

1. Spring动态代理的概念

  1. 概念:通过代理类为原始类(目标类)增加额外功能
  2. 好处:利于原始类(目标类)的维护

2. 搭建开发环境

  1. <dependency>
  2. <groupId>org.springframework</groupId>
  3. <artifactId>spring-aop</artifactId>
  4. <version>5.1.14.RELEASE</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.aspectj</groupId>
  8. <artifactId>aspectjrt</artifactId>
  9. <version>1.8.8</version>
  10. </dependency>
  11. <dependency>
  12. <groupId>org.aspectj</groupId>
  13. <artifactId>aspectjweaver</artifactId>
  14. <version>1.8.3</version>
  15. </dependency>

3. Spring动态代理的开发步骤

  1. 创建原始对象(目标对象)

    1. public class UserServiceImpl implements UserService {
    2. @Override
    3. public void register(User user) {
    4. System.out.println("UserServiceImpl.register 业务运算 + DAO ");
    5. }
    6. @Override
    7. public boolean login(String name, String password) {
    8. System.out.println("UserServiceImpl.login");
    9. return true;
    10. }
    11. }
    1. <bean id="userService" class="com.baizhiedu.proxy.UserServiceImpl"/>
  2. 额外功能
    MethodBeforeAdvice接口

    1. 额外的功能书写在接口的实现中,运行在原始方法执行之前运行额外功能。
    1. public class Before implements MethodBeforeAdvice {
    2. /*
    3. 作用:需要把运行在原始方法执行之前运行的额外功能,书写在before方法中
    4. */
    5. @Override
    6. public void before(Method method, Object[] args, Object target) throws Throwable {
    7. System.out.println("-----method before advice log------");
    8. }
    9. }
    1. <bean id="before" class="com.baizhiedu.dynamic.Before"/>
  3. 定义切入点 ```xml 切入点:额外功能加入的位置

目的:由程序员根据自己的需要,决定额外功能加入给那个原始方法 register login

简单的测试:所有方法都做为切入点,都加入额外的功能。

  1. ```xml
  2. <aop:config>
  3. <aop:pointcut id="pc" expression="execution(* *(..))"/>
  4. </aop:config>
  1. 组装 (2 3整合)
    1. <!--表达的含义:所有的方法 都加入 before的额外功能-->
    2. <aop:advisor advice-ref="before" pointcut-ref="pc"/>

配置文件整体:
image.png

  1. 调用 ```java 目的:获得Spring工厂创建的动态代理对象,并进行调用 ApplicationContext ctx = new ClassPathXmlApplicationContext(“/applicationContext.xml”); 注意:
    1. Spring的工厂通过原始对象的id值获得的是代理对象
    2. 获得代理对象后,可以通过声明接口类型,进行对象的存储

UserService userService=(UserService)ctx.getBean(“userService”);

userService.login(“”) userService.register()

  1. <a name="X1G0t"></a>
  2. ### 4. 动态代理细节分析
  3. 1. Spring 创建的动态代理类在哪里?
  4. ```markdown
  5. Spring框架在运行时,通过动态字节码技术,在JVM创建的,运行在JVM内部,等程序结束后,会和JVM一起消失
  6. 什么叫动态字节码技术:通过第三个动态字节码框架,在JVM中创建对应类的字节码,进而创建对象,当虚拟机结束,动态字节码跟着消失。
  7. 结论:动态代理不需要定义类文件,都是JVM运行过程中动态创建的,所以不会造成静态代理,类文件数量过多,影响项目管理的问题。

image-20200423165547079.png

  1. 动态代理编程简化代理的开发

    1. 在额外功能不改变的前提下,创建其他目标类(原始类)的代理对象时,只需要指定原始(目标)对象即可。
  2. 动态代理额外功能的维护性大大增强

第三章、Spring动态代理详解

1. 额外功能的详解

  • MethodBeforeAdvice 分析 ```java 接口作用:额外功能运行在原始方法执行之前,进行额外功能操作。

public class Before1 implements MethodBeforeAdvice { /* 作用:需要把运行在原始方法执行之前运行的额外功能,书写在before方法中

  1. Method: 额外功能所增加给的那个原始方法
  2. login方法
  3. register方法
  4. showOrder方法
  5. Object[]: 额外功能所增加给的那个原始方法的参数。String name,String password
  6. User
  7. Object: 额外功能所增加给的那个原始对象 UserServiceImpl
  8. OrderServiceImpl
  9. */
  10. @Override
  11. public void before(Method method, Object[] args, Object target) throws Throwable {
  12. System.out.println("-----new method before advice log------");
  13. }

}

  1. before方法的3个参数在实战中,该如何使用。 before方法的参数,在实战中,会根据需要进行使用,不一定都会用到,也有可能都不用。

    Servlet{

    1. service(HttpRequest request,HttpResponse response){
    2. request.getParameter("name") -->
    3. response.getWriter() --->
    4. }

    } ```

  • MethodInterceptor(方法拦截器)

    1. methodinterceptor接口:额外功能可以根据需要运行在原始方法执行 前、后、前后。

    ```java public class Arround implements MethodInterceptor { /*

    1. invoke方法的作用:额外功能书写在invoke
    2. 额外功能 原始方法之前
    3. 原始方法之后
    4. 原始方法执行之前 之后
    5. 确定:原始方法怎么运行
    6. 参数:MethodInvocation Method):额外功能所增加给的那个原始方法
    7. login
    8. register
    9. invocation.proceed() ---> login运行
    10. register运行
    11. 返回值:Object: 原始方法的返回值
    12. Date convert(String name)

    */

  1. @Override
  2. public Object invoke(MethodInvocation invocation) throws Throwable {
  3. System.out.println("-----额外功能 log----");
  4. Object ret = invocation.proceed();//原有方法执行
  5. return ret;
  6. }

}

  1. - 额外功能运行在原始方法执行之后
  2. ```java
  3. @Override
  4. public Object invoke(MethodInvocation invocation) throws Throwable {
  5. Object ret = invocation.proceed();
  6. System.out.println("-----额外功能运行在原始方法执行之后----");
  7. return ret;
  8. }
  • 额外功能运行在原始方法执行之前,之后 ```java 什么样的额外功能 运行在原始方法执行之前,之后都要添加? 事务

@Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println(“——-额外功能运行在原始方法执行之前——“); Object ret = invocation.proceed(); System.out.println(“——-额外功能运行在原始方法执行之后——“);

return ret; }

  1. - 额外功能运行在原始方法抛出异常的时候
  2. ```java
  3. @Override
  4. public Object invoke(MethodInvocation invocation) throws Throwable {
  5. Object ret = null;
  6. try {
  7. ret = invocation.proceed();
  8. } catch (Throwable throwable) {
  9. System.out.println("-----原始方法抛出异常 执行的额外功能 ---- ");
  10. throwable.printStackTrace();
  11. }
  12. return ret;
  13. }
  • MethodInterceptor影响原始方法的返回值 ```markdown 原始方法的返回值,直接作为invoke方法的返回值返回,MethodInterceptor不会影响原始方法的返回值

MethodInterceptor影响原始方法的返回值 Invoke方法的返回值,不要直接返回原始方法的运行结果即可。

@Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println(“———log——-“); Object ret = invocation.proceed(); return false; }

  1. <a name="9decef58"></a>
  2. ### 2. 切入点详解
  3. ```xml
  4. 切入点决定额外功能加入位置(方法)
  5. <aop:pointcut id="pc" expression="execution(* *(..))"/>
  6. exection(* *(..)) ---> 匹配了所有方法 a b c
  7. 1. execution() 切入点函数
  8. 2. * *(..) 切入点表达式

2.1 切入点表达式

image-20200425101728992.png

  1. 方法切入点表达式
  1. * *(..) --> 所有方法
  2. * ---> 修饰符 返回值
  3. * ---> 方法名
  4. ()---> 参数表
  5. ..---> 对于参数没有要求 (参数有没有,参数有几个都行,参数是什么类型的都行)
  • 定义login方法作为切入点 ```markdown
    • login(..)

定义register作为切入点

  • register(..) ```

    • 定义login方法且login方法有两个字符串类型的参数 作为切入点 ```markdown
  • login(String,String)

注意:非java.lang包中的类型,必须要写全限定名

  • register(com.baizhiedu.proxy.User)

..可以和具体的参数类型连用

  • login(String,..) —> login(String),login(String,String),login(String,com.baizhiedu.proxy.User)

    1. - 精准方法切入点限定
    2. ```markdown
    3. 修饰符 返回值 包.类.方法(参数)
    4. * com.baizhiedu.proxy.UserServiceImpl.login(..)
    5. * com.baizhiedu.proxy.UserServiceImpl.login(String,String)
  1. 类切入点

    1. 指定特定类作为切入点(额外功能加入的位置),自然这个类中的所有方法,都会加上对应的额外功能
    • 语法1 ```markdown

      类中的所有方法加入了额外功能

  • com.baizhiedu.proxy.UserServiceImpl.*(..) ```

    • 语法2 ```markdown

      忽略包

  1. 类只存在一级包 com.UserServiceImpl
  • .UserServiceImpl.(..)
  1. 类存在多级包 com.baizhiedu.proxy.UserServiceImpl
  • ..UserServiceImpl.(..) ```
  1. 包切入点表达式 实战

    1. 指定包作为额外功能加入的位置,自然包中的所有类及其方法都会加入额外的功能
    • 语法1 ```markdown

      切入点包中的所有类,必须在proxy中,不能在proxy包的子包中

  • com.baizhiedu.proxy..(..) ```

    • 语法2 ```markdown

      切入点当前包及其子包都生效

  • com.baizhiedu.proxy...(..) ```

2.2 切入点函数
  1. 切入点函数:用于执行切入点表达式
  1. execution ```markdown 最为重要的切入点函数,功能最全。 执行 方法切入点表达式 类切入点表达式 包切入点表达式

弊端:execution执行切入点表达式 ,书写麻烦 execution( com.baizhiedu.proxy...*(..))

注意:其他的切入点函数 简化是execution书写复杂度,功能上完全一致

  1. 2. args
  2. ```markdown
  3. 作用:主要用于函数(方法) 参数的匹配
  4. 切入点:方法参数必须得是2个字符串类型的参数
  5. execution(* *(String,String))
  6. args(String,String)
  1. within ```markdown 作用:主要用于进行类、包切入点表达式的匹配

切入点:UserServiceImpl这个类

execution( ..UserServiceImpl.*(..))

within(*..UserServiceImpl)

execution( com.baizhiedu.proxy...*(..))

within(com.baizhiedu.proxy..*)

  1. 4.[@annotation ](/annotation )
  2. ```xml
  3. 作用:为具有特殊注解的方法加入额外功能
  4. <aop:pointcut id="" expression="@annotation(com.baizhiedu.Log)"/>
  1. 切入点函数的逻辑运算

    1. 指的是 整合多个切入点函数一起配合工作,进而完成更为复杂的需求
    • and与操作 ```markdown 案例:login 同时 参数 2个字符串
  2. execution(* login(String,String))

  3. execution(* login(..)) and args(String,String)

注意:与操作不同用于同种类型的切入点函数

案例:register方法 和 login方法作为切入点

execution( login(..)) or execution( register(..))

  1. - or或操作
  2. ```markdown
  3. 案例:register方法 和 login方法作为切入点
  4. execution(* login(..)) or execution(* register(..))

第四章、AOP编程

1. AOP概念

  1. AOP (Aspect Oriented Programing) 面向切面编程 = Spring动态代理开发
  2. 以切面为基本单位的程序开发,通过切面间的彼此协同,相互调用,完成程序的构建
  3. 切面 = 切入点 + 额外功能
  4. OOP (Object Oritened Programing) 面向对象编程 Java
  5. 以对象为基本单位的程序开发,通过对象间的彼此协同,相互调用,完成程序的构建
  6. POP (Producer Oriented Programing) 面向过程(方法、函数)编程 C
  7. 以过程为基本单位的程序开发,通过过程间的彼此协同,相互调用,完成程序的构建
  1. AOP的概念:
  2. 本质就是Spring得动态代理开发,通过代理类为原始类增加额外功能。
  3. 好处:利于原始类的维护
  4. 注意:AOP编程不会取代 OOP,它只是 OOP 的编程又一补充。

2. AOP编程的开发步骤

  1. 1. 原始对象
  2. 2. 额外功能 (MethodInterceptor)
  3. 3. 切入点
  4. 4. 组装切面 (额外功能+切入点)

3. 切面的名词解释

  1. 切面 = 切入点 + 额外功能
  2. 几何学
  3. = + 相同的性质

image-20200427134740273.png

第五章、AOP的底层实现原理

1. 核心问题

  1. 1. AOP如何创建动态代理类(动态字节码技术)
  2. 2. Spring工厂如何加工创建代理对象
  3. 通过原始对象的id值,获得的是代理对象

2. 动态代理类的创建

2.1 JDK的动态代理
  • Proxy.newProxyInstance方法参数详解

image-20200428175248912.png
image-20200428175316276.png

  • 编码

    1. public class TestJDKProxy {
    2. /*
    3. 1. 借用类加载器 TestJDKProxy
    4. UserServiceImpl
    5. 2. JDK8.x前
    6. final UserService userService = new UserServiceImpl();
    7. */
    8. public static void main(String[] args) {
    9. //1 创建原始对象
    10. UserService userService = new UserServiceImpl();
    11. //2 JDK创建动态代理
    12. /*
    13. */
    14. InvocationHandler handler = new InvocationHandler(){
    15. @Override
    16. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    17. System.out.println("------proxy log --------");
    18. //原始方法运行
    19. Object ret = method.invoke(userService, args);
    20. return ret;
    21. }
    22. };
    23. UserService userServiceProxy = (UserService)Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(),userService.getClass().getInterfaces(),handler);
    24. userServiceProxy.login("suns", "123456");
    25. userServiceProxy.register(new User());
    26. }
    27. }

2.2 CGlib的动态代理
  1. CGlib创建动态代理的原理:父子继承关系创建代理对象,原始类作为父类,代理类作为子类,这样既可以保证2者方法一致,同时在代理类中提供新的实现(额外功能+原始方法)

image-20200429111709226.png

  • CGlib编码 ```java package com.baizhiedu.cglib;

import com.baizhiedu.proxy.User; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class TestCglib { public static void main(String[] args) { //1 创建原始对象 UserService userService = new UserService();

  1. /*
  2. 2 通过cglib方式创建动态代理对象
  3. Proxy.newProxyInstance(classloader,interface,invocationhandler)
  4. Enhancer.setClassLoader()
  5. Enhancer.setSuperClass()
  6. Enhancer.setCallback(); ---> MethodInterceptor(cglib)
  7. Enhancer.create() ---> 代理
  8. */
  9. Enhancer enhancer = new Enhancer();
  10. enhancer.setClassLoader(TestCglib.class.getClassLoader());
  11. enhancer.setSuperclass(userService.getClass());
  12. MethodInterceptor interceptor = new MethodInterceptor() {
  13. //等同于 InvocationHandler --- invoke
  14. @Override
  15. public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
  16. System.out.println("---cglib log----");
  17. Object ret = method.invoke(userService, args);
  18. return ret;
  19. }
  20. };
  21. enhancer.setCallback(interceptor);
  22. UserService userServiceProxy = (UserService) enhancer.create();
  23. userServiceProxy.login("suns", "123345");
  24. userServiceProxy.register(new User());
  25. }

}

  1. - 总结
  2. ```markdown
  3. 1. JDK动态代理 Proxy.newProxyInstance() 通过接口创建代理的实现类
  4. 2. Cglib动态代理 Enhancer 通过继承父类创建的代理类

3. Spring工厂如何加工原始对象

  • 思路分析

image-20200430113353205.png

  • 编码

    1. public class ProxyBeanPostProcessor implements BeanPostProcessor {
    2. @Override
    3. public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    4. return bean;
    5. }
    6. @Override
    7. /*
    8. Proxy.newProxyInstance();
    9. */
    10. public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    11. InvocationHandler handler = new InvocationHandler() {
    12. @Override
    13. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    14. System.out.println("----- new Log-----");
    15. Object ret = method.invoke(bean, args);
    16. return ret;
    17. }
    18. };
    19. return Proxy.newProxyInstance(ProxyBeanPostProcessor.class.getClassLoader(),bean.getClass().getInterfaces(),handler);
    20. }
    21. }

    ```xml

  1. <a name="acuxi"></a>
  2. ## 第六章、基于注解的AOP编程
  3. <a name="gCsdd"></a>
  4. ### 1. 基于注解的AOP编程的开发步骤
  5. 1. 原始对象
  6. 1. 额外功能
  7. 1. 切入点
  8. 1. 组装切面
  9. ```java
  10. # 通过切面类 定义了 额外功能 @Around
  11. 定义了 切入点 @Around("execution(* login(..))")
  12. @Aspect 切面类
  13. package com.baizhiedu.aspect;
  14. import org.aspectj.lang.ProceedingJoinPoint;
  15. import org.aspectj.lang.annotation.Around;
  16. import org.aspectj.lang.annotation.Aspect;
  17. /*
  18. 1. 额外功能
  19. public class MyArround implements MethodInterceptor{
  20. public Object invoke(MethodInvocation invocation){
  21. Object ret = invocation.proceed();
  22. return ret;
  23. }
  24. }
  25. 2. 切入点
  26. <aop:config
  27. <aop:pointcut id="" expression="execution(* login(..))"/>
  28. */
  29. @Aspect
  30. public class MyAspect {
  31. @Around("execution(* login(..))")
  32. public Object arround(ProceedingJoinPoint joinPoint) throws Throwable {
  33. System.out.println("----aspect log ------");
  34. Object ret = joinPoint.proceed();
  35. return ret;
  36. }
  37. }
  1. <bean id="userService" class="com.baizhiedu.aspect.UserServiceImpl"/>
  2. <!--
  3. 切面
  4. 1. 额外功能
  5. 2. 切入点
  6. 3. 组装切面
  7. -->
  8. <bean id="arround" class="com.baizhiedu.aspect.MyAspect"/>
  9. <!--告知Spring基于注解进行AOP编程-->
  10. <aop:aspectj-autoproxy />

2. 细节

  1. 切入点复用 ```java 切入点复用:在切面类中定义一个函数 上面@Pointcut注解 通过这种方式,定义切入点表达式,后续更加有利于切入点复用。

@Aspect public class MyAspect { @Pointcut(“execution(* login(..))”) public void myPointcut(){}

  1. @Around(value="myPointcut()")
  2. public Object arround(ProceedingJoinPoint joinPoint) throws Throwable {
  3. System.out.println("----aspect log ------");
  4. Object ret = joinPoint.proceed();
  5. return ret;
  6. }
  7. @Around(value="myPointcut()")
  8. public Object arround1(ProceedingJoinPoint joinPoint) throws Throwable {
  9. System.out.println("----aspect tx ------");
  10. Object ret = joinPoint.proceed();
  11. return ret;
  12. }

}

  1. 2. 动态代理的创建方式
  2. ```markdown
  3. AOP底层实现 2种代理创建方式
  4. 1. JDK 通过实现接口 做新的实现类方式 创建代理对象
  5. 2. Cglib通过继承父类 做新的子类 创建代理对象
  6. 默认情况 AOP编程 底层应用JDK动态代理创建方式
  7. 如果切换Cglib
  8. 1. 基于注解AOP开发
  9. <aop:aspectj-autoproxy proxy-target-class="true" />
  10. 2. 传统的AOP开发
  11. <aop:config proxy-target-class="true">
  12. </aop>

第七章、AOP开发中的一个坑

  1. 坑:在同一个业务类中,进行业务方法间的相互调用,只有最外层的方法,才是加入了额外功能(内部的方法,通过普通的方式调用,都调用的是原始方法)。如果想让内层的方法也调用代理对象的方法,就要AppicationContextAware获得工厂,进而获得代理对象。
  2. public class UserServiceImpl implements UserService, ApplicationContextAware {
  3. private ApplicationContext ctx;
  4. @Override
  5. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  6. this.ctx = applicationContext;
  7. }
  8. @Log
  9. @Override
  10. public void register(User user) {
  11. System.out.println("UserServiceImpl.register 业务运算 + DAO ");
  12. //throw new RuntimeException("测试异常");
  13. //调用的是原始对象的login方法 ---> 核心功能
  14. /*
  15. 设计目的:代理对象的login方法 ---> 额外功能+核心功能
  16. ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext2.xml");
  17. UserService userService = (UserService) ctx.getBean("userService");
  18. userService.login();
  19. Spring工厂重量级资源 一个应用中 应该只创建一个工厂
  20. */
  21. UserService userService = (UserService) ctx.getBean("userService");
  22. userService.login("suns", "123456");
  23. }
  24. @Override
  25. public boolean login(String name, String password) {
  26. System.out.println("UserServiceImpl.login");
  27. return true;
  28. }
  29. }

第八章、AOP阶段知识总结

image-20200503162625116.png