文章目录
Spring
简介
-
目的:解决企业应用开发的复杂性
-
范围:任何Java应用
-
强大的向后兼容性,代码质量高标准
-
理念:使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架
-
优点:
- 开源免费的框架(容器)
- 轻量级的、非入侵式的框架
- 控制反转、面向切面编程
- 支持事务的处理,对框架整合的支持
-
弊端:发展了太久,违背了原来的理念,配置十分繁琐,“配置地域”
-
总结:Spring是一个轻量级的控制反转(IoC)、面向切面编程(AOP)的框架
-
早期:SSH : Struct2 + Spring + Hibernate(全自动)
-
现代:SSM : SpringMvc + Spring + Mybatis(半自动)
组成
spring 7 大模块
拓展
现代化Java开发
- Spring Boot
- 一个快速开发的脚手架
- 基于Spring Boot可以快速开发单个微服务
- 约定大于配置
- Spring Cloud
- 基于Spring Boot实现的
IoC
IoC理论
之前在MovieLister中创建MovieFinder的具体实现,是写死的一种实现,当以后的test调用类需要换别的实现的时候,除了要修改test类,还需要修改原MovieLister程序重新编译,不能很好的适应变更
这种思想,从本质上解决了问题,程序员不用再去管理对象的创建,让用户去管,系统的耦合性大大降低
IOC本质
控制反转 Inversion of Control 是一种设计思想,依赖注入(DI)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的
控制反转是一种通过描述(XML或注解) 并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入
HelloSpring
-
导入Spring相关jar包
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.1.10.RELEASE</version> </dependency>
-
编写相关代码
Hello类
public class Hello{ private String name; public String getName(){ return name; } public void setName(String name){ this.name = name; } public void show(){ System.out.println("Hello" + name); } }
-
编写spring配置文件,命名为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"> <!-- 使用spring来创建对象,在Spring这些都称为Bean id对应的是变量名 class对应的是类型 property对应的是内部属性--> <bean id="hello" class="com.yunthin.pojo.Hello"> <property name="str" value="nbsszrx"/> </bean> </beans>
-
测试
@Test public void test(){ //解析beans.xml文件,生成管理相应的Bean对象 ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); //getBean : 参数为spring配置文件中bean的id Hello hello = (Hello) context.getBean("Hello"); hello.show(); }
到现在,我们彻底不用在程序中去改动了,要实现不同的操作,只需要在xml配置文件中进行修改,所谓IoC,一句话:对象由Spring创建、管理、装配
思考问题
-
Hello是谁创建的?
Hello对象是由Spring创建的
-
Hello对象的属性是怎么设置的?
Hello对象的属性是由Spring容器设置的
这个过程就叫控制反转
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring来创建的
反转:程序本身不创建对象,而变成被动的接收对象
依赖注入:就是利用set方法来进行注入的
IOC是一种编程思想,由主动的变成变成被动的接收
可以通过ClassPathXmlApplicationContext去浏览一下底层源码
IOC创建对象的方式
-
默认,使用无参构造函数创建对象
-
如果需要使用有参构造创建对象
-
下标赋值
<bean id="user" class="com.yunthin.pojo.User"> <constructor-arg index="0" value="Yunthin Chow"/> </bean>
-
类型
<bean id="user" class="com.yunthin.pojo.User"> <constructor-arg type="java.lang.String" value="Yunthin Chow"/> </bean>
-
参数名
<bean id="user" class="com.yunthin.pojo.User"> <constructor-arg name="name" value="Yunthin Chow"/> </bean>
-
Spring配置
别名
别名区分大小写
<alias name="user" alias="userNew"/>
如果添加了别名,我们也可以使用别名获取到这个对象
bean的配置
name也是别名,而且更高级,可以同时取多个别名,逗号、空格分隔都可以
<bean id="userT" class="com.yunthin.pojo.UserT" name="user2,u2 u3">
<property name="name" value="aaa"/>
</bean>
import
一般用于团队开发,可以将多个配置文件导入合并为一个
假设现在项目中有多个人开发,不同的人负责不同的类,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的,使用的时候,直接使用总的配置xml文件就可以了
applicationContext.xml
<import resource="beans1.xml"/>
<import resource="beans2.xml"/>
<import resource="beans3.xml"/>
DI 依赖注入
构造器注入
前面已经说过了
Set方式注入【重点】
- 依赖注入:本质是set注入
- 依赖:bean对象的创建依赖于容器
- 注入:bean对象中的所有属性由容器来注入
【环境搭建】
-
复杂类型
package com.yunthin.pojo; public class Address { private String address; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
-
真实对象
package com.yunthin.pojo; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; public class Student { private String name; private Address address; private String[] books; private List<String> hobbys; private Map<String, String> card; private Set<String> games; private Properties info; private String wife; } // 相应的get set方法省略
-
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="address" class="com.yunthin.pojo.Address" /> <bean id="student" class="com.yunthin.pojo.Student"> <!-- 第一种, 普通值注入, value--> <property name="name" value="yunthin Chow"/> <!-- 第二种,Bean注入,ref--> <property name="address" ref="address"/> <!-- 第三种,数组注入--> <property name="books"> <array> <value>《红楼梦》</value> <value>《西游记》</value> <value>《水浒传》</value> </array> </property> <!-- 第四种, List注入--> <property name="hobbys"> <list> <value>听歌</value> <value>代码</value> <value>电影</value> </list> </property> <!-- 第五种, map注入--> <property name="card"> <map> <entry key="身份证" value="123"/> <entry key="学生证" value="321"/> <entry key="银行卡" value="666"/> </map> </property> <!-- 第六种,set注入--> <property name="games"> <set> <value>COH</value> <value>NFS</value> </set> </property> <!-- 第七种, null注入--> <property name="wife"> <null/> </property> <!-- 第八种, Properties注入--> <property name="info"> <props> <prop key="学号">181250</prop> <prop key="性别">男</prop> </props> </property> </bean> </beans>
-
测试类
import com.yunthin.pojo.Student; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); Student student = (Student) context.getBean("student"); System.out.println(student.getName()); } }
拓展方式注入
可以使用 p 命名空间和 c 命名空间进行注入
<?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- p命名空间注入,可以直接注入属性的值: property-->
<bean id="user" class="com.yunthin.pojo.User" p:name="a " p:age="18"/>
<!-- c命名空间注入,通过构造器注入: construct-args-->
<bean id="user2" class="com.yunthin.pojo.User" c:age="20" c:name="chow yunthin"/>
</beans>
注意点:p命名和c命名空间不能直接使用,需要导入约束
bean的作用域
-
单例模式(Spring默认机制)
<bean id="user" class="com.yunthin.pojo" scope="singleton"/>
-
原型模式:每次从容器中get的时候,都会产生一个新对象
<bean id="user" class="com.yunthin.pojo" scope="prototype"/>
- 其余的 request、session、application这些只在web开发中使用到
bean的自动装配
- 自动装配是Spring满足bean依赖的一种方式
- Spring会在上下文中自动寻找,并自动给bean装配属性
Spring中有三种装配的方式
- 在xml中显式地配置
- 在java中显式配置
- 隐式地自动装配bean【重要】
测试
- 环境搭建
- 一个人有两个宠物
byName自动装配
会自动在容器上下文中查找和自己对象set方法后面的值(小写首字母)对应的bean的id
<bean id="people" class="class.yunthin.pojo.People" autowire="byName"/>
byType自动装配
会自动在容器上下文中查找,和自己对象属性类型相同的bean id
<bean class="com.yunthin.pojo.Cat"/>
<bean class="com.yunthin.pojo.Dog"/>
<bean id="people" class="class.yunthin.pojo.People" autowire="byType"/>
小结:
- byName的时候,需要保证所有的bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致
- byType的时候,需要保证所有的bean的class唯一,并且这个bean需要和自动注入的属性的类型一样
使用注册实现自动装配
jdk1.5支持的注解 ,Spring2.5就支持注解了
要使用注解须知:
- 导入约束:context约束
- 配置注解的支持:context:annotation-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"
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
https://www.springframework.org/schema/context/spring-context.xsd
">
<context:annotation-config/>
</beans>
@Autowired
官方建议在set方法上使用
可以直接在属性上使用,可以直接省掉set方法,但前提是这个自动装配的属性在IOC(Spring)容器中存在,且类型要与属性相符
可以与Qualifier(value=“id”)使用
public class People{
@Autowired
@Qualifier(value="cat111")
private Cat cat;
@Autowired
@Qualifier(value="dog222")
private Dog dog;
private String name;
}
或者使用
@Resource(value=“qualifier”)
需要加个javax.annotation-api-1.3.2.jar包
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
public class People{
@Resource(name = "cat2")
private Cat cat;
@Resource
private Dog dog;
}
小结:
@Resource 和 @Autowired 的区别:
- 都是用来自动装配的,都可以放在属性字段上
- @Autowired 通过byType的方式实现,而且必须要求这个对象存在,如果有多个对应的对象,再通过byName
- @Resource 默认通过byName的方式实现,找不到名字再byType
使用注解开发
在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
https://www.springframework.org/schema/context/spring-context.xsd
">
<context:annotation-config/>
</beans>
-
bean
-
属性如何注入
//等价于<bean id="user" class="com.yunthin.pojo.User"/> @Component public class User { @Value("yunthin") public String name; } //或者在set方法上注解 //等价于<bean id="user" class="com.yunthin.pojo.User"/> @Component public class User { public String name; @Value("yunthin") public void setName(String name) { this.name = name; } }
-
衍生的注解
@Component有几个衍生的注解,功能和@Component等价,用于web开发中的mvc分层架构的区分,都是代表将某个类注册到Spring中,装配Bean
- dao @Repository
- service @Service
- controller @Controller
-
自动装配置
-
作用域 @Scope
-
小结
xml与注解
- xml更加万能,适用于任何场合,维护简单方便
- 注解 不是自己的类使用不了,维护相对复杂
最佳实践:
- xml用来管理bean
- 属性使用注解注入
- 我们在使用的过程中,只需要注意一个问题:必须让注解生效,就需要开启注解的支持
使用Java的方式配置Spring
我们现在要完全不使用Spring的xml配置了,全权交给Java来做
JavaConfig是Spring的一个子项目,在Spring 4之后,成为了一个核心功能
//这个也会Spring容器托管,注册到容器中,因为Configuration本来就是一个@Component
//@Configuration代表这是一个配置类,等同于之前看到的beans.xml
@Configuration
@ComponentScan("com.yunthin.pojo")
@Import(YunthinConfig.class)
public class YunthinConfig{
//注册一个bean,就相当于我们之前写的一个bean标签
//这个方法的名字,就相当于bean标签中的id属性
//这个方法的返回值,就相当于bean标签中的class属性
@Bean
public User getUser(){
return new User();//就是返回要注入到bean的对象
}
//到时候用的时候就是:context.getBean("getUser")
}
测试类
public class MyTest{
public static void main(String[] args){
//如果完全使用配置类的方法去做,我们就只能通过 AnnotationConfig 上下文来获取容器,通过配置类的class对象加载
ApplicationContext context = new AnnotationConfigApplicationContext(YunthinConfig.class);
User user = (User)context.getBean("getUser");
System.out.println(getUser.getName());
}
}
这种纯Java的配置方式,在SpringBoot中,随处可见
代理模式
因为这就是Spring AOP的底层 【SpringAOP 和 SpringMVC面试必问】
代理模式的分类:
- 静态代理
- 动态代理
静态代理
角色分析:
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
- 客户:访问代理对象的人
代码步骤:
- 接口
- 真实角色
- 代理角色
- 客户端访问代理角色
代理模式的好处:
- 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
- 公共业务就交给代理角色,实现了业务的分工,耦合性降低
- 公共业务发生扩展的时候,方便集中管理
静态代理缺点:
- 一个真实角色就会产生一个代理角色;代码量翻倍,开发效率变低(解决这个问题的方法就叫动态代理)
动态代理
- 动态代理和静态代理角色一样
- 动态代理的代理类是动态生成的,不是我们直接写好的
- 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口——JDK 动态代理 【在这里使用的】
- 基于类:cglib
- java字节码实现:javasist
需要了解两个类:Proxy, InvocationHandler
InvocationHandler是lang.reflect反射包下的一个接口,是调用处理程序
Proxy是lang.reflect反射包下的一个类,是代理
动态代理的好处:
- 静态代理的好处全都有
- 一个动态代理类代理的是一个接口,一般就是对应的一类业务
- 一个动态代理类可以代理多个类,只要是实现了同一个接口即可
public class Client {
public static void main(String[] args) {
Host host = new Host();
ProxyIncovationHandler pih = new ProxyIncovationHandler();
pih.setIntface_to_proxy(host);
Rent proxy = (Rent) pih.getProxy();
proxy.rent();
}
}
public interface Rent {
public void rent();
}
public class Host implements Rent{
@Override
public void rent() {
System.out.println("房东要租房子");
}
}
//用这个类,自动生成代理类
public class ProxyIncovationHandler implements InvocationHandler {
Object intface_to_proxy;
public void setIntface_to_proxy(Object intface_to_proxy) {
this.intface_to_proxy = intface_to_proxy;
}
//生成得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(), intface_to_proxy.getClass().getInterfaces(),this);
}
//处理代理实例,并返回结果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
return method.invoke(intface_to_proxy,args);
}
public void log(String msg){
System.out.println("执行了" + msg + "方法");
}
}
核心就是通过Proxy这个类的静态方法getProxy获得proxy对象,然后通过InvocationHandler的invoke方法,实现代理类方法的托管
AOP
什么是AOP?
- AOP (Aspect Oriented Programming) 面向切面编程。通过预编译的方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的一种延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率
AOP在Spring中的作用
提供声明式事物;允许用户自定义切面
在SpringAOP中,通过Advice定义横切逻辑,Spring中支持5中类型的Advice。即AOP在不改变原有代码的情况下,去增加新的功能
使用Spring实现AOP
【重点】使用AOP织入,需要导入一个依赖包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
方式一:使用Spring的api接口【主要SpringAPI接口实现】
方式二: 使用自定义类来实现【主要是切面定义】
方式三:使用注解实现
声明式事物
- 把一组业务当成一个业务来做,要么都成功,要么都失败
- 事物在项目开发中,十分的重要,涉及到数据的一致性问题,不能马虎
- 确保完整性和一致性
事物的ACID原则:
- Atomicity 原子性
- Consistenncy 一致性
- Isolation 隔离性
- 多个业务可能操作同一个资源,防止数据损坏
- Durability 持久性
- 事物一旦提交,无论系统发生什么问题,结果都不会再被影响,被持久化地写到储存器中
Spring中的事物管理
- 声明式事物:AOP
- 编程式事物:需要在代码中,进行事物的管理
思考:
为什么需要事物?
- 如果不配置事物,可能存在数据提交不一致的情况
- 如果我们不再Spring中去配置声明式事物,我们就需要在代码中手动配置事务
- 事物在项目的开发中十分重要,涉及到数据的一致性和完整性问题,不容马虎