【spring】原生xml配置版
1.概念
在 Java 项目开发过程中,耦合性是一个需要重点关注的问题。尤其是在项目后期进行功能的增加或删除时,最好避免直接修改源代码。为解决这一问题,通常会采用工厂模式结合反射模式来实现解耦。然而,这种方式存在一个弊端,即每次都需要重复创建类的实例化对象,从而导致资源开销增大。Spring 框架的出现则有效地解决了这些问题。Spring 通过将需要实例化的类纳入 Spring IoC(控制反转)容器进行管理,当需要使用类中的方法时,可直接调用容器中相关类的 Bean
小tips:一定要搞清楚bean的意思,在spring中可以就把它理解为类的实例化对象,不然后面一直提到bean就会很晕
2.spring入门环境的搭建
首先在pom.xml文件中导入关于spring的核心依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
</dependencies>
这里也可以使用其他的版本
然后要去定义dao service的接口及其实现类,接着在resources包中创建xml文件,通过bean的标签来管理bean,将bean放在spring的ioc容器中
<bean id="accountDao" class="com.xq.dao.Impl.AccountDaoImpl"/>
<bean id="accountService" class="com.xq.service.impl.AccountServiceImpl"/>
这里的id的值是任意的,但是它必须是唯一的,class值就是你想要实现的类的工程中的路径 ,
在配置好了你想要的类后,当你想使用它时,首先需要初始化一个ioc容器,
创建好之后,ioc容器中就会立刻加载相应类的实例化对象(加载的是单例bean不是多例bean)
//初始化ioc容器
ApplicationContext context=new ClassPathXmlApplicationContext("加入之前配置的包含bean标签的xml文件");
然后调用ioc容器提供的getBean方法获取bean,也就是相应的类的实现对象,用对应的变量去接收它就可以用了。
AccountDao accountDao = (AccountDao) context.getBean("accountDao");
这里的括号里的东西就是之前xml文件中bean标签内的id,通过id去调用相应的bean,再把他强转成相应的数据类型就可以用了。
还有另外一种方法来获取bean,就是通过BeanFactory的方式创建对象,和前面的很像
//通过BeanFactory的方式创建对象
Resource resource=new ClassPathResource("相应的xml文件");
//创建spring bean工厂
BeanFactory beanFactory=new XmlBeanFactory(resource);
再通过BeanFactory的对象去调用getBean方法获取具体的bean就行了。但是他和前面的有点不同,刚刚提到了ioc容器是在刚创建时就立马创建相应的bean,而Bean Factory是在准备获取bean时才创建(就是调用getBean时)。
前面的ApplicationContext间接的继承了BeanFactory,对BeanFactory的方法进行了扩展。
不过这个方法已经被标记为废弃了,只需要了解即可,现在主要还是用第一个方法。
下面来具体讲一下关于ApplicationContext的实现类
ClassPathXmlApplicationContext:加载类路径下面的spring配置文件
FileSystemXmlApplicationContext:加载磁盘绝对路径下面的配置文件
AnnotationConfigApplicationContext:用于直接环境,创建容器(后面再介绍)
接着我们来讲一下管理bean的其他方法
第二种
<!--
spring管理bean的第二种方式:基于实例化工厂管理bean
factory-bean:引用的是工厂bean的id
factory-method:引用的是工厂bean中的获取bean的方法名称
-->
<bean id="personFactory" class="com.xq.factory.PersonFactory"></bean>
<bean id="personDao" class="com.xq.dao.impl.PersonDaoImpl" factory-bean="personFactory" factory-method="getPersonDao"></bean>
public class PersonFactory {
//定义一个方法 方法的放回值就是需要被管理的bean类型
public PersonDao getPersonDao() {
return new PersonDaoImpl();
}
}
factory-bean用于指定创建的工厂类的bean的id,factory-method用于指定工厂类中创建类的方法
调用的时候还是先加载xml文件,然后
PersonDao personDao =(PersonDao) context.getBean("放入id");
第三种
<!--
spring中管理bean的第三种方式:使用静态实例化工厂管理bean
-->
<bean id="orderDaoFactory" class="com.xq.factory.OredrDaoFactory" factory-method="getOrder"></bean>
public class PersonFactory {
//定义一个方法 方法的放回值就是需要被管理的bean类型
public PersonDao getPersonDao() {
return new PersonDaoImpl();
}
}
调用跟上面查不多
OrderDao orderDao=(OrderDao)context.getBean("bean中的id");
3.单例bean与多例bean
首先来讲一下什么叫做单例bean与多例bean
单例bean:在整个应用程序中,单例 Bean 只会存在一个实例。当应用程序启动时,单例 Bean 会被创建并初始化,并且在应用程序的生命周期内,这个 Bean 的实例只会被创建一次。
多例bean:多例 Bean 的创建由 Spring 容器负责,但在销毁时,Spring 容器不会像管理单例 Bean 那样进行统一管理。多例 Bean 在使用完后,如果没有其他对象引用它,会由 Java 的垃圾回收机制根据自身的算法决定是否回收该对象。这意味着多例 Bean 的生命周期管理主要依赖于 Java 的内存管理机制,而不是 Spring 容器的特定生命周期管理方法。
ioc容器默认创建的bean都是单例bean,
UserDao userDao1=(UserDao)context.getBean("userDao");
UserDao userDao2=(UserDao)context.getBean("userDao");
System.out.println(userDao1==userDao2);
像这样,结果是true,构造函数只执行一次
如果像创建多例bean,可以在xml文件中的bean标签中加入scope=prototype。那么此时就会输出false,并且构造函数会执行两次。
4.bean的生命周期
单例对象
单例对象的生命周期和容器的生命周期是一致的。当容器创建时,对象就实例化好了。当容器还在的时 候,对象也就一直存在。当容器销毁,对象也就消亡
我们可以在bean标签中所指向的类中加入init(初始化)和destroy(销毁)方法,并在bean标签中加入init-method=“init”和destroy-method=“destroy”
只要ioc容器被创建init就会被自动执行,最后销毁执行
((ClassPathXmlApplicationContext)context).close();这个方法即可。
多例bean
多例就不用管这么多了,只需要保证我们创建的是多例,销毁时 Spring 容器不会管理,而是由 Java 的垃圾回收机制根据对象是否还有引用决定是否回收。
5.依赖注入
依赖注入是一种设计模式,也是 Spring 框架的核心功能之一。其本质是将对象所依赖的其他对象通过某种方式(如构造函数、方法参数、属性等)传递给对象,实现对象之间的解耦,提高代码的可维护性和可测试性。同时,依赖注入体现了控制反转的思想,将对象依赖关系的管理从对象内部转移到外部容器,由容器负责创建和注入依赖对象
使用setter方法进行依赖注入
但使用该方法的前提是被管的bean所属的类必须提供setter方法。
首先就是在要被调用的bean所属的类中写上各个成员变量的setter方法,然后在xml文件中的bean标签中配置。
public class User {
private String username;
private int age;
private String address;
private Car car;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", age=" + age +
", address='" + address + '\'' +
", car=" + car +
'}';
}
}
public class TestUser {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) context.getBean("user");
System.out.println(user);
}
}
xml相关配置如下
<!--第一种:默认的使用set方法进行注入,使用set方法进行注入的前提是被管理的bean所属的类必须提供set方法
property标签:完成bean的属性的注入
name:描述的bean的属性名称
value:描述bean的属性值(基本数据类型和字符串)
ref:注入的是引用数据类型的值 引用的是另外一个bean的id
-->
<bean id="car" class="com.xq.pojo.Car">
<property name="brand" value="宝马"></property>
<property name="type" value="BMW540"></property>
</bean>
<bean id="user" class="com.xq.pojo.User">
<property name="username" value="kunkun"></property>
<property name="age" value="18"></property>
<property name="address" value="CHINA"></property>
<property name="car" ref="car"></property>
</bean>
先看下面那个bean标签中的内容,他给user类中的每个成员变量都附上了初值,name就是成员变量的名字,value就是想要附的值。但是这里有一点要注意这里的car是一个引用变量,因为一个类中有很多个成员变量,所以不可能向上面一样直接用value来赋值所以又开了一个bean标签单独把car进行了初始化,这里就又点嵌套那味了,相当于用一个ref来将一个类嵌套在一个类中,这里的ref的值就是上面的id。
ref能够在不同的 Bean 之间建立起依赖关系,使得一个 Bean 可以使用另一个 Bean 的功能和属性。
来看输出结果
User{username='kunkun', age=18, address='CHINA', car=Car{brand='宝马', type='BMW540'}}
当然要看输出结果记得在目标类中加上toString方法。
使用构造函数实现依赖注入
第一步还是一样的,现在资源文件夹中的xml文件中导入相关bean标签,其实跟前面那个差不多,相关配置如下
<!--
spring的依赖注入的第二种方式:使用构造函数实现依赖注入
constructor-arg:完成依赖注入 使用的是带参数的构造函数
name:属性名称
value:属性名称所属的值 必须是基本数据类型和字符串类型的值
ref:引用数据类型的值
-->
<bean id="departement" class="com.xq.pojo.Department">
<constructor-arg name="id" value="1"></constructor-arg>
<constructor-arg name="name" value="技术部"></constructor-arg>
<constructor-arg name="address" value="Newyork"></constructor-arg>
</bean>
<bean id="emp" class="com.xq.pojo.Emp">
<constructor-arg name="id" value="1001"></constructor-arg>
<constructor-arg name="name" value="kunkun"></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="department" ref="departement"></constructor-arg>
</bean>
也就是bean标签内的property标签换成了constructor标签,前面那个理解了这个自然是一个道理。但是有点要注意,就像前面一样,既然使用setter方法进行依赖注入需要bean所属的类需要setter方法,那么这个也是一样,需要类中有构造方法才能使用
其余代码如下
public class Emp {
private int id;
private String name;
private int age;
private Department department;
public Emp() {}
@Override
public String toString() {
return "Emp{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", department=" + department +
'}';
}
public Emp(int id, String name, int age, Department department) {
this.id = id;
this.name = name;
this.age = age;
this.department = department;
}
}
public class TestUser {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Emp emp = (Emp)context.getBean("emp");
System.out.println(emp);
}
}
输出结果如下
Emp{id=1001, name='kunkun', age=18, department=Department{id=1, name='技术部', address='Newyork'}}
进行复杂数据类型的依赖注入
上面只讲了简单数据类型,String类型和引用数据类型的依赖注入,下面就是复杂数据类性的依赖注入,例如各种集合,数组。
直接上代码:
public class Animal {
private String[] strs;
private List<String> list;
private Set<String> set;
private Map<String,String> map;
private Properties pros;
public String[] getStrs() {
return strs;
}
public void setStrs(String[] strs) {
this.strs = strs;
}
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
public Set<String> getSet() {
return set;
}
public void setSet(Set<String> set) {
this.set = set;
}
public Map<String, String> getMap() {
return map;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public Properties getPros() {
return pros;
}
public void setPros(Properties pros) {
this.pros = pros;
}
@Override
public String toString() {
return "Animal{" +
"strs=" + Arrays.toString(strs) +
", list=" + list +
", set=" + set +
", map=" + map +
", pros=" + pros +
'}';
}
}
<!--
进行复杂类型值的注入
-->
<bean id="animal" class="com.xq.pojo.Animal">
<!--
注入数组类型的值
-->
<property name="strs">
<array>
<value>str1</value>
<value>str2</value>
<value>str3</value>
</array>
</property>
<!--
注入list类型的值
-->
<property name="list">
<list>
<value>list1</value>
<value>list2</value>
<value>list3</value>
</list>
</property>
<!--注入set-->
<property name="set">
<set>
<value>s1</value>
<value>s2</value>
<value>s3</value>
</set>
</property>
<!--
注入Map
-->
<property name="map">
<map>
<entry key="k1" value="v1"></entry>
<entry key="k2" value="v2"></entry>
<entry key="k3" value="v3"></entry>
</map>
</property>
<!--注入properties-->
<property name="pros">
<props>
<prop key="p1">v1</prop>
<prop key="p2">v2</prop>
<prop key="p3">v3</prop>
</props>
</property>
</bean>
public class TestUser {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Animal animal=(Animal)context.getBean("animal");
System.out.println(animal);
}
}
还是差不多的,标明不同的name以及具体的数据类型就行了。