Bean 的实例化与装配
实验要求:
理解 Spring 的 IoC/DI 的原理,掌握 Spring 使用注解方式和编程及 Spring 的分层设计
方法。
实验 1:集合类型注入
编写 UserInfo 类,包含用户姓名(String)、性别(boolean)、年龄(int)、出生年月(Date)、个人爱好(list 集合),采用 set 注入和构造注入两种方式,实现这些属性值的注入,并在测试程序中显示所有属性的值。
实验 2:
编写工厂类(BeanFactory)、Print 类(属性:pName、pColor 等),要求利用 Spring 容器通过工厂类 BeanFactory 生成 Print 对象,并生成测试结果。
实验 3:
利用 Spring 框架,通过注解方式实现为自己的校园卡充值。
在 Dao 层建立 CardDao 类,编写充值方法,实现充值(在控制台显示充值金额,将用户充值信息保存到数据库),在 Biz 层建立 CardBiz 类,编写充值方法,调用 Dao层中的充值方法,完成充值。在 CardController 类中编写充值方法调用 CardBiz 的充值方法。在程序中,通过代码方式调用控制层充值方法,完成充值,并在控制台显示充值金额。在实现过程中不能利用 new 操作来完成类的实例化。观察@Autowired 和@Resource 进行注解时有何不同。
实验 4:
根据自己的理解,描述 Bean 的装配方式。
实验分析:
- 实验一主要运用Bean基于XML的装配:设值注入、构造注入。
- 实验二采用的是静态工厂实例化。
- 实验三主要涉及到Bean的实例化与装配(基于注解的装配)、此外还涉及了Spring对于数据库的连接。此实验可参考利用Spring框架为自己的校园卡充值。
代码实现:
实验一
set注入
package com.cqust;
import java.util.Date;
import java.util.List;
public class UserInfo1 {
private String name;
private boolean male;
private int age;
private Date birth;
private List<String> Hobby;
public void setName(String name) {
this.name = name;
}
public void setMale(boolean male) {
this.male = male;
}
public void setAge(int age) {
this.age = age;
}
public void setBirth(Date birth) {
this.birth = birth;
}
public void setHobby(List<String> hobby) {
Hobby = hobby;
}
@Override
public String toString() {
return "UserInfo{" +
"姓名='" + name + '\'' +
", 男性=" + male +
", 年龄=" + age +
", 出生日期='" + birth + '\'' +
", 兴趣爱好=" + Hobby +
'}';
}
}
配置文件
<?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="userInfo1" class="com.cqust.UserInfo1">
<property name="name" value="Hades"></property>
<property name="male" value="true"></property>
<property name="age" value="20"></property>
<property name="birth" value="2022/9/16"></property>
<property name="hobby">
<list>
<value>阅读</value>
<value>听音乐</value>
<value>运动</value>
</list>
</property>
</bean>
</beans>
构造注入
package com.cqust;
import java.util.Date;
import java.util.List;
public class UserInfo2 {
private String name;
private boolean male;
private int age;
private Date birth;
private List<String> Hobby;
public UserInfo2(String name, boolean male, int age, Date birth, List<String> hobby) {
this.name = name;
this.male = male;
this.age = age;
this.birth = birth;
Hobby = hobby;
}
@Override
public String toString() {
return "UserInfo{" +
"姓名='" + name + '\'' +
", 男性=" + male +
", 年龄=" + age +
", 出生日期='" + birth + '\'' +
", 兴趣爱好=" + Hobby +
'}';
}
}
配置文件
<?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="userInfo2" class="com.cqust.UserInfo2">
<constructor-arg name="name" value="Hades"></constructor-arg>
<constructor-arg name="male" value="true"></constructor-arg>
<constructor-arg name="age" value="20"></constructor-arg>
<constructor-arg name="birth" value="2022/9/16"></constructor-arg>
<constructor-arg name="hobby">
<list>
<value>阅读</value>
<value>听音乐</value>
<value>运动</value>
</list>
</constructor-arg>
</bean>
</beans>
测试文件(Test1)
import com.cqust.UserInfo1;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test1 {
public static void main(String[] args){
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationBean1_1.xml"); //这里展示的是setter方法,构造方法请更换applicationBean1_2.xml
UserInfo1 userInfo1 = applicationContext.getBean("userInfo1", UserInfo1.class); //更换为userInfo2
System.out.println("属性setter方法注入:"); //更换为构造方法注入
System.out.println(userInfo1); //更换为userInfo2
}
}
实验二
Beanfactory类
package com.cqust;
public class BeanFactory {
public static Print createBean(){
return new Print();
}
}
Print类
package com.cqust;
public class Print {
private String pName;
private String pColor;
public void setpName(String pName) {
this.pName = pName;
}
public void setpColor(String pColor) {
this.pColor = pColor;
}
@Override
public String toString() {
return "Print{" +
"p名称='" + pName + '\'' +
", p颜色='" + pColor + '\'' +
'}';
}
}
配置文件
<?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="print" class="com.cqust.BeanFactory" factory-method="createBean">
<property name="pName" value="Hades的静态工厂实例化"></property>
<property name="pColor" value="red"></property>
</bean>
</beans>
测试文件(Test2)
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test2 {
public static void main(String[] args){
ApplicationContext applicationContext =
new ClassPathXmlApplicationContext("applicationBean2.xml");
System.out.println(applicationContext.getBean("print"));
}
}
实验三
DataSource类:
package com.cqust.biz;
import com.cqust.dao.CardDao;
import com.cqust.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service("cardBiz") // 注册 bean
public class CardBiz {
// 使用注解自动注入并指定注入的 bean 的 id
@Autowired
private CardDao cardDao;
public int save(User user) {
try {
// 执行 dao 中的方法并返回结果
return cardDao.insert(user);
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
public double selectMoney(String username){
try {
return cardDao.selectMoney(username);
}catch (Exception e) {
e.printStackTrace();
}
return -1;
}
public int charge(String username,double money){
try {
return cardDao.charge(username, cardDao.selectMoney(username)+money);
}catch (Exception e) {
e.printStackTrace();
}
return 0;
}
}
配置文件(properties)
jdbc.dirver = com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
jdbc.username=root
jdbc.password=1
User类
package com.cqust.entity;
import org.springframework.stereotype.Component;
@Component
public class User {
private String username;
private double money;
public User(String username, double money) {
this.username = username;
this.money = money;
}
public String getUsername() {return username;}
public double getMoney() {
return money;
}
public void setUsername(String username) {
this.username = username;
}
public void setMoney(double money) {
this.money = money;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", money=" + money +
'}';
}
}
CardDao类
package com.cqust.dao;
import com.cqust.entity.User;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.stereotype.Repository;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@Repository("cardDao")
public class CardDao {
private ComboPooledDataSource dataSource;
public CardDao(ComboPooledDataSource dataSource) {
this.dataSource = dataSource;
}
public int insert(User p) throws Exception {
// 获取数据库连接并预编译 sql 语句
PreparedStatement ps = dataSource.getConnection().prepareStatement("insert into user(username,money)values(?,?)");
// 设置?的值
ps.setString(1,p.getUsername());
ps.setDouble(2,p.getMoney());
// 执行并返回结果
return ps.executeUpdate(); // 省略关闭资源
}
public double selectMoney(String username) throws SQLException {
PreparedStatement ps = dataSource.getConnection().prepareStatement("select money from user where username=?");
ps.setString(1,username);
ResultSet rs= ps.executeQuery();
while(rs.next()){
return rs.getDouble("money");
}
return -1;
}
public int charge(String username,double money) throws SQLException {
PreparedStatement ps = dataSource.getConnection().prepareStatement("update user set money = ? where username = ? ");
ps.setDouble(1,money);
ps.setString(2,username);
return ps.executeUpdate();
}
}
CardBiz类
package com.cqust.biz;
import com.cqust.dao.CardDao;
import com.cqust.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service("cardBiz") // 注册 bean
public class CardBiz {
// 使用注解自动注入并指定注入的 bean 的 id
@Autowired
private CardDao cardDao;
public int save(User user) {
try {
// 执行 dao 中的方法并返回结果
return cardDao.insert(user);
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
public double selectMoney(String username){
try {
return cardDao.selectMoney(username);
}catch (Exception e) {
e.printStackTrace();
}
return -1;
}
public int charge(String username,double money){
try {
return cardDao.charge(username, cardDao.selectMoney(username)+money);
}catch (Exception e) {
e.printStackTrace();
}
return 0;
}
}
CardController类
package com.cqust.controller;
import com.cqust.biz.CardBiz;
import com.cqust.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class CardController {
@Autowired
private CardBiz cardBiz;
public void save(User user){
double money=cardBiz.selectMoney(user.getUsername());
int rs=0;
if(money==-1){
rs=cardBiz.save(user);
}
if (rs==0){
System.out.println("保存失败,该用户已存在!");
}else{
System.out.println("保存成功");
}
}
public double show(User user){
Double money=cardBiz.selectMoney(user.getUsername());
if (money==null){
System.out.println("该用户不存在!");
return 0.0;
}
return money;
}
public void charge(User user,Double money){
int rs=cardBiz.charge(user.getUsername(),money+ user.getMoney());
if(rs==0){
System.out.println("该用户不存在!");
}else{
System.out.println("充值成功!");
}
}
}
测试文件(Test3)
import com.cqust.controller.CardController;
import com.cqust.dao.DataSource;
import com.cqust.entity.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Test3 {
public static void main(String[] args) {
//获取核心容器(IoC)对象 需要引用DataSource类
ApplicationContext anno = new
AnnotationConfigApplicationContext(DataSource.class);
// 根据 bean 的 id 获取对象
CardController cardController = anno.getBean("cardController", CardController.class);
// 执行方法
User user = anno.getBean("user",User.class);
user.setUsername("Hades");
user.setMoney(4.50);
System.out.println(user);
cardController.save(user);
cardController.show(user);
cardController.charge(user,888.88);
}
}
实验四
Bean的装配方式:
&Bean的装配可以理解为依赖关系注入,Bean的装配方式即Bean的依赖注入的方式。
Spring容器支持多种形式的Bean的装配方式,如基于XML的装配、基于注解的装配、自动装配等。其中最常用的是基于注解的装配。
一、基于XML的装配
Spring提供了两种基于XML的装配方式:设值(setter)注入、构造注入
Spring实例化Bean的过程中,Spring首先会调用Bean的默认构造方法来实例化Bean对象,然后通过反射的方式调用setter方法来注入属性值。
设值(setter)注入:
- Bean类必须提供一个默认的无参构造方法
- Bean类必须为需要注入的属性提供对应的setter方法
- 在配置文件中,需要使用 元素为每个属性注入值
构造注入:
- Bean类必须提供有参构造方法
- 在配置文件中,需要使用元素来定义构造方法的参数,也可以使用其value属性来设置该参数的值
二、基于注解的装配
Spring中定义了一系列的注解,常用的注解如下:
-
@Component:描述Spring中的Bean,可以作用在任何层次
-
@Repository:用于将持久层(Dao层)的类标识为Spring中的Bean
-
@Service:用于将业务层(Service层)的类标识为Spring中的Bean
-
@Controller:用于将控制层(Controller层)的类标识为Spring中的Bean
-
@Autowired:用于对Bean的属性变量、属性的setter方法以及构造方法进行标注,配合对应的注解处理器完成Bean的自动装配工作。默认按照Bean的类型进行装配
-
@Resource:其作用与@Autowired一样。其区别在于@Autowired默认Bean的类型装配,而@Resource默认按照Bean的实例名称进行装配
@Resource中有两个重要属性:name和type。Spring将name属性解析为Bean实例名称,type属性解析为Bean实例类型。如果指定name属性,则按实例名称进行装配;如果指定type属性,则按Bean类型进行装配;如果都不指定,则先按Bean的实例名称装配,如果不能匹配,再按照Bean类型进行装配;如果都无法匹配,则抛出 NoSuchBeanDefinitionException异常。
- @Qualifier:与@Autowired注解配合使用,会将默认的按Bean类型装配修改为Bean的实例名称装配,Bean的实例名称由 @Qualifier注解的参数指定
此时,在配置文件中,Spring注解提供了另外一种高效的注解配置方式。
含义是:告知Spring在创建容器时要扫描的包(通知Spring扫描指定包下的所有Bean)
<context:component-scan base-package="cn.itcast"></context:component-scan>
三、自动装配
自动装配:将一个Bean自动地注入到其他Bean的Property中。
Spring的<bean>元素中包含一个 autowire 属性,我们可以通过设置 autowire 的属性值来自动装配Bean。
autowire属性的5个值:
- default(默认):由的default-autowire属性值来确定。
- byName:根据属性的名称自动装配。容器将根据名称查找与属性完全一致的Bean,并将其属性自动装配
- byType:根据属性的数据类型自动装配。
- constructor:根据构造函数参数的数据类型,进行byType模式的自动装配
- no:在默认情况下,不使用自动装配,Bean依赖必须通过ref元素定义
写作时间比较仓促且水平有限,可能导致文中很多地方写的比较简略,没有写太多的注释,部分代码请读者根据自己的电脑设置更改配置,也可能还存在大量的错误,欢迎广大猿友不吝指正!
谢谢浏览!