Spring基础之IoC

1 Spring IoC概述

控制反转是一种通过描述(XML或者注解)并通过第三方去产生或获取特定对象的方式,最大的好处在于降低对象之间的耦合。

2 Spring IoC容器

2.1 BeanFactory和ApplicationContext

IoC容器主要基于BeanFactory和ApplicationContext两个接口,其中BeanFactroy是SpringIoC容器所定义的最底层接口,ApplicationContext是其高级接口之一,并且对BeanFactory功能做了许多拓展,所以一般情况下都会使用ApplicationCOntext作为SpringIoC容器。其接口设计图如下图所示:
在这里插入图片描述

2.1.1 BeanFactory源码分析

BeanFactory源码如下:

public interface BeanFactory {
	
	String FACTORY_BEAN_PREFIX = "&";
	
	Object getBean(String name) throws BeansException;
	<T> T getBean(String name, Class<T> requiredType) throws BeansException;
	<T> T getBean(Class<T> requiredType) throws BeansException;
	Object getBean(String name, Object... args) throws BeansException;
	<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
	boolean containsBean(String name);
	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
	boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
	boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
	Class<?> getType(String name) throws NoSuchBeanDefinitionException;
	String[] getAliases(String name);

}
  • getBean()顾名思义是用来获取SpringIoC容器管理的Bean,从参数类型看可以是字符串,也可以是Class类型;

  • isSingleton()方法用于判断是否是单例模式,如果判断为真,则该Bean在容器中作为唯一单例存在(默认);

  • isPrototype()如果判断为真,则每次从容器中取Bean,容器都会生成一个新的实例;

  • isTypeMatch()方法按照Java类型进行Bean的匹配;

  • getAliases()方法用于获取别名。

2.1.2 ApplicationContext的实现类
  • ClassPathXmlApplicationContext

    从XML文件中加载Beans的定义,XML文件路径基于ClassPath;

  • FileSystemXmlApplicationContext

    从XML文件中加载Beans的定义,需提供XML文件的全路径名;

  • XmlWebApplicationContext

    为Web工程定制。

2.2 容器的初始化和依赖注入

Bean的初始化分为3步:

  1. Resource定位,SpringIoC容器根据开发者的配置,进行资源定位,如XML或者注解配置;

  2. BeanDefinition的载入,Spring根据开发者配置的内容获取对应的POJO,用以生成对应的实例;

  3. BeanDefinition的注册,将步骤2载入的POJO向SpringIoC容器中进行注册,这样开发者就可以通过描述得到容器中的Bean了

依赖注入
lazy-init——是否初始化Bean,默认为false,也就是容器默认会自动初始化Bean,如果将其设置为true,那么只有当我们调用getBean()方法时才会进行初始化,完成依赖注入。

<bean id="testBean" class="com.fhx.TestBean" lazy-init="true">  

2.3 Spring Bean的生命周期

Bean的生命周期如图所示:
在这里插入图片描述

  1. Spring对Bean进行实例化(相当于程序中的new Xx());

  2. Spring将值和Bean的引用注入进Bean对应的属性中;

  3. 如果Bean实现了BeanNameAware接口,Spring将Bean的ID传递给setBeanName()方法(实现BeanNameAware主要是为了通过Bean的引用来获得Bean的ID,一般业务中是很少有用到Bean的ID的);

  4. 如果Bean实现了BeanFactoryAware接口,Spring将调用setBeanDactory(BeanFactory bf)方法并把BeanFactory容器实例作为参数传入。(实现BeanFactoryAware 主要目的是为了获取Spring容器,如Bean通过Spring容器发布事件等);

  5. 如果Bean实现了ApplicationContextAwaer接口,Spring容器将调用setApplicationContext(ApplicationContext ctx)方法,把应用上下文作为参数传入(作用与BeanFactory类似都是为了获取Spring容器,不同的是Spring容器在调用setApplicationContext方法时会把它自己作为setApplicationContext 的参数传入,而Spring容器在调用setBeanDactory前需要程序员自己指定(注入)setBeanDactory里的参数BeanFactory );

  6. 如果Bean实现了BeanPostProcess接口,Spring将调用它们的postProcessBeforeInitialization(预初始化)方法(作用是在Bean实例创建成功后对进行增强处理,如对Bean进行修改,增加某个功能);

  7. 如果Bean实现了InitializingBean接口,Spring将调用它们的afterPropertiesSet方法,作用与在配置文件中对Bean使用init-method声明初始化的作用一样,都是在Bean的全部属性设置成功后执行的初始化方法;

  8. 如果Bean实现了BeanPostProcess接口,Spring将调用它们的postProcessAfterInitialization(后初始化)方法(作用与6的一样,只不过6是在Bean初始化前执行的,而这个是在Bean初始化后执行的,时机不同 );

  9. 经过以上的工作后,Bean将一直驻留在应用上下文中给应用使用,直到应用上下文被销毁

  10. 如果Bean实现了DispostbleBean接口,Spring将调用它的destory方法,作用与在配置文件中对Bean使用destory-method属性的作用一样,都是在Bean实例销毁前执行的方法。

参考:

https://www.jianshu.com/p/1dec08d290c1
https://www.zhihu.com/question/38597960

3 装配Spring Bean

3.1 依赖注入的三种方式

3.1.1 构造器注入

构造器注入依赖于构造方法实现,如以下实体类Role构造方法:

public class Role {
    private Long id;
    private String roleName;
    private String note;

    public Role(String roleName, String note) {
        this.roleName = roleName;
        this.note = note;
    }
}

对应的XML配置文件:

<bean id="role" class="com.ssm.pojo.Role">
	<constructor-arg index="0" value="总经理"/>
	<constructor-arg index="1" value="管理人员"/>
</bean>

constructor-arg标签用于定义构造方法的参数,index用于定义参数的位置,value设置对应的值。

3.1.2 使用setter注入

利用Java Bean规范所定义的setter方法完成注入,优点是灵活且可读性高,允许将构造方法生命为无参的,然后通过反射对应的setter方法注入相应的值。

<bean id="role2" class="com.ssm.pojo.Role">
	<property name="roleName" value="总经理"/>
	<property name="note" value="管理人员"/>
</bean>
3.1.3 接口注入

该方式主要是获取系统外的资源,如通过JNDI获取Tomcat配置的数据源:

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactroyBena">
	<property name="jndiName">
		<value>java:comp/env/jdbc/ssm</value>
	</property>
</bean>

3.2 通过XML配置装配Bean

通过XML装配Bean需要定义对应的XML文件,即引入对应的XML模式(XSD)文件:

<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
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
        <!--Spring Bean配置代码-->
</beans>
  1. 装配简易值
<bean id="source" class="com.ssm.pojo.Source">
        <property name="fruit" value="橙汁"/>
        <property name="sugar" value="无糖"/>
        <property name="size" value="小杯"/>
    </bean>
    <bean id="juiceMaker" class="com.ssm.pojo.JuiceMaker">
        <property name="beverageShop" value="蜜雪冰城"/>
        <property name="source" ref="source"/>
    </bean>
  • id:容器中该Bean对应的编号,非必需属性,如果没有生命,Spring默认采用“全限定名#{num}”的格式生成编号,如“com.ssm.pojo.Source#0”;
  • class:类的全限定名;
  • property:定义类的属性,name对应属性名,value对应属性值;
  • ref:当某属性是一个对象时,可以使用ref结合对象的id属性进行定义。
  1. 装配集合
  • 装配简单集合

实体类代码:

public class ComplexAssembly{
	private Long id;
	private List<String> list;
	private Map<String,String> map;
	private Properties props;
	private Set<String> set;
	private String[] array;
	/**省略getter和setter**/
}

对应的XML配置代码:

<bean id="complexAssembly" class="com.ssm.pojo.ComplexAssembly">
	<property name="id" value="1"/>
	<property name="list">
		<list>
			<value>value-list-1</value>
			<value>value-list-2</value>
			<value>value-list-3</value>
		</list>
	</property>
	<property name="map">
		<map>
			<entry key="key1" value="value-key-1"/>
			<entry key="key2" value="value-key-2"/>
			<entry key="key3" value="value-key-3"/>
		</map>
	</property>
	<property name="props">
		<props>
			<prop key="prop1">value-prop-1</prop>
			<prop key="prop2">value-prop-2</prop>
			<prop key="prop3">value-prop-3</prop>
		</props>
	</property>
	<property name="set">
		<set>
			<value>value-set-1</value>
			<value>value-set-2</value>
			<value>value-set-3</value>
		</set>
	</property>
	<property name="array">
		<array>
			<value>value-array-1</value>
			<value>value-array-2</value>
			<value>value-array-3</value>
		</array>
	</property>
</bean>
  • 装配对象集合

实体类代码:

public class Role{
	private Long id;
	private String roleName;
	private String note;
	/**省略getter和setter**/
}

public class User{
	private Long id;
	private String userName;
	private String  note;
	/**省略getter和setter**/
}

public class UserRoleAssembly{
	private Long id;
	private List<Role> list;
	private Map<Role,User> map;
	private Set<Role> set;
	/**省略getter和setter**/
}

对应的XML配置代码:

<bean id="role1" class="com.ssm.pojo.Role">
	<property name="id" value="1"/>
	<property name="roleName" value="roleName1"/>
	<property name="note" value="roleNote1"/>
</bean>
<bean id="role2" class="com.ssm.pojo.Role">
	<property name="id" value="2"/>
	<property name="roleName" value="roleName2"/>
	<property name="note" value="roleNote2"/>
</bean>
<bean id="user1" class="com.ssm.pojo.User">
	<property name="id" value="1"/>
	<property name="userName" value="userName1"/>
	<property name="note" value="userNote1"/>
</bean>
<bean id="user2" class="com.ssm.pojo.User">
	<property name="id" value="2"/>
	<property name="userName" value="userName2"/>
	<property name="note" value="userNote2"/>
</bean>
<bean id="userRoleAssembly" class="com.ssm.pojo.UserRoleAssembly">
	<property name="id" value="1"/>
	<property name="list">
		<list>
			<ref bean="role1"/>
			<ref bean="role2"/>
		</list>
	</property>
	<property name="map">
		<map>
			<entry key-ref="role1" value-ref="user1"/>
			<entry key-ref="role2" value-ref="user2"/>
		</map>
	</property>
	<property name="set">
		<set>
			<ref bean="role1"/>
			<ref bean="role2"/>
		</set>
	</property>
</bean>

3.3基于XML的自动装配

Spring的自动装配有三种模式:byTpye(根据类型),byName(根据名称)、constructor(根据构造函数)。
在byTpye模式中,Spring容器会基于反射查看bean定义的类,然后找到与依赖类型相同的bean注入到另外的bean中,这个过程需要借助setter注入来完成,因此必须存在set方法,否则注入失败。

//dao层
public class UserDaoImpl implements UserDao{
    //.......
    @Override
    public void done(){
        System.out.println("UserDaoImpl.invoke......");
    }
}
//service层
public class UserServiceImpl implements UserService {
    //需要注入的依赖
    private UserDao userDao;

    /**
     * set方法
     * @param userDao
     */
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    @Override
    public void done(){
        userDao.done();
    }
}

通过使用<bean>的autowire属性启动名称为userService的自动装配功能。

<bean id="userDao"  class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" />
<!-- byType 根据类型自动装配userDao-->
<bean id="userService" autowire="byType" class="com.zejian.spring.springIoc.service.impl.UserServiceImpl" />

实上byType模式可能存一种注入失败的情况,由于是基于类型的注入,因此当xml文件中存在多个相同类型名称不同的实例Bean时,Spring容器依赖注入仍然会失败,因为存在多种适合的选项,Spring容器无法知道该注入那种,此时我们需要为Spring容器提供帮助,指定注入那个Bean实例。可以通过<bean>标签的autowire-candidate设置为false来过滤那些不需要注入的实例Bean。

<bean id="userDao"  class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" />

<!-- autowire-candidate="false" 过滤该类型 -->
<bean id="userDao2" autowire-candidate="false" class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" />

<!-- byType 根据类型自动装配userDao-->
<bean id="userService" autowire="byType" class="com.zejian.spring.springIoc.service.impl.UserServiceImpl" />

除了上述的解决方案外,还可采用byName模式的自动装配,此时Spring只会尝试将属性名与bean名称进行匹配,如果找到则注入依赖bean。

<bean id="userDao"  class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" />
<bean id="userDao2" class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" />

<!-- byName 根据名称自动装配,找到UserServiceImpl名为 userDao属性并注入-->
<bean id="userService" autowire="byName" class="com.zejian.spring.springIoc.service.impl.UserServiceImpl" />

需要了解的是如果Spring容器中没有找到可以注入的实例bean时,将不会向依赖属性值注入任何bean,这时依赖bean的属性可能为null,因此我们需要小心处理这种情况,避免不必要的奔溃。对于constructor模式,在该模式下Spring容器同样会尝试找到那些类型与构造函数相同匹配的bean然后注入。

public class UserServiceImpl implements UserService {

    private UserDao userDao;
    //constructor模式
    public UserServiceImpl(UserDao userDao){
        this.userDao=userDao;
    }

//    /**
//     * set方法
//     * @param userDao
//     */
//    public void setUserDao(UserDao userDao) {
//        this.userDao = userDao;
//    }

    @Override
    public void done(){
        userDao.done();
    }
}

基于xml配置:

<bean id="userDao"  class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" />
<!-- constructor自动装配userDao-->
<bean id="userService" autowire="constructor" class="com.zejian.spring.springIoc.service.impl.UserServiceImpl" />

在实际测试中发现当出现多个bean实例时,如果含有名称与类中声明一样时仍然能正确找到对应类,如userDao、userDao2同时出现,而userService实现类中存在同名属性userDao,此时无需过滤也能正确注入:

<bean id="userDao" class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" />

<!--没有过滤userDao2-->
<bean id="userDao2" class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" />

<bean id="userService" autowire="constructor" class="com.zejian.spring.springIoc.service.impl.UserServiceImpl" />

当只存在userDao2时,仍然能正常注入该类,如下形式

<!--只存在userDao2,仍然能正确注入-->
<bean id="userDao2" class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" />

<bean id="userService" autowire="constructor" class="com.zejian.spring.springIoc.service.impl.UserServiceImpl" />

但是当不存在userDao,只存在userDao2和userDao3时就不能注入了此时必须使用autowire-candidate=”false” 过滤该类型了。

<!--只存在userDao2,userDao3 无法成功注入-->
<bean id="userDao2" class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" />

<bean id="userDao3" class="com.zejian.spring.springIoc.dao.impl.UserDaoImpl" />

<bean id="userService" autowire="constructor" class="com.zejian.spring.springIoc.service.impl.UserServiceImpl" />

因此得出如下结论:在constructor模式下,存在单个实例则优先按类型进行参数匹配(无论名称是否匹配),当存在多个类型相同实例时,按名称优先匹配,如果没有找到对应名称,则注入失败,此时可以使用autowire-candidate=”false” 过滤来解决。

3.4 通过注解装配Bean

3.4.1 使用@Component注册Bean

示例代码:

@Component(value = "role")
public class Role{
	@Value("1")
	private long id;
	@Value("role_name_1")
	private String roleName;
}
  • 注解@Component代表这个类会被扫描成Bean实例,value属性为这个实例的id,可以简写为@Component(“role”),也可以省略不写,默认值为类名首字母小写;
  • 注解@Value用于基本类型或字符串属性的注入。

该注解生效的前提是,指定扫描路径,让Spring知道去哪里进行扫描,有两种实现方式:

  1. 配置XML文件
<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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       					   http://www.springframework.org/schema/beans/spring-beans.xsd
       					   http://www.springframework.org/schema/context
       					   http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 组件扫描,扫描含有注解的类 -->
    <context:component-scan base-package="com.cc.study.annotation"></context:component-scan>
</beans>

2. 使用@ComponentScan注解

@ComponentScan(basePackages={"com.ssm.pojo","com.ssm.service"})
public class ApplicationConfig{
	/**省略**/
}

通过上述配置类获取Bean

public static void testBean(){
	AnnotationConfigApplicationContext cx = new AnnotationConfigApplicationContext(ApplicationConfig.class);
	Role role = cx.getBean(Role.class);
	/**do something**/
}
  • 注解@ComponentScan有两个配置项,1个是basePackages,可以配置一个包路径的数组,Spring会扫名对应的包及其子包;2是basePackagesClasses,可以配置多个类,Spring会扫名配置类所在的包及其子包;
  • 如果多个@ComponentScan扫描重复的类,那么将会生成多个Bean实例,所以一般不要使用多个@ComponentScan注解进行配置。
  1. @Component的衍生注解
  • @Repository ——注解在Dao层
  • @Service——注解在服务层
  • @Controller——注解在控制器
3.4.2 基于@Autowired实现自动装配
  1. @Autowired的3种使用方式
  • 注解在属性上:
public class RoleServiceImpl implements RoleService{
	@Autowired
	private Role role;
	/**do something**/
}

  • 注解在方法上:
public class RoleServiceImpl implements RoleService{
	private Role role;
	@Autowired
	public void setRole(Role role){
		this.role = role;
	}
	/**do something**/
}
  • 注解在参数上:
public class RoleServiceImpl implements RoleService{
	private Role role;
	public RoleServiceImpl(@Autowired Role role){
		this.role = role;
	}
	/**do something**/
}

2.注入失败的解决措施
@Autowired注解会根据目标属性的类型(byType)去容器中获取目标实例实现注入,当根据类型找不到实例或找到多个实例时,都会发生注入失败的情况。

  • required属性——默认为true,设置为false则允许找不到目标对象不注入;
  • @Primary注解 ——实现目标对象的优先级,告诉Spring优先使用哪个类进行注入;
@Component("roleService1")
public class RoleServiceImpl1 implements RoleService{
	/**do something**/
}

@Component("roleService2")
@Primary
public class RoleServiceImpl2 implements RoleService{
	/**do something**/
}

@Component
public class RoleController{
	@Autowired
	private RoleService roleService;//注入的实例为RoleService2
	/**do something**/
}
  • @Qualifier——使用byName的方式消除歧义性
@Component("roleService1")
public class RoleServiceImpl1 implements RoleService{
	/**do something**/
}

@Component("roleService2")
public class RoleServiceImpl2 implements RoleService{
	/**do something**/
}

@Component
public class RoleController{
	@Autowired
	@Qualifier("roleService1")
	private RoleService roleService;//注入的实例为RoleService1
	/**do something**/
}
3.4.3 基于@Resource实现自动装配

与@Autowried具备相同功效的还有@Resource,默认按 byName模式 自动注入,由J2EE提供,需导入Package: javax.annotation.Resource,可以标注在成员变量和set方法上,但无法标注构造函数。@Resource有两个中重要的属性:name和type。Spring容器对于@Resource注解的name属性解析为bean的名字,type属性则解析为bean的类型。因此使用name属性,则按byName模式的自动注入策略,如果使用type属性则按 byType模式自动注入策略。倘若既不指定name也不指定type属性,Spring容器将通过反射技术默认按byName模式注入。

//@Autowired标注成员变量
@Autowired
@Qualifier("userDao")
private UserDao userDao;  
//上述代码等价于@Resource
@Resource(name=“userDao”)
private UserDao  userDao;//用于成员变量

//也可以用于set方法标注
@Resource(name=“userDao”)
public void setUserDao(UserDao userDao) {
   this.userDao= userDao;
}
3.4.4使用@Bean装配Bean

@Component只能注解在类上,不能注解在方法上,当需要引入第三方库中的组件时,无法将@Component注解在相应的类上,这时候Spring提供了@Bean注解来解决这一问题。

例如装配数据源Bean:

@Bean(name="dataSource")
public DataSource getDataSource() {
	Properties props = new Properties();
	 props.setProperty("driver", "com.mysql.jdbc.Driver");
     props.setProperty("url", "jdbc:mysql://localhost:3306/sys");
     props.setProperty("username", "root");
     props.setProperty("password", "123456");
     DataSource dataSource = null;
     try {
         dataSource = BasicDataSourceFactory.createDataSource(props);
     } catch (Exception e) {
         e.printStackTrace();
     }
     return dataSource;
}

这样就实现了DataSource实例被Spring容器管理,此外,@Bean注解的initMethod和destoryMethod属性可自定义实例的初始化方法和销毁方法。

@Bean(name="dataSource",initMethod="init",destoryMethod="destory")
3.4.5 其他常用注解

@Import
@ImportSource
@Resource
@Configuration

3.4 Profile的使用

分别配置一个测试和开发环境的数据库连接池

public class ProfileDataSource {
    @Bean(name = "devDataSource")
    @Profile("dev")
    public DataSource getDevDataSource() {
        Properties props = new Properties();
        props.setProperty("driver", "com.mysql.jdbc.Driver");
        props.setProperty("url", "jdbc:mysql://localhost:3306/sys1");
        props.setProperty("username", "root");
        props.setProperty("password", "123456");
        DataSource dataSource = null;
        try {
            dataSource = BasicDataSourceFactory.createDataSource(props);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return dataSource;
    }
    @Bean(name = "testDataSource")
    @Profile("test")
    public DataSource getTestDataSource() {
        Properties props = new Properties();
        props.setProperty("driver", "com.mysql.jdbc.Driver");
        props.setProperty("url", "jdbc:mysql://localhost:3306/sys2");
        props.setProperty("username", "root");
        props.setProperty("password", "123456");
        DataSource dataSource = null;
        try {
            dataSource = BasicDataSourceFactory.createDataSource(props);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return dataSource;
    }
}

配置了Profile后发现这两个Bean都不会被加载到Spring IoC容器中,需要自行激活Profile,可采取下面几种激活方法:

  1. 采用@ActiveProfile注解
@ActiveProfile("dev")
public class ProfileTest{
	@Autowired
	private DataSource dataSource;
	@Test
	public void test() {
		System.out.println(dataSource.getClass().getName());
	}
}
  1. 配置JVM参数
  • spring.profile.default:默认启动的Profile,如果系统为配置关于Profile的参数,将默认启动它。
  • spring.profile.active:激活的Profile,如果配置了它,default将失效
    比如想启动test对应的Profile,可如下配置:
JAVA_OPTS="-Dspring.profiles.active=test"

在IDEA中可如下图所示配置:
在这里插入图片描述

  1. 在web.xml中配置
<context-param>
	<param-name>spring.profiles.active</param-name>
	<param-value>test</param-value>
</context-param>

3.5 加载properties文件

使用properties配置文件可以减少硬编码,提高配置属性修改的便利性,如下所示的数据库配置文件database-config.properties:

jdbc.database.driver=com.mysql.jdbc.Driver
jdbc.database.url=jdbc:myqsl://localhost:3306/sys
jdbc.database.username=root
jdbc.database.password=123456
3.5.1 使用注解的方式加载属性文件

使用@PropertySource注解加载配置文件

@Configuration
@PropertySource(value={"classpath:database-config.properties"},ignoreResourceNotFound=true)
public class ApplicationConfig{
	/**do something**/
}
  • value:字符串数组,可以配置多个配置文件
  • ignoreResourceNotFound:如果找不到对应的属性文件是否进行忽略处理,默认false

测试加载属性

public class AnnotationMain {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
        String url = context.getEnvironment().getProperty("jdbc.database.url");
        System.out.println(url);
    }
}

PropertySourcesPlaceholderConfigurer解析属性文件
Spring推荐使用一个属性文件解析类进行解析属性占位符,使用它可以解析对应的属性文件,并通过占位符去引用对应的位置
例如解析一个数据库配置文件:

@Configuration
@PropertySource(value={"classpath:database-config.properties"},ignoreResourceNotFound=true)
public class ApplicationConfig{
	@Bean
	public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(){
		return new PropertySourcesPlaceholderConfigurer();
	}
	/**do something**/
}

使用@Value注解和占位符引用定义好的配置:

public class DataSourceBean {
	@Value("${jdbc.database.driver}")
	private String driver;
	@Value("${jdbc.database.url}")
	private String url;
	@Value("${jdbc.database.username}")
	private String username;
	@Value("${jdbc.database.password}")
	private String password;
	
	@Bean
	public DataSource getDataSource() {
        Properties props = new Properties();
        props.setProperty("driver", driver);
        props.setProperty("url", url);
        props.setProperty("username", username);
        props.setProperty("password", password);
        DataSource dataSource = null;
        try {
            dataSource = BasicDataSourceFactory.createDataSource(props);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return dataSource;
    }
}
3.5.2 使用XML的方式加载属性文件
  1. 配置单个属性文件
<context:property-placeholder ignore-resource-not-found="true" location="classpath:database-config.properties"/>

属性location配置属性文件的路径,如果是多个文件需要用逗号隔开
2. 配置多个属性文件
当存在多个属性文件时,以下这种方式可读性更高:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
	<property name="locations">
		<array>
			<value>classpath:database-config.properties</value>
			<value>classpath:log4j.properties</value>
		</array>
	</property>
	<property name="ignoreResourceNotFound" value="true"/>
</bean>

3.6 条件化装配Bean

某些条件下可能不需要装配Bean,如不存在database-config.properties文件中配置的属性时,就不需要根据这个属性文件装配数据源,此时就需要条件化装配Bean。
@Conditional注解,数据源配置代码如下所示:

@Conditional({DataSourceCondition.class})
public class DataSourceBean {
	@Value("${jdbc.database.driver}")
	private String driver;
	@Value("${jdbc.database.url}")
	private String url;
	@Value("${jdbc.database.username}")
	private String username;
	@Value("${jdbc.database.password}")
	private String password;
	
	@Bean
	public DataSource getDataSource() {
        Properties props = new Properties();
        props.setProperty("driver", driver);
        props.setProperty("url", url);
        props.setProperty("username", username);
        props.setProperty("password", password);
        DataSource dataSource = null;
        try {
            dataSource = BasicDataSourceFactory.createDataSource(props);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return dataSource;
    }
}

条件类DataSourceCondition需要实现Condition接口,并实现接口的matches方法:

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class DataSourceCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //获取上下文环境
        Environment env = context.getEnvironment();
        //判断是否存在关于数据源的基础配置
        return env.containsProperty("jdbc.database.drier")
                && env.containsProperty("jdbc.database.url")
                && env.containsProperty("jdbc.database.username")
                && env.containsProperty("jdbc.database.password");
    }
}

该方法有两个参数,通过ConditionContext可以获取Spring的运行环境,通过AnnotatedTypeMetadata可以获得关于Bean的注解信息,如果该方法返回true,则Spring才会创建对应的Bean。

3.7 Bean的作用域

Spring使用@Scope注解提供了4种作用域:

  • singleton——单例,默认的选项,在整个应用中,Spring只生成一个Bean实例;
  • prototype——原型,每次注入Spring都会为它创建一个新的实例;
  • session——会话,在Web应用中使用,在会话过程中Spring只创建一个实例;
  • request——请求,在Web应用中使用,在一次请求中Spring只创建一个实例,不同的请求会创建不同的实例。

参考

https://blog.csdn.net/javazejian/article/details/54561302

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值