使用快速入门的项目继续练习
目录
Bean
基本配置
id:唯一性标识。通过id获得对应的对象。
class:全限定名的属性。
范围配置
scope:指对象的作用范围。
主要记住两个范围:
singleton:默认值,单例的。
prototype:多例的。
测试类:SpringTest.java
package com.springstu;
import com.springstu.dao.UserDao;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTest {
@Test
//测试scope属性
public void test1(){
String xml = "applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xml);
UserDao userDao1 = (UserDao) applicationContext.getBean("userDao");
UserDao userDao2 = (UserDao) applicationContext.getBean("userDao");
System.out.println(userDao1);
System.out.println(userDao2);
}
}
运行结果:
打印出两个地址都是相同的,说明这两个是属于同一个对象,证明它是单例的。
将xml中UserDao的bean中scope的配置改为:prototype,再次执行。SpringTest类。
配置文件:applicationContext.xml
<bean id="userDao" class="com.springstu.dao.impl.UserDaoImpl" scope="prototype"></bean>
从以下打印出的地址不同地址能够看出对象的作用范围已经发生了改变。表示当前存在多个UserDao。
1) 当scope的取值为singleton时
Bean的实例化个数:1个
Bean的实例化时机:当Spring核心文件被加载时,实例化配置的Bean实例
Bean的生命周期:
对象创建:当应用加载,创建容器时,对象就被创建了
对象运行:只要容器在,对象一直活着
对象销毁:当应用卸载,销毁容器时,对象就被销毁了
2) 当scope的取值为prototype时
Bean的实例化个数:多个
Bean的实例化时机:当调用getBean()方法时实例化Bean
对象创建:当使用对象时,创建新的对象实例
对象运行:只要对象在使用中,就一直活着
对象销毁:当对象长时间不用时,被 Java 的垃圾回收器回收了
Bean生命周期配置
定义初始化方法与销毁方法
init-method:指定类中的初始化方法名称
destroy-method:指定类中销毁方法名称
applicationContext.xml:
<?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="userDao" class="com.springstu.dao.impl.UserDaoImpl" init-method="init" destroy-method="destroy"></bean>
</beans>
UserDaoImpl.java
package com.springstu.dao.impl;
import com.springstu.dao.UserDao;
public class UserDaoImpl implements UserDao {
public UserDaoImpl() {
System.out.println("UserDaoImpl创建...");
}
public void init(){
System.out.println("初始化方法...");
}
public void destroy(){
System.out.println("销毁方法...");
}
@Override
public void save() {
System.out.println("save data...");
}
}
UserDaoDemo.java:
package com.springstu.demo;
import com.springstu.dao.UserDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserDaoDemo {
public static void main(String[] args) {
String xml = "applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xml);
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
userDao.save();
}
}
运行结果:
注意点:
由之前的学习能够知道Spring在创建bean时会默认调用无参构造函数,之所以会打印语句是因为在实现类中重写了无参构造函数。
init-method=“init” destroy-method="destroy"
要在配置文件的bean中配置初始化函数与销毁函数,否则是不会识别到的。
Bean实例化的三种方式
无参构造方法实例化
在默认中,Spring会自己找到无参构造函数进行实例化。
工厂静态方法实例化
新建文件夹Factory
StaticFactory
新建,StaticFactory.java:
package com.springstu.factory;
import com.springstu.dao.UserDao;
import com.springstu.dao.impl.UserDaoImpl;
public class StaticFactory {
public static UserDao getUserDao(){
return new UserDaoImpl();
}
}
在xml的文件中将bean中的配置修改成以下的形式。
<?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="userDao" class="com.springstu.dao.impl.UserDaoImpl"></bean>-->
<bean id="userDao" class="com.springstu.factory.StaticFactory" factory-method="getUserDao"></bean>
</beans>
将class的中的属性改为工厂类的全包名。
factory-method:设置了该属性后,就会使用配置中的方法返回对象,不会再使用无参构造函数去获得返回对象。
SpringTest .java:
package com.springstu;
import com.springstu.dao.UserDao;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringTest {
@Test
//测试scope属性
public void test1(){
String xml = "applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xml);
UserDao userDao1 = (UserDao) applicationContext.getBean("userDao");
System.out.println(userDao1);
}
}
运行截图:
此时bean是使用配置中StaticFactory类中的getUserDao返回的对象。
工厂实例方法实例化
applicationContext.xml:
<?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="userDao" class="com.springstu.dao.impl.UserDaoImpl"></bean>-->
<!-- <bean id="userDao" class="com.springstu.factory.StaticFactory" factory-method="getUserDao"></bean>-->
<bean id="factory" class="com.springstu.factory.DynamicFactory"></bean>
<bean id="userDao" factory-bean="factory" factory-method="getUserDao" />
</beans>
新建,DynamicFactory.java:
package com.springstu.factory;
import com.springstu.dao.UserDao;
import com.springstu.dao.impl.UserDaoImpl;
public class DynamicFactory {
public UserDao getUserDao(){
return new UserDaoImpl();
}
}
配置解析:
<bean id="factory" class="com.springstu.factory.DynamicFactory"></bean>
<bean id="userDao" factory-bean="factory" factory-method="getUserDao" />
由于在DynamicFactory类中的getUserDao不是一个静态的方法,需要先实例DynamicFactory对象才能调用getUserDao方法。
所以第一条bean就是在实例化一个getUserDao对象,第二个bean调用上一个实例化的DynamicFactory对象中的getUserDao方法。
第一个bean表示,要要获得userDao对象要找到factory工厂的getUserDao方法。
运行SpringTest.java,这个文件中不需要修改任何代码。
运行结果:
此时的UserDao对象就是从factory工厂中的getUserDao方法中得到的。
应用场景
重点是无参构造函数实例化。
工厂实例化中适用于实例化的对象需要传入参数的情况。
Bean的依赖注入入门
概念
依赖注入(Dependency Injection):它是 Spring 框架核心 IOC 的具体实现。
在编写程序时,通过控制反转,把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况。
IOC 解耦只是降低他们的依赖关系,但不会消除。例如:业务层仍会调用持久层的方法。
那这种业务层和持久层的依赖关系,在使用 Spring 之后,就让 Spring 来维护了。
简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。——黑马程序员
依赖注入的方式
构造方法
applicationContext.xml:
<?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="userDao" class="com.springstu.dao.impl.UserDaoImpl" />
<bean id="userService" class="com.springstu.service.Impl.UserServiceImpl">
<constructor-arg name="userDao" ref="userDao"></constructor-arg>
</bean>
</beans>
UserServiceImpl .java:
package com.springstu.service.Impl;
import com.springstu.dao.UserDao;
import com.springstu.service.UserService;
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
public UserServiceImpl() {
}
@Override
public void save() {
userDao.save();
}
}
解析:
在applicationContext.xml中
UserServiceImpl .java:
package com.springstu.service.Impl;
import com.springstu.dao.UserDao;
import com.springstu.service.UserService;
public class UserServiceImpl implements UserService {
private UserDao userDao;
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
public UserServiceImpl() {
}
@Override
public void save() {
userDao.save();
}
}
set方法
新建文件夹service以及Impl文件夹,项目结构:
UserService.java
package com.springstu.service;
public interface UserService {
public void save();
}
UserServiceImpl.java
package com.springstu.service.Impl;
import com.springstu.dao.UserDao;
import com.springstu.service.UserService;
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save() {
userDao.save();
}
}
applicationContext.xml:
<?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="userDao" class="com.springstu.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.springstu.service.Impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
</beans>
解析:
在该示例中,框架将持久层传入业务层,而不是我们自己获取的。
在UserServiceImpl.java文件中,我们定义了一个UserDao的对象,并且设有set方法。
在applicationContext.xml使用property标签对UserServiceImpl中的userDao对象进行注入。
property标签中的name是UserServiceImpl中setUserDao的UserDao的部分不过第一个字母要变成小写于是就成了userDao。
ref表示对象的引用,在UserServiceImpl注入的对象是userDao所有ref中就写的是它在配置文件中的id。
<bean id="userService" class="com.springstu.service.Impl.UserServiceImpl">
<property name="userDao" ref="userDao"></property>
</bean>
这段代码表示将userDao通过userService中的setUserDao方法注入,当然ref对应的bean在xml文件中要配置的。
P命名空间
知道就行,常用的还是上面那种标签的方式
p命名空间就是简化一下代码。
applicationContext.xml:
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="com.springstu.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.springstu.service.Impl.UserServiceImpl" p:userDao-ref="userDao"/>
</beans>
常见错误
Cannot resolve bean ‘userDao’
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'userDao' available
No bean named ‘userDao’ available:在xml文件中没有定义userDao这个bean对象。
Caused by: org.springframework.beans.NotWritablePropertyException: Invalid property 'userDao' of bean class
[com.springstu.service.Impl.UserServiceImpl]: Bean property 'userDao' is not writable or has an invalid setter method.
Does the parameter type of the setter match the return type of the getter?
Bean property ‘userDao’ is not writable or has an invalid setter method.:userDao这个属性没有set方法。
依赖注入的数据类型
上面的操作,都是注入的引用Bean,除了对象的引用可以注入,普通数据类型,集合等都可以在容器中进行注入。
注入数据的三种数据类型
普通数据类型
引用数据类型
集合数据类
——黑马程序员
普通属性注入
UserDaoImpl .java
package com.springstu.dao.impl;
import com.springstu.dao.UserDao;
public class UserDaoImpl implements UserDao {
private String username;
private int age;
public void setUsername(String username) {
this.username = username;
}
public void setAge(int age) {
this.age = age;
}
@Override
public void save() {
System.out.println(username + "=====" + age);
System.out.println("save data...");
}
}
applicationContext.xml
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userDao" class="com.springstu.dao.impl.UserDaoImpl">
<property name="username" value="zhangsan"/>
<property name="age" value="18"/>
</bean>
</beans>
解析:
这里使用value是因为这两个属性值是普通变量,ref是当注入的是对象时使用的。
集合的注入
新建domain文件夹,文件目录结构:
新建User.java:
package com.springstu.domain;
public class User {
private String name;
private String addr;
public void setName(String name) {
this.name = name;
}
public void setAddr(String addr) {
this.addr = addr;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", addr='" + addr + '\'' +
'}';
}
}
UserDaoImpl.java:
package com.springstu.dao.impl;
import com.springstu.dao.UserDao;
import com.springstu.domain.User;
import java.util.List;
import java.util.Map;
import java.util.Properties;
public class UserDaoImpl implements UserDao {
private List<String> strList;
private Map<String, User> userMap;
private Properties properties;
public void setStrList(List<String> strList) {
this.strList = strList;
}
public void setUserMap(Map<String, User> userMap) {
this.userMap = userMap;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public void save() {
System.out.println(strList);
System.out.println(userMap);
System.out.println(properties);
System.out.println("save data...");
}
}
applicationContext.xml:
<?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="userService" class="com.springstu.service.Impl.UserServiceImpl">
<constructor-arg name="userDao" ref="userDao" />
</bean>
<bean id="user1" class="com.springstu.domain.User">
<property name="name" value="tom"/>
<property name="addr" value="beijing"/>
</bean>
<bean id="user2" class="com.springstu.domain.User">
<property name="name" value="lucy"/>
<property name="addr" value="tianjin"/>
</bean>
<bean id="userDao" class="com.springstu.dao.impl.UserDaoImpl">
<property name="strList">
<list>
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</list>
</property>
<property name="userMap">
<map>
<entry key="u1" value-ref="user1"></entry>
<entry key="u2" value-ref="user2"></entry>
</map>
</property>
<property name="properties">
<props>
<prop key="p1">ppp1</prop>
<prop key="p2">ppp2</prop>
<prop key="p3">ppp3</prop>
</props>
</property>
</bean>
</beans>
解析:
本章小结:
Spring的重点配置
bean:对象。
id属性:在容器中Bean实例的唯一标识,不允许重复。
class属性:要实例化的Bean的全限定名。
scope属性:Bean的作用范围,常用是Singleton(默认)和prototype。
property:属性注入。
name属性:属性名称。
value属性:注入的普通属性值。
ref属性:注入的对象引用值。
list:集合。
map:键值对。
properties:键值对。
constructor-arg:使用构造函数,给属性赋值。
引入其它配置文件(分模块开发)
实际开发中,Spring的配置内容多且杂,可以根据自己需求将部分配置拆解到其他配置文件中,而在Spring主配置文件通过import标签进行加载。
applicationContext.xml:
<import resource="applicationContext-product.xml"/>
<import resource="applicationContext-user.xml"/>
Spring相关API
ApplicationContext的继承体系
applicationContext:接口类型,代表应用上下文,可以通过其实例获得Spring 容器中的Bean对象。——黑马程序员
ClassPathXmlApplicationContext:它是从类的根路径(resources)下加载配置文件,推荐使用。
FileSystemXmlApplicationContext:它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。(不推荐)
AnnotationConfigApplicationContext:
当使用注解配置容器对象时,需要使用此类来创建spring 容器。它用来读取注解。(还没学不着急)
getBean使用方法
这是getBean方法的源码。
public Object getBean(String name) throws BeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(name);
}
public <T> T getBean(Class<T> requiredType) throwsBeansException {
assertBeanFactoryActive();
return getBeanFactory().getBean(requiredType);
}
分析:
第一种方法传入的参数是一个字符串,该字符串就是传入一个id,也就是在xml文件中配置的id,使用该方法获得的是object对象,要进行类型的强转,也可以使用getBean(“id”,类名.class),这种方式不需要进行强转。
第二种方式传入字节码对象(类型),参数:类型.class,直接根据你需要的对象给你返回你所需要的类型对象,使用该方法获取bean对象时不需要进行强转。
注意:当你的配置文件中有相同类型的bean时第二种方法就会不知道该使用哪个进行报错。只能够使用第一种id的方式,id具有唯一性,能够找到。