谈谈你对IOC和DI的理解?


IOC 控制翻转,是一种设计理念,由第三方来管理与控制对象。

通常我们创建对象时都是通过new
关键字来实现的。这就带来一些问题。

  • 1,每次我们需要使用 对象时,都要手动创建。

  • 2,当我们需要对某一个类进行修改时,需要手动将所有通过new 关键字创建的对象,从新编写。工作量将会非常恐怖。

@WebServlet(name = "LoginServlet", urlPatterns = "/check_login")
public class LoginServlet extends HttpServlet {
    private   IUserService userService = new UserService();
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    ......省略若干.....
    }
}    

or

@RestController
public class UserController extends ApiController {
    @Resource
    private IUserService userService;
    
    @PostMapping("/login")
    public Object login(@Valid @RequestBody(required = false) UserReq userReq, HttpSession session) {
     ......省略若干.....
    }
}   

如只负责与数据库的数据进行交互DAO层也是如此

public class UserService {
    private UserDao userDao = new UserDao();
    private RbacDao rbacDao = new RbacDao();

or

@Service
public class UserServiceImpl implements UserService {
    @Resource
    private UserMapper userMapper;

解决办法就是使用“对象容器”,动态生成JavaBean 对象。通过使用“容器”(applicationContext)我们可以程序中有效的依赖实现“解耦”(降低耦合接触耦合)。

控制翻转--传统情况下我们使用“new”关键字创建对象,也就是由我们编译的程序代码决定使用哪个对象,我们称之为“正向控制”。而“控制反转”则是将对象的控制权交个第三方的“对象容器”,由“对象容器”在运行时创建对应的对象,再赋值给对应的变量。

IOC--最大的优点就是,让 对象 和 对象之间的引用 实现了有效的解耦。因为是在运行时完成的,所以当我们系统底层的类发生变化的时候,只需要修改配置文件就可以了。(极大的方便了工程维护和代码管理)

这里的“对象容器”(applicationContext)我们通常称之为,“IOC”容器。

说说DI是如何在运行时动态注入对象的?

在运行时实例化userDAO类

DI--依赖注入,是IOC控制翻转理念的具体技术实现,由对象容器在运行时动态注入对象。

DI-依赖注入是基于两种技术来实现的。

1,反射--在运行时,进行动态的创建、设置以及管理。

2,工厂模式

说说Java反射吧?

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。

反射的应用场景

在我们平时的项目开发过程中,基本上很少会直接使用到反射机制,但这不能说明反射机制没有用,实际上有很多设计、开发都与反射机制有关,例如模块化的开发,通过反射去调用对应的字节码;动态代理设计模式也采用了反射机制,还有我们日常使用的 Spring/Hibernate 等框架,也是利用CGLIB 反射机制才得以实现,下面就举例最常见的两个例子,来说明反射机制的强大之处:

JDBC 的数据库的连接

在JDBC 的操作中,如果要想进行数据库的连接,则必须按照以上的几步完成

  1. 通过Class.forName()加载数据库的驱动程序 (通过反射加载,前提是引入相关了Jar包)
  2. 通过 DriverManager 类进行数据库的连接,连接的时候要输入数据库的连接地址、用户名、密码
  3. 通过Connection 接口接收连接
public class ConnectionJDBC {  
  
    /** 
     * @param args 
     */  
    //驱动程序就是之前在classpath中配置的JDBC的驱动程序的JAR 包中  
    public static final String DBDRIVER = "com.mysql.jdbc.Driver";  
    //连接地址是由各个数据库生产商单独提供的,所以需要单独记住  
    public static final String DBURL = "jdbc:mysql://localhost:3306/test";  
    //连接数据库的用户名  
    public static final String DBUSER = "root";  
    //连接数据库的密码  
    public static final String DBPASS = "NewPassword";  
      
      
    public static void main(String[] args) throws Exception {  
        Connection con = null; //表示数据库的连接对象  
        Class.forName(DBDRIVER); //1、使用CLASS 类加载驱动程序 ,反射机制的体现 
        con = DriverManager.getConnection(DBURL,DBUSER,DBPASS); //2、连接数据库  
        System.out.println(con);  
        con.close(); // 3、关闭数据库  
    }  

国际化i18n的项目

定义I18N接口
public interface I18N {
    String say();
}
网站先对中文支持
//中文语言支持类
public class Zhcn implements I18N{
    @Override
    public String say() {
       return "生命不息 奋斗不止!";
    }
}
添加配置文件
language=top.xiongmingcai.i18n.Zhcn
读取配置文件
public class Application {
    public static void start() {
        Properties properties = new Properties();
        String name = "/config.properties";
        String configPath = Application.class.getResource(name).getPath();

        try {
            String decode = URLDecoder.decode(configPath, "UTF-8");
            properties.load(new FileInputStream(decode));
            String language = properties.getProperty("language");

            System.out.println("language = " + language);//language = top.xiongmingcai.i18n.Zhcn
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {
        Application.start();
    }
}

采用ServletContext读取,读取配置文件的realpath,然后通过文件流读取出来 · MingCaiXiong/java-reflection-learn@c205891

反射调用
 I18N i18N = (I18N)Class.forName(language).newInstance();
            String say = i18N.say();
            System.out.println("say = " + say);
扩展功能
//中文语言支持类
public class En implements I18N {
    @Override
    public String say() {
        return "Cease to struggle and you cease to live.";
    }
}
language=top.xiongmingcai.i18n.En

之所以反射在运行下实例化那个类,所有对我们程序留下更多扩展空间,就拿上面例子来说

Class.forName(language).newInstance()

java是不知道实例化那个对象的,因为包含具体类的这个内容他是存放在配置文件中
i18n.En只有当启动以后他才决定实例化那个类,基于这种特性未来增加德文,法文对原始程序不需要任何调整
在线上运行源代码不需要做如何调整,只需在额外添加实现,只需要把对应I18N接口实现,产生*.class 上传服务器,再调整一下对应文件,我们的应用程序就支持对应的语言支持
相关代码