一、Spring 框架概述
Spring 是一个主流的 Java Web 开发框架,该框架是一个轻量级的、非入侵式的应用框架,可以解决企业应用开发的复杂性。
Spring 以 IoC(Inverse of Control,控制反转)和 AOP(Aspect Oriented Programming,面向切面编程)为内核:
- IOC:控制反转,把创建对象过程交给Spring 进行管理
- Aop:面向切面,不修改源代码进行功能增强
Spring 特点:
(1)方便解耦,简化开发
(2)Aop 编程支持
(3)方便程序测试
(4)方便和其他框架进行整合。通常服务器端采用三层体系架构,分别为表现层(web)、业务逻辑层(service)、持久层(dao)。Spring 对每一层框架都提供了技术支持,在表现层提供了与 Struts2 框架的整合,在业务逻辑层可以管理事务和记录日志等,在持久层可以整合Mybatis、 Hibernate 和 JdbcTemplate 等技术。
(5)方便进行事务操作
(6)降低API 开发难度
为什么需要Bean容器?
一般的项目搭建,都需要将各层进行分离以降低代码之间的耦合度。比如我们常用Dao层(数据访问层)、service层来封装和数据库的交互部分,其中Dao层负责具体的增删查改,service层负责调用Dao层的方法来对外提供服务,即用户只需要调用业务层,不需要接触Dao层。例如:
使用这种方法相当于将对象创建放到了方法中,此时对象对应的变量(局部变量)随着栈帧的创建而出现,随着栈帧的退出而销毁。那么由于没有引用指向它,该对象就会很快变成可回收状态。如果对象的创建初始化较为复杂,那么此处的对象创建就会变得效率低下。所以这种方法存在对象创建销毁的问题,极大的影响了效率,且当Dao层增加代码时,业务层也要随之改变,所以我们应该去寻求一种用户只管调用,业务层无需程序员更改的一种模式。
针对以上问题我们可以用Bean容器来解决,类似于下面的形式,将对象交给Spring来管理:
Spring相关jar包:
为了方便使用Spring,我们可以使用maven:我们只需要导入一个spring-webmvc就好了,因为spring-webmvc依赖了很多其他的包。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
spring的核心依赖项基本上都包含在spring-webmvc中了。
二、IOC 容器:通过容器统一管理对象的生命周期
1、什么是IOC容器?
(1)控制反转
- 把对象创建和对象之间的调用过程,交给Spring 进行管理;
- Spring 通过一个配置文件描述Bean 及Bean 之间的依赖关系,利用Java 语言的反射功能实例化Bean 并建立Bean 之间的依赖关系。
- Bean对象从程序自行管理的方式,转变为Spring容器管理,控制权发生了反转。简单来讲,IOC就是把对象的生产和使用分离。让我们只需要去使用对象,而不用关心对象具体是怎么来的。
(2)使用IOC 目的:
- 降低耦合度。
- 减少对象创建、销毁的步骤,提高了效率:无需每次都在方法中创建对象,退出方法后对象又很快变为可回收状态。
- 统一管理对象之间的依赖关系。(管理设置不同对象的属性)
(3) Spring 容器高层视图
Spring 启动时读取Bean 配置信息,并在Spring 容器中生成一份相应的Bean 配置注册表,然后根据这张注册表实例化Bean,装配好Bean 之间的依赖关系,为上层应用提供准备就绪的运行环境。其中Bean 缓存池为HashMap 实现.
Spring容器在初始化时先读取配置文件,根据配置文件或者元数据创建与组织对象存入容器中,程序使用时再从Ioc容器中取出需要的对象。
2、IOC 底层原理
xml 解析、工厂模式、反射
3、IOC 接口(BeanFactory)
IOC 思想基于IOC 容器完成,IOC 容器底层就是对象工厂
IOC 容器两种对象工厂实现方式:(两个接口)
- BeanFactory:IOC 容器基本实现,是Spring 内部的使用接口,不提供开发人员进行使用。
- ApplicationContext:BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人员进行使用。
注意:
- BeanFactory加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象。
- ApplicationContext加载配置文件时候就会把在配置文件对象进行创建。
ApplicationContext接口的实现类
- ClassPashXmlApplicationContext:需要配置xml文件的磁盘路径
- ClassPathXmlApplicationContext:需要配置xml文件的类路径
三、Bean管理
- Spring对象的创建
- Spring属性的注入
1、 Bean 管理(基于xml)
(1)基于XML方式创建对象:
1)在spring 配置文件中,使用bean 标签,标签里面添加对应属性,就可以实现对象创建
2)在bean 标签有很多属性,介绍常用的属性
- id 属性:唯一标识
- class 属性:类全路径(包类路径)
3)创建对象时候,默认执行无参数构造方法完成对象创建
(2)基于xml 方式注入属性
依赖注入(Dependency Injection,DI):注入属性,在Spring中实现控制反转的是Ioc容器,其具体实现方法就是依赖注入。
- 依赖:bean对象的创建依赖于容器。
- 注入:bean对象中的所有属性由容器注入。
IOC和DI的区别:
- IOC:把创建对象交给Spring进行配置
- DI: 向类里面的属性中赋值
1)第一种注入方式:使用set 方法进行注入
利用对象方法中的set方法进行注入,方法中要包含应该有的setter方法。 如果属性注入时,对应的属性没有set方法,那么xml对应的bean中属性名会报错。
(1)创建类,定义属性和对应的set 方法
public class Book {
//创建属性
private String name;
private String author;
//创建属性对应的set方法
public void setBname(String name) {
this.name = name;
}
public void setBauthor(String author) {
this.author = author;
} }
(2)在spring 配置文件配置对象创建,配置属性注入
<bean id="book" class="com.atguigu.spring5.Book">
<property name="bname" value="易筋经"></property>
<property name="bauthor" value="达摩老祖"></property>
</bean>
2)第二种注入方式:使用有参数构造进行注入
使用有参构造函数时,对应属性可以不用写set方法。
(1)创建类,定义属性,创建属性对应有参数构造方法
public class Orders {
//属性
private String oname;
private String address;
//有参数构造
public Orders(String oname,String address) {
this.oname = oname; this.address = address;
}
}
(2)在spring 配置文件中进行配置
<bean id="orders" class="com.atguigu.spring5.Orders">
<constructor-arg name="oname" value="电脑"></constructor-arg>
<constructor-arg name="address" value="China"></constructor-arg>
</bean>
注:有参构造方法有三种传参方式:
下标: <constructor-arg index="0" value="56"/>
类型:<constructor-arg type="java.lang.String" value="glp"/>
参数名:<constructor-arg name="age" value="56"/>
3)第三种注入方式:工厂注入属性
参考博客:Spring工厂注入属性
4)第四种注入方式p命名空间:对应set属性注入
需要在beans的首部加入xmlns:p="http://www.springframework.org/schema/p"
传统属性注入
<bean name="john-classic" class="com.example.Person">
<property name="name" value="John Doe"/>
<property name="spouse" ref="jane"/>
</bean>
P命名空间直接进行属性注入:
<bean name="john-modern"
class="com.example.Person"
p:name="John Doe"
p:spouse-ref="jane"/>
5)第五种注入方式c命名空间:对应构造器注入
需要在beans的首部加入xmlns:c="http://www.springframework.org/schema/c"
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="beanTwo" class="x.y.ThingTwo"/>
<bean id="beanThree" class="x.y.ThingThree"/>
传统构造器注入
<bean id="beanOne" class="x.y.ThingOne">
<constructor-arg name="thingTwo" ref="beanTwo"/>
<constructor-arg name="thingThree" ref="beanThree"/>
<constructor-arg name="email" value="something@somewhere.com"/>
</bean>
c命名空间使用:
<bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo"
c:thingThree-ref="beanThree" c:email="something@somewhere.com"/>
注:
p命名和c命名不能直接使用,需要导入xml约束。
7)空值、null 值、特殊符号注入:
空值注入:
<property name="ducks2" value=""/>
null值注入:
<property name="address"> <null/> </property>
包含特殊符号的属性值:把带特殊符号内容写到CDATA
<property name="address">
<value><![CDATA[<<南京>>]]></value> </property>
输出:User{address='<<南京>>', age=0}
8)注入属性-外部bean-内部bean-级联赋值
参考博客:https://blog.youkuaiyun.com/glpghz/article/details/109727241
9)xml 注入集合属性
数组array:
<property name="nums">
<array>
<value> 1</value>
<value> 2</value>
</list>
</property>
list:
<property name="duck">
<list>
<ref bean="duck1" />
<ref bean="duck2" />
</list>
</property>
map:
<property name="ducks1">
<map>
<entry key="" value=""/>
<entry key="" value=""/>
</map>
</property>
set:
<property name="ducks2">
<set>
<value> 1</value>
<value> 2</value>
</set>
</property>
(3)bean的两种类型
普通bean:在配置文件中定义bean 类型就是返回类型
工厂bean(FactoryBean):在配置文件定义bean 类型可以和返回类型不一样
1)第一步创建类,让这个类作为工厂bean,实现接口FactoryBean
2)第二步实现接口里面的方法,在实现的方法中定义返回的bean 类型
public class MyBean implements FactoryBean<Course> {
//定义返回bean
@Override
public Course getObject() throws Exception {
Course course = new Course();
course.setCname("abc");
return course;
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
return false;
}
}
xml配置:
<bean id="myBean"
class="com.atguigu.spring5.factorybean.MyBean"> </bean>
测试:
@Test
public void test3() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean.xml");
Course course = context.getBean("myBean", Course.class);
System.out.println(course);
}
(4)bean的作用域
Bean 定义了5 中作用域,分别为
- singleton(单例)Spring中的Bean默认为单例的
- prototype(原型)
- request
- session
- global session
1) singleton:单例模式(多线程下不安全)
Spring IoC 容器中只会存在一个共享的Bean 实例,无论有多少个
Bean 引用它,始终指向同一对象。
2)prototype:原型模式每次使用时创建
每次通过Spring 容器获取prototype 定义的bean 时,容器都将创建
一个新的Bean 实例,每个Bean 实例都有自己的属性和状态,而singleton 全局只有一个对象。根据经验,对有状态的bean 使用prototype 作用域,而对无状态的bean 使用singleton作用域。
注:
- 有状态对象(Stateful Bean),就是有实例变量的对象 ,可以保存数据,是非线程安全的。
- 无状态对象(Stateless Bean),就是没有实例变量的对象 .不能保存数据,是不变类,是线程安全的。
- 在Spring中,绝大部分Bean都可以声明为singleton作用域。是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用
ThreadLocal
进行处理,让它们也成为线程安全的状态,因此有状态的Bean就可以在多线程中共享了。 - scope 值是singleton 时候,加载spring 配置文件时候就会创建单实例对象。
scope 值是prototype 时候,不是在加载spring 配置文件时候创建对象,在调用
getBean 方法时候创建多实例对象
3)Request:一次request 一个实例
在同一个Http 请求中,容器会返回该Bean 的同一实例。而对不同的Http 请求则会产生新的Bean,而且该bean 仅在当前Http Request 内有效,当前Http 请求结束,该bean实例也将会被销毁。
(5)bean的生命周期
bean 的后置处理器,bean 生命周期有七步
1)通过构造器创建bean 实例(无参数构造)
2)为bean 的属性设置值和对其他bean 引用(调用set 方法)
3)把bean实例传递bean后置处理器的方法postProcessBeforeInitialization
4)调用bean 的初始化的方法(需要进行配置初始化的方法)
5)把bean 实例传递bean 后置处理器的方法postProcessAfterInitialization
6)bean 可以使用了(对象获取到了)
7)当容器关闭时候,调用bean 的销毁的方法(需要进行配置销毁的方法)
创建后置处理器:创建类,实现接口BeanPostProcessor
public class MyBeanPost implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在初始化之前执行的方法"); return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在初始化之后执行的方法");
return bean;
}
}
当这个类实现了BeanPostProcessor,并注入到Bean容器中后,Spring就会识别出这个后置处理器,并将该后置处理器置于每个Bean的生命周期中。
参考博客:Spring生命周期——简介
(6)xml 自动装配
根据指定装配规则(属性名称或者属性类型),Spring 自动将匹配的属性值进行注入。
1)注入属性时根据属性名称自动注入
员工类Emp中有一个部门dept属性
<bean id="emp" class="com.atguigu.spring5.autowire.Emp"
autowire="byName"></bean>
<bean id="dept" class="com.atguigu.spring5.autowire.Dept"></bean>
2)注入属性时根据属性类型自动注入
员工类Emp中有一个部门dept属性,类型为Dept
<bean id="emp" class="com.atguigu.spring5.autowire.Emp"
autowire="byType"> </bean>
<bean id="dept" class="com.atguigu.spring5.autowire.Dept"></bean>
(7)外部属性文件
把外部properties 属性文件引入到spring 配置文件中
1)引入context 名称空间
2)使用context标签引入外部属性文件jdbc.properties
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${prop.driverClass}"></property>
<property name="url" value="${prop.url}"></property>
<property name="username" value="${prop.userName}"></property>
<property name="password" value="${prop.password}"></property>
</bean>
applicationContext.xml中的常用配置:
<beans> </beans>:
用来容纳一些bean对象<bean></bean>:
用来定义bean对象<import resource = "beans.xml"/>
:用来合并xml,将其他xml中的bean对象导入进来。<alias name="gg" alias="别名"/>:
用来给bean对象起别名<bean id="user" class = "com.glp.hello" name = "U Username haha">
:可以直接在bean中用name起别名,而且可以起多个别名,中间用空格,逗号分割都可以。
2、IOC :Bean 管理(基于注解)
(1)什么是注解
1)注解是代码特殊标记,格式:@注解名称(属性名称=属性值, 属性名称=属性值…)
2)使用注解,注解作用在类上面,方法上面,属性上面
3)使用注解目的:简化xml 配置
(2)Spring中的常用注解
1)@Component
可以使用此注解描述 Spring 中的 Bean,但它是一个泛化的概念,仅仅表示一个组件(Bean),并且可以作用在任何层次。使用时只需将该注解标注在相应类上即可。
2)@Repository
用于将数据访问层(DAO层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
3)@Service
通常作用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
4)@Controller
通常作用在控制层(如 Struts2 的 Action),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
5)@Configuration:声明当前类是一个配置类,相当于一个Spring配置的xml文件。把一个类作为一个IoC容器和@Bean搭配使用,某个方法头上如果注册了@Bean,就会作为这个Spring容器中的Bean。
6) @Bean:表示注册一个名称为方法名的Bean对象到容器中。此时方法的名称就相当于bean标签中的id属性,方法返回值就相当于bean标签中的class属性。
- @Bean配置的类的默认id是方法的名称,但是我们可以通过value或者name给这个bean取别名。
@Bean(name="aservice")
- value和name属性不能并存。 而且如果配置了value或者name,那么我们将无法在通过方法名称获取这个bean了。
7)@Import(config.class):可以引入一个配置类,将两个配置类整合到一起。
8) @ComponentScan(com.glp.config) : ComponentScan做的事情就是告诉Spring从哪里找到bean.哪些包需要被扫描, 一旦指定好之后Spring将会将在被指定的包及其下级的包(sub packages)中寻找bean。
@ComponentScan(com.glp.config)
public class config{
}
9) @Scope(“prototype”): 原生模式,不加的话默认就是单例模式。
注意:
@Component 、@Repository 、@Service
只是用来分层没有具体的含义。@Controller
是用于做客户端请求的层。- @Component、@Repository、@Service、@Controller 在类上使用,如果能扫描到该类,则将该类注册到容器中。只会实例化为一个对象。
- @Bean 在方法上使用,根据需要可以注册多个Bean 。
说明:
在使用@Bean(name=“bean1”)给注册对象起别名时,可以用Bean的name属性,或者@Qualifier.
@Bean(name="bean1")
public A getA() {
return new A();
}
@Bean
@Qualifier("bean2")
public A getB() {
return new A();
}
使用时可以用 @Qualifier通过名称从容器中引入:
@Autowire
@Qualifier("bean1")
A testa;
(3)基于注解方式实现对象创建
1)第一步 引入依赖
spring-aop-5.2.6.RELEASE.jar
2)第二步 开启组件扫描:将包中含有注解的组件注入到容器中
<context:component-scan base-package="com.atguigu">
</context:component-scan>
3)第三步创建类,在类上面添加创建对象注解
@Component(value = "userService")
public class UserService {
public void add() {
System.out.println("service add.......");
}
}
注意:
- 在注解里面value属性值可以省略不写,
- 默认值是类名称,首字母小写——UserService – userService
- 等同于在xml中配置:
(4)开启组件扫描细节配置
filter context:include-filter ,设置扫描哪些内容
<context:component-scan base-package="com.atguigu"
use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
context:exclude-filter:设置哪些内容不进行扫描
<context:component-scan base-package="com.atguigu">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
注意:
use-default-filters="false"
表示现在不使用默认filter,自己配置
(5)基于注解方式实现属性注入
1)@Autowired:根据属性类型进行自动装配
第一步把service 和dao 对象创建,在service 和dao 类添加创建对象注解
第二步在service 注入dao 对象,在service 类添加dao 类型属性,在属性上面使用注解
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void add() {
System.out.println("service add.......");
userDao.add();
} }
注:
不需要添加set方法
2)@Qualifier:根据名称进行注入
@Qualifier 注解需要和上面@Autowired 一起使用
@Autowired //根据类型进行注入
@Qualifier(value = "userDaoImpl1") //根据名称进行注入
private UserDao userDao;
3)@Resource:可以根据类型注入,可以根据名称注入
//@Resource //根据类型进行注入
@Resource(name = "userDaoImpl1") //根据名称进行注入
private UserDao userDao;
注:
默认根据类型注入
@Bean所在的方法user2,要使用user1,可以用方法参数的方式注入,使用@Qualifier("user1")
指明要使用的Bean对象。
@Bean
public User user2(@Qualifier("user1") User user1){};
4)@Value:注入普通类型属性
@Value(value = "abc")
private String name;
@value("glp")
public void setName(String name){
this.name =name;
}
(6)完全注解开发
1)创建配置类,替代xml配置文件
@Configuration //作为配置类,替代xml配置文件
@ComponentScan(basePackages = {"com.atguigu"})
public class SpringConfig {
@Bean
public User user(){
//定义了一个名称为方法名user1的user对象,并注册到容器中
User user = new User();
user.setUsername("烤鸭1");
user.setPassword("123");
user.setBirthday(new Date());
return user;
}
}
相当于在xml中配置了一个User对象
2)编写测试类
@Test
public void testService2() {
//加载配置类
ApplicationContext context =
new AnnotationConfigApplicationContext(SpringConfig.class);
UserService userService =
context.getBean("userService", UserService.class);
System.out.println(userService);
userService.add();
}
注:
ApplicationContext有两个实现类,一个ClassPathXmlApplicationContext
用来获取xml中的Bean,一个AnnotationConfigApplicationContext
用来获取注解中的Bean.
ApplicationContext Context = new AnnotationConfigApplicationContext(Test.class);
User user = (User)Context.getBean("getUser");
四、AOP
参考博客:Spring框架的核心组件——Aop
五、JdbcTemplate
参考博客:Spring——JdbcTemplate
六、Spring——事务
参考博客:Spring——事务管理