1. Spring
spring内容包含IOC、AOP理解、IOC的各种实现方式、Bean声明周期解析、懒加载、两种事务解析等等
IOC:控制反转,把创建对象的过程交给Spring进行管理。IOC创建对象交给容器,通过容器去获取对象, 注解方式创建对象 IOC(通过spring的ioc容器,将对象间的依赖关系交由Spring进⾏控制,方便解耦,简化开发)
Aop:面向切面,不能修改源代码进行功能增强
Spring 的核心容器是其他模块建立的基础,由 Beans 模块、Core 核心模块、Context 上下文模块和 Expression Language 表达式语言模块组成,具体介绍如下。
Beans 模块:提供了 BeanFactory,是工厂模式的经典实现,Spring 将管理对象称为 Bean。
Core 核心模块:提供了 Spring 框架的基本组成部分,包括 IoC 和 DI 功能。
Context 上下文模块:建立在核心和 Beans 模块的基础之上,它是访问定义和配置任何对象的媒介。ApplicationContext 接口是上下文模块的焦点。
Expression Language 模块:是运行时查询和操作对象图的强大的表达式语言
1.1 简介
官网:https://spring.io/projects/spring-framework#overview
官方下载地址:https://docs.spring.io/spring-framework/docs/4.3.9.RELEASE/spring-framework-reference/
GitHub:https://github.com/spring-projects/spring-framework
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.8</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.8</version>
</dependency>
- Spring 是一个开源免费的框架(容器)
- Spring是一个轻量级的、非入侵式的框架
- 控制反转(IOC)、面向切面编程(AOP)
- 支持事务处理,对框架整合的支持
总结:Spring就是一个轻量级的控制反转(IOC)和面向切面(AOP)编程的框架!
1.2 Spring组成
1、核心容器(Spring Core)
核心容器提供Spring框架的基础功能。Spring以bean的方式进行java应用的各大组件及关系的组织和管理。Spring使用BeanFactory来产生和管理bean,是工厂模式的实现。BeanFactory使用控制反转(IOC)模式来将应用的配置和依赖性规范与实际的应用程序代码分开。
2、应用上下文(Spring Context)
实现了ApplicationContext接口,Spring的上下文,拓展了核心容器,提供事件处理、国际化等功能。
3、Spring面向切面编程(Spring AOP)
提供切面支持,是个轻量级的容器。Spring管理的任何对象都支持AOP,SpringAOP模块基于Spring的应用程序中的对象提供了事务管理服务,通过使用SpringAOP,就可以将声明性事务管理集成在应用程序中。
4、JDBC和DAO模块(Spring DAO)
提供对JDBC的支持,还提供了DAO的支持,提供事务支持。
JDBC、DAO的抽象层,提供了有意义的异常层次结构实现,可用该结构来管理异常处理,和不同数据库提供商抛出的错误信息,异常层次结构简化了错误处理,并且极大的降低了需要编写的代码数量,比如打开和关闭链接。
5、对象实体映射(Spring ORM)
ORM:Object Relational Mapping,指对象实体映射。Spring插入了若干个ORM框架,提供了ORM对象的关系工具,其中包括Hibernate,JDO和IBatisSQL Map等,所有这些都遵从Spring的通用事务和DAO异常层次结构。
6、Web模块(Spring Web)
拓展了Spring上下文,提供Web应用上下文,对Web开发提供功能上的支持,如请求、表单、异常等。
7、MVC模块(SpringWebMVC)
MVC框架是一个全功能的构建Web应用程序的MVC实现,通过策略接口,MVC框架编程高度可配置的,MVC容纳了大量视图技术,其中包括JSP,POI等,模型由JavaBean来构成,存放于m当中,而视图是一个接口,负责实现模型,控制器表示逻辑代码,由c的事情。
2. IOC理论推导
测试 须在业务层调用DAO层
如果多了一个dao实现类
调用该方法只能修改UerServiceImpl
因为需求的改变,程序需要每次都进行对应的修改
/**
* private UserDao userDao = new UserDaoImpl();
* 耦合性高
* 因为用户的需求发生改变,需要修改代码 程序适应不了用户的变化
*
* 基类UserDao有多个实现
* 用户使用不同的方法调用(例如默认方法、mysql方法、Oracle方法调用)
* 此时userDao指向的派生类需要发生改变, 才能调用对应的方法
**/
/**
* 设计思想:程序不动,让客户端去做一些事情
*/
private UserDao userDao;
//利用set进行动态实现值的注入!
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
-
之前 程序是主动创建对象!控制权在程序员手上!
-
使用了set注入,程序不再具有主动性,而变成了被动的接受对象!
这种思想,从本质上解决了问题,程序员不用再去管理对象的创建,系统的耦合性大大降低,可以更加专注的在业务的实现上,这是IOC的原型!
2.1 IOC本质
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方。控制反转即获得依赖对象的方式反转了。
Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到零配置的目的
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转时IoC容器,其实现方法时依赖注入(Dependency Injection, DI)
2.2 Hello程序
-
Hello实体类
package com.wlf.pojo; public class Hello { private String str; // public Hello(String str) { // this.str = str; // } public String getStr() { return str; } public void setStr(String str) { this.str = str; } @Override public String toString() { return "Hello{" + "str='" + str + '\'' + '}'; } }
-
beans.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="helloa" class="com.wlf.pojo.Hello"> <!-- 利用类中的set方法来进行依赖注入--> <property name="str" value="Spring"/> </bean> </beans>
-
MyTest测试类
import com.wlf.pojo.Hello; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /* 控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建(new)的,使用Spring后,对象由Spring创建 反转:程序本身不创建对象,而变成被动的接收对象。 依赖注入:利用set方法来进行注入 * */ public class MyTest { public static void main(String[] args) { //获取spring的上下文对象! ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); //对象都在Spring中管理, 使用时可以直接去里面取出来 Hello hl = (Hello) context.getBean("helloa"); System.out.println(hl.toString()); } }
- Hello对象是谁创建的? 对象由Spring创建
- Hello对象的属性是怎么设置的? 属性由Spring容器设置
这个过程就叫控制反转:
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建(new)的,使用Spring后,对象由Spring创建
反转:程序本身不创建对象,而变成被动的接收对象。
依赖注入:利用set方法来进行注入
IoC是一种编程思想,由主动的编程变成被动的接收,要实现不同的操作,只需要在xml配置文件中进行修改,所谓的IoC就是:对象由Spring来创建、管理、装配!
IoC 最常见以及最合理的实现方式叫做依赖注入(Dependency Injection,简称 DI)。
2.3 IOC创建对象的方式
User.java 实体类[com.wlf.pojo.User]
public class User {
private String name;
public User() {
System.out.println("使用无参构造方式 初始化User\n");
this.name = "无参构造:wlf";
}
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
....
}
MyTest.java 测试类
public class MyTest {
public static void main(String[] args) {
/**
* 使用ClassPathXmlApplicationContext加载XML配置
* context 获取Spring的上下文对象
*/
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
/**
* IoC创建对象
* 无参构造 user
* 有参构造 userParam
*/
//无参构造
User user = (User) context.getBean("user");
System.out.println(user.getName());
//有参构造
User userParam = (User) context.getBean("userParam");
System.out.println(userParam.getName());
}
}
-
使用无参构造创建对象。默认!
beans.xml
<bean id="user" class="com.wlf.pojo.User"></bean>
-
使用有参构造创造对象
-
第一种,下标赋值!
<bean id="userParam" class="com.wlf.pojo.User"> <!--第一种:下标赋值!--> <constructor-arg index="0" value="下标赋值:wlf"></constructor-arg> </bean>
-
第二种,类型匹配创建,不建议使用!
<bean id="userParam" class="com.wlf.pojo.User"> <!--第二种:类型匹配赋值,不建议使用!--> <constructor-arg type="java.lang.String" value="类型匹配:wlf"></constructor-arg> </bean>
-
第三种,直接通过参数名来设置
<bean id="userParam" class="com.wlf.pojo.User"> <!--第三种:直接通过参数名来设置--> <constructor-arg name="name" value="参数名赋值:wlf"></constructor-arg> </bean>
总结:在配置文件加载的时候,容器中管理的对象就已经初始化了!
-
3. Spring配置
3.1 alias 别名
user2是容器中user的别名
<alias name="user" alias="user2"/>
3.2 beans 配置
<!--
id: bean的唯一标识符,相当于对象名
class: bean对象对应的全限定名(包名+类名)
name: 别名 可以取多个别名 可以用空格、逗号、分号等分割
-->
<bean id="user" class="com.wlf.pojo.User" name="user2,u2"></bean>
3.3 import
一般用于团队开发使用,他可以将多个配置文件,导入合并为一个。
项目中会有多个人开发,bean1.xml bean2.xml…,不同的类需要注册不同的bean中,可以利用import将所有的bean.xml合并为一个总的! 使用的时候直接使用总的配置
applicationContext.xml
<import resource="bean1.xml"/>
<import resource="bean2.xml"/>
.....
4 依赖注入
4.1 Set方式注入【重点】
- 依赖注入:Set注入!
- 依赖:bean对象的创建依赖于容器!
- 注入:bean对象中的所有属性,由容器注入!
【环境搭建】
-
复杂类型
public class Address { private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
-
真实测试对象
public class Person { private String name; private Address address; private String[] books; private List<String> hobbies; private Map<String,String > card; private Set<String> games; private String child; private Properties info; //.......set and get methods }
-
beans.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"> <!-- set方式注入 --> <bean id="address" class="com.wlf.pojo.Address"> <property name="address" value="南昌市"/> </bean> <bean id="person" class="com.wlf.pojo.Person"> <!--第一种,普通值(String name)注入, value--> <property name="name" value="wlf"/> <!--第二种,自定义类(Address address) Bean注入,ref 输出格式:address=Address{address='南昌市'} --> <property name="address" ref="address"/> <!--第三种,数组(String[] books)注入 输出格式:books=[红楼梦, 三国演义, 西游记] --> <property name="books"> <array> <value>红楼梦</value> <value>三国演义</value> <value>西游记</value> </array> </property> <!--第四种,list(List<String> hobbies)注入 输出格式:hobbies=[听歌, 看书, 写字] --> <property name="hobbies"> <list> <value>听歌</value> <value>看书</value> <value>写字</value> </list> </property> <!--第五种,map(Map<String,String > card)注入 输出格式:card={身份证=123344, 银行卡=2344555} --> <property name="card"> <map> <entry key="身份证" value="123344"> </entry> <entry key="银行卡" value="2344555"> </entry> </map> </property> <!--第六种,集合set(Set<String> games)注入 输出格式:games=[LOL, BOB] --> <property name="games"> <set> <value>LOL</value> <value>BOB</value> </set> </property> <!--第七种,null空值注入 输出格式:child='null' --> <property name="child"> <null/> </property> <!--第八种,Properties(Properties info)注入 输出格式:info={password=12345, username=root} --> <property name="info"> <props> <prop key="username">root</prop> <prop key="password">12345</prop> </props> </property> </bean> </beans>
-
测试类
public class MyTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
System.out.println("\n=========================Set方法注入=======================\n");
Person person = (Person) context.getBean("person");
System.out.println(person.toString());
}
}
4.2 构造器注入
4.3 拓展方式注入
<!-- p命名空间和c命名空间注入 -->
<beans
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
....
>
<!-- p命名空间注入,可以直接注入属性的值:property -->
<bean id="user" class="com.wlf.pojo.User" p:name="wlf"/>
<!-- c命名空间注入,通过构造器注入:construct-args -->
<bean id="user" class="com.wlf.pojo.User" c:name="wlf"/>
</beans>
4.4 Bean作用域
-
单例模式(Spring默认机制 id与该bean定义相匹配,则只会返回bean的同一实例)
在SpringIoC容器中仅存在一个Bean实例,Bean以单例方式存在,默认值
对象创建时间:在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象
<bean id="user" class="com.wlf.pojo.User" p:name="wlf" scope="singleton"/>
-
原型模式(每次从容器中get的时候,都会产生一个新对象!一个bean定义对应多个对象实例)
每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行了new XXXBean()
对象创建时间:在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。
<bean id="user" class="com.wlf.pojo.User" p:name="wlf" scope="prototype"/>
-
其余的request、session、application这些会在web开发中使用到!
# 5. Bean的自动装配
- 自动装配是Spring满足bean依赖的一种方式
- Spring会在上下文中自动寻找,并自动给bean装配属性!
在Spring中有三种装配的方式
public class Person {
private Cat cat;
private Dog dog;
private String name;
....
}
5.1 XML手动配置
-
在xml中显示的配置
<!--方式一:xml显示配置--> <bean id="cat" class="com.wlf.pojo.Cat"/> <bean id="dog" class="com.wlf.pojo.Dog"/> <bean id="person" class="com.wlf.pojo.Person"> <property name="name" value="wlf"/> <!--Person类中含基类cat对象 private Cat cat; name是变量名 ref是赋值参数--> <property name="cat" ref="cat"/> <property name="dog" ref="dog"/> </bean>
-
在java中显示配置
-
隐式的自动装配bean【重要】 ByName/ByType自动装配
<bean id="Cat" class="com.wlf.pojo.Cat"/> <bean id="dog" class="com.wlf.pojo.Dog"/> <!--方式二:隐式自动装配--> <!-- public void setCat(Cat cat){} byName:会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanID(首字母要小写)! byType:会自动在容器上下文中查找,和自己对象属性类型相同的bean! --> <bean id="person" class="com.wlf.pojo.Person" autowire="byName"> <property name="name" value="wlf"/> </bean>
小结:
- ByName 需要保证所有bean的id唯一,并且这个bean需要 和 自动注入的属性的set方法的值一致!!
- ByType 需要保证所有bean的class唯一,并且这个bean需要 和 自动注入的属性的类型一致!
5.2 使用注解实现自动装配
要使用注解须知:
-
导入约束: context约束
<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:annotation-config/ > 【重要】
@Autowired 直接在属性上(也可在set方法上)使用即可! 类中也可不添加set方法
@Resource 根据名字(ByName)或者类型(ByType)来匹配
@Resource(name=“XXX”)指定唯一的bean
@Nullable 若字段标记了这个注解,说明这个字段可以为null public Person(@Nullable String name) { this.name = name; }
public @interface Autowired { boolean required() default true; }
测试
//xml <bean id="cat" class="com.wlf.pojo.Cat"/> <bean id="dog" class="com.wlf.pojo.Dog"/> <bean id="person" class="com.wlf.pojo.Person"/>
public class Person{
//如果显示定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为空
@Autowired(required = false)
private Cat cat;
/**
*若@Autowired自动装配的环境比较复杂,可以使用@Qualifier(value="XXX")指定唯一的bean对象
**/
@Autowired
@Qualifier(value="dog")
private Dog dog;
private String name;
}
小结:
@Resource和@Autowired的区别:
- 都是用来自动装配的,都可以放在属性字段上
- @Autowired 默认通过ByType方式实现 配合@Qualified(value=“XXX”)使用
- @Resource (name=“XXX”) 默认通过ByName寻找,如果找不到名字就通过ByType
6. 使用注解开发
在Spring4之后,要使用注解开发,必须要保证aop的包导入了
使用注解需要导入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:annotation-config/>
<!--指定要扫描的包,这个包下面的注解就会生效-->
<context:component-scan base-package="com.wlf.pojo"/>
</beans>
-
注册bean
-
属性如何注入
@Component //等价于new了一个对象 <bean class="com.wlf.pojo.User" id="user"></bean> public class User { //相当于 <property name="name" value="wlf"/> @Value("wlf") private String name; //或者注解在set方法上面 @Value("wlf") public void setName(String name) { this.name = name; } }
-
衍生的注解
@Component有几个衍生注解,我们在web开发中,会按照mvc三层架构分层
-
dao【@Repository】
-
service【@Service】
-
controller【@Controller】
这四个注解功能都是一样的,都代表将某个类注册到Spring容器中,装配bean
标注在一个类上,作用是将被标注的类注册在Spring容器中,将类的实例化交给Spring管理,完成的是bean的注册
**@Autowired是标注在类中的成员变量或方法上,完成的是bean的注入。**当一个类Class A中需要一个B类型的变量时,在声明变量时加上这个注解,Spring会在容器中寻找有没有。
-
-
自动装配
@Autowired:自动装配通过类型、名字匹配 如果Autowired不能唯一自动装配上,则需要通过@Qualifier(value="xxx") @Resource:自动装配通过名字和类型匹配
-
作用域
@Component //等价于new了一个对象 <bean class="com.wlf.pojo.User" id="user"></bean> @Scope("prototype") public class User { .... }
总结:
- xml更加万能,适用于任何场合!维护简单方便
- 注解 不是自己类使用不了,维护相对复杂
xml于注解最佳实践:
-
xml来管理bean
-
注解只负责完成属性的注入
-
我们在使用的过程中,只需要注意一个问题:必须让注解生效,就需要开启注解支持
<!--指定要扫描的包,这个包下的注解就会生效--> <context:component-scan base-package="com.wlf.pojo"/> <!--注解驱动--> <context:annotation-config/>
# 7. 使用Java的方式配置Spring
完全不使用Spring的xml配置,全权交给Java来做。JavaConfig是Spring的一个子项目,在Spring4之后,它成为了一个核心功能。
package com.wlf.config;
import com.wlf.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/*
* 1 如果开启包扫描,加载配置类以后就可以通过反射拿到配置类中的对象
* 2. @Bean只写在方法上,返回的是一个对象,但一般不获得已经在容器中的对象
* */
@Configuration
@ComponentScan("com.wlf.pojo")
public class wlfConfig {
/*注册一个bean,就相当于之前写的bean标签
这个方法的名字,相当于bean标签中的id属性
这个方法的返回值,相当于bean标签中的class属性
* */
@Bean
public User getUser(){
return new User(); //就是返回要注入到bean的对象!
}
}
@Test
public void test(){
ApplicationContext context = new AnnotationConfigApplicationContext(wlfConfig.class);
User user = (User) context.getBean("getUser");
System.out.println(user.getName());
}