目录
7.3、session、request、application
1、什么是Spring
-
Spring:春天 也就是软件行业的春天 设计之初的目的是为了简化企业级的应用开发过程
-
历史:2002年,Rod Jahnson首次推出了Spring框架雏形interface21框架。
2004年3月24日,Spring框架以interface21框架为基础,经过重新设计,发布了1.0正式版
-
优点
1、Spring是一个开源免费的框架 , 容器 .
2、Spring是一个轻量级的框架 , 非侵入式的 .
3、控制反转 IoC , 面向切面 Aop(核心)
4、对事物的支持 , 对框架的支持
一句话概括:
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架)。
2、Spring环境搭建
2.1、依赖导入
<--Spring webmvc-->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.8</version>
</dependency>
这个依赖里面包含了我们所要用的大部分依赖 ,我们学习Spring也就需要8个依赖 而这个就已经包含了6个 足以应对我们前期的学习
依赖导入完成之后 就是编写配置文件了 Spring和maven一样 都是一个约束大于配置的工具
2.2、文件配置
在resources文件目录下 创建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="" class="">
<property name="" ref=""></property>
</bean>
<bean id="" class="">
<property name="" value=""></property>
</bean>
<!-- more bean definitions go here -->
</beans>
这里面的id使我们将来获取bean的变量名 class是我们需要配置的对应的实体类的绝对地址
如果这个类中有变量 或者返回值我们就用<property>这个标签
如果这个类中需要配置的是一个简单变量 我们用value给这个bean赋值
如果这个类中需要配置的是一个类引用 我们用ref来指向我们需要引用的这个类
3、了解IOC
3.1、什么是IOC?
IOC(Inversion of Control)中文意思 控制反转 通俗的讲就是 控制权的准换
以前的开发 控制权在开发人员手上 用户需要什么需要 开发人员就得按照需求 修改大量的底层源码 从前台响应servlet到service层再到Dao层 牵一发而动全身
3.2、初识IOC
举个例子
我们有一个Dao层 一个Service层 一个Servlet(用test来模拟Servlet)它的大致结构是这样的
Dao层
public interface UserDao {
void getuser();
}
public class UserDaoImpl implements UserDao {
public void getuser() {
System.out.println("正在获取用户");
}
}
public class UserMysqlImpl implements UserDao{
public void getuser() {
System.out.println("正在获取Mysql");
}
}
Service层
public interface UserServcie {
void getUser();
}
public class UserServiceImpl implements UserServcie{
//调用userdaoImlp
private UserDao userDao=new UserDaoImpl();
//调用userMysqlImpl
private UserDao userDao1=new UserMysqlImpl();
//DaoImpl方法实现
public void getUser() {
userDao.getuser();
}
//MysqlImpl方法实现
public void getUser1() {
userDao1.getUser()
}
}
Servlet层(Test模拟)
public class UserTest {
@Test
public void get(){
UserServcie userServcie=new UserServiceImpl();
//调用DaoImpl
userServcie.getUser();
//调用MysqlImpl
userServcie.getUser1();
}
从上面的例子我们可以看出 如果这是我们多了一个新的实现类 我们需要从头改到尾 假设我们有100个、1000个这样的需求 那我们的工作量可想而知
于是我们开始考虑优化 我们可以在Service层的实现类中设置一个接口 使用Set方法 构造一个函数 使得每次servlet调用Service层时 只需要将需要引用的类作为参数输入就行 根据这种想法 我们有了下面这种思路
Service层
public class UserServiceImpl implements UserServcie{
private UserDao userDao;
public void setUserDao (UserDao userDao) {
this.userDao = userDao;
}
public void getUser() {
userDao.getuser();
}
}
Servlet层(Test模拟)
public class UserTest {
@Test
public void get(){
UserServcie userServcie=new UserServiceImpl();
((UserServiceImpl) userServcie).setUserDao( new UserMysqlImpl());
userServcie.getUser();
}
new UserMysqlImpl() 就是我们需要引用的方法 我们现在只需要在这里输入就行了 无需改动Service层
这就是一个控制反转的典型例子 刚开始 我们通过把程序写死的方式 得到了一个完整的程序 但是后期的维护 、更新 代价太大 我们完全依靠程序自身创建 运行,改动过后 程序变得单纯许多 它只负责运行 至于运行的内容是什么 都有我们在Servlet层进行传递 也就直说控制权由程序自身 转到了我们身上
3.3、HelloSpring
接下来 我们使用Spring来使我们的程序变得更轻巧
首先 编写一个简单的HelloSpring‘来验证我们的环境是否搭建成功
创建实体类
public class Hello {
private String hello;
public Hello(String hello) {
this.hello = hello;
}
public Hello() {
}
public String getHello() {
return hello;
}
public void setHello(String hello) {
this.hello = hello;
}
@Override
public String toString() {
return "Hello{" +
"hello='" + hello + '\'' +
'}';
}
}
编写配置文件ApplicationContext
<?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="hello" class="com.llf.Pojo.Hello">
<property name="hello" value="Spring">
</property>
</bean>
</beans>
这些文件可以直接从官网上copy 千万不要手敲
配置完之后 你的配置文件旁边就会多了一个绿色的小叶子
这样就表示你的实体类与Spring配置完成了
如果没有小叶子 我们可以在Idea插件中心下载安装spring assistant插件 类中就有小叶子的提示了
第一种
File-->Settings-->plugins-->搜索 spring assistant-->Inall
第二种
File-->Settings-->Project Structure-->Modules-->选择自己程序、点击右边的加号 添加
然后编写测试类
public class HelloTest {
@Test
public void get(){
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
System.out.println(context.getBean("hello").toString());
}
}
new ClassPathXmlApplicationContext(" ")这里来填入我们的xml配置文件进行资源加载,可以填入多个xml文件 我们可以输入new CPX来让Idea提示 得到它
这里我们发现返回的是ApplicationContext,而不是ClassPathXmlApplicationContext,通过查看源码我们知道ClassPathXmlApplicationContext进行了多次的继承 而最终的父类是ApplicationContext 因此可以返回ApplicationContext
输出运行
由此可见我们的环境已经搭建成功
接下来返回我们之前的例子 使用SPring来实现它
3.4、使用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">
<bean id="userDaoImpl" class="com.llf.Dao.UserDaoImpl"></bean>
<bean id="userMysql" class="com.llf.Dao.UserMysqlImpl"></bean>
<bean id="UserService" class="com.llf.Service.UserServiceImpl">
<property name="userDao" ref="userMysql"></property>
</bean>
</beans>
接下来就是在Test中加载xml文件 并运行
public class UserTest {
@Test
public void get1(){
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
UserServcie userServcie = (UserServcie) context.getBean("UserService");
userServcie.getUser();
}
}
而如果我们需要调用DaoImpl方法 只需要在xml文件中修改即可
<bean id="UserService" class="com.llf.Service.UserServiceImpl">
<property name="userDao" ref="userDaoImpl"></property>
</bean>
比我们之前的方法更加简单 代码更易于实现 即使是外行 也可以仅仅在xml文件中修改参数来实现不同的效果
这种思想 , 从本质上解决了问题 , 我们程序员不再去管理对象的创建了 , 更多的去关注业务的实现 . 耦合性大大降低 . 这也就是IOC的原型 !
4、IOC对象创建的几种方式
4.1、使用无参构造(默认)
这是默认的创建方式 但前提时必须要有无参函数 否则就会报错
<bean id="User" class="com.llf.Pojo.User">
</bean>
4.2、使用有参构造
-
通过实体类参数下标创建对象
<bean id="User1" class="com.llf.Pojo.User"> <constructor-arg index="0" value="llf1"></constructor-arg> </bean>
-
通过参数类型创建对象 若多个参数的参数类型一致 会产生歧义 (不推荐使用)
<bean id="User2" class="com.llf.Pojo.User"> <constructor-arg type="java.lang.String" value="llf2"></constructor-arg> </bean>
type:参数类型 基本类型的参数可以写简称 引用类型必须写全称
-
通过参数名来创建对象 (推荐)
<bean id="User3" class="com.llf.Pojo.User"> <constructor-arg name="name" value="llf3"></constructor-arg> </bean>
4.3、对象何时创建
对象在我们加载ApplicationContext.xml文件的时候就已经创建成功了,而且是将xml配置中的所有Bean的对象都已经创建,我们需要的时候直接从Context容器中拿即可
举例:
我们可以发现 我们在执行HelloTest的时候 User的无参构造也被加载了进来 这说明User对象已经被创建了 但这绝对不是缓存
5、Spring配置
5.1、Import
import:文件导入合并 用于多人开发时 将其他人的ApplicationContext.xml文件进行合并引用 对于相同id 相同内容 也会进行合并
<import resource="ApplicationContext1.xml"></import>
5.2、Bean和Beans
<bean id="User4" class="com.llf.Pojo.User" name="userT userTT,USER;userTTT">
<constructor-arg index="0" value="llf1"></constructor-arg>
</bean>
id:bean的唯一标识符 class: bean对应的实体类 必须全限命名 包名+类名 name:起别名 它可以起多个别名 中间可以用空格、逗号、分号来进行分割 起别名后,原来的名字也可以访问
Beans:一个Beans可以由多个Bean组成,使用Beans标签将创建的Bean包裹
5.3、alias
alias:起别名
<alias name="hello" alias="Helll"></alias>
不同于name的是 它只能是一对一的关系 且不能在一个标签中有多个名字
6、依赖注入(DI)
依赖注入(Dependency import)DI
依赖注入的方式有容器注入、set注入(重点)、拓展注入
容器注入
所谓的容器注入其实就是构造器注入 即有参构造注入( <constructor-arg /> )
<bean id="User1" class="com.llf.Pojo.User">
<constructor-arg index="0" value="llf1"></constructor-arg>
</bean>
set注入
<!--Bean注入-->
<bean id="address" class="com.llf.Pojo.Address">
<property name="address" value="河南省洛阳市"></property>
</bean>
<!--普通注入-->
<bean id="student" class="com.llf.Pojo.Student">
<property name="name" value="小杨"></property>
<property name="address" ref="address"></property>
<!--数组注入-->
<property name="books">
<array>
<value>三国演义</value>
<value>红楼梦</value>
<value>水浒传</value>
<value>西游记</value>
</array>
</property>
<!--List-->
<property name="hobbys">
<list>
<value>篮球</value>
<value>听歌</value>
</list>
</property>
<!--Map注入-->
<property name="card">
<map>
<entry key="身份证" value="410325199812014012"></entry>
</map>
</property>
<!--Set注入-->
<property name="games">
<set>
<value>LOL</value>
<value>DNF</value>
</set>
</property>
<!--空值注入-->
<property name="wife">
<null/>
</property>
<!--Properties注入-->
<property name="info">
<props>
<prop key="driver">com.mysql.cj.jdbc.Driver</prop>
<prop key="url">jdbc:mysql://localhost:3306/mydb1?serverTimezone=UTC</prop>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</props>
</property>
</bean>
其中最基本的注入是 Bean注入和普通注入 其他注入都是关键词标签里面嵌套 value标签
拓展注入
c命名空间和p命名空间
<!--c标签注入 本质是容器注入 需要有参方法-->
<bean id="teacher" class="com.llf.Pojo.Teacher" c:name="李老师" c:age="18"/>
<!--p标签注入 还是普通注入的方式-->
<bean id="teacher1" class="com.llf.Pojo.Teacher" p:name="王老师" p:age="20"/>
使用c标签和P标签 都需要引入外部配置
xmlns:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
两个外部配置的区别就只是开头和结尾的C、P的区别
7、Bean的作用域
7.1、singleton 单例 (Spring默认)
单例就是单一实例,相同的Bean的id 无论被取值多少次 它的对象只会被创建一次
将teacher这个id取值两次 然后输出这两次的hashcode() 是相等的 而且两者做==判断 也是true 证明它就是单例模式 由于是Spring默认的作用域 所以不需要在xml中额外配置
7.2、prototype 原型
和单例模式相反,它的含义是 每一次对相同bean的id进行取值时 都会创建一个新的对象
<bean id="teacher" class="com.llf.Pojo.Teacher" c:name="李老师" c:age="18" scope="prototype"/>
通过观察我们可以知道 两次的取值 它分别对应两个不同的hashcode() 说明这个对象被创建了两次 也就是我们在java实体类中的操作一样 new了两次
7.3、session、request、application
这三个只能在java-web的开发中中使用
session:对象被存放在session中 页面关闭失效 也可以自己设置失效时间
requset:对象只在一次请求中有效
application: 全局有效 从服务器开启到服务器关闭的这段时间 也就是服务器一运行就产生 一关闭 就失效
8、Bean的自动装配(自动注入)
Bean自动装配有三种方式 Byname、Bytype、使用注解
8.1、Byname
<bean id="cat" class="com.llf.Pojo.Cat"/>
<bean id="dog" class="com.llf.Pojo.Dog"/>
<bean id="people" class="com.llf.Pojo.People" autowire="byName"/>
8.2、Bytype
<bean id="people1" class="com.llf.Pojo.People" autowire="byType">
<property name="name" value="小许"></property>
</bean>
关键字:autowire 它有Byname、Bytype
8.3、使用注解
使用注解前我们需要引入配置文件
<!--配置文件 注意它和Spring配置文件的区别 我们只需要将spring换成context-->
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
<!--开启自动装配-->
<context:annotation-config/>
public class People {
@Autowired
@Qualifier (value="dog")
private Dog dog;
@Autowired
@Qualifier (value="cat")
private Cat cat;
@Nullable
private String name;
使用注解开发 我们只需要在bean的id对应的类的属性上添加@Autowired即可
@Nullable 字段标记这个注解 说明字段可以为空值
如果xml文件中 同一个类有多个Bean的id 我们可以指定类中的属性为某一个Bean的id所装配
<bean id="dog" class="com.llf.Pojo.Dog"/>
<bean id="dog111" class="com.llf.Pojo.Dog"/>
<bean id="1" class="com.llf.Pojo.Dog"/>
<bean id="dg1" class="com.llf.Pojo.Dog"/>
就像这种情况 我们可以在dog属性上面添加@Qualifier (value="dog") 它是不可以单独使用的
另外 我们也可以用java自带的 @Resource 注解来实现
public class People {
@Resource
private Dog dog;
@Resource
private Cat cat;
private String name;
}
8.4、小结
-
使用Byname 必须保证Bean的id唯一并且类的构造方法中有该id关键字的构造方法
比如 id为dog 那么类的构造方法中就必须得用setDog()方法
-
使用Bytype 优点:可以不用配置Bean的id 它会自动按照class的类对应的属性进行装配
缺点:但是他不适用于多个对象指向同一个class
由于我们配置了两个beanID并指向了同一个类 所以它就直接报错了 当我们随便删除一个beanID它就恢复正常了
-
@Autowired与@Resource
相同点:都是用来装配Bean的方法 都可以写在字段上或setter()方法上
不同点:@Autowired 通过bytype实现 然后byname 且必须要求对象存在
@Resource 先通过byname 然后bytype 最后才会报空指针异常
9、使用注解开发
9.1、Bean的实现
在之前的开发中 都是在xml文件中使用bean标签来注册bean 现在我们使用注解来实现
首先需要在配置文件中扫描包配置的注解 使xml文件可以得到它
<!--扫描配置包下的注解-->
<context:component-scan base-package="com.llf.Pojo"></context:component-scan>
然后在实体类中使用注解
@Component
public class User {
}
@Componment的作用就等同于 <bean id="user" class="com.llf.Pojo.User"/>
使用@Componment注册Bean以后 Bean的id 默认为类名 且首字母小写
测试
9.2、属性注入
之前我们在xml文件中使用<property name="name" value="lili"></property>这样的方式赋值
现在我们可以使用注解@Value来实现,这种情况下 我们可以不用创建set()方法
@Value("lili")
/** 作用类似于这句话
<property name="name" value="lili"></property>
**/
private String name;
测试
9.3、自动装配
使用@Autowired与@Resource 来实现自动装配
也就是对引用值进行自动匹配
9.4、衍生注解
通过使用@Componment为了配合开发 我们又衍生出了不同层所用的不同注解 不过他们的功能相同 都是注册Bean
-
@Controller:web层
-
@Service:service层
-
@Repository:dao层
9.5、小结
xml与注解的区别
xml可以适用于任何场合 且修改方便 只是代码量相对较多
注解适用于常规的情况下 且代码量相对较少 只是后期维护修改困难
于是我们可是综合使用 在xml中注册Bean 然后用注解@Value来为属性赋值 用@Autowired实现自动装配
这样就不用在xml中扫描包了 扫描包只是为了加载类上的注解即@Componment