Spring学习笔记
1、简介
- Spring是一个开源的免费的框架(容器)
- Spring是一个轻量级的、非入侵式的框架
- 控制反转((IOC),面向切面编程(AOP)
- 支持事务的处理,对框架整合的支持
总结一句话: Spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架。
org.springframework
spring-webmvc
5.3.14
2、IOC本质
- 控制反转IOC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IOC的一种方法,也有人认为DI只是IOC的另一种说法。没有lOC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。
- 采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
- 控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在 Spring 中实现控制反转的是IOC容器,其实现方法是依赖注入(Dependency Injection,Dl)。
3、XML创建实体对象
3.1 创建实体类Hello
package pojo;
// 只有无参构造函数
public class Hello {
private String str;
/*
public Hello(String name){
this.str = name;
}
*/
public String getStr() {
return str;
}
public void setStr( String str ) {
this.str = str;
}
@Override
public String toString() {
return "Hello{" +
"str='" + str + '\'' +
'}';
}
}
3.2 Spring配置文件Beans.xml
<?xml version="1.0" encoding="UTF-8" ?>
- 编写完成后记得配置应用程序上下文
3.3 测试运行
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import pojo.Hello;
public class MyTest {
@Test
public void Test() {
// 获取Spring的上下文对象!
ApplicationContext context = new ClassPathXmlApplicationContext( "beans.xml" );
// 我们的对象现在都在Spring中的管理了,我们要使用,直接去里面取出来就可以!
Hello hello = (Hello) context.getBean( "hello" );
System.out.println( hello.toString() );
//输出:Hello{str='Spring'}
}
}
4、Spring配置
4.1 别名
- 别名和原名都能够取到对象
ApplicationContext context = new ClassPathXmlApplicationContext( "beans.xml" );
// 以下两种均可
Hello hello = (Hello) context.getBean( "user" );
Hello hello = (Hello) context.getBean( "userNew" );
4.2 Bean的配置
4.3 import
这个import,一般用于团队开发使用,他可以将多个配置文件,导入合并为一个配置文件。
假设,现在项目中有多个人开发,这三个人复制不同的类开发,不同的类需要注册在不同的bean中。
我们可以利用import将所有人的beans.xml合并为一个总的!
使用的时候,使用总的配置就可以了
正式的总配置文件名为applicationContext.xml
5、依赖注入
5.1 构造器注入
见3.2 Spring配置文件Beans.xml
5.2 set方式注入(重点)
public class Student {
private String name;
private Address address;
private String[] books;
private List hobbys;
private Map card;
private Setgames;
private String wife;
private Properties info;
}
红楼梦
西游记
水浒传
三国演义
听歌
敲代码
看电影
LOL
coc
BOB
xxx
xxx
root
123456
5.3 拓展方式注入
5.3.1 p命名空间注入
- 注意导入schema:xmlns:p="http://www.springframework.org/schema/p"
5.3.2 C命名空间注入
- 注意导入schema:xmlns:c="http://www.springframework.org/schema/c"
5.4 Bean的作用域
-
单例模式 Singleton(Spring默认机制)
-
原型模式 Prototype :每次从容器中getBean的时候,都会产生一个新对象!
-
其余的request、session、application只能在Web开发中使用到。
6、Bean的自动装配
- 自动装配是 Spring 满足 bean 依赖的一种方式!
- Spring 会在上下文中自动寻找,并自动给 bean 装配属性!
- 在 Spring 中有三种装配的方式
- 在 XML 中显式配置
- 在 Java 中显式配置
- 隐式的自动装配 bean
6.1 ByName自动装配
6.2 ByType自动装配
小结:
-
byName 的时候,需要保证所有 bean 的 id 存在且唯一,并且这个 bean 需要和自动注入的属性的 set 方法的值一致!
-
byType 的时候,需要保证所有 bean 的 class 存在且唯一,并且这个 bean 需要和自动注入的属性的类型一致!
6.3 使用注解实现自动装配
使用注解须知:
-
导入约束
-
配置注解的支持:
<?xml version="1.0" encoding="UTF-8"?>
...
6.3.1 @Autowired
直接在属性上使用!也可以在set方式上使用!
使用@Autowired 我们可以不用编写Set方法了,前提是你这个自动装配的属性Type在 Spring 容器中存在,且符合名字byName!
public class People {
@Autowired
private cat cat;
//@Autowired无法实现时,要配合@qualifier(value="")来针对某一个bean进行装配
@Autowired
@Qualifier(value="dog222")
private Dog dog;
private string name;
}
6.3.2 @Resource
public class People {
@Resource(name = "cat11")
private cat cat;
@Resource
private Dog dog;
}
@Resource 和 @Autowired 的区别:
- 都是用来自动装配的,都可以放在属性字段上
- @Autowired 先通过byType的方式实现,后通过byName,而且必须要求这个对象存在!【常用】
- @Resource 默认通过byname的方式实现,如果找不到名字,则通过byType实现!如果两个都找不到的情况下,就报错!【常用】
小结:
-
@Autowired 先byType,如果同类型个数大于1,再byName。
-
dog只注册了一个的时候,dog的id可以随便取都能自动装配相当于byType。当dog注册了多个时,只能绑定id和person属性名一样的bean相当于byName,如果多个都没有对应的id就报错
-
如果 @Autowired 自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候、我们可以使用@Qualifier(value="xxx") 去配置 @Autowired 的使用,指定一个唯一的bean对象注入!
这个value对应的是bean的id,相当于byName。
7、使用注解开发
在 Spring 4 之后,要使用注解开发。必须要保证 aop 的包导入了!
使用注解需要导入 context 约束,增加注解的支持。
<?xml version="1.0" encoding="UTF-8"?>
7.1 常用注解
7.1.1 @Autowired
- 通过类型、名字,自动装配
- 如果 @Autowired 不能唯一装配上属性,则需要通过 @Qualifier( value = "xxx" ) 指定唯一bean
7.1.2 @Resource
- 通过名字、类型,自动装配
7.1.3 @Nullable
- 字段标记了这个注解,说明这个字段可以为null
7.1.4 @Component
- 组件,放在类上,说明这个类被 Spring 管理了,成为Bean。
//等价于:
@Component
public class User {
public string name;
public void setName(string name) {
this.name = name;
}
}
7.1.5 @Value
- @Value( ) 放在属性或set方法上均可。
@component
public class User {
//相当于
@Value( "kuangshen2")
public string name;
public void setName( String name ) {
this.name = name;
}
}
7.1.6 @Scope
@component
//确定作用域:单例或原型
@scope("prototype")
public class User {
public string name;
@Value( "kuangshen2")
public void setName( String name ) {
this.name = name;
}
}
7.2 衍生的注解
-
@Component有几个衍生注解,我们在web开发中,会按照MVC三层架构分层!
- dao【@Repository】
- service【@Service】
- controller【@Controller】
这四个注解功能都是一样的,都是代表将某个类注册到 Spring 容器中,装配Bean。
8、使用 Java 的方式(注解)配置 Spring
我们现在要完全不使用 Spring 的 XML 配置了,全权交给 Java来做!
JavaConfig 是 Spring 的一个子项目,在 Spring 4 之后,它成为了一个核心功能
package config;
import pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
// 这个也会由Spring容器托管,注册到容器中,因为他本来就是一个@Component
// @Configuration代表这是一个配置类,就和我们之前看的applicationContext.xml一样
@configuration //相当于
@ComponentScan( "pojo" ) //扫描某个包下的所有@Component
@Import(AppConfig2.class)
public class AppConfig {
// 注册一个bean ,就相当于我们之前写的一个标签
// 这个方法的名字,就相当于bean标签中的id属性
// 这个方法的返回值,就相当于bean标签中的class属性
@Bean
public User user() {
return new User();
}
}
- 测试
public class MyTest {
public static void main(string[] args) {
// 如果完全使用了配置类方式去做,我们就只能通过 AnnotationConfig 上下文来获取容器
// 通过配置类的class对象加载!
Applicationcontext context = new
AnnotationConfigApplicationContext(Appconfig.class);
User user = (user) context.getBean("user");
System.out.println(user.getName());
}
}
9、代理模式
9.1 静态代理
- 代理对象和真实对象都有共同的接口
- 客户直接通过代理对象直接调用方法
- 在代理类中静态扩展代码
9.2 动态代理
- 动态代理和静态代理角色是一样的。
- 动态代理的代理类是动态生成的,不是我们直接写好的!
- 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口 —— JDK 动态代理
- 基于类 —— cglib
- Java字节码实现 —— Javassist
需要了解两个类: Proxy:代理 Invocationhandler:调用处理程序
//等我们会用这个类,自动生成代理类!
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private object target;
public void setTarget(object target) {
this.target = target;
}
//生成得到代理类
public object getProxy(){
return Proxy.newProxyInstance(this.getclass().getclassLoader(),
target.getclass().getInterfaces(), this);
}
//处理代理实例,并返回结果:
public object invoke(0bject proxy,Method method,object[] args) throws Throwable {
log(method.getName());
object result = method.invoke(target, args);
return result;
}
public void log(string msg){
system.out.println("执行了" + msg + "方法");
}
}
public class client {
public static void main( string[ ] args) {
//真实角色 (实现类)
UserServiceImpl userService = new UserServiceImpl();
//创建代理生成类的实例
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setTarget(userservice); //设置要代理的对象
//代理角色 (动态生成代理类)
UserService proxy = (UserService) pih.getProxy();
proxy.query();
}
}
动态代理相对于静态代理的好处:
无需编写代理类:对于静态代理,每个类都要写对应的代理类,会多出许多冗余的类;而动态代理,仅需通过一个代理生成类,动态生成对应类的对应代理对象,减少了代码量。
10、AOP
10.1 图解AOP
- 什么叫面向切面编程?这就是:
10.2 Spring 实现 AOP
org.aspectj
aspectjweaver
1.9.7
10.2.1 使用原生 Spring API
10.2.2 自定义类实现
10.2.3 注解实现AOP
//方式三:使用注解方式实现AOP
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
@Aspect //标注这个类是一个切面
public class AnnotationPointcut {
@Before( "execution(* com.kuang.service.UserServiceImpl.*(..))")
public void before(){
system.out.println( "=======方法执行前======" );
}
@After( "execution(* com.kuang.service.UserServiceImpl.*(..))")
public void after(){
system.out.println( "=======方法执行后======" );
}
// 在环绕增强中,我们可以给定一个参数,代表我们要获取处理切入的点;
// 可以获取切入点的相关信息。
@Around( "execution(*com.kuang.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
system.out.println("环绕前");
object proceed = jp.proceed(); //执行方法
system.out.println("环绕后");
}
}
10.3 Spring AOP 五大通知
-
Before 前置通知
-
AfterReturning 后置通知
-
Around 环绕通知
-
AfterThrowing 异常通知
-
After 最终通知
五种通知的执行顺序
在目标方法没有抛出异常的情况下
前置通知 → 环绕通知的调用目标方法之前的代码 → 目标方法 → 环绕通知的调用目标方法之后的代码 →
后置通知 → 最终通知
在目标方法抛出异常的情况下
前置通知 → 环绕通知的调用目标方法之前的代码 → 目标方法 → 抛出异常 异常通知 → 最终通知
11、整合Mybatis
? 详细内容可以查看文档
11.1 基础方式
-
导入相关Jar包
- Junit
- Mybatis
- MySQL数据库
- Spring 相关
- AOP 织入
- mybatis-spring 【new】
-
编写 Spring 、Mybatis 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8"?>
-
写 Mapper.xml(sql语句)
......
-
写 Mapper 的实现类 MapperImpl
import org.mybatis.spring.sqlSessionTemplate; import java.util.List; public class UserMapperImpl implements UserMapper { private sqlsessionTemplate sqlsession; public void setSqlSession(sqlSessionTemplate sqlSession) { this.sqlSession = sqlSession; } public List
selectUser() { UserMapper mapper = sqlSession.getMapper(UserMapper.class); return mapper.selectuser(); } }
11.2 SqlSessionDaoSupport
- 实现类去掉属性 sqlSession 和 set 方法
- 直接通过getSqlSession()获取sqlSession,无需注入依赖
import org.mybatis.spring.sqlSessionTemplate;
import java.util.List;
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {
public List selectUser() {
return getSqlSession().getMapper(UserMapper.class).selectuser();
}
}
同时,在 spring-dao.xml 中去掉:
并修改:
12、事务
12.1 什么叫事务?
- 把一组业务当成一个业务来做;要么都成功,要么都失败!
- 事务在项目开发中,十分的重要,涉及到数据的一致性问题,不能马虎!·确保完整性和一致性;
- 事务ACID原则:
- 原子性:一个事务要么全部执行,要么不执行。
- 一致性:数据库事务不能破坏关系数据的完整性及业务逻辑上的一致性。
- 隔离性:多个业务可能操作同一个资源,防止数据损坏。
- 持久性:事务一旦提交,无论系统发生什么问题,结果都不会再被影响,被持久化的写到存储器中。
12.2 Spring 事务管理
- 声明式事务:AOP
- 编程式事务:需要在代码中,进行事务的管理
为什么需要事务?
如果不配置事务,可能存在数据提交不一致的情况。
如果我们不在 Spring 中去配置声明式事务,我们就需要在代码中手动配置事务!
事务在项目的开发中十分重要,设计到数据的一致性和完整性问题,不容马虎!
12.3 Spring 声明式事务