Spring之IoC

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring之IoC相关的知识,希望对你有一定的参考价值。

  • 什么是IOC

将对象的管理交给Spring容器,对象间的依赖进行解耦,想要某个对象,从Spring容器中进行获取

 

  • IOC的类型

构造函数注入:Spring已经实现

成员变量注入:Spring已经实现

接口注入:Spring未实现

 

  • 实现IOC的Java基础

反射技术等

 

 

Spring提供了比JDK更好的访问资源的API:Resource接口

 

技术分享
/*
 * Copyright 2002-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.core.io;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URL;

/**
 * Interface for a resource descriptor that abstracts from the actual
 * type of underlying resource, such as a file or class path resource.
 *
 * <p>An InputStream can be opened for every resource if it exists in
 * physical form, but a URL or File handle can just be returned for
 * certain resources. The actual behavior is implementation-specific.
 *
 * @author Juergen Hoeller
 * @since 28.12.2003
 * @see #getInputStream()
 * @see #getURL()
 * @see #getURI()
 * @see #getFile()
 * @see WritableResource
 * @see ContextResource
 * @see UrlResource
 * @see ClassPathResource
 * @see FileSystemResource
 * @see PathResource
 * @see ByteArrayResource
 * @see InputStreamResource
 */
public interface Resource extends InputStreamSource {

    /**
     * Determine whether this resource actually exists in physical form.
     * <p>This method performs a definitive existence check, whereas the
     * existence of a {@code Resource} handle only guarantees a valid
     * descriptor handle.
     */
    boolean exists();

    /**
     * Indicate whether the contents of this resource can be read via
     * {@link #getInputStream()}.
     * <p>Will be {@code true} for typical resource descriptors;
     * note that actual content reading may still fail when attempted.
     * However, a value of {@code false} is a definitive indication
     * that the resource content cannot be read.
     * @see #getInputStream()
     */
    boolean isReadable();

    /**
     * Indicate whether this resource represents a handle with an open stream.
     * If {@code true}, the InputStream cannot be read multiple times,
     * and must be read and closed to avoid resource leaks.
     * <p>Will be {@code false} for typical resource descriptors.
     */
    boolean isOpen();

    /**
     * Return a URL handle for this resource.
     * @throws IOException if the resource cannot be resolved as URL,
     * i.e. if the resource is not available as descriptor
     */
    URL getURL() throws IOException;

    /**
     * Return a URI handle for this resource.
     * @throws IOException if the resource cannot be resolved as URI,
     * i.e. if the resource is not available as descriptor
     * @since 2.5
     */
    URI getURI() throws IOException;

    /**
     * Return a File handle for this resource.
     * @throws IOException if the resource cannot be resolved as absolute
     * file path, i.e. if the resource is not available in a file system
     */
    File getFile() throws IOException;

    /**
     * Determine the content length for this resource.
     * @throws IOException if the resource cannot be resolved
     * (in the file system or as some other known physical resource type)
     */
    long contentLength() throws IOException;

    /**
     * Determine the last-modified timestamp for this resource.
     * @throws IOException if the resource cannot be resolved
     * (in the file system or as some other known physical resource type)
     */
    long lastModified() throws IOException;

    /**
     * Create a resource relative to this resource.
     * @param relativePath the relative path (relative to this resource)
     * @return the resource handle for the relative resource
     * @throws IOException if the relative resource cannot be determined
     */
    Resource createRelative(String relativePath) throws IOException;

    /**
     * Determine a filename for this resource, i.e. typically the last
     * part of the path: for example, "myfile.txt".
     * <p>Returns {@code null} if this type of resource does not
     * have a filename.
     */
    String getFilename();

    /**
     * Return a description for this resource,
     * to be used for error output when working with the resource.
     * <p>Implementations are also encouraged to return this value
     * from their {@code toString} method.
     * @see Object#toString()
     */
    String getDescription();

}
Resource接口

Resource接口演示

package org.zln.spring.resource;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Date;

/**
 * Created by sherry on 16/10/13.
 */
public class SpringResourceDemo {

    private static Logger logger = LoggerFactory.getLogger(SpringResourceDemo.class);

    public static void main(String[] args) throws IOException {
//        test1();
        String ss = "哇哈哈";
        ByteArrayResource byteArrayResource = new ByteArrayResource(ss.getBytes("UTF-8"));


    }

    private static void test1() throws IOException {
        Resource resource = new ClassPathResource("log4j.properties");
        logger.debug("exists:"+resource.exists());
        logger.debug("isReadable:"+resource.isReadable());
        logger.debug("getFile:"+resource.getFile().getAbsolutePath());
        logger.debug("contentLength:"+resource.contentLength());
        logger.debug("lastModified:"+new Date(resource.lastModified()));
        InputStream inputStream = resource.getInputStream();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
        for (String line = bufferedReader.readLine(); StringUtils.isNotEmpty(line);){
            logger.info(line);
            line = bufferedReader.readLine();
        }
        bufferedReader.close();
        inputStream.close();
        bufferedReader = null;
        inputStream = null;
    }
}

 

  • Resource的实现类

ByteArrayResource:二进制数组表示的资源,可以在内存中通过程序构造

String ss = "哇哈哈";
ByteArrayResource byteArrayResource = new ByteArrayResource(ss.getBytes("UTF-8"));

 

 

ClassPathResource:类路径下的资源

FileSystemResource:文件系统的资源

ServletContextResource:Web容器上下文的资源

UrlResource:URL表示的资源

 

Spring的Resource可以脱离Spring容器使用

 

 

  • 资源的表达方式

classpath:、classpath:/,等价,都是从相对于classpath的根路径进行访问,只加载第一个

classpath*:扫描所有符合条件的文件

file:,UrlResource从文件系统获取

http://,web服务器

ftp://,ftp服务器

没有前缀,按照实现类的类型判断资源的形式

 

资源的表示形式支持Ant风格

? 任意一个字符

* 任意多个字符

** 多层路径

 

 

  • 在XML中配置Bean演示
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.2.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
    <bean id="car" class="org.zln.beans.Car"
          p:brand="红旗CA72"
          p:color="黑色"
          p:maxSpeed="200"/>
</beans>

 

初始化

package org.zln.beans;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
 * Created by sherry on 16/7/5.
 */
public class BeanFactoryTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        System.out.println("初始化Bean工厂");
        Car car = (Car) applicationContext.getBean("car");
        System.out.println(car);
    }
}

 

  • Java类配置Bean
package org.zln.beans;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * Created by sherry on 16/7/5.
 */
@Configuration
public class BeansConf {
    @Bean(name = "car")
    public Car carBuilder(){
        Car car = new Car();
        car.setBrand("红旗");
        car.setColor("黑色");
        car.setMaxSpeed(200);
        return car;
    }
}
package org.zln.beans;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
 * Created by sherry on 16/7/5.
 */
public class BeanFactoryTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeansConf.class);
        System.out.println("初始化Bean工厂");
        Car car = applicationContext.getBean("car",Car.class);
        System.out.println(car);
    }
}

 

 

  • WebApplicationContext

该类是专门为Web环境准备的,允许从相对于Web根路径的位置加载Spring配置文件

WebApplicationContext可以获取ServletContext的引用,整个Web应用上下文对象将作为属性放置在ServletContext对象中,以便Web应用可以访问Spring应用上下文

 

Spring提供了一个工具类:WebApplicationContextUtils.getWebApplicationContext(ServletContext sc),用于获取WebApplicationContext实例

 

通过Web监听器启动Spring

 

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    <!--① 启动Spring-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:applicationContext.xml</param-value>
    </context-param>
    <!--② 启动Spring容器的监听,引用①处的上下文参数获取Spring配置文件地址-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
</web-app>

WebApplicationContext的启动需要用到日志功能,默认日志配置文件在classes根目录下

如果配置到了其他路径,则需要另外说明,并且保证在Spring框架启动前完成日志信息的装载

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
......
    <!--配置日志-->
    <context-param>
        <param-name>log4jConfigLocation</param-name>
        <param-value>classpath:log4j2.xml</param-value>
    </context-param>
    <!--通过监听装载日志配置文件-->
    <listener>
        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
    </listener>
......
</web-app>

如果配置文件写在了Class类中,则需要如下配置才能启动

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    <context-param>
        <param-name>contextClass</param-name>
        <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
    </context-param>
    <!--① 启动Spring-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>Spring配置类的全限定名</param-value>
    </context-param>
    <!--配置日志-->
    <context-param>
        <param-name>log4jConfigLocation</param-name>
        <param-value>classpath:log4j2.xml</param-value>
    </context-param>
    <!--通过监听装载日志配置文件-->
    <listener>
        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
    </listener>
    <!--② 启动Spring容器的监听,引用①处的上下文参数获取Spring配置文件地址-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!--③ 配置Spring MVC地址映射-->
    <servlet>
        <servlet-name>bbt</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>bbt</servlet-name>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>
</web-app>

contextClass参数用于指定WebApplicationContext的实现类
    <!--Spring解决乱码问题-->
    <filter>
        <filter-name>encoding</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
将此配置在web.xml中

 

 

  • 获取Spring容器的方法
1、定义工具类
public class SpringApplicationContextHolder implements ApplicationContextAware {
    private static ApplicationContext context;
    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        SpringApplicationContextHolder.context = context;
    }
   
    public static Object getSpringBean(String beanName) {
        notEmpty(beanName, "bean name is required");
        return context==null?null:context.getBean(beanName);
    }
 
    public static String[] getBeanDefinitionNames() {
        return context.getBeanDefinitionNames();
    }
}
2、注册工具类
将工具类在Spring中进行配置即可
3、使用工具类获取Spring管理的Bean
使用工具类的静态方法 getSpringBean 得到Bean实例

 

 

  • 将Bean纳入Spring容器的三种方式

1、将Bean配置在类中,加上注解

2、将Bean配置在xml中

3、将Bean加上合适的注解,在xml中扫描

 

 

 

  • 配置Bean之间依赖的几种方式

1、内部依赖使用内部bean(XML)

2、构造函数(XML、注解),优点:减少了setter方法,保证必须的依赖都初始化  缺点:初始化必须赋值,即使填null

3、成员变量(XML、注解),如果用注解,堪称完美

 

 

支持级联属性

 

支持集合属性的注入

使用util命名空间配置会方便很多

 

 

lookup方法注入

 

Bean之间的关系

如果多个Bean之间存在相同的配置信息,Spring允许我们定义父Bean,子Bean将自动继承父Bean的配置信息

父Bean如果不需要实例化的话,可以定义为抽象  abstract=“true”

 

依赖

depends-on  在xml配置后,优先实例化依赖的Bean

多个依赖使用逗号分隔

 

 

整合多个配置

xml使用import标签

 

 

注解配置

类:添加合适的注解

普通类:@Component

Dao:@Repository

Service:@Service

Controller:@Controller

XML:配置扫描路径

<context:component-scan base-package="org.zln" resource-pattern="**/*Dao.class"/>

<context:component-scan base-package="org.zln">

<context:include-filter type=“regex” expression=“”/> 需要包含的,可以配置多个

<context:exclude-filter type=“aspectj” expression=“”/> 需要排除的,可以配置多个

</context>

说明:

type:

annotation:expression填写某个注解,过滤出使用了这些注解的类

assignable:expression填写某个类或接口,过滤出扩展或继承了某个类的类

aspectj:这个比较强大

regex:类名满足正则表达式

custom:

 

将Bean被Spring扫描管理后,如何设置依赖呢?

1、在依赖的成员变量上添加 @Autowired注解(可以设置required属性规定是否必须从容器中获取到),表示通过类型进行依赖,可以同时添加@Qualifier 指明Bean的名称

2、在方法上添加@Autowired注解,会将对象注入给方法参数[email protected]注解可以添加到方法参数类型的前面

 

设置Bean的生命周期

在Bean中添加@Scope

在初始化执行的方法上,相当于xml中配置init-method的方法上,添加 @PostConstruct 容器关闭的时候,添加 @PreDestory 注解的方法会执行

 

 

  • XML、注解、Java类,三种配置Bean的方式比较

XML:配置第三方类库,使用命名空间等

注解:自己写的一些类,尽量用注解+扫描的形式

Java类:初始化比较复杂的情况下使用

以上是关于Spring之IoC的主要内容,如果未能解决你的问题,请参考以下文章

Spring IOC源代码具体解释之容器依赖注入

Spring IOC源代码具体解释之容器初始化

3.1.2 Spring之IoC

Spring系列之IOC容器

Spring入门之IoC

面试之Spring框架IOC和AOP的实现原理