当前位置:   article > 正文

Java 和 SpringBoot 中的设计模式 详解

Java 和 SpringBoot 中的设计模式 详解

一、建造者模式

发生场景

假如有一结果api结果返回值的类Person,其在代码中频繁被使用。如果要使用它,一般的方法是:

  1. public class Main {
  2. public static void main(String[] args) {
  3. //方法1,使用全量的构造函数
  4. Person person1 = new Person("Tom", 28, "Male");
  5. //方法2,使用空的构造函数加setter函数赋值
  6. Person person2 = new Person();
  7. person2.setName("Tom");
  8. person2.setAge(28);
  9. person2.setGender("Male");
  10. }
  11. }
  12. //--》产品
  13. class Person {
  14. private String name;
  15. private int age;
  16. private String gender;
  17. public Person() {
  18. }
  19. public Person(String name, int age, String gender) {
  20. this.name = name;
  21. this.age = age;
  22. this.gender = gender;
  23. }
  24. public void setName(String name) {
  25. this.name = name;
  26. }
  27. public void setAge(int age) {
  28. this.age = age;
  29. }
  30. public void setGender(String gender) {
  31. this.gender = gender;
  32. }
  33. }

这两种使用方法的弊端有:

(1)方法一:当只需要部分参数的时候需要再定义个构造函数(比如失败的情况只需要code和message,结果肯定是空,因此不需要data),且一旦参数较多,则构造函数冗长

(2)方法二setter冗长

使用建造者模式优化上述场景

  1. //使用方法--》指导者
  2. Person person = Person.builder()
  3. .name("Tom") //相当于setName("Tom"),只是方法名取name
  4. .age(28)
  5. .gender("Male")
  6. .build();
  7. //--》产品
  8. public class Person {
  9. private String name;
  10. private int age;
  11. private String gender;
  12. //⑤
  13. public Person(String name, int age, String gender) {
  14. this.name = name;
  15. this.age = age;
  16. this.gender = gender;
  17. }
  18. //①
  19. public static PersonBuilder builder() {
  20. return new PersonBuilder();
  21. }
  22. }
  23. // Java 实现--》建造者
  24. public class PersonBuilder {
  25. private String name;
  26. private int age;
  27. private String gender;
  28. //②空构造器
  29. public PersonBuilder() {
  30. }
  31. //③然后使用setter方法进行设置
  32. public PersonBuilder name(String name) {
  33. this.name = name;
  34. return this; //方法返回当前对象的引用。这样可以支持方法链式调用。
  35. //通过返回当前对象的引用,可以在一个语句中连续调用对象的方法,而无需每次调用都创建一个新的对象。
  36. }
  37. public PersonBuilder age(int age) {
  38. this.age = age;
  39. return this;
  40. }
  41. public PersonBuilder gender(String gender) {
  42. this.gender = gender;
  43. return this;
  44. }
  45. //④
  46. public Person build() {
  47. return new Person(name, age, gender);
  48. }
  49. }

应用案例

Lombok插件的@Builder注解

  1. //使用
  2. Person person = Person.builder()
  3. .name("Tom")
  4. .age(28)
  5. .gender("Male")
  6. .build();
  7. //lombok优化后
  8. import lombok.AllArgsConstructor;
  9. import lombok.Builder;
  10. import lombok.Data;
  11. import lombok.NoArgsConstructor;
  12. @Data
  13. @Builder
  14. @NoArgsConstructor
  15. @AllArgsConstructor
  16. public class Person {
  17. private String name;
  18. private int age;
  19. private String gender;
  20. }

二、代理模式

避免客户端直接访问真实对象,必须通过代理对象访问真实对象。

好处:①在代理对象中限制访问权限(权限校验);②在代理对象中扩展功能(日志记录)

静态代理

  1. 代理对象真实对象实现了同一个接口,这样代理对象就具有了和真实对象一样的同名方法
  2. 代理对象持有真实对象的实例(代理对象构造器输入参数为公共接口,这样可以代理多个实现类)
  3. 调用时使用代理对象的方法,在代理对象的方法里再调用真实对象同名方法和增加一些扩展操作

缺点:

  • 代理对象的一个接口只服务于一种类型的对象,如果要代理的类很多,势必要为每一种类都进行代理,在程序规模稍大时静态代理代理类就会过多会造成代码混乱
  • 2、如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法,增加了代码维护的复杂度。

动态代理

1.JDK 动态代理(反射)——目标对象实现了接口

  1. 定义一个接口及其实现类;
  2. 自定义 InvocationHandler 并重写invoke方法,在 invoke 方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑;
  3. 通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法创建代理对象;
  1. //公共接口
  2. public interface SmsService {
  3. String send(String message);
  4. }
  5. //真实对象
  6. public class SmsServiceImpl implements SmsService {
  7. public String send(String message) {
  8. System.out.println("send message:" + message);
  9. return message;
  10. }
  11. }
  12. //代理对象(自定义 InvocationHandler)
  13. import java.lang.reflect.InvocationHandler;
  14. import java.lang.reflect.InvocationTargetException;
  15. import java.lang.reflect.Method;
  16. public class DebugInvocationHandler implements InvocationHandler {
  17. /**
  18. * 代理类中的真实对象
  19. */
  20. private final Object target;
  21. public DebugInvocationHandler(Object target) {
  22. this.target = target;
  23. }
  24. public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
  25. //调用方法之前,我们可以添加自己的操作
  26. System.out.println("before method " + method.getName());
  27. Object result = method.invoke(target, args);
  28. //调用方法之后,我们同样可以添加自己的操作
  29. System.out.println("after method " + method.getName());
  30. return result;
  31. }
  32. }
  33. //代理对象的工厂类
  34. public class JdkProxyFactory {
  35. public static Object getProxy(Object target) {
  36. return Proxy.newProxyInstance( //创建代理对象
  37. target.getClass().getClassLoader(), // 目标类的类加载
  38. target.getClass().getInterfaces(), // 代理需要实现的接口,可指定多个
  39. new DebugInvocationHandler(target) // 代理对象对应的自定义 InvocationHandler
  40. );
  41. }
  42. }
  43. //实际使用
  44. SmsService smsServiceProxy = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
  45. smsServiceProxy.send("java");

2.CGLIB 动态代理机制(反射+继承)——目标对象没有实现接口

  1. 定义一个类;
  2. 自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法,和 JDK 动态代理中的 invoke 方法类似;
  3. 通过 Enhancer 类的 create()创建代理类;
  1. //真实对象(类和方法都不能是final的,因为CGLIB代理用到了继承)
  2. public class SmsService {
  3. public String send(String message) {
  4. System.out.println("send message:" + message);
  5. return message;
  6. }
  7. }
  8. //代理对象(自定义 MethodInterceptor)
  9. public class MethodInterceptor implements MethodInterceptor {
  10. //被代理对象
  11. private Object target;
  12. public MethodInterceptor(Object target) {
  13. this.target = target;
  14. }
  15. public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
  16. System.out.println("CGLib商店打广告!");
  17. //调用原来方法
  18. Object invoke = method.invokeSuper(target, objects);
  19. System.out.println("CGLib商店做售后!");
  20. return invoke;
  21. }
  22. }
  23. //代理对象的工厂类
  24. public class CGlibFactoryProxy {
  25. public Object createProxy(Object target){
  26. this.target = target;
  27. //创建代理对象
  28. Enhancer enhancer = new Enhancer();
  29. //设置父类,以便 CGLIB 去生成该类的子类
  30. enhancer.setSuperclass(this.target.getClass());
  31. //设置方法回调MethodInterceptor实现,你可以认为是设置增强方法
  32. enhancer.setCallback(new MethodInterceptor());
  33. //返回代理对象
  34. return enhancer.create();
  35. }
  36. }
  37. //实际使用
  38. CGlibFactoryProxy factoryProxy = new CGlibFactoryProxy();
  39. SmsService smsServiceProxy = (SmsService) factoryProxy.createProxy(new SmsService());
  40. smsServiceProxy.send("java");

3.应用案例

Spring 框架的 AOP 使用了动态代理模式来实现切面编程。通过代理,Spring 能够在目标对象的方法执行前、执行后或抛出异常时插入切面逻辑,而不需要修改原始代码。

  • (切点)在哪里切入,也就是权限校验等非业务操作在哪些业务代码中执行。
  • (处理时机)在什么时候切入,是业务代码执行前还是执行后。
  • (处理内容)切入后做什么事,比如做权限校验、日志记录等。

Joint point:一个方法的执行或者一个异常的处理。

Weaving:织入,就是通过动态代理,在目标对象方法中执行处理内容的过程。

基于AspectJ实现AOP操作

Spring框架一般都是基于AspectJ实现AOP操作。AspectJ不是Spring组成部分,独立AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作。

1.基于xml配置文件实现

2.基于注解方式实现(使用,重要)

  1. //切面类
  2. @Aspect
  3. @Component
  4. public class UserProxy {
  5. //1.相同切入点抽取
  6. @Pointcut(value = "execution(* com.jin.aopanno.User.add(..))")
  7. public void pointdemo(){}
  8. @Before(value = "pointdemo()")
  9. public void before(){
  10. System.out.println("before ...");
  11. }
  12. //2.不同切入点抽取
  13. //前置通知(value = 切入点表达式)
  14. @Before(value = "execution(* com.jin.aopanno.User.add(..))")
  15. public void before(){
  16. System.out.println("before ...");
  17. }
  18. //3.自定义注解
  19. @Before("@annotation(com.easychat.annotation.GlobalInterceptor)")
  20. public void before(){
  21. System.out.println("before ...");
  22. }
  23. private void checkLogin(Boolean checkAdmin) {
  24. }
  25. }
  26. //自定义注解
  27. import java.lang.annotation.ElementType;
  28. import java.lang.annotation.Retention;
  29. import java.lang.annotation.RetentionPolicy;
  30. import java.lang.annotation.Target;
  31. @Target(ElementType.METHOD)
  32. @Retention(RetentionPolicy.RUNTIME)
  33. public @interface GlobalInterceptor {
  34. //校验登录
  35. boolean checkLogin() default true;
  36. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/秋刀鱼在做梦/article/detail/875697
推荐阅读
相关标签
  

闽ICP备14008679号