DI及注解

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

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

实现原理:反射

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

**DI:**Dependency 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,则一个致命错误将会发生。
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框架,配置了注解的类的位置。

img

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 类: 泛型方法参数控制

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

五、实例

1、Spring配置

<?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"
         xsi:schemaLocation="http://www.springframework.org/schema/beans 
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/context 
         http://www.springframework.org/schema/context/spring-context-4.0.xsd">
         <!-- 
         	init-method="getstart" 该类初始化执行的方法
         	destroy-method="getdestroy"  该类被销毁时执行的方法
         	scope="singleton" 默认为单列模式 可以  不写
         	scope="prototype" 多列模式
         	<property name="该类的属性名" value="给该类属性名注入的值"></property>
         	<import resource="user.xml"/> 若有多个配置文件可以用 inport 
          -->
     <!--  <bean id="car" class="com.sumeng.web.Car" init-method="getstart" destroy-method="getdestroy" scope="">
     	<property name="carName" value="奥迪"></property>
     </bean>  
     <import resource="user.xml"/>-->
     <!--  通过 注解 注入  类扫描器 -->
     <context:component-scan base-package="com.sumeng.web"></context:component-scan>
</beans>

2、创建UserService和Car两个类

UserService

/** 
* @author 作者 Your-Name: 
* @version 创建时间:2019年5月30日 上午9:42:34 
* 类说明 
*/ 
package com.sumeng.web;
 
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
 
/**
 * @author Administrator
 *@Component(value="car")   通用 其中car 想当于bean中的id
 *@Controller web层用
 *@Service service层用
 *@Scope 不写或者为 @Scope为单列模式 
 *@Scope("prototype") 为多列模式  prototype(多列)
 */
@Component(value="car")
@Scope("prototype")
public class Car {
	@Value("宝马") 	//给carName属性注入值
	private String carName;
	
	/**
	 * @Autowired 对象注入   ----实际开发常用 
	 * @Autowired  	@Qualifier("user") 两个一起使用  也是对象注入  其中 user相当于bean中的id
	 * @Resource(name="user")  jdk自带注解 和前两个同样的性质 
	 * 生命周期
	 * 		@PostConstruct 相当于  bean中的    init-method
	 * 		@PreDestroy  相当于bean中的   destroy-method
	 */
	
	@Resource(name="user")
	private UserService userservice;
	
	public void getCar(){
		System.out.println("我是汽车"+this.carName);
		userservice.getsave();
	}
	
	@PostConstruct
	public void getstart(){
		System.out.println("我被创建了-car!");
	}
	@PreDestroy
	public void getdestroy(){
		System.out.println("我背销毁了-car!!");
	}
 
	public String getCarName() {
		return carName;
	}
 
	public void setCarName(String carName) {
		this.carName = carName;
	}
 
	@Override
	public String toString() {
		return "Car [carName=" + carName + "]";
	}
	public UserService getUser() {
		return userservice;
	}
	public void setUser(UserService user) {
		this.userservice = user;
	}
 
}

Car

/** 
* @author 作者 Your-Name: 
* @version 创建时间:2019年5月30日 上午10:19:45 
* 类说明 
*/ 
package com.sumeng.web;
 
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
 
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
 
/**
 * @author Administrator
 *
 */
@Component(value="user") //相当于 bean 【id  class】
public class UserService {
	
	@PostConstruct		//相当于bean中的init-method
	public void init(){
		System.out.println("user对象  创建之前执行的方法");
	}
	@PreDestroy			//箱单于bean中的destroy-method
	public void destroy(){
		System.out.println("user对象 销毁后执行的方法");
	}
	public void getsave(){
		System.out.println("我是user");
	}
 
}

3.创建测试类

/** 
* @author 作者 Your-Name: 
* @version 创建时间:2019年5月30日 上午9:44:57 
* 类说明 
*/ 
package com.sumeng.action;
 
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
import com.sumeng.web.Car;
import com.sumeng.web.UserService;
 
/**
 * @author Administrator
 *
 */
public class Test {
	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("config/Spring.xml");
		Car car = (Car)context.getBean("car");
		Car car2 = (Car)context.getBean("car");
		System.out.println(car == car2);
		car.getCar();
	}
	
 
}

4、效果

img

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值