Spring 核心(IOC)- 基本使用

    技术2022-07-12  69

    Spring 核心(IOC)- 基本使用

    一、 模拟简单的IOC容器二、Spring IOC1. ApplicationContext和BeanFactory的区别2. 三种创建Bean对象方式3. Scope作用域4. Scope作用域代理

    基于Spring官方文档

    采用Spring版本:5.x.x

    一、 模拟简单的IOC容器

    Bean在计算机英语中有可重用组件的含义。

    先实现一个简单的IOC容器(利用反射和单例模式思想):

    通过一个配置文件配置所需bean,内容:唯一标识(全限定类名)创建BeanFactory工厂类并定义一个Map用于存放创建的对象,称之为容器BeanFactory工厂类通过读取配置文件中所有key存放至String数组或Enum中遍历String数组或Enum,通过key获取全限定类名,然后反射创建对象存放至容器中(完成容器初始化)通过BeanFactory工厂getBean静态方法获取对象 //1.创建一个UserDao类,里面不写东西 //2.创建beans-config.properties文件,内容如下 userDao=org.lhy.dao.UserDao //3.创建BeanFatory类 public class BeanFatory { private static Properties props; private static Map<String,Object> beans; static { props = new Properties(); try { props.load(BeanFatory.class.getClassLoader() .getResourceAsStream("beans-config.properties")); beans = new HashMap<String, Object>(); Enumeration keys = props.keys(); while (keys.hasMoreElements()){ String key = keys.nextElement().toString(); String beanPath = props.getProperty(key); Object value = Class.forName(beanPath).newInstance(); beans.put(key,value); } } catch (Exception e) { System.out.println("容器初始化失败");; } } public static Object getBean(String beanName){ Object bean = beans.get(beanName); return bean; } //main方法执行 public static void main(String[] args) { int i = 1; while (i++ <= 3){ UserDao userDao = (UserDao) BeanFatory.getBean("userDao"); System.out.println(userDao.toString()); /** 打印: org.lhy.dao.UserDao@4554617c org.lhy.dao.UserDao@4554617c org.lhy.dao.UserDao@4554617c */ } } }

    二、Spring IOC

    含义:IOC(控制反转),通过把创建对象的权力交由框架,是Spring的重要特征。其中还包含DI(依赖注入)和DL(依赖查找)。

    作用:降低代码的耦合度(解除依赖关系)。

    导入Spring的Maven依赖 <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.5.RELEASE</version> </dependency> 创建UserDao类和Spring配置文件 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="userDao" class="org.lhy.dao.UserDao"/> </beans> 实例化容器对象,获取UserDao对象 public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); UserDao userDao = (UserDao) ac.getBean("userDao"); System.out.println(userDao.toString()); }

    结合之前IOC容器的模拟示例,简单的了解到IOC容器底层的相关原理,也方便学习的深入,但这只是冰山半个角。如果你是在IDEA上,你可以通过Ctrl+H快捷键直观的看到ApplicationContext接口的子类和继承。

    可以看到BeanFactory接口是SpringIOC顶层接口,它为Spring IOC提供了底层基础,而ApplicationContext有三个常用的实现类:ClassPathXmlApplicationContext、FileSystemXmlApplicationContext、AnnotationConfigApplicationContext。

    ClassPathXmlApplicationContext:可以加载类路径下的配置文件,要求配置文件必须在类路径下。(常用)

    FileSystemXmlApplicationContext:可以加载本地磁盘下的配置文件(需有访问权限)。

    AnnotationConfigApplicationContext:用于读取注解创建容器。

    1. ApplicationContext和BeanFactory的区别
    ApplicationContext:它在构建核心容器时,创建对象采取的策略是采用立即加载的方式。(读取完配置文件立即创建配置的对象)BeanFactory:它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式。(什么时候需要对象,什么时候创建)

    可以在对象的类构造方法打印字符串进行测试。

    两种容器创建方式因为对象创建时间点不同而应用在不同的场景下,我们在实际开发中要根据不同情况使用不同的创建容器方式。**在使用立即加载方式创建对象时,单例模式最为适用的。而延迟加载方式,则是多例模式适用的。**但是,这并不是固定的形式,还是要根据对象实例使用情况和加载情况而定的,在后续还会有通过配置来修改创建对象策略和模式。

    实际上,除非特殊原因(如需对Bean要完全控制的场景),我们都应该使用ApplicationContext,它包含BeanFactory的所有功能,通过表格一看便知。

    功能BeanFactoryApplicationContextBean的实例化部署YesYes集成的生命周期管理NoYesBeanPostProcessor 自动注册NoYesBeanFactoryPostProcessor自动注册NoYes方便的MessageSource 国际化访问NoYes内置 ApplicationEvent 事件发布机制NoYes

    在扩展的容器特性中都需要注解处理和AOP代理,BeanPostProcessor 是必不可少的,而使用BeanFactory的实现类DefaultListableBeanFactory,默认情况下是不会自动注册BeanPostProcessor的,所以请谨慎使用。

    2. 三种创建Bean对象方式
    第一种方式:默认构造方法创建(若无默认构造方法,则无法创建) <bean id="userDao" class="org.lhy.dao.UserDao"/> 第二种方式:使用实例工厂的方法创建对象 //创建工厂类 public class InstanceFactory { public UserDao getUserDao(){ return new UserDao(); } } <!-- 第二种方法:使用实例工厂的方法创建对象 步骤:1)首先配置工厂Bean 2)配置对象 其中factory-bean指向工厂Bean的ID,factory-method指向工厂方法 --> <bean id="instanceFactory" class="org.lhy.InstanceFactory"/> <bean id="userDao" factory-bean="instanceFactory" factory-method="getUserDao"/> 第三种方式:使用工厂的静态方法创建对象 //创建工厂类 public class StaticFactory { public static UserDao getUserDao(){ return new UserDao(); } } <!-- 第三种方法:使用工厂的静态方法创建对象 --> <bean id="userDao" class="org.lhy.StaticFactory" factory-method="getUserDao" />

    在实际开发中,我们经常要导入一些jar包使用,但jar包中有些类并不提供默认构造方法(无法修改源码),从而导致无法配置Bean对象,所以第二种和第三种方法就是解决jar包中的这些问题的。

    3. Scope作用域

    在Spring中,默认采用单例模式创建对象,既然是默认,那么显然是可以修改。配置文件中,bean属性scope就为我们提供了修改创建对象模式的权力。

    通过其scope属性设置Bean作用域:

    作用域描述singleton单例,整个容器共享唯一实例(默认,常用)prototype多例,整个容器可有多个实例(常用)request作用于web应用的请求范围session作用于web应用的会话范围application作用于web应用的全局范围websocket作用于web应用的WebSocket范围global-session作用于集群环境下会话范围(全局),若不是集群环境,则就指session <bean id="userDao" class="org.lhy.dao.UserDao" scope="prototype"/>

    按住Ctrl点击scope进去查看:

    可以看到,默认情况下,bean是一个单例,除非这个bean有一个父bean,这种情况下,它将继承父bean的作用域(scope)。其中还提出,单例是最常用的,是多线程服务对象的理想选择。最后一段说,内部定义的bean是跟随外部bean定义走的,除非它显式指定。

    4. Scope作用域代理

    在实际开发中,有时需要你在一个较短作用域的Bean中注入一个较长作用域的Bean。如:在web开发中,单例作用域A类存在一个request作用域B类作为属性,当容器初始化时,按正常流程会实例化A类,但由于B类只会在发送请求访问时才会实例化,所以就会注入失败。这时候我们就可以使用Scope作用域代理来解决这个问题。

    通过在需要代理的bean中配置<aop:scoped-proxy/>即可。

    <bean id="userPreferences" class="com.something.UserPreferences" scope="session"> <aop:scoped-proxy/> </bean>

    Scope作用域代理的含义为:当你要注入一个比该Bean作用域更长的Bean时,它会通过AOP动态代理代替这个作用域更长的bean,这个代理Bean与原有Bean具有相同的方法接口。

    切记!CGLIB代理只提供公共方法调用,不要试图在代理中调用非公有方法。

    AOP动态代理默认采用基于CGLIB的类代理方式,当然,你可以通过<aop:scoped-proxy proxy-target-class="false"/>选择基于JDK代理的接口代理方式。

    CGLIB代理:通过实现类进行代理。JDK代理:通过实现该类的接口进行代理,这也意味着此类必须至少有一个接口。
    Processed: 0.013, SQL: 9