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,则一个致命错误将会发生。 |
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