# SpringStudy **Repository Path**: Mr-Sponge/spring-study ## Basic Information - **Project Name**: SpringStudy - **Description**: 根据B站狂神教学视频学习-源代码 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-08-26 - **Last Updated**: 2024-09-03 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # SpringStudy # 狂神说Spring笔记+源码 # Spring5学习 # Spring-Study 代码:https://github.com/Donkequan/Spring-Study ## 1. 简介 spring理念:是现有的技术更加容易使用,本身是一个大杂烩。 - SSH:Struct2 + Spring + Hibernate - SSM: SpringMVC + Spring + Mybatis 官网: https://spring.io/projects/spring-framework#overview 官方下载: https://repo.spring.io/release/org/springframework/spring/ GitHub: https://github.com/spring-projects/spring-framework [Spring Web MVC](https://mvnrepository.com/artifact/org.springframework/spring-webmvc) **»** [5.2.5.RELEASE](https://mvnrepository.com/artifact/org.springframework/spring-webmvc/5.2.5.RELEASE) ```xml org.springframework spring-webmvc 5.2.5.RELEASE org.springframework spring-jdbc 5.2.3.RELEASE ``` - spring是开源的免费的容器。 - spring是一个轻量级的,非入侵式的。 - 控制反转(IOC),面向切面编程 (AOP)。 - 支持事务处理,对框架整合的支持。 总结:spring是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架。 ## 2.IOC理论 1. UserDao 2. UserDaoImp 3. UserSevice 4. UserServiceImp 在之前,用户的需求可能会影响原来的代码。 使用一个set。 ```java public void setUserDao(UserDao userDao){ this.userDao = userDao; } ``` - 之前是主动创建对象,控制权在程序员手上。 - 使用set之后,是被动接受对象。 ## 3. Hello Spring pojo中 ```java package com.pzb.pojo; /** * @Description TODO * @Author zbpeng * @DATE 2024/8/25 11:57 */ public class Hello { private String str; public String getStr() { return str; } public void setStr(String str) { this.str = str; } @Override public String toString() { return "Hello{\n" + "\tstr = " + str + "\n}"; } } ``` resource中 ```xml ``` test ```java import com.hou.pojo.Hello; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Mytest { public static void main(String[] args) { //获取spring上下文对象 ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); //我们的对象下能在都在spring·中管理了,我们要使用,直接取出来就可以了 Hello hello = (Hello) context.getBean("hello"); System.out.println(hello.toString()); } } ``` bean = 对象 id = 变量名 class = new的对象 property 相当于给对象中的属性设值 核心用set注入 第一个文件中 ```xml ``` ## 4. IOC创建对象的方式 1. 使用无参构造创建对象,默认。 2. 使用有参构造 * 下标赋值 ```xml ``` * 类型赋值(不建议使用) ```xml ``` * 直接通过参数名 ```xml ``` Spring类似于婚介网站! 你想不想要,对象都在里面。注册bean之后用不用都被实例化。(这些类相当于项目的静态属性) * 源码 beans.xml ```xml ``` MyTest.java ```java import com.pzb.pojo.User; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @Description TODO * @Author zbpeng * @DATE 2024/8/25 16:03 */ public class MyTest { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); // 无参构造 User user = (User) context.getBean("user"); System.out.println(user.toString()); // User{name='zhangsan'} // 有参构造--下标方式 User user2 = (User) context.getBean("user2"); System.out.println(user2.toString()); // User{name='lisi'} // 有参构造--类型方式 (不推荐,当有多个相同类型的就乱套了) User user3 = (User) context.getBean("user3"); System.out.println(user3.toString()); // User{name='wangwu'} // 有参构造--参数名方式 (推荐,理由:简单易懂) User user4 = (User) context.getBean("user4"); System.out.println(user4.toString()); // User{name='laoliu'} } } ``` ## 5. Spring配置 **别名** ```xml ``` > 别名可以重复,如果别名相同,会自动覆盖上一个数据。id不能重复,一旦重复就会报错 **bean的配置** ```xml ``` - id:bean的id标识符 - class:bean对象所对应的类型 - name:别名,更高级,可以同时取多个别名(多个别名用`,`、`;`或者空格进行分割)。 **import** 一般用于团队开发,它可以将多个配置文件,导入合并为一个 例如:张三用了`beans.xml`配置文件开发,李四用了`beans2.xml`配置文件开发,进行代码合并的时候可以合并成一个配置文件 ![image-20240825164442226](assets/image-20240825164442226.png) 创建一个`applicationContext.xml`配置文件,在文件中添加如下信息: ```xml ``` > 不过要注意以下 id 重复的问题,合并的文件允许重复,运行**不会报错**,重复的会被后面来的数据覆盖。 ## 6. DI(Dependency Injection)依赖注入 **构造器注入** 就是上面,略 **set方式注入**(重点) - 依赖:bean对象的创建依赖于容器 - 注入:bean对象中的所有属性,由容器来注入 【环境搭建】 1. 复杂类型 2. 真实测试对象 Student.java ```java package com.pzb.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; /** * @Description TODO * @Author zbpeng * @DATE 2024/8/25 16:53 */ @Data @AllArgsConstructor @NoArgsConstructor public class Student { private String name; private Address address; private String[] books; private List hobbys; private Map card; private Set games; private String wife; private Properties info; } ``` Address.java ```java package com.pzb.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; /** * @Description TODO * @Author zbpeng * @DATE 2024/8/25 16:53 */ @NoArgsConstructor @AllArgsConstructor @Data public class Address { private String address; } ``` beans.xml ```xml 西游记 红楼梦 三国演义 听歌 敲代码 CF Apex 123456 ``` MyTest.java ```java import com.pzb.pojo.Student; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * @Description TODO * @Author zbpeng * @DATE 2024/8/25 17:00 */ public class MyTest { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); Student student = (Student) context.getBean("student"); System.out.println(student.toString()); /*\ Student( name=zhangsan, address=Address( address=广东 ), books=[西游记, 红楼梦, 三国演义], hobbys=[听歌, 敲代码], card={身份证=123456789, 银行卡=123456789}, games=[CF, Apex], wife=null, info={学号=123456, 性别=男} ) */ } } ``` > 学习感受:真tm麻烦啊 **第三方** p标签和c标签 ```java @Test public void test2(){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("userBean.xml"); User user = context.getBean("user", User.class); System.out.println(user); // User(name=zhangsan, age=18) User user2 = context.getBean("user2", User.class); System.out.println(user2); // User(name=lisi, age=19) } ``` > 注意:p、c命名不能直接引用,需要导入约束,不过一般IDEA有提示。 **bean的作用域** ![1586093707060](assets/image-20240825174410843.png) * 单例模式(默认) ```xml ``` * 原型模式: 每次从容器中get的时候,都产生一个新对象! ```xml ``` MyTest.java ```java @Test public void test2(){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("userBean.xml"); User user = context.getBean("user", User.class); System.out.println(user); // User(name=zhangsan, age=18) User user1 = context.getBean("user", User.class); System.out.println(user == user1); // false } ``` * 其余的request、session、application这些只能在web开放中使用! > 思考:单例和原型模式都有哪些好处 ## 7. Bean的自动装配 - 自动装配是Spring是满足bean依赖的一种方式 - Spring会在上下文自动寻找,并自动给bean装配属性 在Spring中有三种装配的方式 1. 在xml中显示配置 2. 在java中显示配置 3. 隐式的自动装配bean 【重要】 4. 环境搭建:一个人有两个宠物 ```xml ``` 5. Byname自动装配:byname会自动查找,和自己对象set对应的值对应的id 保证所有id唯一,**并且和set注入的值一致** beans.xml ```xml ``` Test.java ```java @Test public void test1(){ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); People people = context.getBean("people", People.class); people.getCat().shout(); // 喵喵喵~~~ people.getDog().shout(); // java.lang.NullPointerException } ``` > 由于autowire 的类型为byName ,beans.xml的配置文件中没有id为dog的配置信息,所以不能自动注入dog数据,报了空指针异常。 6. Bytype自动装配:byType会自动查找,和自己对象属性相同的bean 保证所有的class唯一 ```java public class Cat { public void jiao(){ System.out.println("miao"); } } public class Dog { public void jiao(){ System.out.println("wow"); } } package com.pojo; public class People { private Cat cat; private Dog dog; private String name; @Override public String toString() { return "People{" + "cat=" + cat + ", dog=" + dog + ", name='" + name + '\'' + '}'; } public Cat getCat() { return cat; } public void setCat(Cat cat) { this.cat = cat; } public Dog getDog() { return dog; } public void setDog(Dog dog) { this.dog = dog; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ``` **使用注解自动装配** jdk1.5支持的注解,spring2.5支持的注解 The introduction of annotation-based configuration raised the question of whether this approach is “better” than XML. ==导入context约束== ```xml ``` 没有导入的xml文件 ```xml ``` @Autowire 在属性上个使用,也可以在set上使用 我们可以不用编写`set`方法了 ```java public class People { @Autowired private Cat cat; @Autowired private Dog dog; private String name; } @Nullable 字段标志的注解,说明这个字段可以为null ``` 如果@Autowired自动装配环境比较复杂。自动装配无法通过一个注解完成的时候 我们可以使用@Qualifier(value = "dog")去配合使用,指定一个**唯一的id**对象 ```java @Data @AllArgsConstructor @NoArgsConstructor public class People { private String name; // 如果定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为空 @Autowired(required = false) private Dog dog; @Autowired @Qualifier(value = "cat11") private Cat cat; } ``` @Resource(name="dog")也可以(这是java自带的注解) 区别: - @autowire通过byType实现,而且必须要求这个对象存在 - @resource默认通过byName实现,如果找不到,通过byType实现,如果都找不到就会报错 ## 8. 使用注解开发 在spring4之后,必须要保证aop的包导入 使用注解需要导入contex的约束 ```xml ``` 1. 类的注解 ```java @Component // 等价于 public class User { public String name; } public class MyTest { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); User user = context.getBean("user", User.class); System.out.println(user.name); } } ``` 2. 属性如何注入:@Value ```java @Component // 等价于 public class User { @Value("zhangsan") public String name; public Integer age; @Value("18") // @Value不仅可以给属性赋值,还可以给方法参数赋值 public void setAge(Integer age){ this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } } ``` 3. 衍生的注解 @Component有几个衍生注解,会按照web开发中,mvc架构中分层。 - dao (@Repository) - service(@Service) - controller(@Controller) 这四个注解功能一样的,都是代表将某个类注册到容器中 4. 作用域 @Scope("singleton") ```java @Component @Scope("prototype") public class User { @Value("dong") private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } ``` 小结: xml与注解 - xml更加万能,维护简单 - 注解,不是自己的类,使用不了,维护复杂 最佳实践: - xml用来管理bean - 注解只用来完成属性的注入 ```xml ``` ## 9. 使用java方式配置spring JavaConfig Spring的一个子项目,在spring4之后,他成为了核心功能 MyConfig.java ```java package com.pzb.config; import com.pzb.pojo.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; /** * @Description TODO * @Author zbpeng * @DATE 2024/8/26 11:26 */ @Configuration // 这个也会被spring容器托管,注册到容器中,因为他本来就是一个@Component,@Configuration代表这是一个配置类,就和我们之前看的beans.xml一样 (具体可以点进去看看源码) // 既然@Configuration相当于beans.xml,那它也可以支持扫描类功能 @ComponentScan("com.pzb.pojo") // 那他也支持多个“beans.xml”合并的功能 @Import(MyConfig2.class) public class MyConfig { // 注册一个bean,就相当于之前的 bean 标签,id为这个方法的名字,class为这个方法的返回值 @Bean public User getUser(){ return new User(); // 返回的就是要注入到bean的对象 } } ``` User.java ```java public class User { public String name; public String getName() { return name; } @Value("zhangsan") public void setName(String name) { this.name = name; } @Override public String toString() { return "User{" + "name='" + name + '\'' + '}'; } } ``` MyConfig2.java ```java @Configuration public class MyConfig2 { } ``` MyTest.java ```java public class MyTest { public static void main(String[] args) { // 通过配置类加载bean AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class); // 通过配置类下的方法名进行调用bean User getUser = context.getBean("getUser", User.class); System.out.println(getUser.toString()); } } ``` 这种纯java配置方式 在springboot中,随处可见 ## 10. 动态代理 动态代理和静态代理 角色一样 动态代理类是动态生成的,不是我们直接写好的! 动态代理:基于接口,基于类 - 基于接口:JDK的动态代理【使用】 - 基于类:cglib - java字节码 InvocationHandler Proxy ```java import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; //会这个类,自动生成代理类 public class ProxyInvocation implements InvocationHandler { //被代理的接口 private Rent rent; public void setRent(Rent rent) { this.rent = rent; } //生成代理类 public Object getProxy(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this); } //处理代理实例,并返回结果 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { seeHouse(); Object result = method.invoke(rent, args); fare(); return result; } public void seeHouse(){ System.out.println("see house"); } public void fare(){ System.out.println("fare"); } } public interface Rent { void rent(); } public class Host implements Rent { public void rent() { System.out.println("host rent"); } } public class Client { public static void main(String[] args) { //真实角色 Host host = new Host(); //代理角色 ProxyInvocation proxyInvocation = new ProxyInvocation(); //通过调用程序处理角色来处理我们要调用的接口对象 proxyInvocation.setRent(host); Rent proxy = (Rent) proxyInvocation.getProxy(); //这里的proxy是动态生成的 proxy.rent(); } } ``` ## 11.AOP ```xml org.aspectj aspectjweaver 1.9.4 ``` 方法一:使用spring接口【springAPI接口实现】 ```xml public class UserServiceImp implements UserService { public void add() { System.out.println("add"); } public void delete() { System.out.println("delete"); } public void query() { System.out.println("query"); } public void update() { System.out.println("update"); } } import org.springframework.aop.MethodBeforeAdvice; import java.lang.reflect.Method; public class Log implements MethodBeforeAdvice { //method:要执行的目标对象的方法 //args:参数 //target:目标对象 public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println(target.getClass().getName()+method.getName()); } } public class AfterLog implements AfterReturningAdvice { //returnVaule: 返回值 public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable { System.out.println(method.getName()+returnValue); } } public class Mytest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("ApplcationContext.xml"); //动态代理代理的是接口 UserService userService = (UserService) context.getBean("userservice"); userService.add(); } } ``` 方法二:自定义来实现AOP【主要是切面定义】 ```xml public class DiyPointcut { public void before(){ System.out.println("before"); } public void after(){ System.out.println("after"); } } ``` 方法三:注解方式 ```xml import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect //标注这个类是一个切面 public class Annotation { @Before("execution(* com.service.UserServiceImp.*(..))") public void before(){ System.out.println("before"); } @After("execution(* com.service.UserServiceImp.*(..))") public void after(){ System.out.println("after"); } //在环绕增强中,我们可以给地暖管一个参数,代表我们要获取切入的点 @Around("execution(* com.service.UserServiceImp.*(..))") public void around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("around"); Object proceed = joinPoint.proceed(); System.out.println("after around"); } } ``` ## 12. 整合mybatis 文档: https://mybatis.org/spring/zh/ ```xml spring-study com.hou 1.0-SNAPSHOT 4.0.0 spring-10-mybatis mysql mysql-connector-java 5.1.47 org.mybatis mybatis-spring 2.0.4 org.springframework spring-jdbc 5.2.3.RELEASE org.mybatis mybatis 3.5.2 org.aspectj aspectjweaver 1.9.4 org.projectlombok lombok 1.18.12 src/main/resources **/*.properties **/*.xml src/main/java **/*.properties **/*.xml true public interface UserMapper { List selectUser(); } ``` 整合 方法一: ![1586177510119]() UserMapperImpl ```java package com.mapper; import com.pojo.User; import org.mybatis.spring.SqlSessionTemplate; import java.util.List; public class UserMapperImpl implements UserMapper { private SqlSessionTemplate sqlSessionTemplate; public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) { this.sqlSessionTemplate = sqlSessionTemplate; } public List selectUser() { UserMapper mapper = sqlSessionTemplate.getMapper(UserMapper.class); return mapper.selectUser(); } } ``` mybatis.xml ```xml ``` spring.xml ```xml ``` test ```java import com.mapper.UserMapper; import com.pojo.User; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.io.IOException; public class Mytest { public static void main(String[] args) throws IOException { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml"); UserMapper userMapper = context.getBean("userMapper", UserMapper.class); for (User user : userMapper.selectUser()) { System.out.println(user); } } } ``` 方法二: ```xml package com.mapper; import com.pojo.User; import org.apache.ibatis.session.SqlSession; import org.mybatis.spring.support.SqlSessionDaoSupport; import java.util.List; public class UserMapperIml2 extends SqlSessionDaoSupport implements UserMapper { public List selectUser() { SqlSession sqlSession = getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); return mapper.selectUser(); } } ``` ## 13. 声明式事务 - 要么都成功,要么都失败 - 十分重要,涉及到数据一致性 - 确保完整性和一致性 事务的acid原则: - 原子性 - 一致性 - 隔离性 - 多个业务可能操作一个资源,防止数据损坏 - 持久性 - 事务一旦提交,无论系统发生什么问题,结果都不会被影响。 Spring中的事务管理 - 声明式事务 - 编程式事务 声明式事务 ```xml ``` Mapper ```java package com.mapper; import com.pojo.User; import java.util.List; public interface UserMapper { List selectUser(); int addUser(User user); int delete(int id); } insert into mybatis.user (id, name, pwd) values (#{id}, #{name}, #{pwd}) delete from mybatis.user where id=#{id} package com.mapper; import com.pojo.User; import org.apache.ibatis.session.SqlSession; import org.mybatis.spring.support.SqlSessionDaoSupport; import java.util.List; public class UserMapperIml2 extends SqlSessionDaoSupport implements UserMapper { public List selectUser() { User user = new User(6, "long", "zhi"); SqlSession sqlSession = getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); mapper.addUser(user); mapper.delete(6); return mapper.selectUser(); } public int addUser(User user) { return getSqlSession().getMapper(UserMapper.class).addUser(user); } public int delete(int id) { return getSqlSession().getMapper(UserMapper.class).delete(id); } } ```