Spring依赖注入及使用注解注入bean

Spring 最认同的技术是控制反转的依赖注入(DI模式。控制反转(IoC)是一个通用的概念,它可以用许多不同的方式去表达,依赖注入仅仅是控制反转的一个具体的例子

IOC : Inverse Of Control 控制反转
      作用:创建对象 并且维护对象之间的依赖关系
DI :依赖注入 Spring支持的注入方式
    构造方法注入
     setter注入
     注解注入

实现原理:反射

IOC:Inversion of Control,控制反转。指的是对象的创建权反转(交给)给Spring,其作用是实现了程序的解耦合。也可这样解释:获取对象的方式变了。对象创建的控制权不是“使用者”,而是“框架”或者“容器”。

DIDependency Injection 依赖注入,其实是当一个bean实例引用到了另外一个bean实例时spring容器帮助我们创建依赖bean实例并注入(传递)到另一个bean中,如上述案例中的UserServiceImpl类依赖于UserDao的实现类UserDaoImpl,Spring容器会在创建UserService的实现类和UserDao的实现类后,把UserDao的实现类注入UserService实例中,有关依赖注入后面还要详细讲的。

1. 属性(设值)注入:  〈property〉

     指 IoC 容器通过成员变量的 setter 方法来注入被依赖对象。这种注入方式简单、直观,因而在 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]

两种注入方式的对比

setter注入优点:

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

构造注入优点:

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

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

二. 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,则一个致命错误将会发生。
autodetect Spring首先尝试通过 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 4.x以上版本才有

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

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

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

注释参考文章:  https://blog.csdn.net/IT_faquir/article/details/78025203

猜你喜欢

转载自blog.csdn.net/qq_42402854/article/details/81914011