Spring 入门
1、Spring
1.1 Spring 简介
- 2002年,spring框架的前身出现:interface21框架
- 2004年,在基于interface21框架上进行重新开发设计 发行了初代版本的spring1.0框架
- Rod Johnson Spring Framework的创始人,著名作者 ;他是 悉尼大学 的 音乐学位博士 而居然不是计算机!
- Spring理念:使现有的技术更加容易使用
下载方式:
官网全版本地址:https://repo.spring.io/ui/native/release/org/springframework/spring/
maven方式:
maven导包推荐导 spring web mvc 的包
和
spring jdbc的包
maven导入这两个包后 会将我们需要的依赖包一起导入,就不用再一一去导入
1.2 Spring 优点
- Spring是一个 免费且开源的 框架!
- Spring是一个 轻量级、非入侵式 的框架 框架本身小,引入方便;使用Spring不会改变原来代码的情况
- 控制反转(IOC)、面向切面编程(AOP) 十分重要 面试必问
1.3 Spring 组成
Spring Core(核心容器):Spring框架底层 提供框架的基本功能 Spring使用BeanFactory来产生和管理Bean 也就是工厂模式的实现 BeanFactory使用控制反转(IoC)模式将应用的配置和依赖性规范与实际的应用程序代码分开
Spring Context(上下文):Spring Context是一个配置文件,向Spring框架提供上下文信息。Spring上下文包括企业服务,如JNDI、EJB、电子邮件、国际化、校验和调度功能
Spring AOP(AOP面向切面编程):Spring AOP 模块直接将面向方面的编程功能集成到了 Spring框架中。所以,可以很容易地使 Spring框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。
Spring DAO(JDBC模块和DAO模块):JDBC、DAO的抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理,和不同数据库供应商所抛出的错误信息。异常层次结构简化了错误处理,并且极大的降低了需要编写的代码数量,比如打开和关闭链接。
Spring ORM(对象实体映射):Spring框架插入了若干个ORM框架,从而提供了ORM对象的关系工具,其中包括了Hibernate、JDO和 IBatis SQL Map等,所有这些都遵从Spring的通用事物和DAO异常层次结构。
Spring Web(web模块):Web上下文模块建立在应用程序上下文模块之上,为基于web的应用程序提供了上下文。所以Spring框架支持与Struts集成,web模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
Spring Web MVC(MVC模块):MVC框架是一个全功能的构建Web应用程序的MVC实现。通过策略接口,MVC框架变成为高度可配置的。MVC容纳了大量视图技术,其中包括JSP、POI等,模型来有JavaBean来构成,存放于m当中,而视图是一个街口,负责实现模型,控制器表示逻辑代码,由c的事情。Spring框架的功能可以用在任何J2EE服务器当中,大多数功能也适用于不受管理的环境。
2、IOC(控制反转)
2.1 什么是IOC
控制反转的控制 就是程序控制权的意思,反转呢就是将控制权力反转、互换
比如:==一个程序员对程序有控制权,如果一个客户需要什么功能,程序员将程序修改好之后,提交给客户使用,这个控制权就是在程序员的手中==
==经过控制反转(IOC)后 ,客户可以根据自己的需要,来选择对应的功能或者模块,就不再需要程序员去进行修改程序,这个控制权就是在客户手中==
==这也就是所谓的 控制反转(IOC)==
2.2 实现方法实例
在代码中实现:
例如用户有需要普通数据查询和在Mysql中查询的两种数据源
Dao(Mapper)层:
UserMapper
package cn.mianju.dao;
public interface UserMapper {
void getUser();
}
UserMapperImpl
package cn.mianju.dao;
public class UserMapperImpl implements UserMapper{
@Override
public void getUser() {
System.out.println("This method in the UserMapperImpl");
}
}
UserMysqlMapperImpl
package cn.mianju.dao;
public class UserMysqlMapperImpl implements UserMapper {
@Override
public void getUser() {
System.out.println("This method in the User!Mysql!MapperImpl");
}
}
service层:
UserService
package cn.mianju.service;
public interface UserService {
void getUser();
}
*UserServiceImpl(主要的修改就在这部分)
package cn.mianju.service;
import cn.mianju.dao.UserMapper;
import cn.mianju.dao.UserMysqlMapperImpl;
public class UserServiceImpl implements UserService{
private UserMapper userMapper;
// private UserMapper userMapper = new UserMysqlMapperImpl();
public void setUser(UserMapper userMapper) {
this.userMapper = userMapper;
}
@Override
public void getUser() {
userMapper.getUser();
}
}
首先是设立一个Mapper(dao)的查询接口 UserMapper 然后有两种方式查询,一种是普通方式,一种是Mysql方式 普通方式为 UserMapperImpl Mysql方式为 UserMysqlMapperImpl ;然后在service层调用Mapper层的查询方法 接口类:UserService 实现类: UserServiceImpl
在做 控制反转 之前呢,正常来说是在service实现类**(UserServiceImpl)**中去赋值需要调用的方式、类
private UserMapper userMapper = new UserMysqlMapperImpl();
但是如果这样每次客户需要换一种方式的话就需要修改源代码,这就违反了OOP(开闭原则) 所以说,可以在service实现类**(UserServiceImpl)中去定义个setting** 使其可以在运行中的时候set注入 来让用户自由选择,用户需要什么就把它set成什么
调用的时候就需要把它先转换成子类再转换回来,这样才可以调用子类的set方法 idea快捷键:Ctrl+alt+空格 可以用子类方法
public class MyTest {
@Test
public void t(){
UserService userService = new UserServiceImpl();
// ((UserServiceImpl) userService).setUser(new UserMapperImpl());//This method in the UserMapperImpl
((UserServiceImpl) userService).setUser(new UserMysqlMapperImpl());//This method in the User!Mysql!MapperImpl
userService.getUser();
}
}
2.3 IOC总结
控制反转loC(Inversion of Control)
是一种设计思想,DI(依赖注入)是实现loC的一种方法,也有人认为DI只是loC的另一种说法。
没有loC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。
在Spring中实现控制反转的是loC容器,其实现方法是依赖注入(Dependency Injection,DI)
示例图: 原本的程序控制权是在业务层上,也就是代码上,但是经过控制反转后,控制权就移交到了用户手上,用户可以自行去调用所需,更灵活
3、Spring 的 "HelloWorld"
现在来写一个“第一段Spring代码”
3.1 创建项目 导入依赖包
新建一个Maven项目
修改pom.xml文件,导入对应的包:
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.17</version>
</dependency>
</dependencies>
3.2 编写配置文件和类
编写一个hello 实体类 设置一个属性:
package cn.mianju.pojo;
public class Hello {
private String str;
@Override
public String toString() {
return "Hello{" +
"str='" + str + '\'' +
'}';
}
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
}
然后在resources文件夹下新建一个xml配置文件(idea点击resources文件夹按alt+Ins可以生成)
编写配置文件(applicationContext.xml)
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 使用spring来创建对象 , 在spring中 这些对象都称为bean-->
<bean id="hello" class="cn.mianju.pojo.Hello">
<property name="str" value="Spring"/>
</bean>
</beans>
配置文件解析:
在原本的Java程序中:
//类型 变量名 = new 类型();
Hello hello = new Hello();
Spring的Bean:
<!--
id = 变量名
class = new的对象
property = 给对象中的一个属性赋值
在property中还有个值是 ref:引用Spring中创建好的对象 也就是引用配置文件中配置好的Bean类型
-->
<bean id="hello" class="cn.mianju.pojo.Hello">
<property name="str" value="Spring"/>
</bean>
3.3 测试
去创建一个测试类,在Test下
import cn.mianju.pojo.Hello;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
//获取spring的上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Hello hello = (Hello) context.getBean("hello");//这里的hello指向的就是applicationContext.xml中的id = hello
System.out.println(hello.getStr());
//Spring
}
}
ApplicationContext context = new ClassPathXmlApplicationContext("配置文件1","配置文件2");
这行语句是固定写法,可以读取一个配置文件也可以读取多个配置文件
Hello hello = (Hello) context.getBean("hello");
然后get到他们的Bean,在Spring中不需要声明对象,Bean就是Spring的对象
getBean的参数在Spring的配置文件中是需要注册的,参数就是Bean标签的id
System.out.println(hello.getStr());
调用Bean中的方法
3.4 小结
- **问题1:**Hello对象是谁创建的?
- Hello对象是由Spring创建的
- **问题2:**Hello对象的属性是怎么设置的?
- Hello对象的属性是由Spring容器设置的,也就是在 applicationContext.xml 中设置的
- **问题3:**Bean是什么,它的作用是什么?
- Bean是由Spring创建的一个对象;开始是程序员或是说程序创建的对象,也就是new一个对象;但是经过Spring的 控制反转(Ioc)机制后,对象的创建全交由Spring来做
这个过程就叫做控制反转:
- 控制:谁来控制对象的创建,传统应用是程序员/程序来控制对象的创建;而Spring对象是统一交由Spring创建
- 反转:程序本身不创建对象,而变成被动的接受对象
- 依赖注入:利用Set方法来进行注入
IOC是一种编程思想,由主动的编程变成被动的接收!
4、Spring的对象创建方式
4.1 对象创建时间
在spring框架中, 我们没有用到任何的new关键字来new对象,都是通过spring来帮我们创建的,那么spring是什么时候开始创建对象的呢?
做个测试:声明一个pojo user类
package cn.mianju.pojo;
public class User {
private String name;
public User() {
System.out.println("no args");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
当对象创建时 就会调用它的无参构造器
到applicationContext.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="cn.mianju.pojo.User">
<property name="name" value="xf"/>
</bean>
</beans>
然后写一个MyTest类来测试:
import cn.mianju.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
public static void main(String[] args) {
//加载配置文件的时候就已经把配置文件中的类创建好了
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//no args
// User user = (User) context.getBean("user");
}
}
运行一遍我们可以看到,在加载配置文件的时候,就已经把配置文件中的类创建完成了
4.2 有参构造
那如果想要有参构造器的类去创建,用上面的方法会出现报错
Caused by: java.lang.NoSuchMethodException: cn.mianju.pojo.User.<init>()
也就是初始化失败,需要参数
这个时候怎么传参给构造器呢
有三种方法:
第一种:
<?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="cn.mianju.pojo.User">
<!-- <property name="name" value="xf"/>-->
<constructor-arg index="0" value="xf"/>
<!--通过下标位置来传入参数-->
</bean>
</beans>
第二种: 这种方式不建议使用
<?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="cn.mianju.pojo.User">
<!-- <property name="name" value="xf"/>-->
<!-- <constructor-arg index="0" value="xf"/>-->
<constructor-arg type="java.lang.String" value="xf"/>
<!--通过类型匹配来传入参数 tips:基本类型可以直接用 但是引用类型必须用全类名-->
</bean>
</beans>
第三种:
<?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="cn.mianju.pojo.User">
<!-- <property name="name" value="xf"/>-->
<!-- <constructor-arg index="0" value="xf"/>-->
<!-- <constructor-arg type="java.lang.String" value="xf"/>-->
<constructor-arg name="name" value="xf"/>
<!--根据参数名来传入参数-->
</bean>
</beans>
但是如果要传入引用类型就需要用到ref
<?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="cn.mianju.pojo.User">
<constructor-arg name="name" value="xf"/>
<constructor-arg name="teacher" ref="teacher"/>
</bean>
<bean id="teacher" class="cn.mianju.pojo.Teacher">
<constructor-arg name="name" value="myTeacher"/>
</bean>
<!--如果需要传入引用类型 需要先声明一个bean 然后传入这个bean的id-->
</beans>
首先 需要传入的类型如果是引用类型(非String)
那么我们需要先声明一个Bean来表述这个类或者说创建这个类
然后将这个Bean的id 传入ref的标签中<constructor-arg name="teacher" ref="teacher"/>
这样就可以传入一个引用类型