美文网首页
Spring | 基础

Spring | 基础

作者: EclipseO2 | 来源:发表于2018-06-26 22:00 被阅读8次

    加入架包

    • spring-beans-5.0.7.RELEASE.jar
    • spring-context-5.0.7.RELEASE.jar
    • spring-core-5.0.7.RELEASE.jar
    • spring-expression-5.0.7.RELEASE.jar
    • commons-logging-1.2.jar

    1. IOC和DI

    • IOC:其思想是反转资源获取的方向,传统的资源查找方式要求组件向容器发起请求查找资源,作为回应,容器适时的返回资源。而应用了 IOC 之后,则是容器主动地将资源推送给它所管理的组件,组件所要做的仅是选择一种合适的方式来接受资源。这种行为也被称为查找的被动形式。

    • DI:IOC的另一种表述方式,即组件以一些预先定义好的方式(setter方法)接受来自容器的资源注入。

    图解

    可见,有了IOC容器之后,对象的创建都是在容器中进行,用户所要做的只是从容器中获取已经创建好的对象而已

    2. 配置Bean

    2.1 Bean 的配置方法

    2.1.1 在 xml 文件中通过全类名(反射)来配置 Bean

    <bean id="helloWorld2" class="edu.just.spring.beans.HelloWorld"></bean>
    

    class:bean 的全类名, 通过反射的方式在 IOC 容器中创建 Bean, 所以要求 Bean 中必须有无参的构造器
    id:标识容器中的 bean. id 唯一.

    2.1.2 在 xml 中通过静态工厂方法来配置 Bean
    调用静态工厂方法创建 Bean 是将对象创建的过程封装到静态方法中,当客户端需要对象时,只需要简单地调用静态方法,而不用关心创建对象的细节

    创建 Bean 实例

    public class Car {
    
        private String brand;
        private String corp;
        private double price;
        
        public Car(String brand, String corp, double price) {
            this.brand = brand;
            this.corp = corp;
            this.price = price;
        }
    
    }
    
    /**
     * 静态工厂方法: 只要调用某一个类的静态方法就可以返回 Bean 的实例
     */
    public class StaticCarFactory {
    
        private static Map<String, Car> cars = new HashMap<>();
        
        static {
            cars.put("audi", new Car("audi", "xixi", 2000));
            cars.put("ford", new Car("ford", "hello", 401111));
        }
        
        public static Car getCar(String name) {
            return cars.get(name);
        }
        
    }
    

    配置 Bean:
    class 属性:指向静态工厂方法的全类名
    factory-method:指向静态工厂方法的名字
    constructor-arg:如果工厂方法需要传入参数, 则用 constructor-arg 来配置参数

    <bean id="car1" class="edu.just.spring.beans.factory.StaticCarFactory" factory-method="getCar">
        <constructor-arg value="audi"></constructor-arg>
    </bean>
    

    结果:

    Car [brand=audi, corp=xixi, price=2000.0]
    

    2.1.3 在 xml 中通过实例工厂方法配置 Bean
    将对象的创建过程封装到另一个对象实例的方法中,当客户端需要请求对象时,只需要简单的调用该实例方法而不需要关系对象的创建细节

    /**
     * 实例工厂方法: 实例工厂的方法, 即需要创建工厂本身, 在调用工厂的实例方法来返回 bean 的实例
     */
    public class InstanceCarFactory {
    
        private Map<String, Car> cars = null;
        
        public InstanceCarFactory() {
            cars = new HashMap<>();
            cars.put("audi", new Car("audi", "xixi", 20000));
            cars.put("ford", new Car("ford", "hha", 30000));
        }
        
        public Car getCar(String name) {
            return cars.get(name);
        }
    }
    

    配置 Bean:
    factory-bean :指向实例工厂方法的 Bean
    factory-method :指向实例工厂方法的名字
    constructor-arg:如果工厂方法需要传入参数, 则用 constructor-arg 来配置参数

    <!-- 配置工厂的实例 -->
    <bean id="carFactory" class="edu.just.spring.beans.factory.InstanceCarFactory"></bean>
    
    <bean id="car2" factory-bean="carFactory" factory-method="getCar">
        <constructor-arg value="ford"></constructor-arg>
    </bean>
    

    结果:

    Car [brand=ford, corp=hha, price=30000.0]
    

    2.1.4 实现 FactoryBean 接口配置 Bean

    • Spring 中有两种类型的 Bean,一种是普通 Bean,另一种是工厂 Bean,即 FactoryBean
    • 工厂 Bean 跟普通 Bean 不同,其返回的对象不是指定类的一个实例,而是该工厂 Bean 的 getObject 方法所返回的对象
    //自定义的 FactoryBean 需要实现 FactoryBean 接口
    public class CarFactoryBean implements FactoryBean<Car>{
        
        private String brand;
        
        public void setBrand(String brand) {
            this.brand = brand;
        }
        
        //返回 Bean 的对象
        @Override
        public Car getObject() throws Exception {
            return new Car(brand, 500000);
        }
    
        //返回 Bean 的类型
        @Override
        public Class<?> getObjectType() {
            return Car.class;
        }
    
        @Override
        public boolean isSingleton() {
            return true;
        }
    }
    

    配置 Bean:
    通过 FactoryBean 来配置 Bean 的实例
    class:指向 FactoryBean 的全类名
    property:配置 FactoryBean 的属性

    但实际返回的实例却是 FactoryBean 的 getObject() 方法返回的实例

    <bean id="carFactoryBean" class="edu.just.spring.beans.FactoryBean.CarFactoryBean">
        <property name="brand" value="audi"></property>
    </bean>
    

    结果:

    Car [brand=audi, price=500000.0]
    



    2.1.5 基于注解的方式来配置 Bean

    2.2 ApplicationContext

    在Spring IOC 容器读取 Bean 配置创建 Bean 实例之前,必须对它进行实例化。只有在容器实例化之后,才可以从 IOC 容器里获取 Bean 实例并使用。Spring 提供了两种类型的 IOC 容器实现

    • BeanFactory:IOC 容器的基本实现
    • ApplicationContext:提供了更多的高级特性,是 BeanFactory 的子接口

    BeanFactory 是 Spring 框架的基础设施,面向 Spring 本身
    ApplicationContext 面向使用 Spring 框架的开发者,几乎所有应用场合都直接使用 ApplicationContext 而非底层的 BeanFactory

    public class Main {
        public static void main(String[] args) {
            //1. 创建 Spring 得 IOC 容器对象
            //ApplicationContext 代笔 IOC 容器
            //ClassPathXmlApplicationContext: 是 ApplicationContext 接口的实现类, 该实现类从类路径下来加载配置文件.
            ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
            
            //2. 对 IOC 容器中获取 Bean 实例
            //利用 id 定位到 IOC 容器中的 bean
            HelloWorld helloWorld = (HelloWorld) ctx.getBean("helloWorld2");
        
            //利用类型返回 IOC 容器中的 Bean, 但要求 IOC 容器中必须只能有一个该类型的 Bean
    //      HelloWorld helloWorld = ctx.getBean(HelloWorld.class);
            System.out.println(helloWorld);
        
    //      //3. 调用方法
    //      helloWorld.hello();
        }
    }
    

    2.3 依赖注入的方式

    属性注入:即通过 setter 方法注入 Bean 的属性值或依赖的对象。属性注入是实际应用中最常用的注入方式

    <bean id="helloWorld2" class="edu.just.spring.beans.HelloWorld">
        <property name="name" value="Spring"></property>
    </bean>
    



    构造器注入:通过构造方法注入 Bean 的属性值或依赖的对象,它保证了 Bean 实例在实例化之后就可以使用。构造器注入在 <constructor-arg> 元素里面声明属性,<constructor-arg> 中没有 name 属性

    <!-- 通过构造方法来配置 bean 的属性 -->
    <bean id="car1" class="edu.just.spring.beans.Car">
        <constructor-arg value="nihao" index="0"></constructor-arg>
        <constructor-arg value="baoma" index="1"></constructor-arg>
        <constructor-arg value="200000" type="double"></constructor-arg>
    </bean>
    

    2.4 注入属性值细节

    字面值:可用字符串表示的值,可以通过 <value>200</value> 元素标签或 value="nihao" 属性进行注入。
    PS:若字面值中包含特殊字符,可以使用 <![CDATA[]]>把字面值包裹

    引用其他 Bean:在 Bean 的配置文件中,可以通过 <ref> 元素或者 ref 属性为 Bean 的属性或者构造器参数指定对 Bean 的引用

    <bean id="car1" class="edu.just.spring.beans.Car"></bean>
    
    <bean id="person" class="edu.just.spring.beans.Person">
        <property name="name" value="Tom"></property>
        <property name="age" value="25"></property>
        <!-- 可以使用 property 的 ref 属性建立 bean 之间的引用关系 -->
        <property name="car" ref="car1"></property>
        
        <!-- 通过 ref 属性添加引用 -->
        <property name="car">
            <ref bean="car1"/>
        </property>
        
        <!-- 内部 Bean, 不能被外部引用, 只能在内部使用 -->
        <property name="car">
            <bean class="edu.just.spring.beans.Car">
                <constructor-arg value="Ford"></constructor-arg>
                <constructor-arg value="Changan"></constructor-arg>
                <constructor-arg value="200000"></constructor-arg>
            </bean>
        </property>
    </bean>
    

    内部 Bean:内部 Bean 声明直接包含在 <poperty> 或者 <constructor-arg> 元素里,不需要设置任何 id 或 name 属性。同时内部 Bean 不能使用在其他任何地方

    集合属性:在 Spring 中可以通过一组内置的 xml 标签,如 <list><set><map>

    • list 标签
    <!-- 测试如何配置集合属性 -->
    <bean id="person3" class="edu.just.spring.beans.collections.Person">
        <property name="name" value="Mike"></property>
        <property name="age" value="20"></property>
        <property name="cars">
            <!-- 使用 list 节点为 List 类型的属性赋值 -->
            <list>
                <ref bean="car1"/>
                <ref bean="car2"/>
                <!-- 可以指定内部 Bean -->
                <bean class="edu.just.spring.beans.Car">
                    <constructor-arg value="Ford"></constructor-arg>
                    <constructor-arg value="Changan"></constructor-arg>
                    <constructor-arg value="200000"></constructor-arg>
                </bean>
            </list>
        </property>
    </bean>
    
    • map 标签
    <!-- 配置 Map 属性值 -->
    <bean id="newPerson" class="edu.just.spring.beans.collections.Person2">
        <property name="name" value="Rose"></property>
        <property name="age" value="29"></property>
        <property name="cars">
            <!-- 使用 map 节点 的 entry 子节点配置 Map 类型的成员变量 -->
            <map>
                <entry key="AA" value-ref="car1"></entry>
                <entry key="BB" value-ref="car2"></entry>
            </map>
        </property>
    </bean>
    
    • property 标签
    <bean id="dataSource" class="edu.just.spring.beans.collections.DataSource">
        <property name="properties">
            <!-- 使用 props 和 prop 子节点为 Properties 属性赋值 -->
            <props>
                <prop key="user">root</prop>
                <prop key="password">password</prop>
                <prop key="jdbcUrl">jdbc:mysql:///test</prop>
                <prop key="driverClass">com.mysql.jdbc.Driver</prop>
            </props>
        </property>
    </bean>
    

    使用utility scheme定义集合
    使用基本的集合标签定义集合时,不能将集合作为独立的 Bean 定义,导致其他 Bean 无法引用该集合,所以无法在不同 Bean 直接共享集合

    <!-- 配置独立的集合 bean, 以供多个 bean 进行引用 -->
     <util:list id="cars">
        <ref bean="car1"/>
        <ref bean="car2"/>
     </util:list>
        
    <bean id="person4" class="edu.just.spring.beans.collections.Person">
        <property name="name" value="Jack"></property>
        <property name="age" value="100"></property>
        <property name="cars" ref="cars"></property>
    </bean>
    

    使用 p 命名空间

    <!-- 通过 p 命名空间为 bean 的属性赋值, 需要先导入 p 命名空间, 相对于传统的配置方式更加的简洁. -->
    <bean id="person5" class="edu.just.spring.beans.collections.Person" p:name="luwenhe"
        p:age="20" p:cars-ref="car1"></bean>
    

    2.5 自动装配

    Spring IOC 容器可自动装配 Bean,需要做的仅仅是在 <bean> 的 autowire 属性里指定自动装配的模式

    可以使用 autowire 属性指定 自动装配 的方式.
    byName:根据 bean 的名字(id 的 值)和当前 bean 的 setter 风格的属性名进行自动装配,若有匹配的,则进行自动装配,若没有匹配的,则不装配
    byType:根据 bean 的类型和当前 bean 的属性的类型进行自动装配,若 IOC 容器中有 1 个以上的类型匹配的 bean,则抛出异常

    缺点:如果只希望装配个别属性时, autowire 就不够灵活,autowire 属性要么根据类型自动装配, 要么根据名称自动装配, 不能两者兼而有之。在实际项目中很少使用自动装配功能, 因为和自动装配功能所带来的好处比起来, 明确清晰的配置文档更加具有说服力

    <bean id="person" class="edu.just.spring.beans.autowire.Person" p:name="Tim"
        autowire="byName"></bean>
            
    <bean id="person" class="edu.just.spring.beans.autowire.Person" p:name="Tim"
        autowire="byType"></bean>
    

    2.6 Bean 之间的关系:继承、依赖

    • 被继承的 Bean 成为父 Bean。继承这个父 Bean 的 Be'an 称为子 Bean
    • 子 Bean 继承父 Bean 的配置,也可以覆盖从父 Bean 继承过来的配置
    • 若只想把父 Bean 作为模板,可以设置 <bean> 的 abstract 属性为 true
    • 也可以忽略父 Bean 的 class 属性,让子 Bean 指定自己的类。此时 abstract 必须设置为 true
    <!-- 
        抽象 bean: bean 的 abstract 属性为 true 的 bean. 这样的 bean 不能被 IOC 容器实例化, 只用来被继承配置 
        若某一个 bean 的 class 属性没有指定, 则该 bean 必须是一个抽象 bean
    -->
    <bean id="address" p:city="Beijing^^" p:street="WuDAOkOU" abstract="true"></bean>
        
    <!-- bean 配置的继承: 使用 bean 的 parent 属性指定继承哪个 bean 的配置 -->
    <bean id="address2" class="edu.just.spring.beans.autowire.Address"
        p:street="DaZhongSi" parent="address"></bean>
        
    <bean id="car" class="edu.just.spring.beans.autowire.Car"
        p:brand="BenChi" p:price="100000"></bean>
         
    <!-- 要求在配置 Person 时, 必须有一个关联的 car! 即 person 这个 bean 依赖于 Car 这个 bean -->
    <bean id="person" class="edu.just.spring.beans.autowire.Person"
        p:name="Tom" p:address-ref="address2" depends-on="car"></bean>
    
    • Spring 允许用户通过 depend-on 属性设定 Bean 前置依赖的 Bean,前置依赖 Bean 会在本 Bean 实例化之前创建好

    2.7 Bean 的作用域

    Spring 可以在 <bean> 元素的 scope 属性里设置 Bean 的作用域

    • singleton:默认值, 容器初始化时创建 bean 实例, 在整个容器的生命周期内只创建这一个 bean, 是单例的
    • prototype:原型的, 容器初始化时不创建 bean 的实例, 而在每次请求时都创建一个新的 Bean 实例, 并返回
    <bean id="car" class="edu.just.spring.beans.autowire.Car" scope="prototype">
        <property name="brand" value="Audi"></property>
        <property name="price" value="20000"></property>
    </bean>
    

    <b/>

    2.8 使用外部属性文件

    db.properties:

    user=root
    password=lwh011305
    driverClass=com.mysql.jdbc.Driver
    jdbcUrl=jdbc:mysql://localhost:3306/fileupload?useSSL=true
    

    导入属性文件:

    <!-- 导入属性文件 -->
    <context:property-placeholder location="classpath:db.properties"/>
    
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!-- 使用外部化属性文件的属性 -->
        <property name="user" value="${user}"></property>
        <property name="password" value="${password}"></property>
        <property name="driverClass" value="${driverClass}"></property>
        <property name="jdbcUrl" value="${jdbcUrl}"></property>
    </bean>
    

    2.9 Spring 表达式语言:SpEL

    类似于 EL:SpEL 使用 #{...} 作为定界符,所以在大框号中的字符被认为是 SpEL

    为属性赋一个字面量:

    <bean id="address" class="edu.just.spring.beans.spel.Address">
        <!-- 使用 spel 为属性赋一个字面值 -->
        <property name="city" value="#{'beijing'}"></property>
        <property name="street" value="wudaokou"></property>
    </bean>
    

    使用 SpEL 引用类的静态属性:

    <bean id="car" class="edu.just.spring.beans.spel.Car">
        <property name="brand" value="Audi"></property>
        <property name="price" value="2000"></property>
        <!-- 使用 Spel 引用类的静态属性 -->
        <property name="tyrePerimeter" value="#{T(java.lang.Math).PI * 80}"></property>
    </bean>
    

    使用 SpEL 来引用其他 Bean

    <bean id="person" class="edu.just.spring.beans.spel.Person">
        <property name="name" value="luwenhe"></property>
        
        <!-- 使用 Spel 来引用其他的 Bean 的属性 -->
        <property name="city" value="#{address.city}"></property>
            
        <!-- 使用 Spel 来引用其他的 Bean -->
        <property name="car" value="#{car}"></property>
            
        <!-- 使用 Spel 中使用运算符 -->
        <property name="info" value="#{car.price > 30000 ? '金领' : '白领'}"></property>
    </bean>
    

    2.10 IOC 容器中 Bean 的生命周期方法

    Spring IOC 容器对 Bean 的生命周期进行管理的过程:

    • 通过构造器或者工厂方法创建 Bean 实例
    • 为 Bean 的属性设置值和对其他 Bean 的引用
    • 调用 Bean 的初始化方法
    • Bean 可以使用了
    • 当容器关闭时,调用了 Bean 的销毁方法

    创建 Bean 方法

    public class Car {
    
        public Car() {
            System.out.println("Car's Constructor ... ");
        }
        
        private String brand;
        
        public void setBrand(String brand) {
            System.out.println("setBrand ... ");
            this.brand = brand;
        }
        
        public String getBrand() {
            return brand;
        }
        
        public void init() {
            System.out.println("init ... ");
        }
        
        public void destory() {
            System.out.println("destroy ... ");
        }
    
        @Override
        public String toString() {
            return "Car [brand=" + brand + "]";
        }
    }
    

    为 Bean 设置属性值:init-methoddestory-method 属性为 Bean 指定初始化和销毁方法

    <bean id="car1" class="edu.just.spring.beans.cycle.Car"
        init-method="init" destroy-method="destory">
        <property name="brand" value="Audi"></property>
    </bean>
    

    调用 Bean 的初始化方法

    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans-cycle.xml");
            
    Car car = (Car) ctx.getBean("car1");
    System.out.println(car);
            
    ctx.close();
    

    输出:

    Car's Constructor ... 
    setBrand ... 
    init ... 
    Car [brand=Audi]
    六月 28, 2018 5:24:15 下午 org.springframework.context.support.ClassPathXmlApplicationContext doClose
    信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@6438a396: startup date [Thu Jun 28 17:24:14 CST 2018]; root of context hierarchy
    destroy ... 
    

    创建 Bean 的后置处理器

    • Bean 后置处理器允许在调用初始化方法前后对 Bean 进行额外处理
    • Bean 后置处理器对 IOC 容器里的所有 Bean 实例逐一处理,而非单一实例
    • 需要实现 BeanPostProcessor 接口

    Spring IOC 容器对 Bean 的生命周期进行管理的过程

    • 通过构造器或者工厂方法创建 Bean 实例
    • 为 Bean 的属性设置值和对其他 Bean 的引用
    • 将 Bean 实例传递给 Bean 后置处理器的 postProcessBeforeInitialization 方法
    • 调用 Bean 的初始化方法
    • 将 Bean 实例传递给 Bean 后置处理器的 postProcessAfterInitialization 方法
    • Bean 可以使用
    • 当容器关闭时,调用 Bean 的销毁方法

    实现 BeanPostProcessor 接口:
    bean:bean 实例本身
    beanName:IOC 容器配置的 bean 的名字.
    返回值:是实际上返回给用户的那个 Bean,注意:可以在以上两个方法中修改返回的 Bean,甚至返回一个新的 Bean

    public Object postProcessBeforeInitialization(Object bean, String beanName):init-method 之前被调用
    public Object postProcessAfterInitialization(Object bean, String beanName):init-method 之后被调用的实现

    public class MyBeanPostProcessor implements BeanPostProcessor{
            
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("postProcessBeforeInitialization: " + bean + ", " + beanName);
            
            if("car".equals(beanName)) {
                //..
            }
            
            return bean;
        }
        
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("postProcessAfterInitialization: " + bean + ", " + beanName);
        
            Car car = new Car();
            car.setBrand("Ford");
            return car;
        }
    } 
    

    配置 Bean

    <!-- 配置 Bean 的后置处理器: 不需要配置 id, IOC 容器自动识别是一个 BeanPostProcessor -->
    <bean class="edu.just.spring.beans.cycle.MyBeanPostProcessor"></bean>
    

    输出:

    Car's Constructor ... 
    setBrand ... 
    postProcessBeforeInitialization: Car [brand=Audi], car1
    init ... 
    postProcessAfterInitialization: Car [brand=Audi], car1
    Car's Constructor ... 
    setBrand ... 
    Car [brand=Ford]
    六月 28, 2018 5:53:28 下午 org.springframework.context.support.ClassPathXmlApplicationContext doClose
    信息: Closing org.springframework.context.support.ClassPathXmlApplicationContext@6438a396: startup date [Thu Jun 28 17:53:27 CST 2018]; root of context hierarchy
    destroy ... 
    

    2.11 泛型依赖注入

    泛型依赖注入:子类之间的依赖关系由其父类泛型以及父类之间的依赖关系来确定,父类的泛型必须为同一类型

    BaseRepository 类

    public class BaseRepository<T> {}
    

    UserRepository 类:标识持久层

    @Repository
    public class UserRepository extends BaseRepository<User>{}
    

    BaseService 类:通过 Autowired 自动装配 BaseRepository 实例

    public class BaseService<T> {
    
        @Autowired
        protected BaseRepository<T> repository;
        
        public void add() {
            System.out.println("add ...");
            System.out.println(repository);
        }
        
    }
    

    UserService 类:通过父类的泛型实例,在 UserService 中已经注入了 UserRepository 实例

    @Service
    public class UserService extends BaseService<User>{}
    

    输出:

    add ...
    edu.just.spring.beans.generic.di.UserRepository@799d4f69
    

    相关文章

      网友评论

          本文标题:Spring | 基础

          本文链接:https://www.haomeiwen.com/subject/bgnmyftx.html