Spring和SpringMvc(一)

目录

一:简单介绍Spring:

二:spring家族:

三:spring的核心技术之一:控制反转(IoC)

(一):目前项目中的问题:

(二)Bean,DI,IOC,IOC容器的概念

(三):案例基础实现IOC和DI

(四):bean实例化:

(五):bean的生命周期:

四:依赖注入DI


参考

Spring框架教程icon-default.png?t=N7T8https://blog.youkuaiyun.com/One_L_Star/article/details/100924963

Spring框架icon-default.png?t=N7T8https://www.w3cschool.cn/wkspring/

Spring框架 · 语雀课程目标理解Spring框架的两大核心掌握Spring...icon-default.png?t=N7T8https://www.yuque.com/neuedu/campus/zkd7cg

一:简单介绍Spring:

Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。Spring的核心是控制反转(IoC)和面向切面(AOP)

其中最核心的就是ioc和aop技术

二:spring家族:

官网: https://spring.io ,从官网我们可以大概了解到:
Spring 能做什么 : 用以开发 web 、微服务以及分布式系统等 , 光这三块就已经占了 JavaEE 开发
的九成多。
Spring 并不是单一的一个技术,而是一个大家族,可以从官网的 Projects 中查看其包含的所
有技术。
Spring 发展到今天已经形成了一种开发的生态圈 ,Spring 提供了若干个项目 , 每个项目用于完成
特定的功能。
Spring 已形成了完整的生态圈,也就是说我们可以完全使用 Spring 技术完成整个项目的构
建、设计与开发。
总而言之,就是spring可以做很多很多事情。
Spring Framework:Spring 框架,是 Spring 中最早最核心的技术,也是所有其他技术的
基础。
SpringBoot:Spring 是来简化开发,而 SpringBoot 是来帮助 Spring 在简化的基础上能
更快速进行开发。
SpringCloud: 这个是用来做分布式之微服务架构的相关开发。

(1) 核心层:
Core Container: 核心容器,这个模块是 Spring 最核心的模块,其他的都需要依赖该模块
(2)AOP 层:
AOP: 面向切面编程,它依赖核心层容器,目的是 在不改变原有代码的前提下对其进行功能增强
Aspects:AOP 是思想 ,Aspects 是对 AOP 思想的具体实现
(3) 数据层
Data Access: 数据访问, Spring 全家桶中有对数据访问的具体实现技术 Data Integration: 数据集成, Spring 支持整合其他的数据层解决方案,比如 Mybatis
Transactions: 事务, Spring 中事务管理是 Spring AOP 的一个具体实现,也是后期学习的
重点内容
(4)Web
这一层的内容将在 SpringMVC 框架具体学习
(5)Test
Spring 主要整合了 Junit 来完成单元测试和集成测试。

三:spring的核心技术之一:控制反转(IoC)

(一):目前项目中的问题:

            当前项目中会出现耦合度很高的代码,而代码最好的实现就是高内聚,低耦合。耦合度降低,使得代码效率安全更好。

   比如:现在的dao层需要对应的impl ,需要new对象,回调的时候,需要一一对应,这样就闲的代码耦合度很高,如果实现层方面改变了,就需要重新new一个对象。

而spring就解决了这样的问题,使用对象的时候,在程序中不要主动使用new产生对象,转换为由外部提供对象,这时候就引出来了ioc的概念。

(二)Bean,DI,IOC,IOC容器的概念

 IOC(Inversion of Control):控制反转,就是说之前需要new产生一个对象,现在不需要了,直接由外部提供的对象,就可以直接实现,这种思想就叫做控制反转。

IOC容器:对于那个外部,Spring就提供了一个容器,就是IOC容器,就是充当了IOC思想中的外部。IOC容器就是负责对象的创建,初始化等一系列工作,其中包含了各种类的对象,而这些类的对象,创建的或者被管理的对象,就成为Bean。IOC容器中放的就是一个个bean对象,需要用的时候,就可以直接提供出来。

DI(Dependency Injection):依赖注入,在容器中,建立了bean与bean之间的关系,就是注入依赖。比如说service的业务层就要和dao层的数据操作层建立起来依赖关系。

总结一下:

IOC: 控制反转,控制反转的是对象的创建权
DI: 依赖注入,绑定对象与对象之间的依赖关系
Spring 创建了一个容器用来存放所创建的对象,这个容器就叫 IOC 容器

容器中所存放的一个个对象就叫BeanBean对象

(三):案例基础实现IOC和DI

1.需要导入依赖:

<dependencies>
        <dependency>
        <groupId> org.springframework </groupId>
        <artifactId> spring-context </artifactId>
        <version> 5.2.10.RELEASE </version>
        </dependency>
        <dependency>
        <groupId> junit </groupId>
        <artifactId> junit </artifactId>
        <version> 4.12 </version>
        <scope> test </scope>
        </dependency>
</dependencies>

2.配置好文件:

在resource下面创建spring Config的xml

提醒一下,需要先写了依赖才能出来spring config。

<?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 标签标示配置 bean
id 属性标示给 bean起名字
class 属性表示给 bean 定义类型
-->
<bean id = "bookDao" class = "com.blue.dao.impl.BookDaoImpl" />
<bean id = "bookService" class = "com.blue.service.impl.BookServiceImpl" />
</beans>

其中需要特别主义的就是,那个class后面的全路径报名,不能对应接口,因为很简单,接口不能创建对象,因为这个bean其实替代的就是一个对象。

3获取IOC容器以及执行相应的对象:

public static void main ( String [] args ) {
// 获取 IOC 容器
ApplicationContext ctx = new ClassPathXmlApplicationContext ( "applicationContext.xml" );
}

其中那个后面的applicationContext.xml 就填你自己的springconfig 的配置文件的名字,一一对应就好。

public static void main(String[] args) {
//获取IOC容器
ApplicationContext ctx = new
ClassPathXmlApplicationContext("applicationContext.xml");
// BookDao bookDao = (BookDao) ctx.getBean("bookDao");
// bookDao.method();
BookService bookService = (BookService) ctx.getBean("bookService");
bookService.method();
}

这样就不会出现了new对象的情况,耦合度降低了。

但是这时候会发现,在service中还存在着new daoimpl的操作,这时候就需要了依赖注入。

将一些东西在配置文件之中就对应好关系,这样就会进一步降低了耦合度。

4简单应用DI依赖注入:

public class BookServiceImpl implements BookService {
//删除业务层中使用new的方式创建的dao对象
private BookDao bookDao;
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
//提供对应的set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}

配置文件改为这样: 

<bean id="bookDao" class="com.blue.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.blue.service.impl.BookServiceImpl">
    <!--配置server与dao的关系-->
    <!--property标签表示配置当前bean的属性
    name属性表示配置哪一个具体的属性
    ref属性表示参照哪一个bean
    -->
    <property name="bookDao" ref="bookDao"/>
</bean>

详细说明一下这里的 name里面的 bookDao 就是service里面通过set方法找到的对应的 bookDao

而ref就是对应的这个配置文件中的某一个具体已经对应好的bean对象。这样就使得两者之间建立了详细的联系。

总结一下几个注意事项:

1.NoSuchBeanDefinitionException 说明那个bean在配置文件里面没有。或者你名字写错了。

2.bean对象  除了可以直接用id获取,name同样可以获取,name就是别名,可以添加很多个值。id必须是唯一的,bean与bean之间的id尽量还是不要重复。

3scope:可以控制bean对象的创建是否是单例的,

<bean id="bookDao" name="dao" class="com.blue.dao.impl.BookDaoImpl"
scope="prototype"/>

其中singleton默认是单例的,protoype就是非单例的。

单例的好处:

bean 为单例的意思是在 Spring IOC 容器中只会有该类的一个对象
bean 对象只有一个就避免了对象的频繁创建与销毁,达到了 bean 对象的复用,性能高
非单例好处:
如果对象是有状态对象,即该对象有成员变量可以用来存储数据的,
因为所有请求线程共用一个 bean 对象,所以会存在线程安全问题。
如果对象是无状态对象,即该对象没有成员变量没有进行数据存储的,
因方法中的局部变量在方法调用完成后会被销毁,所以不会存在线程安全问题。

4.

java.lang.NoSuchMethodException: 抛出的异常为 没有这样的方法异常
5.
BeanInstantiationException: 翻译为 bean 实例化异常

(四):bean实例化:

提供三种方法:构造方法,静态工厂,实例工厂

(1)构造方法:这种方法就是什么都不要干,静静的写一个配置文件就好了

public interface BookDao {
public void save();
}
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
}
<bean id="bookDao" class="com.blue.dao.impl.BookDaoImpl"/>
public class AppForInstanceBook {
public static void main(String[] args) {
ApplicationContext ctx = new
ClassPathXmlApplicationContext("applicationContext.xml");
BookDao bookDao = (BookDao) ctx.getBean("bookDao");
bookDao.save();
}
}

此时就是一个非常简单的无参构造方法进行了创建bean,也是之前开始案例使用的

public class BookDaoImpl implements BookDao {
public BookDaoImpl() {
System.out.println("book dao constructor is running ....");
}
public void save() {
System.out.println("book dao save ...");
}
}

这样可以检测出来使用通过的无参构造方法 创建的bean对象。

讲无参构造方法的public改成private,能执行成功 , 说明内部走的依然是构造函数 , 能访问到类中的私有构造方法 , 显而易见
Spring 底层用的是反射

(2)静态工厂:

与之前不同的就是 在创建一个新的类:DaoFactory并提供一个静态方法。

所以叫做静态工厂。

//静态工厂创建对象
public class DaoFactory {
    public static Dao getDao(){
    return new DaoImpl();
}
}

配置文件加上 

<bean id="orderDao" class="com.blue.factory.OrderDaoFactory" factory-method="getOrderDao"/>
class: 工厂类的类全名
factory-mehod: 具体工厂类中创建对象的方法名

其实看似这个静态工厂没啥太大用处,就是多了一个类,在获得这个dao层的对象,但是换个角度想,既然又创建了这个类,那么是不是就可以在返回这个对象之前进行一些操作,比如初始化一些东西,加载一些东西。

//静态工厂创建对象
public class DaoFactory {
    public static Dao getDao(){
    System.out.println("Hello nihaoakeke");
    return new DaoImpl();
}
}

这样的话就是在执行getdao最后返回值之前,会输出一句话:就是 Hello nihaoakek。

这就是单独创建一个工厂类的好处。

(3)实例工厂:

就是改成public,相比较static静态不能之前加载,需要提前创建一个bean,然后进行对应。

public class UserDaoFactory {
public UserDao getUserDao(){
return new UserDaoImpl();
}
}

所以配置文件:需要提前有一个userFactory的bean

<bean id = "userFactory" class = "com.blue.factory.UserDaoFactory" />
<bean id = "userDao" factory-method = "getUserDao" factory-bean = "userFactory" />

到这里,实例化工厂的方式就已经介绍完了,配置的过程还是比较复杂,所以spring为了简化这种配置方式就提供了一种叫做factorybean的方式来简化。

(4)factortybean的使用:

就是直接创建一个userdaofactorybean的类,实现factorybean接口,重写接口方法

public class UserDaoFactoryBean implements FactoryBean<UserDao> {
//代替原始实例工厂中创建对象的方法
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
//返回所创建类的Class对象
public Class<?> getObjectType() {
return UserDao.class;
}
}
方法一 :getObject() ,被重写后,在方法中进行对象的创建并返回
方法二 :getObjectType(), 被重写后,主要返回的是被创建类的 Class 对象

在配置文件中:

<bean id = "userDao" class = "com.blue.factory.UserDaoFactoryBean" />

其中

default boolean isSingleton () {
  return true ;
  }

这个方法是处理 是否单例化的。

总结一下:

在这三种方法中,重点需要掌握的是构造方法和factorybean就可以了。

需要注意的一点是,构造方法在类中默认会提供,但是如果重写了构造方法,默认的就会消失,在使
用的过程中需要注意,如果需要重写构造方法,最好把默认的构造方法也重写下。

(五):bean的生命周期:


就是从创建到消亡的完整过程。

public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
//表示bean初始化对应的操作
public void init(){
System.out.println("init...");
}
//表示bean销毁前对应的操作
public void destory(){
System.out.println("destory...");
}
}

配置文件:

<bean id = "bookDao" class = "com.blue.dao.impl.BookDaoImpl" init-method = "init"
destroy-method = "destory" />

这时候会发现这里的destory方法没有执行,因为在执行完了main方法之后,jvm已经关闭,destory方法还没有来的及就以己经关闭了。

所以可以用:

ClassPathXmlApplicationContext ctx = new
ClassPathXmlApplicationContext ( "applicationContext.xml" );
ctx.close();//关闭
//ctx.registerShutdownHook();  也可以
相同点 : 这两种都能用来关闭容器
不同点 :close() 是在调用的时候关闭, registerShutdownHook() 是在 JVM 退出前调用关闭

其实,这里的方法实现起来会很复杂,为了简化,又出现了新的方式:

public class BookServiceImpl implements BookService, InitializingBean,
	       DisposableBean {
		private BookDao bookDao;
		public void setBookDao(BookDao bookDao) {
			this.bookDao = bookDao;
		}

		public void save() {
			System.out.println("book service save ...");
			bookDao.save();
		}
		public void destroy() throws Exception {
			System.out.println("service destroy");
		}
		public void afterPropertiesSet() throws Exception {
			System.out.println("service init");
		}
}

其中注意到setBookDao先执行,后执行init(); 如果在setBookDao中有东西,则会先执行。

四:依赖注入DI

Spring ,为我们提供了两种注入方式,分别是 :
setter 注入:
简单类型
引用类型
构造器注入:
简单类型
引用类型

(一)setter注入:

public interface BookDao {
	 public void save();
	
}

public class BookDaoImpl implements BookDao {
		public void save() {
			System.out.println("book dao save ...");
		}
}
public interface UserDao {
	public void save();
}
public class UserDaoImpl implements UserDao {
		public void save() {
			System.out.println("user dao save ...");
		}
}

public interface BookService {
	public void save();
}

public class BookServiceImpl implements BookService {
		private BookDao bookDao;
		private UserDao userDao;
		public void setUserDao(UserDao userDao) {
			this.userDao = userDao;
		}
		public void setBookDao(BookDao bookDao) {
			this.bookDao = bookDao;
		}
        public void save() {
	        System.out.println("book service save ...");
	        bookDao.save();
	        userDao.save();
}
}
public class AppForDISet {
		public static void main( String[] args ) {
			ApplicationContext ctx = new
			ClassPathXmlApplicationContext("applicationContext.xml");
			BookService bookService = (BookService) ctx.getBean("bookService");
			bookService.save();
		}
}
<bean id="bookDao" class="com.blue.dao.impl.BookDaoImpl"/>
<bean id="userDao" class="com.blue.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.blue.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
<property name="userDao" ref="userDao"/>
</bean>
上面就是介绍的setter注入的引用类型
ref 是指向 Spring IOC 容器中的另一个 bean 对象的,对于简单数据类型,没有对应的 bean 对象,
该如何配置 ?
public class BookDaoImpl implements BookDao {
		private String databaseName;
		private int connectionNum;
		public void setConnectionNum(int connectionNum) {
			this.connectionNum = connectionNum;
		}
		public void setDatabaseName(String databaseName) {
			this.databaseName = databaseName;
		}
		public void save() {
			System.out.println("book dao save
			                   ..."+databaseName+", "+connectionNum);
		}
}
<bean id = "bookDao" class = "com.blue.dao.impl.BookDaoImpl" >
                <property name = "databaseName" value = "mysql" />
                <property name = "connectionNum" value = "10" />
</bean>
<bean id = "userDao" class = "com.blue.dao.impl.UserDaoImpl" />
<bean id = "bookService" class = "com.blue.service.impl.BookServiceImpl" >
                <property name = "bookDao" ref = "bookDao" />
                <property name = "userDao" ref = "userDao" />
</bean>

注意:注入类型值得时候,value得值类型要和name对应的类型一样。因为它是自动类型转换的,不然就会出现报错。

(2)构造器进行注入注入引用类型:

把setter方法删除,并且附加上带有引用类型的构造器方法

private BookDao bookDao ;
private UserDao userDao ;
public BookServiceImpl ( BookDao bookDao , UserDao userDao ) {
this . bookDao = bookDao ;
this . userDao = userDao ;
}

配置文件

<bean id = "bookDao" class = "com.blue.dao.impl.BookDaoImpl" />
<bean id = "userDao" class = "com.blue.dao.impl.UserDaoImpl" />
<bean id = "bookService" class = "com.blue.service.impl.BookServiceImpl" >
<constructor-arg name = "bookDao" ref = "bookDao" />
<constructor-arg name = "userDao" ref = "userDao" />
</bean>

同样的问题,导入普通的数据类型

private String databaseName ;
private int connectionNum ;
public BookDaoImpl ( String databaseName , int connectionNum ) {
this . databaseName = databaseName ;
this . connectionNum = connectionNum ;
}

配置文件中:

<bean id = "bookDao" class = "com.blue.dao.impl.BookDaoImpl" >
<constructor-arg name = "databaseName" value = "mysql" />
<constructor-arg name = "connectionNum" value = "666" />
</bean>
<bean id = "userDao" class = "com.blue.dao.impl.UserDaoImpl" />
<bean id = "bookService" class = "com.blue.service.impl.BookServiceImpl" >
<constructor-arg name = "bookDao" ref = "bookDao" />
<constructor-arg name = "userDao" ref = "userDao" />
</bean>
这两个 <contructor - arg> 的配置顺序可以任意

综合上述的情况,发现耦合度依然很高,name的名字依然需要和之前那个位置的名字一一对一个

有两种方式解决这个问题:

方法一:删除name属性,添加type的属性,按照类型进行注入

<bean id = "bookDao" class = "com.blue.dao.impl.BookDaoImpl" >
                <constructor-arg type = "int" value = "10" />
                <constructor-arg type = "java.lang.String" value = "mysql" />
</bean>

方法二:删除type,添加index属性

<bean id = "bookDao" class = "com.blue.dao.impl.BookDaoImpl" >
                <constructor-arg index = "1" value = "100" />
                <constructor-arg index = "0" value = "mysql" />
</bean>

注意:如果构造器注入,构造器里面的类型必须全部给值,但是setter方法用一个给一个很方便。

(三)自动装配

IoC 容器根据 bean 所依赖的资源在容器中自动查找并注入到 bean 中的过程称为自动装配

按照类型进行装配:

把property标签删除,加入autowire属性,

<bean class = "com.blue.dao.impl.BookDaoImpl" />
<!--autowire 属性:开启自动装配,通常使用按类型装配 -->
<bean id = "bookService" class = "com.blue.service.impl.BookServiceImpl"
autowire = "byType" />

注意事项:

需要注入属性的类中对应属性的 setter 方法不能省略
被注入的对象必须要被 Spring IOC 容器管理
按照类型在 Spring IOC 容器中如果找到多个对象,会报 NoUniqueBeanDefinitionException

如果IOC中有多个对象,则用byName的方式

<bean class = "com.blue.dao.impl.BookDaoImpl" />
<!--autowire 属性:开启自动装配,通常使用按类型装配 -->
<bean id = "bookService" class = "com.blue.service.impl.BookServiceImpl"
autowire = "byName" />

byname就是根据的set后面的名字进行的匹配注入,耦合度较高不推荐使用。

自动装配适合于引用类型的注入,不能对简单类型进行注入。

集合注入:

注入数组类型

<property name = "array" >
<array>
<value> 100 </value>
<value> 200 </value>
<value> 300 </value>
</array>
</property>

注入list类型

<property name = "list" >
<list>
<value> itcast </value>
<value> itheima </value>
<value> boxuegu </value>
<value> chuanzhihui </value>
</list>
</property>

注入set类型

<property name = "set" >
<set>
<value> itcast </value>
<value> itheima </value>
<value> boxuegu </value>
<value> boxuegu </value>
</set>
</property>

注入map类型

<property name = "map" >
<map>
<entry key = "country" value = "china" />
<entry key = "province" value = "henan" />
<entry key = "city" value = "kaifeng" />
</map>
</property>

注入properties类型数据:

<property name = "properties" >
<props>
<prop key = "country" > china </prop>
<prop key = "province" > henan </prop>
<prop key = "city" > kaifeng </prop>
</props>
</property>

特别注意在加入这些数据的时候,需要放在bean与bean之间

<bean id = "bookDao" class = "com.blue.dao.impl.BookDaoImpl" >
//注入
</bean>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值