Spring IoC/DI和注解注入bean生命周期

一、简介

      Spring 的核心是控制反转(IoC)和面向切面(AOP)。

      Spring 官网:https://spring.io/projects/spring-framework

1、IoC:Inversion of Control(控制反转)

    控制反转是一个通用的概念,不是什么新技术而是一种设计思想,好比于MVC。

    其本意就是将原本程序中手动创建对象的控制权,交由 Spring框架来原理。

    其作用是创建对象并且维护对象之间的依赖关系,实现了程序的解耦合。

2、DI:Dependency Injection(依赖注入)

    依赖注入是指 Spring创建对象的过程中,将对象依赖的属性(常量,对象,集合)通过配置设值给该对象。

    Spring支持的依赖注入方式:xml配置注入( setter方法注入,构造方法注入等)和注解注入

3、Spring IoC容器(其原理:反射和内省机制

      BeanFactory:生产 bean对象的工厂,Spring最底层的接口,只提供了IoC容器的功能:负责配置,创建和管理bean,被 Spring IoC容器管理的对象称为bean。Spring IoC容器通过读取配置文件中的配置元数据,通过元数据对应用中的各个对象进行实例化和装配。在应用中,一般不使用BeanFactory,而推荐使用 ApplicationContext(应用上下文)

     ApplicationContext 接口继承了BeanFactory接口,还提供了AOP集成、国际化处理等功能

4、BeanFactory与ApplicationContext创建bean的区别

     BeanFactory:需要等到获取某个bean的时候才会才会创建该bean,即延迟初始化

     ApplicationContext:在启动Spring容器时就会创建所有的bean(Web应用中推荐)

    在xml中也可以配置延迟 lazy-init

二、xml配置注入bean

1. setter方法注入

     指 IoC容器通过成员变量的 setter 方法来注入被依赖对象,使用 property标签。这种注入方式简单、直观,因而在 Spring 依赖注入里大量使用。

mvc 类

public class User {
	private int id;
	private String username;
	private String password;
	private Map<String,String> map = new HashMap<String,String>();
	private List<String> list = new ArrayList<>(); 	// Set 类同 List

    ... 必须提供 setter 方法
}


public class UserServiceImpl implements UserService{
	//面向接口编程
	private UserDao userDao;

	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}

    ... 必须提供 setter 方法
}

配置文件:

<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="user" class="cn.jq.springdemo.model.User">
		<property name="id" value="5"></property>
		<property name="username" value="admin"></property>
                <property name="password" >
			<value><![CDATA[>123<admin><]]></value>
		</property>
		<property name="map">
			<map>
				<entry key="java" value="80"></entry>
				<entry key="spring" value="85"></entry>
			</map>
		</property>
		<property name="list">
			<list>
				<value>lisi</value>
				<value>zs</value>
			</list>
		</property>
	</bean>
	<bean id="userDao" class="cn.jq.springdemo.dao.UserDaoImpl"></bean>

	<bean id="userService"
		class="cn.jq.springdemo.service.UserServiceImpl">
		<property name="userDao" ref="userDao"></property>
	</bean>

</beans>

测试类:

	@Test
	public void test() {
		// 1.初始化ioc容器(装对象的容器)
		ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
	
		//2.从容器中获得创建好的对象
		User user = context.getBean("user", User.class);
		System.out.println(user);
	}

-------
User [id=5, username=admin, password=>123<admin><, map={java=80, spring=85}, list=[lisi, zs]]

2. 构造方法注入:

    指利用构造器来设置依赖关系的方式,使用 constructor-arg标签。通俗来说,就是驱动Spring在底层以反射方式执行带指定参数的构造器,当执行带参数的构造器时,就可利用构造器参数对成员变量执行初始化——这就是构造注入的本质。

mvc 类:

public class UserServiceImpl implements UserService{
	//面向接口编程
	private UserDao userDao;
	private User user;
	private int num;
	private String str;

	public UserServiceImpl(UserDao userDao, User user, int num, String str) {
		super();
		this.userDao = userDao;
		this.user = user;
		this.num = num;
		this.str = str;
	}
}

配置文件:

<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="user" class="cn.jq.springdemo.model.User">
		<property name="id" value="5"></property>
		<property name="username" value="admin"></property>
	</bean>
	<bean id="userDao" class="cn.jq.springdemo.dao.UserDaoImpl"></bean>

	<bean id="userService"	class="cn.jq.springdemo.service.UserServiceImpl">
		<constructor-arg type="cn.jq.springdemo.dao.UserDao" ref="userDao"></constructor-arg>
		<constructor-arg index="1" ref="user"></constructor-arg>
		<constructor-arg type="int" value="20"></constructor-arg>
		<constructor-arg type="java.lang.String" value="adminstr"></constructor-arg>
	</bean>
</beans>

name - 参数的名字   或者   index - 参数的索引位置 从0开始(注意:构造方法的参数顺序)

ref - 指引用类型,指向另外一个bean标签的id  

vlaue -  指简单类型的值

测试类:

	@Test
	public void test() {
		// 1.初始化ioc容器(装对象的容器)
		ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
	
		//2.从容器中获得创建好的对象
		UserServiceImpl userService = context.getBean("userService",UserServiceImpl.class);
		System.out.println(userService);
	}
-------	
UserServiceImpl [userDao=cn.jq.springdemo.dao.UserDaoImpl@543c6f6d, user=User [id=5, username=admin, password=null, map={}, list=[]], num=20, str=adminstr]

上面两种注入方式的对比

1)setter方法注入优点:

  • 与传统的JavaBean的写法更相似,程序开发人员更容易理解、接受。通过setter方法设定依赖关系显得更加直观、自然。
  • 对于复杂的依赖关系,如果采用构造注入,会导致构造器过于臃肿,难以阅读。Spring在创建Bean实例时,需要同时实例化其依赖的全部实例,因而导致性能下降。而使用设值注入,则能避免这些问题。
  • 尤其在某些成员变量可选的情况下,多参数的构造器更加笨重。

2)构造注入优点:

  • 构造注入可以在构造器中决定依赖关系的注入顺序,优先依赖的优先注入。
  • 对于依赖关系无需变化的Bean,构造注入更有用处。因为没有setter方法,所有的依赖关系全部在构造器内设定,无须担心后续的代码对依赖关系产生破坏。
  • 依赖关系只能在构造器中设定,则只有组件的创建者才能改变组件的依赖关系,对组件的调用者而言,组件内部的依赖关系完全透明,更符合高内聚的原则。

注意:
      建议采用settter注入为主,构造注入为辅的注入策略。对于依赖关系无须变化的注入,尽量采用构造注入;而其他依赖关系的注入,则考虑采用设值注入。

3、Spring的自动装配(了解)

    Spring自动装配可通过 <beans/> 元素的 default-autowire属性指定,该属性对配置文件中所有的Bean起作用;

     也可通过对 <bean/> 元素的 autowire属性指定,该属性只对该Bean起作用。

模式描述
no这是默认的设置,它意味着没有自动装配,你应该使用显式的bean引用来连线。你不用为了连线做特殊的事。在依赖注入章节你已经看到这个了。
byName属性名自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byName。然后尝试匹配,并且将它的属性与在配置文件中被定义为相同名称的 beans 的属性进行连接。
byType属性数据类型自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byType。然后如果它的类型匹配配置文件中的一个确切的 bean 名称,它将尝试匹配和连接属性的类型。如果存在不止一个这样的 bean,则一个致命的异常将会被抛出。
constructor类似于 byType,但该类型适用于构造函数参数类型。如果在容器中没有一个构造函数参数类型的 bean,则一个致命错误将会发生。
autodetectSpring首先尝试通过 constructor 使用自动装配来连接,如果它不执行,Spring 尝试通过 byType 来自动装配。

常用两种模式:

      byTpye模式:Spring容器会基于反射查看bean定义的类,然后找到与依赖类型相同的bean注入到另外的bean中,这个过程需要借助setter注入来完成,因此必须存在set方法,否则注入失败。

      byName模式:此时Spring只会尝试将属性名与bean名称进行匹配,如果找到则注入依赖bean。

mvc 类

public class UserServiceImpl implements UserService{
	private UserDao userDao; //面向接口编程
	private User user;

    ... 必须提供 setter 方法
}

配置文件:

<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="user" class="cn.jq.springdemo.model.User">
		<property name="id" value="5"></property>
		<property name="username" value="admin"></property>
		<property name="password" >
			<value><![CDATA[>123<admin><]]></value>
		</property>
	</bean>
	<bean id="userDao" class="cn.jq.springdemo.dao.UserDaoImpl"></bean>

	<!--自动装配userService类成员属性
	autowire 
            - byName 自动装配和成员属性名称相同的bean 调用的是setter方法注入
		   注意:   确保成员属性的 名称规范 有对应setter方法 还有就是bean的id和成员属性的名称相同
	      byType 根据类型自动装配
		同一种类型的 bean 只能配置一个唯一的标签
	 -->
	<bean id="userService"	class="cn.jq.springdemo.service.UserServiceImpl" autowire="byName">
	</bean>
</beans>

测试类:

	@Test
	public void test() {
		// 1.初始化ioc容器(装对象的容器)
		ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
	
		//2.从容器中获得创建好的对象
		UserServiceImpl userService = context.getBean("userService",UserServiceImpl.class);
		System.out.println(userService);
	}
------
UserServiceImpl [userDao=cn.jq.springdemo.dao.UserDaoImpl@13c27452, user=User [id=5, username=admin, password=>123<admin><, map={}, list=[]]]

    如果Spring容器中没有找到可以注入的实例bean时,将不会向依赖属性值注入任何bean,这时依赖bean的属性为 null 或 基本类型默认值。

三、注解注入bean

       Spring 使用注解,通过"@XXX"的方式,让注解与Java Bean紧密结合,既大大减少了配置文件的体积,又增加了Java Bean的可读性与内聚性。注意:

      首先,在 applicationContext.xml核心配置文件中需要用到 context 命名空间,然后使用 <context:component-scan>标签告诉spring框架,配置了注解的类的位置。     

    base-package属性:指定spring扫描注解的类所在的包(包含子包)。当需要扫描多个包的时候,可以使用逗号分隔。

	<!-- 使用注解驱动 自动注册bean  -->
	<context:component-scan base-package="cn.jq.springdemo"></context:component-scan>

    

1. 注解说明:

       Component 最初spring框架设计的,后来为了标识不同代码层,衍生出Controller,Service,Repository三个注解 作用相当于配置文件的bean标签,被注解的类,spring始化时,就会创建该对象。它们的功能都是相同的,只是用于标注不同类型的组件

只能添加在类上  不能添加在抽象类和接口上:

      @Controller       用于标注控制层组件,即web业务层。

      @Service          用于标注业务层组件,即service层。

      @Repository(value="userDao")     用于标注数据访问组件,即dao层。

      @Component    泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。

      @Scope(scopeName="singleton")    用于指定scope作用域的(控制类生成的时候采用单例还是多例)

注解属性 value 对应的是创建的对象的名字 和id相同 (缺省配置下默认的名字为 类名首字母小写)

定义在类的 属性字段上:

      @Value(value="112")      给简单类型属性赋值,可以用在方法上或属性上

      @Resource(name="user")对象引用类型赋值,该值user类必须已经声明(在配置文件中已配置或在类中已经注解)

说一下@Resource 的装配顺序:
(1)、@Resource后面没有任何内容,默认通过name属性去匹配bean,找不到再按type去匹配
(2)、指定了name或者type则根据指定的类型去匹配bean
(3)、指定了name和type则根据指定的name和type去匹配bean,任何一个不匹配都将报错

 

    @Autowired 自动装配,给对象引用类型赋值,其作用是为了消除代码Java代码里面的getter/setter与bean属性中的property。

    @Autowired(required=false) :Spring容器找不到属性就抛出异常,若设置required=false即不再抛出异常而认为属性为null。

 

实现类要是有多个,此时可以使用@Qualifier注解来指定你要注入Bean的名称

    @Autowired
    @Qualifier("student2")

2. @Autowired和@Resource两个注解的区别:
    1)、@Autowired 默认按照byType方式进行bean匹配,@Resource默认按照byName方式进行bean匹配
    2)、@Autowired 是Spring的注解,@Resource是J2EE的注解,

3. Spring常用注解汇总

@Configuration把一个类作为一个IoC容器,它的某个方法头上如果注册了@Bean,就会作为这个Spring容器中的Bean。
@Scope注解 作用域
@Lazy(true) 表示延迟初始化
@Service用于标注业务层组件、 
@Controller用于标注控制层组件(如struts中的action)
@Repository用于标注数据访问组件,即DAO组件。
@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
@Scope用于指定scope作用域的(用在类上

@PostConstruct用于指定初始化方法(用在方法上)
@PreDestory用于指定销毁方法(用在方法上)
@DependsOn:定义Bean初始化及销毁时的顺序
@Primary:自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常
@Autowired 默认按类型装配,如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。如下:
@Autowired @Qualifier("personDaoBean") 存在多个实例配合使用
@Resource默认按名称装配,当找不到与名称匹配的bean才会按类型装配。

@PostConstruct 初始化注解
@PreDestroy 摧毁注解 默认 单例  启动就加载
@Async异步方法调用

注释参考文章:  Spring注解大全

四、泛型依赖注入(spring 4.x以上版本才有

       泛型依赖注入就是允许我们在使用spring进行依赖注入的同时,利用泛型的优点对代码进行精简,将可重复使用的代码全部放到一个类之中,方便以后的维护和修改。同时在不增加代码的情况下增加代码的复用性。

       BaseDao<T> 类: 泛型方法参数控制

       BaseService<T>   类: 对象引用属性 baseDao控制,泛型注入 

五、转载 Spring中bean的作用域与生命周期

      Spring中的 bean默认都是单例的,详情参考转载的这篇文章,图来自网络

    

ends~ 

  • 5
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值