Spring
Spring快速入门
1.Spring的jar包的下载
Spring的各种版本jar包下载地址:https://repo.spring.io/release/org/springframework/spring 我们这次试用的版本是5.2.6
2.创建一个工程导入相关jar包
Spring的核心结构图:
所以我们主要使用的jar包是:
在项目目录下创建lib目录,将上面的5个jar包复制到lib目录下:
将jar包导入项目中:
全部选中导进去.
3.测试:
创建一个类
package com.yfsn.springframework; public class User { public void show() { System.out.println("我已经被spring容器托管的类"); } }
在src(类路径下)创建一个spring的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="user" class="com.yfsn.springframework.User"></bean> </beans>
<bean>标签是将这个类托管给spring容器,id是唯一标识,方便获取,class指向的是被托管类的全路径.
通过容器获取User对象:
首先加载spring的配置文件获得核心容器对象,然后通过容器对象的getBean方法获得指定的对象.
package com.yfsn.springframework.com.yfsn.testdemo; import com.yfsn.springframework.User; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TheTest { @Test public void UserTest() { //首先需要加载配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("springConfig.xml"); //获取对象 User user = context.getBean("user", User.class); user.show(); } }
运行结果:
IOC(控制反转)
控制反转实现用到的技术(了解)
- 工厂模式
- xml解析技术
- 反射
IOC的底层实现原理:
如果我们在一个类中的方法;里面使用另一个类的方法,非常普通的方法是直接在这个类中的方法里创建另一个类的实例,然后进行调用.但是这种创建对象的方法使我们的代码的耦合度变得很高,一旦被调用类的
方法或者被调用类的包路径发生改变的时候我们在本类中也就会出现错误.如果是一整个项目的或那么就是牵一发而动全身.
IOC的底层实现实际上是在工厂模式里面使用xml解析技术和反射的技术实现类的对象创建的过程.
package com.yfsn.springframework.com.yfsn.testdemo; import com.yfsn.springframework.User; public class BeanFactoryDemo { public static User getUser() throws ClassNotFoundException, IllegalAccessException, InstantiationException { // 第一步使用xml解析的技术解析spring配置文件中的<bean>标签,获得class属性的类的全路径名称 String path = "解析出来的路径"; // 2.使用反射技术根据全路径创建对象 Class clazz=Class.forName(path); return (User)clazz.newInstance(); } }
IOC容器创建的两种方式:
spring容器提供了两种创建IOC容器(context或者说是对象工厂beanfactory,其实所谓的容器就是一个对象工厂,独享工厂帮我们创建我们所需要的对象)的方法(提供了两个接口):
- 通过BeanFactory创建
特点:IOC容器基本实现,是Spring内部的使用接口,不提供开发人员进行使用.
*加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象 - 通过ApplicationContext创建
特点:BeanFactory.接口的子接口,提供更多更强大的功能,一般由开发人员进行使用
*加载配置文件时候就会把在配置文件对象进行创建
通过ctrl+H可以查看BeanFactory的继承结构:
我们来演示一下他们各自创建对象的时机案例:
BeanFactory:
@Test public void UserTest() { //首先需要加载配置文件 Resource resource = new ClassPathResource("springConfig.xml"); BeanFactory context = new XmlBeanFactory(resource); //获取对象 // User user = context.getBean("user", User.class); // user.show(); }
控制台并没有输出东西.
但是当我们使用容器工厂获得对象的时候:
@Test public void UserTest() { //首先需要加载配置文件 Resource resource = new ClassPathResource("springConfig.xml"); BeanFactory context = new XmlBeanFactory(resource); //获取对象 User user = context.getBean("user", User.class); // user.show(); }
控制台输出:
ApplicationContext
@Test public void UserTest() { //首先需要加载配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("springConfig.xml"); //获取对象 // User user = context.getBean("user", User.class); // user.show(); }
运行控制台输出:
IOC容器工厂的Bean管理(基于XML方式)
IOC容器工厂的bean管理其实就是对容器工厂中被托管的对象进行创建对象和对对象属性赋值的过程.
对对象的创建其实就是:
注意:
对象的创建默认创建对象的时候是使用的无参构造的方法,当我们声明一个类的时候如果没有无参和有参构造,那么会默认给我们创建一个无参构造方法,但是当我们声明了有构造但是并没有声明无参构造的
时候就不会自动的帮我们创建创建一个无参构造,那么这个bean是无法进行创建对象的.
如果我们在User类中添加上属性name和age,并定义有参构造:
package com.yfsn.springframework; public class User { private String name; private Integer age; public User(String name, Integer age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public void show() { System.out.println("我已经被spring容器托管的类"); } }
像这种情况spring的容器工厂并不能帮我们创建User对象:
DI依赖注入(注入属性)
IOC容器工厂的属性注入的方式有两种:
- set方法注入
- 有参构造的方法注入
set方法注入
使用set注入那么这个类必须要有set方法.
package com.yfsn.springframework; public class User { private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } //为了测试输出的toString方法 @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
我们在XML中的set注入操作是:
<bean id="user" class="com.yfsn.springframework.User"> <property name="age" value="23"></property> <property name="name" value="ZYH"></property> </bean>
注意:set注入的过程也是先创建对象在进行属性的注入.
测试结果:
@Test public void UserTest() { //首先需要加载配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("springConfig.xml"); //获取对象 User user = context.getBean("user", User.class); System.out.println(user); }
结果:
有参构造的方法注入
有参构造方法那么需要这个类含有有参构造函数:
package com.yfsn.springframework; public class User { private String name; private Integer age; public User(String name, Integer age) { this.name = name; this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
XML中的有参构造注入操作是:
<bean id="user" class="com.yfsn.springframework.User"> <constructor-arg name="name" value="ZYH"></constructor-arg> <constructor-arg name="age" value="23"></constructor-arg> </bean>
测试结果:
接下来在set注入的基础上学习一些特殊属性的注入方式:
null值和特殊符号的注入:
XML中的配置:
<bean id="user" class="com.yfsn.springframework.User"> <!-- null值的注入--> <property name="age"> <null></null> </property> <!-- 将name的值设置为含有特殊符号的<<>>--> <property name="name"> <value><![CDATA[<<南京>>]]></value> </property> </bean>
测试结果:
外部bean的注入
如果我们注入的属性不再是基本数据类型当然也不是String类型,被注入的属性也是一个存在于容器工厂中的托管对象,那么我们就可以使用外部bean的方式进行注入.
<!--User托管到容器工厂--> <bean id="user" class="com.yfsn.springframework.User"> <property name="name" value="ZYH"></property> <property name="age" value="23"></property> <!-- 使用外部注入将dog为User的dog属性进行注入--> <property name="dog" ref="dog"></property> </bean> <!--Dog托管到容器工厂--> <bean id="dog" class="com.yfsn.springframework.Dog"> <property name="age" value="1"></property> <property name="name" value="大黄"></property> </bean>
测试结果:
内部bean的注入:(运用于1对多的关系中)
我们将User类看成是主人,另外我们再创建一个Dog类
package com.yfsn.springframework; /** * @Author ZYH * @Date 2021年06月02日 14:22 * @Version 1.0 */ public class Dog { private String name; private Integer age; private User user; public User getUser() { return user; } public void setUser(User user) { this.user = user; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "Dog{" + "name='" + name + '\'' + ", age=" + age + ", user=" + user + '}'; } }
XML中使用内部bean对Dog进行注入的操作是:
<!--Dog托管到容器工厂--> <bean id="dog" class="com.yfsn.springframework.Dog"> <property name="age" value="1"></property> <property name="name" value="大黄"></property> <property name="user"> <!--User托管到容器工厂--> <bean id="user" class="com.yfsn.springframework.User"> <property name="name" value="ZYH"></property> <property name="age" value="23"></property> </bean> </property> </bean>
输出结果:
集合属性的注入
我们首先创建User对象,对象的属性包括list集合,数组,map集合,set集合:
package com.yfsn.springframework; import java.lang.reflect.Array; import java.util.List; import java.util.Map; import java.util.Set; public class User { private List<String> list; private String[] habby; private Map<String, String> map; private Set<String> set; public List<String> getList() { return list; } public void setList(List<String> list) { this.list = list; } public String[] getHabby() { return habby; } public void setHabby(String[] habby) { this.habby = habby; } public Map<String, String> getMap() { return map; } public void setMap(Map<String, String> map) { this.map = map; } public Set<String> getSet() { return set; } public void setSet(Set<String> set) { this.set = set; } }
XML中的注入操作:
<!-- 集合的注入--> <bean id="user" class="com.yfsn.springframework.User"> <property name="habby"> <array> <value>篮球</value> <value>乒乓球</value> </array> </property> <property name="list"> <list> <value>java</value> <value>python</value> </list> </property> <property name="map"> <map> <entry key="like" value="women"></entry> <entry key="name" value="YQ"></entry> </map> </property> <property name="set"> <set> <value>123</value> <value>345</value> </set> </property> </bean>
测试结果:
IOC-Bean管理中bean的作用域
bean的作用域也就是托管到容器工程中的bean创建实例的时候是单例的还是多例的,当然,spring容器在默认情况下创建的bean都是单例的,但是我们可以在配置中改变bean的作用域,将它编程多例的:
默认情况下的托管bean:
<!-- 集合的注入--> <bean id="user" class="com.yfsn.springframework.User"> <property name="habby"> <array> <value>篮球</value> <value>乒乓球</value> </array> </property> <property name="list"> <list> <value>java</value> <value>python</value> </list> </property> <property name="map"> <map> <entry key="like" value="women"></entry> <entry key="name" value="YQ"></entry> </map> </property> <property name="set"> <set> <value>123</value> <value>345</value> </set> </property> </bean>
在bean标签中具有一个scope属性:
如果属性值是prototype则这个bean就是单例的,
如果属性值是singleton则这个bean就是多例的.
关于单例和多例的区别其实我们在加载配置文件的时候,前面提到过,使用ApplicationContext加载配置文件的时候会自动创建对象,但是当我们将bean的作用域改变成多例的时候,那么
spring将不会在加载配置文件的时候帮我们创建这个bean的实例
IOC-Bean管理中bean的生命周期
1、生命周期
(1)从对象创建到对象销毁的过程
2,bean生命周期
(1)通过构造器创建bean实例(无参数构造).
(2)为bean的属性设置值和对其他bean引用(调用set方法)
(3)调用bean的初始化的方法(需要进行配置初始化的方法)
(4)bean 可以使用了(对象获取到)
(5)当容器关闭时候,调用bean的销毁的方法(需要进行配置销毁的方法).
IOC-Bean管理属性的自动注入
我们前面讲述的使用<property>标签进行属性注入的过程实际上是手动进行的bean的属性的注入,但是spring为我们提供了自动注入属性值的操作,当注入的属性值是一个对象,并且这个对象被容器工厂托管,
那么我们就可以对相应的属性值进行自动注入.
我们创建一个Dog类并且将它托管到容器工厂:
package com.yfsn.springframework; /** * @Author ZYH * @Date 2021年06月02日 14:22 * @Version 1.0 */ public class Dog { private String name; private Integer age; private User user; public User getUser() { return user; } public void setUser(User user) { this.user = user; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "Dog{" + "name='" + name + '\'' + ", age=" + age + ", user=" + user + '}'; } }
相应的User类的属性是一个Dog;
package com.yfsn.springframework; import java.lang.reflect.Array; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; public class User { private Dog dog; public Dog getDog() { return dog; } public void setDog(Dog dog) { this.dog = dog; } }
在容器工厂中进行托管:
<bean id="user" class="com.yfsn.springframework.User" autowire="byName"> </bean> <bean id="dog" class="com.yfsn.springframework.Dog"></bean>
测试输出User实例结果:
自动装配使用的标签是autowire,这个标签的属性主要的是byName和byType两种:
- 当我们使用byName的时候,这个时候属性的自动注入流程是按照类中的属性的属性名称在容器工厂中寻找以这个属性名为id的被托管的bean,然后将这个bean的实例进行自动注入.
- 当我们使用byType的时候,这个时候的属性的自动注入流程是按照类中对应属性的类型在容器工厂中寻找类型一致的bean然后将这个bean的实例进行自动注入.
注意:当使用byType的时候容器工厂中只能含有一个与要注入属性类型相同的bean.
IOC-Bean管理(注解方式)
托管注解
使用spring对bean进行管理首先就需要将那些需要被管理的类托管到容器工厂中去.
Spring提供了4个托管注解,其实他们的作用是相同的.
- (1)@Component.
- (2)@Service
- (3)@Controller
- (4)@Repository
当我们使用这四个注解之中的任意一个注解加到我们需要被托管的类的上面的时候,那么这个类就会被托管到我们的容器工厂中去.
步骤:
- 使用托管注解的前提是导入aop的jar包
- 在spring配置文件中开启注解扫描
经过上面两步我们才能使用托管注解.
- 使用托管注解的前提是导入aop的jar包
2.在spring配置文件中开启注解扫描
步骤:
- 在配置文件中开启context命名空间
- 使用context开启扫描路径
对应的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: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" (和这里)> <!-- 开启注解路径扫描,base-package属性值可以填写多个路径,中间使用,隔开-->
<context:component-scan base-package="com.yfsn.springframework"></context:component-scan> </beans>
我们在User类上面添加托管注解:
package com.yfsn.springframework; import org.springframework.stereotype.Component; //下面相当于<bean id="user class="......."> @Component(value = "user")//在这里这个value的值对应的就是托管到容器工厂中的<bean>标签中id的值,当然如果只添加注解不适用value值,那么默认id属性的值是被托管的类的类名首字母小写 public class User { }
测试代码:
@Test public void UserTest() { //首先需要加载配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("springConfig.xml"); //获取对象 User user = context.getBean("user", User.class); System.out.println(user); }
测试结果:
开启组件扫描中的细节处理问题
当我们使用:<context:component-scan base-package="com.yfsn.springframework"></context:component-scan>开启注解扫描的时候,这种配置默认是会扫描配置包下面的所有的类将所有具有托管类注解的类都进行托管到容器工厂中去.
但是我们也能会要求不进行全部的托管呢,或者只进行部分类的托管呢?
<!-- 示例1 use-default-fiiters="false"表示现在不使用默认filter,自己配置filter context:include-filter,设置扫描哪些内容--> <context:component-scan base-package="com.yfsn.springframework" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <!-- 示例2 下面配置扫描包所有内容 context:exclude-filter:设置哪些内容不进行扫描--> <context:component-scan base-package="com.yfsn.springframework"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
属性自动注入注解:
关于属性的自动注入我们分为两类:
- 基本数据类型和String类型属性的自动注入
- 自定义引用数据类型属性的自动注入
使用的注解:
(1)@Autowired:根据属性类型进行自动装配.
(2)@Qualifier:根据属性名称进行注入.需要和@Autowired注解一起使用.(先试用@Autowired注解寻找到所有符合类型的集合,然后@Qualifier(value="指定的托管的bean的id值")根据名字进行精确的注入)
(3)@Resource:可以根据类型注入,可以根据名称注入.根据类型注入和@Autowired使用相同,根据名称注入:@Resource(name="指定的托管bean的id值")
(4)@Value:注入普通类型属性: 使用:@Value(value="需要设置的值")
完全注解开发
使用完全注解开发言外之意就是不再使用的我们配置文件,那么就需要一个配置文件的替代品,这个替代品就是配置类,在配置类中我们进行相关的配置,来代替配置文件中的配置.
步骤:
- 定义配置类
- 开启组件扫描
- 编写测试类
配置类如下:
package com.yfsn.springframework.com.yfsn; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; /** * @Author ZYH * @Date 2021年06月03日 15:15 * @Version 1.0 */ @Configuration //配置类注解,代表这个类是一个配置类 @ComponentScan(basePackages = {"com.yfsn.springframework"}) //组件扫描注解(注解扫描) public class SpringConfigration { }
测试类如下:
@Test public void UserTest2() { //首先需要加载配置文件 ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfigration.class);//在这里发生了改变 //获取对象 User user = context.getBean("user", User.class); System.out.println(user); }
测试结果: