前言

Spring的核心=IOC+AOP

  • IOC   为Inversion of Control的缩写,意为:控制反转=接口+工厂模式实现调用方和被调用方之间的解耦合
  • AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,底层是动态代理

 

一、接口+工厂模式解耦合

如何设计低耦合、易复用的软件,面向接口+工厂模式是不错的选择;

1.面向接口编程思想

为什么在MVC三层架构中service层一定要定义接口呢?web层直接调用service实现类不好吗?

1.1.什么是耦合?

在1个系统系统架构中,各个组件之间的耦合是不可避免的

耦合就是依赖,B依赖A的方法才能执行。

1.2.面向接口编程的流程?

在程序开发之前预先定义接口,接口中定义方法;

强制被调用方按照接口中定义的方法开发实现类;

调用方在调用被调用方时只关注接口(中间层)中定义的方法即可,被调用方修改了接口的实现类逻辑,也不会影响调用方调用

例如:在web层(调用方)和service层(被调用方)之间定义接口层;

1.3.面向接口编程的目的

面向接口编程实现了低耦合,但是没有完全解开耦合;

我们需要借助面向接口编程+工厂模式,继续解开耦合 ;

 

2.接口+工厂模式

使用接口+工厂模式可以解开程序编译阶段的耦合,实现程序高内聚和低耦合。

2.1.Java程序执行流程

Java程序分为编译和运行阶段;

  • 编译(开发)阶段耦合:是可以解开的
  • 运行阶段耦合:              是无法避免的不可以解开的

2.2.解编译阶段的耦合 

程序之间的耦合是不可避免的,借助面向接口编程+工厂模式:

  • 可以实现程序编译阶段的耦合,
  • 无法实现程序运行阶段的耦合;

2.3.编译阶段耦合解开代码体现

使用接口+工厂模式解耦合(编译阶段耦合解开)的代码体现是

  • 调用方在调用被调用方时,无需调用方new而是通过给工厂类传递被调用方的对象参数,工厂类接收到对象参数,由工厂类创建被调用方的实现类对象,实现程序的高内聚和低耦合; 
  • 即使注释/删除被调用方的代码
  • 调用方调用被调用方法的代码,程序在编译阶段也不会报错

2.4.实现流程分析

调用方和被调用方之间增加工厂类

 

实现工厂类

package com.itheima.factory;

import java.util.ResourceBundle;

//创建service层(serviceImp)对象的工厂
public class BeanFactory {
    private static Object bean = null;

    public static Object getBean(String beanId) {
        try {
            //1.读取配置文件
            ResourceBundle resourceBundle = ResourceBundle.getBundle("beans");
            //2.从配置文件中根据beanId获取到类的全限定名称
            String stringClassName = resourceBundle.getString(beanId);
            //3.根据上一步获取到的类权限定名称创建对象
            Class aClass = java.lang.Class.forName(stringClassName);
            bean = aClass.newInstance();
            //4.将上一步创建的对象返回
        } catch (Exception e) {
            // 出现异常打印异常
            e.printStackTrace();
            //谁调用就把异常扔给谁
            throw new RuntimeException();
        }
        return bean;
    }
}
BeanFactory.java

 

调用方法通过工厂类创建被调用方对象

   //1.面向接口创建studentService对象:需要手动new调用方的对象
    //StudentService studentService = new StudentServiceImpl();
    

//2.面向接口+工厂模式创建studentService对象:无需调用方手动new调用方的对象,由BeanFactory来创建 StudentService studentService = (StudentService) BeanFactory.getBean("StudentService");

 

2.5.工厂类优化

工厂类加载时创建配置文件中配置的所有bean实例,避免调用方每次调用工厂类的getBean()方法都创建1次对象

package com.itheima.factory;

import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;

public class BeanFactory2 {
    //存储所有bean的HashMap
    private static Map<String, Object> beanMap = new HashMap<>();
    //类加载时创建配置文件中配置的所有bean实例,避免调用方每次调用工厂类的getBean()方法都创建1次对象
    static {
        try {
            //1.读取配置文件
            ResourceBundle resourceBundle = ResourceBundle.getBundle("beans");
            //2.遍历配置文件中所有key,实例化bena集中存放到beanMap中
            Enumeration<String> keys = resourceBundle.getKeys();
            while (keys.hasMoreElements()) {
                String key = keys.nextElement();
                String stringClassName = resourceBundle.getString(key);
                Class aClass = Class.forName(stringClassName);
                Object instance = aClass.newInstance();
                beanMap.put(key, instance);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException();

        }

    }

    //获取bean
    public static Object getBean(String beanId) {
        Object bean = beanMap.get(beanId);
        return bean;
    }
}
BeanFactory2.java

 

 

二、代理模式

23种设计模式中有一种代理模式。

使用代码模拟静态代理和动态代理;

  • 目标对象(明星):              原始对象
  • 代理对象(明星的经纪人):帮助原始对象的对象,一般用于在不改变原始对象的前提下,对原始对象的进行功能的增强。

 

1.静态代理

如果想要在不改变原始对象的前提下,使用代理对象增强原始对象的功能,那么原始对象和代理对象都需要实现同一个接口

原始+代理对象的接口

package com.itheima.test.proxy;

//演员和经纪人都需要实现的接口
public interface Performer {
    //唱歌的方法
    void sing();

    //跳舞的方法
    void dance();
}
Performer.interface

原始对象的类

package com.itheima.test.proxy;

//男演员类实现了演员接口
public class Actor implements Performer {
    private String name;
    private Integer age;

    public Actor() {
    }


    public Actor(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public void sing() {
        System.out.printf("%s唱了一首歌\n", this.name);
    }

    @Override
    public void dance() {
        System.out.printf("%s跳了一支舞", this.name);
    }

    @Override
    public String toString() {
        return "actor{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
Actor.java

 代理对象的类

package com.itheima.test.proxy;

//经纪人类:再拥有演员功能的前提下,增加新的功能
public class Agent implements Performer {
    //经纪人服务的演员
    private Performer performer;

    public Agent() {
    }

    public Agent(Performer performer) {
        this.performer = performer;
    }

    @Override
    public void sing() {
        System.out.println("经纪人帮助演员谈谈演出费用问题");
        this.performer.sing();
        System.out.println("经济人帮助演员收费");
        System.out.println("经济人帮助演员照顾演员家里人");
    }

    @Override
    public void dance() {
        System.out.println("经纪人帮助演员谈谈演出费用问题");
        this.performer.dance();
        System.out.println("经济人帮助演员收费");
        System.out.println("经纪人帮助演员照顾演员家里人");
    }
}
Agent.java

 

2.动态代理

静态代理有以下弊端

  • 实现了代理对象给原始对象添加新功能的需求;
  • 但每新增1个原始对象都需要新增1代理对象,单独为这个原始对象服务 ;

动态代理

  • 动态代理拥有静态代理的代理功能,即在不改变原始对象的前提下,使用代理对象增强原始对象的功能;
  • 动态代理还可以在不定义代理类的前提下,直接创建1个代理对象,使用1个代理对象服务多个原始对象;
package com.itheima.test.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Customer {
    public static void main(String[] args) {
        //客户要求演员-宝强 唱首歌
        //方式1:直接实例化1个演员类
        Performer actor1 = new Actor("宝强", 41);//原始对象
        actor1.sing();
        System.out.println("----------------------------------------");
        //方式2:客户要求演员-宝强 唱首歌, 需要找到宝强的经纪人,通过代理找到演员
        Performer actor2 = new Actor("宝强", 41);
        Agent agent = new Agent(actor2); //代理对象
        agent.sing();
        System.out.println("----------------------------------------");

        //方式3动态代理:虽然方式2实现了给原始对象添加功能的需求;
        // 但每1个演员都需要,创建1为单独的经纪人,能不能让多个演员公用1个代理人?
        //再简化一步:不定义代理类,直接创建代理对象
        //代理对象=目标对象的功能+增强逻辑
        //1.创建目标对象
        Actor actor3 = new Actor("奶量", 41);
        //2.编写增强逻辑

        //3.使用JDK动态创建代理对象
        Performer instance = (Performer) Proxy.newProxyInstance(
                //指定1个类加载器:跟目标对象使用同一个类加载器
                actor3.getClass().getClassLoader(),
                //接口:限制代理对象可以使用目标对象哪些方法?
                actor3.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //调用目标对象的方法:
                        //参数1:目标对象,
                        //参数2:目标对象方法执行需要
                        System.out.println("经纪人帮助演员谈谈演出费用问题");
                        method.invoke(actor3, args);
                        System.out.println("经济人帮助演员收费");
                        System.out.println("经济人帮助演员照顾演员家里人");
                        return null;
                    }
                }

        );
        //4.调用代理对象的方法
        instance.sing();
        instance.dance();
    }
}
Customer.java

 

 

 

posted on 2022-05-16 15:58  Martin8866  阅读(1725)  评论(0编辑  收藏  举报