Spring
1 Spring的初步认识
1.1 基本概述
Spring是一个设计层面的框架(不属于某一层),用于整合业务和其他层的交互,简化项目的设计和开发。核心是ioc和aop。
- IOC:inversion of control,控制反转容器,用来整合其他框架。将对象的创建,初始化,分配,使用,维护和销毁等功能交给Spring来进行管理。
- DI:Dependency Injection,依赖注入,容器知道那个组件运行时需要另一个类,在需要的时候利用反射将组件注入到指定的组件中去。
- AOP:Aspect Oriented Programming,面向切面编程,主要用于对事务的管理。
- 面向过程:专注功能的实现。
- 面向对象:专注于某个对象。
- 面向切面:专注于所有对象的某个功能。实现核心业务逻辑和辅助功能的分离。
- 组件的划分:
1.2 项目的搭建
使用的idea的maven项目
1 创建maven项目并配置pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>ssm</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>03_spring</artifactId>
<dependencies>
<!--导入spring基础五个包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!--导入单元测试的包-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
2 创建spring的核心配置文件
<?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">
<!--1.创建一个javaBean对象,使用property赋值,实际上使用set方法-->
<bean id="student01" class="com.zhiyou.pojo.Student">
<property name="sid" value="001"/>
<property name="sname" value="张三丰" />
<property name="sex" value="男" />
<property name="score" value="98" />
</bean>
<!--2.构造方法进行对象创建并赋值-->
<bean id="student02" class="com.zhiyou.pojo.Student">
<constructor-arg name="sid" value="002" index="0" />
<constructor-arg name="sname" value="尼古拉斯赵六" index="1" />
<constructor-arg name="score" value="98" index="3"/>
<constructor-arg name="sex" value="仙" index="2" />
</bean>
</beans>
注:配置文件的作用是为了让Spring感知到这些组件的存在。
3 创建测试类进行ioc容器的测试
public class IOCTest {
ApplicationContext ioc= new ClassPathXmlApplicationContext("spring-config.xml");
@Test
public void test01(){
Object student01 = ioc.getBean("student01");
System.out.println(student01);
Object student02 = ioc.getBean("student02");
System.out.println(student02);
}
}
结果展示:
2 SpringIOC
2.1 bean的赋值
2.1.1 简单属性的赋值
1 使用property标签
<!--1.创建一个javaBean对象,使用property赋值,实际上使用set方法-->
<bean id="student01" class="com.zhiyou.pojo.Student">
<property name="sid" value="001"/>
<property name="sname" value="张三丰" />
<property name="sex" value="男" />
<property name="score" value="98" />
</bean>
property赋值的方式是使用该类的setter方法来实现的。
2 使用构造器实例化
<!--2.构造方法进行对象创建并赋值-->
<bean id="student02" class="com.zhiyou.pojo.Student">
<constructor-arg name="sid" value="002" index="0" />
<constructor-arg name="sname" value="尼古拉斯赵六" index="1" />
<constructor-arg name="score" value="98" index="3"/>
<constructor-arg name="sex" value="仙" index="2" />
</bean>
2.1.2 手动装配
手动装配:如果类中存在其他类对象,可以采用ref引用的方式来引用其他bean来实现注入。
<!--1.创建一个javaBean对象,使用property赋值,实际上使用set方法-->
<bean id="student01" class="com.zhiyou.pojo.Student">
<property name="sid" value="001"/>
<property name="sname" value="张三丰" />
<property name="sex" value="男" />
<property name="score" value="98" />
<property name="car" ref="car01" />
</bean>
<!--2.构造方法进行对象创建并赋值-->
<bean id="student02" class="com.zhiyou.pojo.Student">
<constructor-arg name="sid" value="002" index="0" />
<constructor-arg name="sname" value="尼古拉斯赵六" index="1" />
<constructor-arg name="score" value="98" index="3"/>
<constructor-arg name="sex" value="仙" index="2" />
<constructor-arg name="car" ref="car01" />
</bean>
<!--3-->
<bean class="com.zhiyou.pojo.Car" id="car01">
<property name="carName" value="保时捷" />
<property name="carColor" value="蓝色" />
<property name="price" value="99.8" />
</bean>
代码测试:
public class IOCTest {
ApplicationContext ioc= new ClassPathXmlApplicationContext("spring-config.xml");
@Test
public void test01(){
Student student01 = (Student)ioc.getBean("student01");
System.out.println(student01);
student01.getCar().setCarName("奔驰--梅萨瑟斯");
Object student02 = ioc.getBean("student02");
System.out.println(student02);
}
}
结果展示
多个对象引用获取的到car是同一个对象,如果我们修改了其属性,其他对象获取到的该对象的属性值也会发生变化。
2.1.3 基于Xml的自动装配
自动装配:
- autowire:“default”:不进行自动装配
- autowire:“byName”:按照名字来赋值,以属性名为id在容器中寻找这个组件。如果找到且类型匹配就装配,否则为null。相当于getBean(car)
- autowire:“byType”:按照属性的类型在容器中寻找该类型的组件,
- 如果找到一个则进行装配。相当于getBean(Car.class)。
- 如果存在多个该类型的bean,则会报错。
- 如果找不到,则装配null。
- autowire:“constructor”:先按照有参构造器的参数类型进行装配。如果没有则装配一个null;如果按照类型存在多个,参数的名作为id继续进行装配,如果有则进行装配,如果没有找到则装配null。
- autowire:“no”:
如果存在List,在xml中如果存在多个book,我们选择使用byType的时候会将这些bean实例化,并自动赋值给List
代码实现:
<bean class="com.zhiyou.pojo.Car" id="car01">
<property name="carName" value="保时捷" />
<property name="carColor" value="蓝色" />
<property name="price" value="99.8" />
</bean>
<!--自动装配-->
<bean id="student03" class="com.zhiyou.pojo.Student" autowire="byType">
</bean>
<!--手动装配-->
<bean id="student04" class="com.zhiyou.pojo.Student">
<property name="car" ref="car01" />
</bean>
2.2 bean的作用域
2.2.1 懒加载
默认情况下,当ioc容器初始化的时候会创建bean对象,我们可以通过设置lazy-init="true"来设置bean进行懒加载。
- 如果为true则表示懒加载,在第一次获取该对象的时候创建对象。
- 如果是false为非懒加载,容器初始化的时候创建该对象。
<bean id="student04" class="com.zhiyou.pojo.Student" lazy-init="true">
<property name="car" ref="car01" />
</bean>
2.2.2 通过scope属性设置作用域
scope设置bean的作用域存在五个属性。
- prototype:原型模式。
- 在容器启动的时候不会创建该类对象,只有在调用的时候才会进行创建。类似于懒加载的效果。
- 多次获取则是多个对象。
- singleton:单例模式。默认情况下为该模式。
- 在容器启动完成之前就已经创建好对象,保存在容器中。
- 不论在哪里获取,都是同一个对象。
- request:在web环境下,同一次请求创建一个bean实例
- session:web的情况下,同一个会话创建一个bean实例
<bean id="car01" class="com.zhiyou.pojo.Car" scope="prototype"></bean>
<bean id="car02" class="com.zhiyou.pojo.Car" scope="singleton"></bean>
2.2.3 bean的生命周期
在对象创建和销毁前后调用指定的方法。
<bean id="buyCar" class="com.zhiyou.pojo.Car" init-method="initMethod" destroy-method="destoryMethod">
<property name="carName" value="玛莎拉蒂" />
<property name="price" value="998" />
</bean>
@Test
public void test05(){
ConfigurableApplicationContext ca=(ConfigurableApplicationContext)ioc;
ca.close();
}
特点:
- 如果我们指定懒加载,且是单例bean的情况下,在容器启动的时候就加载这个对象,调用初始化的方法。
- ioc容器销毁则调用该对象的销毁方法。
- 如果对象是非单例bean或配置了懒加载,则在获取的时候调用初始化方法。
2.3 复杂类型的赋值
2.3.1 赋值null,外部引用,内部创建
<bean id="person01" class="com.atguigu.bean.Person">
<!--value赋值都是一些简单的属性,null不在其中-->
<property name="lastName" value="NULL"></property>
<!-- 真正的给属性赋予null值的方式-->
<property name="age"><null/></property>
<!-- ref代表引入当前已经存在组件,是严格的引用-->
<!-- <property name="car" ref="car01"></property> -->
<!--相当于car=new Car();-->
<property name="car">
<bean class="com.atguigu.bean.Car">
<property name="carName" value="自行车"></property>
</bean>
</property>
</bean>
2.3.2 为一些复杂的属性进行赋值
为list,map,properties属性进行赋值
<bean id="person02" class="com.atguigu.bean.Person">
<!--配置list类型的属性-->
<property name="books">
<!-- 相当于books=new ArrayList() -->
<list>
<!-- p命名空间赋值一本书 -->
<bean class="com.atguigu.bean.Book" p:bookName="西游记"></bean>
<!-- 引入外部一个元素 -->
<ref bean="book01"/>
</list>
</property>
<!--配置map类型的属性-->
<property name="maps">
<map>
<!-- 一个entry就代表是一个键值对 -->
<entry key="key01" value="张三"></entry>
<entry key="key02" value="李四"></entry>
<entry key="key03" value-ref="book01"></entry>
<entry key="key04">
<bean class="com.atguigu.bean.Car">
<property name="carName" value="奔驰"></property>
</bean>
</entry>
</map>
</property>
<!--配置properties类型的属性-->
<property name="properties">
<!-- properties=new Properties(); key-value都是String类型 -->
<props>
<prop key="username">root</prop>
<prop key="password">19971001</prop>
</props>
</property>
</bean>
代码测试:
@Test
public void test04() {
//ApplicationContext:代表ioc容器
//ClassPathXmlApplicationContext:代表当前应用的xml配置文件在ClassPath下
ApplicationContext ioc=new ClassPathXmlApplicationContext("ioc2.xml");
//获取容器帮我们创建好的对象
Person person=(Person)ioc.getBean("person02");
System.out.println("list=====");
List<Book> books = person.getBooks();
for (int i = 0; i < books.size(); i++) {
System.out.println(books.get(i));
}
System.out.println("map=====");
Map<String, Object> maps = person.getMaps();
Iterator<Entry<String, Object>> iterator = maps.entrySet().iterator();
while (iterator.hasNext()) {
Entry<String, Object> next = iterator.next();
System.out.println(next.getKey()+"==="+next.getValue());
}
System.out.println("propertis=====");
Properties properties = person.getProperties();
System.out.println(properties);
}
注意:内部bean不能被其他bean组件获取到,只能内部自己使用。
3 util名称空间创建集合类型的bean
- 导入util命名空间
- 在xml中配置相应的集合属性
<bean class="com.atguigu.bean.Person" id="person03">
<!-- 此时需要引用其他bean的map属性,使用util -->
<property name="maps" ref="mymap">
</property>
</bean>
<!-- 相当于new LinkedHashMap -->
<util:map id="mymap">
<!-- 一个entry就代表是一个键值对 -->
<entry key="key01" value="张三"></entry>
<entry key="key02" value="李四"></entry>
<entry key="key03" value-ref="book01"></entry>
<entry key="key04">
<bean class="com.atguigu.bean.Car">
<property name="carName" value="奔驰"></property>
</bean>
</entry>
</util:map>
优点:
存在id可以被其他bean复用。
可以直接根据ioc容器根据id来进行获取
2.4 工厂模式初始化对象
bean的创建默认是框架利用反射new出来的bean实例。工厂模式:工厂帮我们创建对象,有一个专门帮我们创建对象的类,这个类就是工厂。配置通过静态工厂方法创建的bean,实例工厂方法创建的bean,FactoryBean。
AirPlane ap=AirPlaneFactory.getAirPlane(String jzName);
静态工厂:工厂本身不需要创建对象;通过静态方法的调用,对象=工厂类.工厂方法名();
实例工厂:工厂本身创建对象;需要先创建一个工厂类的对象。
工厂类 工厂对象=new 工厂类();
工厂对象.getAirPlane(“张三”);
2.4.1 静态工厂
<!-- 1.静态工厂(不需要创建工厂本身)factory-method="getAirPlane" 指定那个方法是工厂方法。容器启动就创建
class:指定静态工厂的全类名
factory-method:指定工厂方法
constructor-arg:可以为方法传参。
-->
<bean id="airPlaneFactory" class="com.atguigu.factory.AirPlaneStaticFactory" factory-method="getAriPlane">
<constructor-arg name="jzName" value="张三"></constructor-arg>
</bean>
2.4.2 实例工厂
<!-- 2.实例工厂的工厂方法、容器启动就创建
先创建实例工厂对象。
创建AirPlane对象。
factory-bean:指定工厂bean
factory-method: 指定工厂方法
-->
<bean id="airPlaneFactory2" class="com.atguigu.factory.AirPlaneInstanceFactory">
</bean>
<bean id="airplane02" class="com.atguigu.bean.AirPlane" factory-bean="airPlaneFactory2" factory-method="getAriPlane">
<constructor-arg name="jzName" value="李四"></constructor-arg>
</bean>
2.4.3 实现FactoryBean接口
需要实现的方法
- getObject():spring调用,创建对象的。
- getObjectType():spring调用,返回类的类型
- isSingleton():是否是单例
使用:
- 创建类实现接口
- ioc容器中进行组件注册
<!-- FactoryBean:是Spring规定的一个接口,只要是这个接口的实现类,Spring都认为是一个工厂
实现创建对象的实际和单多实例没有关系,都是在创建实例的时候创建的。
-->
<bean id="myFactoryBeanImpl" class="com.atguigu.factory.MyFactoryBeanImpl"></bean>
2.5 注解的形式注册组件
分别创建Dao,Service,Controller
1 注解方式配置组件
<!-- 使用注解创建DAO,Service,Controller
通过给bean添加某些注解,可以快速的将bean加入到ioc容器中
存在四个注解:某个类上添加上任何一个注解,都可以将这个bean添加到ioc容器中
@Controller:推荐给控制器层组件添加这个注解。
@Service:推荐给业务逻辑层的组件添加这个注解。
@Repository:推荐给dao层的组件添加这个注解。
@Component:组件,给不属于以上几层的组件添加这个注解,ioc容器启动的时候就加载。
注意:Spring底层不会验证这个组件,我们推荐加各自的注解,是给我们自己看的。
注解方式:将组件加入到容器中去
1)给要添加的组件上添加注解
2)配置ioc自动扫描添加注解的组件,
导入context命名空间.
配置组件:component-scan
3)一定要导入aop包,支持添加注解模式的。
-->
<!-- base-package:扫描的基础包,会扫描该包即子包的所有加了注解的类。自动的扫描进ioc容器中
注意:最好稍微精确一些。否则可能将lib下的类也扫描到。
Caused by: java.lang.ClassNotFoundException: org.springframework.aop.TargetSource
使用配置和注解的方式扫描进ioc容器的行为默认都是一致的。
1)组件的id为类名首字母小写。
@Controller("bookServletTest"):此方式可以修改组件的id。
2)组件默认是单例的.
通过使用新的注解@Scope(value="prototype"):可以将该组件调整为多实例的
-->
<context:component-scan base-package="com.atguigu"></context:component-scan>
**思考:**为什么存在注解,还要学习配置bean的方式?
因为注解的方式仅仅适用于自己写的类,如果想要配置jar包中类,使用注解的方式是不现实的,因此我们可以使用配置bean的方式来实现。
2 排除哪些指定的组件
<context:component-scan base-package="com.atguigu">
<!-- context:exclude-filter指定扫描包时候不包含的类
排除一些不需要的组件。
type="annotation":指定排除的规则,按照注解进行排除,标注了指定注解的组件就不需要。
expression="org.springframework.stereotype.Controller":注解的全类名。
type="assignable":指定排除某个具体类。按照类进行排除.
-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="assignable" expression="com.atguigu.service.BookService"/>
</context:component-scan>
3 只要哪些组件
<context:component-scan base-package="com.atguigu" use-default-filters="false">
<!-- context:include-filter指定扫描包时候包含的类
只扫描哪些组件,默认全部扫描进来。
-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
注意:需要关闭默认的过滤器。
2.6 使用@Autowired实现DI
1 使用方式
在需要自动注入的属性上面添加该注解即可。
2 @Autowired原理:
- 先按照类型去容器中寻找相应的组件:bookSerice=ioc.getBean(BookService.class)。
- 如果找到一个就赋值;
- 如果没找到就抛出异常;
- 如果找到多个。就根据变量名为id继续在容器中进行寻找。
- 如果找到则进行装配。
- 如果没有找到则进行报错。
@Qualifier(“名称”):指定一个名称作为一个id。让Spring不使用变量名称作为id。如果依旧没找到,则会接着进行报错。
可以在AutoWired(“required=false”)设置如果找不到就装配null。
注意:任何情况下,Autowired标注的自动装配的属性都必须找到,否则会报错。
3 Autowired可以装配的位置
如果装配到方法上面:
- 这个方法回在bean创建的时候自动运行。
- 这个方法上的每一个参数都会自动注入值。
4 @Qualifier可以装配的位置
可以实现自动装配的注解:Autowired,Resource,Inject
5 Autowired和Resource的区别?
- @Autowired:最强大,是Spring自己的注解,@Resource:是j2ee,java的标准。
果找到一个就赋值;- 如果没找到就抛出异常;
- 如果找到多个。就根据变量名为id继续在容器中进行寻找。
- 如果找到则进行装配。
- 如果没有找到则进行报错。
@Qualifier(“名称”):指定一个名称作为一个id。让Spring不使用变量名称作为id。如果依旧没找到,则会接着进行报错。
可以在AutoWired(“required=false”)设置如果找不到就装配null。
注意:任何情况下,Autowired标注的自动装配的属性都必须找到,否则会报错。
3 Autowired可以装配的位置
[外链图片转存中…(img-LaXSrUe2-1603959314281)]
如果装配到方法上面:
- 这个方法回在bean创建的时候自动运行。
- 这个方法上的每一个参数都会自动注入值。
4 @Qualifier可以装配的位置
[外链图片转存中…(img-kkb5s53X-1603959314283)]
可以实现自动装配的注解:Autowired,Resource,Inject
5 Autowired和Resource的区别?
- @Autowired:最强大,是Spring自己的注解,@Resource:是j2ee,java的标准。
- @Resource是标准,是java的标准,扩展性更强;@Autowired是Spring自己的标准,只支持自己家的标准。