小案例
Spring是一个很优秀的框架,是后端开发不可避免需要提到的技术,现在我来学习这项技术。首先,在编写代码之前我需要声明,我使用的是专业版的IDEA。创建一个Spring Initializr的项目并且利用maven添加了Spring Web依赖,这就是我在编写代码之前所做的准备工作。
首先,我创建了一个User类,为了理解方便,我会加上它所在的包的信息。
package com.spring;
public class User {
public void add(){
System.out.print("test01");
}
}
一般情况下,如果我们要调用User类中的add()方法,就需要声明一个User对象并且通过这个对象来调用add()方法。但是我们要使用Spring风格的方法来达到这个效果,在resources文件下声明一个xml文件命名为bean1,并且添加User配置<bean id="user" class="com.spring.User"></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="user" class="com.spring.User"></bean>
</beans>
然后就可以着手写一个测试代码了:
package com.test;
import com.spring.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class springtest {
@Test
public void test1(){
//加载Spring配置文件
//ApplicationContext context =new FileSystemXmlApplicationContext("bean1.xml的绝对路径");
ApplicationContext context=new ClassPathXmlApplicationContext("bean1.xml");
//获取配置创建的对象
User user=context.getBean("user",User.class);
System.out.println(user);
user.add();
}
}
需要注意的是加载配置文件的部分,ClassPathXmlApplicationContext 是 Spring 框架中的一个应用上下文(Application Context)实现类,用于从类路径下的 XML 配置文件中加载和管理 Bean,传入的是相对路径并且是要在类路径下,FileSystemXmlApplicationContext需要传入的是配置文件的绝对路径。而以上操作,就是IOC的实现操作,以下是IOC的理论部分。
什么是IOC?是控制反转,把对象创建和对象之间的调用过程,交给 Spring 进行管理。目的是什么?为了降低耦合度。
1、IOC 思想基于 IOC 容器完成,IOC 容器底层就是对象工厂
2、Spring 提供 IOC 容器实现两种方式:(两个接口)
(1)BeanFactory:IOC 容器基本实现,是 Spring 内部的使用接口,不提供开发人员进行使用
* 加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象
(2)ApplicationContext:BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人
员进行使用
* 加载配置文件时候就会把在配置文件对象进行创建
类的嵌套实现
接下来再看一个使用案例:我声明一个接口并让一个类实现它
public interface people {
public void update();
}
public class peopleimpl implements people{
@Override
public void update() {
System.out.println("peopleimpl start");
}
}
然而这个我决定将这个类的一个对象作为另一个类的属性:
public class student {
private peopleimpl stu1;
public void setStu1(peopleimpl stu1){
this.stu1=stu1;
}
public void add(){
System.out.println("student start");
stu1.update();
}
}
好了,接下来就创建一个新的xml文件名字叫bean2:
<?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="peopleid" class="com.spring.peopleimpl"></bean>
<bean id="studentid" class="com.spring.student">
<property name="stu1" ref="peopleid"></property>
</bean>
</beans>
先配置第一个类,自定义其id,再配置第二个类,其中的第一个类的示例对象需要注入,就用上一条的配置中的自定义的id填入ref。需要注意的是,name字段需要跟第二个类中的属性对象名一致。测试代码就不放了。
一对多关系的演示
接下来我将演示如何表示实体类中一对多关系的表现,有teacher类如下:
package com.spring;
import java.util.Map;
public class teacher {
private int[] classnum;
private Map<Integer,String> map;
public int[] getClassnum() {
return classnum;
}
public void setClassnum(int[] classnum) {
this.classnum = classnum;
}
public Map<Integer, String> getMap() {
return map;
}
public void setMap(Map<Integer, String> map) {
this.map=map;
}
}
修改xml配置文件bean2:
<?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="teacherid" class="com.spring.teacher">
<property name="classnum">
<array>
<value>1</value>
<value>2</value>
</array>
</property>
<property name="map">
<map>
<entry key="1" value="张三"></entry>
<entry key="2" value="李四"></entry>
</map>
</property>
</bean>
</beans>
名称空间引入
在这个基础上,我们还可以通过名称空间util引入的方式来注入数据,结果类似:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
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
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<util:map id="maputil">
<entry key="1" value="张三"></entry>
<entry key="2" value="李四"></entry>
<entry key="3" value="王五"></entry>
</util:map>
<bean id="teacherid" class="com.spring.teacher">
<property name="classnum">
<array>
<value>1</value>
<value>2</value>
</array>
</property>
<property name="map" ref="maputil"></property>
</bean>
</beans>
设置单实例或多实例
(1)在 spring 配置文件 bean 标签里面有属性(scope)用于设置单实例还是多实例
(2)scope 属性值第一个值 默认值,singleton,表示是单实例对象第二个值 prototype,表示是多实例对象。我们以User类为例,有测试代码如下:
@Test
public void test1(){
//ApplicationContext context =new FileSystemXmlApplicationContext("E:\\IDEAworks\\works\\demo\\src\\main\\java\\bean1.xml");
ApplicationContext context=new ClassPathXmlApplicationContext("bean1.xml");
User user1=context.getBean("user",User.class);
User user2=context.getBean("user",User.class);
System.out.println(user1);
System.out.println(user2);
}
}
如果对bean不做修改,那么两个输出的值是完全一致的。
如果修改bean代码如下,即设置scope属性为prototype:
<?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="com.spring.User" scope="prototype">
<property name="uname" value="志愿者一号"></property>
<property name="uage" value="22"></property>
</bean>
</beans>
运行结果为:
bean的生命周期
(1)通过构造器创建 bean 实例(无参数构造)
(2)为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
(3)调用 bean 的初始化的方法(需要进行配置初始化的方法)
(4)bean 可以使用了(对象获取到了)
(5)当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
有示例代码:
public class Orders {
//无参数构造
public Orders() {
System.out.println("第一步 执行无参数构造创建 bean 实例");
}
private String oname;
public void setOname(String oname) {
this.oname = oname;
System.out.println("第二步 调用 set 方法设置属性值");
}
//创建执行的初始化的方法
public void initMethod() {
System.out.println("第三步 执行初始化的方法");
}
//创建执行的销毁的方法
public void destroyMethod() {
System.out.println("第五步 执行销毁的方法");
}
}
在xml文件中添加的代码:
<bean id="orders" class="com.spring.Orders" init-method="initMethod" destroy-method="destroyMethod">
<property name="oname" value="手机"></property>
</bean>
就可以书写测试代码啦:
@Test
public void test4() {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("bean4.xml");
Orders orders = context.getBean("orders", Orders.class);
System.out.println("第四步 获取创建 bean 实例对象");
System.out.println(orders);
//手动让 bean 实例销毁
context.close();
}
如果添加了后置处理器,那么周期就有七步:
(1)通过构造器创建 bean 实例(无参数构造)
(2)为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
(3)把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization
(4)调用 bean 的初始化的方法(需要进行配置初始化的方法)
(5)把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization
(6)bean 可以使用了(对象获取到了)
(7)当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
如何添加呢?就需要创建一个类实现接口 BeanPostProcessor,创建后置处理器。
public class MyBeanPost implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("在初始化之前执行的方法");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("在初始化之后执行的方法");
return bean;
}
}
在xml文件中添加代码如下(最好在最后段)就可以为以上的添加后置器啦:
<bean id="myBeanPost" class="com.atguigu.spring5.bean.MyBeanPost"></bean>
运行上一个测试代码,就会发现结果不一样了。
XML自动装配
自动装配是根据指定装配规则(属性名称或者属性类型),Spring 自动将匹配的属性值进行注入。(1)根据属性名称自动注入bean 标签属性 autowire,配置自动装配 autowire 属性常用两个值:(1)byName 根据属性名称注入 ,注入值 bean 的 id 值和类属性名称一样(2)byType 根据属性类型注入
根据属性名称(名称需要与对象名一致):
<bean id="emp" class="com.spring.Emp" autowire="byName">
<!--<property name="dept" ref="dept"></property>-->
</bean>
<bean id="dept" class="com.spring.Dept"></bean>
根据属性类型(只能创建一个bean,创建多个会无法识别选择哪一个而报错):
<bean id="emp" class="com.pring.Emp" autowire="byType">
<!--<property name="dept" ref="dept"></property>-->
</bean>
<bean id="dept" class="com.spring.Dept"></bean>
bean管理外部属性文件(以配置德鲁伊数据库连接池为例)
首先就是引入druid依赖的jar包,然后创建properties属性文件:
t.user=数据库用户名
t.password=数据库密码
t.url=jdbc:mysql://localhost:3306/数据库名
t.driverClass=com.mysql.cj.jdbc.Driver
然后就是引入名称空间context
<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"
xmlns:util="http://www.springframework.org/schema/util"
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/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
然后就是引入外部文件和配置连接池了:
<!--引入外部属性文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${t.driverClass}"></property>
<property name="url" value="${t.url}"></property>
<property name="username" value="${t.userName}"></property>
<property name="password" value="${t.password}"></property>
</bean>
基于注解的方式创造对象
Spring 针对 Bean 管理中创建对象提供注解
(1)@Component
(2)@Service
(3)@Controller
(4)@Repository
* 上面四个注解功能是一样的,都可以用来创建 bean 实例
首先,要导入相关的依赖包,即Spring-AOP中RELEASE包。其次就是创建配置文件bean4.xml并且开启组件扫描,引入context名称空间:
<?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: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:component-scan base-package="com.spring"></context:component-scan>
</beans>
然后我们就可以任选一个类在其类名上添加注解来创建了,这里我选择写过的Class类:
@Component(value = "zjClass")//value可看为bean操作中的自定义id
public class Class {
private String claname;
public String getClaname() {
return claname;
}
public void setClaname(String claname) {
this.claname = claname;
}
}
最后有测试方法并且成功打印:
@Test
public void test4(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
Class zjClass =context.getBean("zjClass", Class.class);
System.out.println(zjClass);
}
组件扫描有个细节
<!--示例 1
use-default-filters="false" 表示现在不使用默认 filter,自己配置 filter
context:include-filter ,设置扫描哪些内容
-->
<context:component-scan base-package="com.spring" use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
//只扫描Controller标签
<!--示例 2
下面配置扫描包所有内容
context:exclude-filter: 设置哪些内容不进行扫描
-->
<context:component-scan base-package="com.spring">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
//Controller标签不扫描,其余都扫描
基于注解方式注入属性
1. @Autowired:根据属性类型进行自动装配
第一步 把 service 和 dao 对象创建,在 service 和 dao 类添加创建对象注解
第二步 在 service 注入 dao 对象,在 service 类添加 dao 类型属性,在属性上面使用注解
@Service
public class UserService {
//定义 dao 类型属性
//不需要添加 set 方法
//添加注入属性注解
@Autowired
private UserDao userDao;
public void add() {
System.out.println("service add.......");
userDao.add();
}
}
2.@Qualifier:根据名称进行注入
这个@Qualifier 注解的使用,和上面@Autowired 一起使用
//定义 dao 类型属性
//不需要添加 set 方法
//添加注入属性注解
@Autowired //根据类型进行注入
@Qualifier(value = "userDaoImpl1") //根据名称进行注入
private UserDao userDao;
3.@Resource:可以根据类型注入,可以根据名称注入
//@Resource //根据类型进行注入
@Resource(name = "userDaoImpl1") //根据名称进行注入
private UserDao userDao;
4.@Value:注入普通类型属性
@Value(value = "abc")
private String name;
完全注解开发
如果想使用纯注解开发,我们可以创建一个配置类来替代配置文件。
@Configuration //作为配置类,替代 xml 配置文件
@ComponentScan(basePackages = {"com.spring"})
public class SpringConfig {
}
另外就是测试类中的
ApplicationContext context = new ClassPathXmlApplicationContext("??.xml");
需要替换成:
ApplicationContext context
= new AnnotationConfigApplicationContext(SpringConfig.class);
其余不变即可。