简单记录-Java EE企业级应用开发教程(Spring+Spring MVC+MyBatis)-Spring中的Bean
控制反转(IoC)是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)。
依赖注入
什么是Bean的装配?
Bean的装配方式是将Bean装配注入到Spring IoC容器中 ,Bean的装配可以理解为依赖关系注入,Bean的装配方式即Bean依赖注入的方式。Spring容器支持多种形式的Bean的装配方式,如基于XML的装配、基于注解(Annotation)的装配和自动装配(其中最常用的是基于注解的装配)。基于注解的装配方式尤其重要,它是当前的主流装配方式。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
Spring提供了两种基于XML的装配方式:设值注入(Setter Injection)和构造注入(Constructor Injection)。下面就讲解下如何在XML配置文件中使用这两种注入方式来实现基于XML的装配。
在Spring实例化Bean的过程中,Spring首先会调用Bean的默认构造方法来实例化Bean对象,然后通过反射的方式调用setter方法来注入属性值。因此,设值注入要求一个Bean必须满足以下两点要求。
使用设值注入时,在Spring配置文件中,需要使用元素的子元素来为每个属性注入值;
而使用构造注入时,在配置文件里,需要使用元素的子元素来定义构造方法的参数,可以使用其value属性(或子元素)来设置该参数的值。
下面通过一个案例来演示基于XML方式的Bean的装配。
基于XML的装配,使用方式如下:
创建Java类,提供有参、无参构造以及属性setter方法;
(1)在chapter02模块的src/main/java目录下,创建一个com.awen.assemble包,在该包中创建User类,并在类中定义username、password和list集合三个属性及其对应的setter方法,如文件所示。文件 User.java
```java
package com.awen.assemble;
import java.util.List;
public class User {
private String username;
private Integer password;
private List list;
/**
* 1.使用构造注入
* 1.1提供带所有参数的有参构造方法。
/
public User(String username, Integer password, List list) {
super();
this.username = username;
this.password = password;
this.list = list;
}
/*
* 2.使用设值注入
* 2.1提供默认空参构造方法 ;
* 2.2为所有属性提供setter方法。
*/
public User() {
super();
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(Integer password) {
this.password = password;
}
public void setList(List list) {
this.list = list;
}
@Override
public String toString() {
return “User [username=” + username + “, password=” + password +
“, list=” + list + “]”;
}
}
```
在文件User.java中,由于要使用构造注入,所以需要其有参和无参的构造方法。同时,为了输出时能够看到结果,还重写了其属性的toString()方法。
(2)在src/main/resources目录下,创建Spring配置文件beans5.xm,创建配置文件beans5.xml,在配置文件中通过构造注入和设值注入的方式装配User类的实例,如文件所示。文件 beans5.xml
<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="user1" class="com.awen.assemble.User">
<constructor-arg index="0" value="柳小子" />
<constructor-arg index="1" value="123456" />
<constructor-arg index="2">
<list>
<value>"constructorvalue1"value>
<value>"constructorvalue2"value>
list>
constructor-arg>
bean>
<bean id="user2" class="com.awen.assemble.User">
<property name="username" value="子小柳">property>
<property name="password" value="654321">property>
<property name="list">
<list>
<value>"setlistvalue1"value>
<value>"setlistvalue2"value>
list>
property>
bean>
beans>
在上述配置文件中,元素用于定义构造方法的参数,其属性index表示其索引(从0开始), value属性用于设置注入的值,其子元素来为User类中对应的list集合属性注入值。然后又使用了设值注入方式装配User类的实例,其中元素用于调用Bean实例中的setter方法完成属性赋值,从而完成依赖注入,而其子元素同样是为User类中对应的list集合属性注入值。
(3)在com.awen.assemble包中,创建测试类XmlBeanAssembleTest,在类中分别获取并输出配置文件中的user1和user2实例,如文件所示。文件 XmlBeanAssembleTest.java
package com.awen.assemble;
import org.springframework.context.ApplicationContext;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
public class XmlBeanAssembleTest {
public static void main(String[] args) {
// 加载配置文件
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("beans5.xml");
// 构造方式输出结果
System.out.println(applicationContext.getBean("user1"));
// 设值方式输出结果
System.out.println(applicationContext.getBean("user2"));
}
}
执行程序后,控制台的输出结果如下所示,
D:\Environments\jdk-11.0.2\bin\java.exe -javaagent:D:\Java\ideaIU-2019.2.win\lib\idea_rt.jar=3035:D:\Java\ideaIU-2019.2.win\bin -Dfile.encoding=UTF-8 -classpath D:\IdeaProjects\JavaEE-enterprise-application-development-tutorial\chapter02\target\classes;D:\Environments\apache-maven-3.6.2\maven-repo\javax\annotation\jsr250-api\1.0\jsr250-api-1.0.jar;D:\Environments\apache-maven-3.6.2\maven-repo\org\springframework\spring-webmvc\5.2.3.RELEASE\spring-webmvc-5.2.3.RELEASE.jar;D:\Environments\apache-maven-3.6.2\maven-repo\org\springframework\spring-aop\5.2.3.RELEASE\spring-aop-5.2.3.RELEASE.jar;D:\Environments\apache-maven-3.6.2\maven-repo\org\springframework\spring-beans\5.2.3.RELEASE\spring-beans-5.2.3.RELEASE.jar;D:\Environments\apache-maven-3.6.2\maven-repo\org\springframework\spring-context\5.2.3.RELEASE\spring-context-5.2.3.RELEASE.jar;D:\Environments\apache-maven-3.6.2\maven-repo\org\springframework\spring-core\5.2.3.RELEASE\spring-core-5.2.3.RELEASE.jar;D:\Environments\apache-maven-3.6.2\maven-repo\org\springframework\spring-jcl\5.2.3.RELEASE\spring-jcl-5.2.3.RELEASE.jar;D:\Environments\apache-maven-3.6.2\maven-repo\org\springframework\spring-expression\5.2.3.RELEASE\spring-expression-5.2.3.RELEASE.jar;D:\Environments\apache-maven-3.6.2\maven-repo\org\springframework\spring-web\5.2.3.RELEASE\spring-web-5.2.3.RELEASE.jar com.awen.assemble.XmlBeanAssembleTest
User [username=柳小子, password=123456, list=["constructorvalue1", "constructorvalue2"]]
User [username=子小柳, password=654321, list=["setlistvalue1", "setlistvalue2"]]
Process finished with exit code 0
从运行结果可以看出,已经成功地使用基于XML装配的构造注入和设值注入两种方式装配了User实例。
OK , 到了现在 , 我们彻底不用再去程序中去改动了 , 要实现不同的操作 , 只需要在xml配置文件中进行修改 , 所谓的IoC,一句话搞定 : 对象由Spring 来创建 , 管理 , 装配 !
思考
user 对象是谁创建的 ?
【 user 对象是由Spring创建的 】
user 对象的属性是怎么设置的 ?
【user 对象的属性是由Spring容器设置的 】
这个过程就叫控制反转(IoC) :
控制 : 谁来控制对象的创建 , 传统应用程序的对象是由程序本身控制创建的 , 使用Spring后 , 对象是由Spring来创建的
反转 : 程序本身不创建对象 , 而变成被动的接收对象 .
依赖注入 : 就是利用set方法来进行注入的.
IOC是一种编程思想,由主动的编程变成被动的接收
可以通过newClassPathXmlApplicationContext去浏览一下底层源码 .
除了通过设值注入(Setter Injection)和构造注入(Constructor Injection)实现IoC,还可以通过Spring提供的注解方法实现IoC,这也是企业开发过程中最常用的一种IoC实现方式。
在Spring中,尽管使用XML配置文件可以实现Bean的装配工作,但如果应用中有很多Bean时,会导致XML配置文件过于臃肿,给后续的维护和升级工作带来一定的困难。为此,Spring提供了对Annotation(注解)技术的全面支持。
Spring中定义了一系列的注解,常用的注解如下所示。
下面,通过一个案例来演示如何通过这些注解来装配Bean。
(1)在chapter02模块的src/main/java目录下,创建一个com.awen.annotation包,在该包中创建接口UserDao,并在接口中定义一个save()方法,如文件所示。文件 UserDao.java
package com.awen.annotation;
public interface UserDao {
public void save();
}
(2)在com.awen.annotation包中,创建UserDao接口的实现类UserDaoImpl,该类需要实现接口中的save()方法,如文件所示。文件 UserDaoImpl.java
package com.awen.annotation;
import org.springframework.stereotype.Repository;
@Repository("userDao")
public class UserDaoImpl implements UserDao{
public void save(){
System.out.println("userdao...save...");
}
}
在文件UserDaoImpl.java中,首先使用@Repository注解将UserDaoImpl类标识为Spring中的Bean,其写法相当于配置文件中
的编写。然后在save()方法中输出打印一句话,用于验证是否成功调用了该方法。
(3)在com.awen.annotation包中,创建接口UserService,在接口中同样定义一个save()方法,如文件所示。文件UserService.java
package com.awen.annotation;
public interface UserService {
public void save();
}
(4)在com.awen.annotation包中,创建UserService接口的实现类UserServiceImpl,该类需要实现接口中的save()方法,如文件所示。文件 UserServiceImpl.java
package com.awen.annotation;
import javax.annotation.Resource;
import org.springframework.stereotype.Service;
@Service("userService")
public class UserServiceImpl implements UserService{
@Resource(name="userDao")
private UserDao userDao;
public void save() {
//调用userDao中的save方法
this.userDao.save();
System.out.println("userservice....save...");
}
}
在文件 UserServiceImpl.java中,首先使用@Service注解将UserServiceImpl类标识为Spring中的Bean,这相当于配置文件中
的编写;然后使用@Resource注解标注在属性userDao上,这相当于配置文件中
的写法;最后在该类的save()方法中调用userDao中的save()方法,并输出一句话。
(5)在com.awen.annotation包中,创建控制器类UserController,编辑后如文件所示。文件UserController.java
package com.awen.annotation;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;
@Controller("userController")
public class UserController {
@Resource(name="userService")
private UserService userService;
public void save(){
this.userService.save();
System.out.println("userController...save...");
}
}
在文件UserController.java中,首先使用@Controller注解标注了UserController类,这相当于在配置文件中编写
;然后使用了@Resource注解标注在userService属性上,这相当于在配置文件中编写
;最后在其save()方法中调用了userService中的save()方法,并输出一句话。
(6)在src/main/resources目录下,创建配置文件beans6.xml,在配置文件中编写基于Annotation装配的代码,如文件所示。文件 beans6.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:annotation-config />
<bean id="userDao" class="com.awen.annotation.UserDaoImpl" />
<bean id="userService"
class="com.awen.annotation.UserServiceImpl" />
<bean id="userController"
class="com.awen.annotation.UserController" />
beans>
从上述代码可以看出,文件beans6.xml与之前的配置文件有很大不同。首先,在元素中,增加了第4行,第7行和第8行中包含有context的约束信息;然后通过配置
。
上述Spring配置文件中的注解方式虽然较大程度简化了XML文件中Bean的配置,但仍需要在Spring配置文件中一一配置相应的Bean,为此Spring注解提供了另外一种高效的注解配置方式(对包路径下的所有Bean文件进行扫描),其配置方式如下。
所以可以将上述文件文件 beans6.xml中第9~16行代码进行如下替换(推荐)。
<context:component-scan base-package="com.awen.annotation" />
(7)在com.awen.annotation包中,创建测试类AnnotationAssembleTest,在类中编写测试方法并定义配置文件的路径,然后通过Spring容器加载配置文件并获取UserController实例,最后调用实例中的save()方法,如文件所示。文件 AnnotationAssembleTest.java
package com.awen.annotation;
import org.springframework.context.ApplicationContext;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
public class AnnotationAssembleTest {
public static void main(String[] args) {
// 定义配置文件路径
String xmlPath = "com/awen/annotation/beans6.xml";
// 加载配置文件
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext(xmlPath);
// 获取UserController实例
UserController userController =
(UserController) applicationContext.getBean("userController");
// 调用UserController中的save()方法
userController.save();
}
}
总是出错 一叶障目
执行程序后,控制台的输出结果如图所示。
userdao...save...
userservice....save...
userController...save...
从运行结果可以看到,Spring容器已成功获取了UserController的实例,并通过调用实例中的方法执行了各层中的输出语句,这说明已成功实现了基于Annotation装配Bean
小提示上述案例中如果使用@Autowired注解替换@Resource注解,也可以达到同样的效果。
提示: 除了可以像示例中通过元素来配置Bean外,还可以通过包扫描的形式来配置一个包下的所有Bean:
虽然使用注解的方式装配Bean,在一定程度上减少了配置文件中的代码量,但是也有企业项目中,是没有使用注解方式开发的,那么有没有什么办法既可以减少代码量,又能够实现Bean的装配呢?答案是肯定的,Spring的元素中包含一个autowire属性,我们可以通过设置autowire的属性值来自动装配Bean。所谓自动装配,就是将一个Bean自动地注入到其他Bean的Property中。autowire属性有5个值,其值及说明如表所示。表元素的autowire属性值及说明
自动装配,使用方式如下:
修改上一节UserServiceImple和UserController,分别增加类属性的setter方法;
修改Spring配置文件beans6.xml,将配置文件修改成自动装配形式,也就是使用autowire属性配置Bean;
重新测试程序。
<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">
<bean id="userDao"
class="com.awen.annotation.UserDaoImpl" />
<bean id="userService"
class="com.awen.annotation.UserServiceImpl" autowire="byName" />
<bean id="userController"
class="com.awen.annotation.UserController" autowire="byName"/>
beans>
上述配置文件中,用于配置userService和userController的元素中除了id和class属性外,还增加了autowire属性,并将其属性值设置为byName。在默认情况下,配置文件中需要通过ref来装配Bean,但设置了autowire="byName"后,Spring会自动寻找userService Bean中的属性,并将其属性名称与配置文件中定义的Bean做匹配。由于UserServiceImpl中定义了userDao属性及其setter方法,这与配置文件中id为userDao的Bean相匹配,所以Spring会自动地将id为userDao的Bean装配到id为userService的Bean中。执行程序后,控制台的输出结果如下所示。 从运行结果可以看出,使用自动装配同样完成了依赖注入
D:\Environments\jdk-11.0.2\bin\java.exe -javaagent:D:\Java\ideaIU-2019.2.win\lib\idea_rt.jar=5574:D:\Java\ideaIU-2019.2.win\bin -Dfile.encoding=UTF-8 -classpath D:\IdeaProjects\JavaEE-enterprise-application-development-tutorial\chapter02\target\classes;D:\Environments\apache-maven-3.6.2\maven-repo\javax\annotation\jsr250-api\1.0\jsr250-api-1.0.jar;D:\Environments\apache-maven-3.6.2\maven-repo\org\springframework\spring-webmvc\5.2.3.RELEASE\spring-webmvc-5.2.3.RELEASE.jar;D:\Environments\apache-maven-3.6.2\maven-repo\org\springframework\spring-aop\5.2.3.RELEASE\spring-aop-5.2.3.RELEASE.jar;D:\Environments\apache-maven-3.6.2\maven-repo\org\springframework\spring-beans\5.2.3.RELEASE\spring-beans-5.2.3.RELEASE.jar;D:\Environments\apache-maven-3.6.2\maven-repo\org\springframework\spring-context\5.2.3.RELEASE\spring-context-5.2.3.RELEASE.jar;D:\Environments\apache-maven-3.6.2\maven-repo\org\springframework\spring-core\5.2.3.RELEASE\spring-core-5.2.3.RELEASE.jar;D:\Environments\apache-maven-3.6.2\maven-repo\org\springframework\spring-jcl\5.2.3.RELEASE\spring-jcl-5.2.3.RELEASE.jar;D:\Environments\apache-maven-3.6.2\maven-repo\org\springframework\spring-expression\5.2.3.RELEASE\spring-expression-5.2.3.RELEASE.jar;D:\Environments\apache-maven-3.6.2\maven-repo\org\springframework\spring-web\5.2.3.RELEASE\spring-web-5.2.3.RELEASE.jar com.awen.annotation.AnnotationAssembleTest
userdao...save...
userservice....save...
userController...save...
Process finished with exit code 0
手动装配
自动装配
在Spring中有三种装配的方式:
Autowired 自动装配
ByName自动装配
ByType自动装配
小结:
使用注解实现自动装配
jdk1.5支持注解,Spring2.5开始支持注解。
要使用注解须知:
导入约束:context约束。
配置注解的支持:context:annot-config/
<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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
beans>
@Autowired
直接在属性上使用即可!也可以在set方式上使用!
使用Autowired我们可以不用编写Set方法了,前提是你这个自动装配的属性在IoC(Spring)容器中存在,且符合名字byName!
科普:
@Nullable 字段标记了这个注解,说明这个字段可以为null
public People(@Nullable String name){
this.name = name;
}
public @interface Autowired {
boolean required() default true;
}
如果@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解@Autowired完成的时候,我们可以使用@Qualifier(value=“xxx”)去配置@Autowired的使用,指定一个唯一的bean对象注入!
@Resource注解
小结:
@Resource和@Autowired的区别:
在spring4之后,要使用注解开发,必须要保证aop的包导入了。
使用注解需要导入context约束,增加注解的支持!
<context:component-scan base-package="com.kuang.pojo"/>
bean
属性如何注入
//等价于
//@Component 组件
@Component
public class User {
//相当于
public String name;
@Value("小憨批")
public void setName(String name){
this.name = name;
}
}
衍生的注解
@Component有几个衍生注解,我们在web开发中,会按照mvc三层架构分层!
这四个注解功能都是一样的,都是代表将某个类注册到Spring中,装配Bean!
自动装配
-@Autowired:自动装配通过类型,名字
如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value="xxx")
-@Nullable:字段标记了这个注解,说明这个字段可以为null
-@Resource:自动装配通过名字,类型
作用域
@Scope("singleton")
public class User {
//相当于
public String name;
@Value("小憨批")
public void setName(String name){
this.name = name;
}
}
小结
xml与注解:
xml与注解最佳实践:
<context:component-scan base-package="com.awen"/>
<context:annotation-config/>