Study-Spring
1.简介
1.1 拓展
- Spring Boot
- 一个快速开发的脚手架
- 基于SpringBoot可以快速开发单个微服务
- 约定打与配置
- Spring Cloud
- 基于SpringBoot实现
大部分公司在进行SpringBoot进行快速开发,学习SpringBoot的前提,需要完全掌握Spring和SpringMVC。承上启下的作用。
弊端:发展了太久后,违背了以前的理念!配置十分繁琐。人称配置地狱
2.IOC理论推导
1.UserDao
2.UserDaoImpl
3.Service
4.ServiceImpl
- 之前程序主动创建对象!控制权在程序员手上。
- set注入后,程序不在具有主动性,而是变成被动接收对象。
- IOC 程序员不用管理对象的创建,而是交给用户处理,降低系统的耦合性。程序员可以专注在业务的实现上。这就是IOC的原型。
使用IOC进行依赖反转(橙色表示程序创建主动权)
百度百科----------控制反转**(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做**依赖注入(Dependency Injection,简称DI**),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
3.HelloSpring
控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象是由Spring创建的。
反转:程序本身不创建对象,而是北城被动的接收对象。
依赖注入:依赖set方法进行注入的。
IOC是一种编程思想,由主动的编程便被被动的接收。
ok,使用Spring容器后,我们cedilla不再用程序中进行改动,要实现不同的new操作,只需要在xml配置文件中进行修改。所谓的IOC,就是对象由Spring来创建、管理、装配
4、IOC创建对象的方式
通过对象的无参构造函数创建,当需要创建对象的pojo的类没有无参构造时,报错无法初始化
Caused by: java.lang.NoSuchMethodException: com.guo.pojo.User.<init>()
1.使用无参构造创建对象,默认!
2.若想使用带参构造器,则需要使用配置constructor-arg标签
-
第一种办法–下标赋值
<!-- 第一种方法:下标赋值 --> <bean id="user" class="com.guo.pojo.User"> <constructor-arg index="0" value="郭海亮"/> <property name="name" value="Coder Guo" /> </bean>
-
第二种办法–根据类型匹配
<bean id="user" class="com.guo.pojo.User"> <constructor-arg type="java.lang.String" value="用type匹配"/> </bean>
-
通过参数名进行匹配
<bean id="user" class="com.guo.pojo.User">
<constructor-arg name="name" value="通过name进行匹配"/>
</bean>
总结:在配置文件加载的时候,容器中管理的对象就已经被初始化了。
5、Spring配置
5.1别名
<!-- 使用别名 -->
<alias name="user" alias="userNew"/>
5.2 Bean的配置
<!--
id: bean的唯一标识符,也相当于变量名
class: bean对象所对应的全限定名-包名+类名
name: 别名,而且name可以同时取多个别名
-->
<bean id="userT" class="com.guo.pojo.UserT" name="userT2,ut2 ut3" />
5.3 import
一般用于团队开发使用,它可以将多个配置文件合并为同一个
<import resource="bean1.xml" />
6 依赖注入(DI)
6.1 构造器注入
6.2 Set方式注入(重点)
- 依赖注入:set注入
- 依赖:bean对象的创建依赖于容器
- 注入:bean对象中的所有属性,由容器来注入
环境搭建:
1.复杂类型
package com.guo.pojo;
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
2.真实测试对象
public class Student {
private String name;
private Address address;
private String[] books;
private List<String> hobbies;
private Map<String, String> card;
private Set<String> games;
private String wife;
private Properties info;
}
3.applicationContext.xml
<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="student" class="com.guo.pojo.Student">
<!-- 第一中国,普通值得注入 -->
<property name="name" value="Coder Guo" />
<!-- 第二种,使用bean注入 -->
<property name="address" ref="address" />
<!-- 3.数组注入 -->
<property name="books">
<array>
<value>红楼梦</value>
<value>西游记</value>
<value>水浒传</value>
<value>三国演义</value>
<value>java核心思想</value>
</array>
</property>
<!--4.list-->
<property name="hobbies">
<list>
<value>游泳</value>
<value>唱歌</value>
<value>听音乐</value>
<value>打游戏</value>
</list>
</property>
<!--5.map注入-->
<property name="card">
<map>
<entry key="身份证" value="3131231231231231231231313" />
<entry key="银行卡" value="2131231231231221354356476568758" />
</map>
</property>
<!--6.Set注入-->
<property name="games">
<set>
<value>LOL</value>
<value>WOW</value>
<value>CF</value>
<value>DNF</value>
</set>
</property>
<!--7.null值注入-->
<property name="wife">
<null />
</property>
<property name="info">
<props>
<prop key="userName">CoderGuo</prop>
<prop key="pwd">21389012</prop>
</props>
</property>
</bean>
<bean id="address" class="com.guo.pojo.Address" />
</beans>
用不同的方式进行注入
6.3 拓展方式注入
我们可以使用p命名空间和c命名空间进行注入
使用p命名空间和c命名空间在xml导入约束
xmlns:p="http://www.springframework.org/schema/p"
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: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命名空间的注入,可以直接注入属性的值: p:=property -->
<bean id="user" class="com.guo.pojo.User" p:name="CoderGuo" p:password="123" />
<!--c命名空间的注入,通过构造器注入 p:=property -->
<bean id="user2" class="com.guo.pojo.User" c:name="CoderGuo2" c:password="123456" />
</beans>
测试:
@Test
public void testC() {
ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
User user2 = context.getBean("user2", User.class);
System.out.println(user2.toString());
}
6.4、Bean的作用域
1.单例模式
<bean id="user2" class="com.guo.pojo.User" c:name="CoderGuo2" c:password="123456" scope="singleton"/>
2.原型模式
<bean id="user" class="com.guo.pojo.User" p:name="CoderGuo" p:password="123" scope="prototype"/>
3.其余的request、seesion、application这些这技能在web开发中使用到。
7、Bean的自动装配
- 自动装配是Spring满足bean依赖的一种方式
- Spring会在上下文中自动寻找、并自动给bean装配属性
在Spring中一共由三种装配的方式
- 在xml中显示的配置
- 在java中显示配置
- 隐式的自动装配
7.1 测试
环境搭建:一天个人拥有两个宠物dog和cat
7.2 ByName自动装配
!--
byName: 会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanId
byType: 会自动在容器上下文中查找,和自己对象睡醒相同的bean-弊端:id全局唯一
-->
<bean id="person" class="com.guo.pojo.Person" autowire="byName">
<property name="name" value="Coder Guo"/>
</bean>
7.3ByType自动装配
<!--
byName: 会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanId
byType: 会自动在容器上下文中查找,和自己对象睡醒相同的bean-弊端:id全局唯一
-->
<bean id="person" class="com.guo.pojo.Person" autowire="byType">
<property name="name" value="Coder Guo"/>
</bean>
小结:
- byName的时候,需要保证所有bean的i的唯一,并且这个bean需要和自动注入的属性的set方法的值一致。
- byType的时候,需要保证所有bean的Class唯一,并且这个bean需要和自动注入的属性的类型一致
7.4 使用注解实现自动装配
jdk1.5开始支持,spring2.5以后开始支持注解
@Autowired
1.先在xml导入约束 context
<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/content
http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
- 使用context标签-开启注解支持
<context:annotation-config />
3.使用@Autowired
直接在属性上使用即可,以可以在set方法上使用,
使用Autowired以后我们可以不在编写set方法
package com.guo.pojo;
import org.springframework.beans.factory.annotation.Autowired;
public class Person {
private Dog dog;
@Autowired
private Cat cat;
private String name;
public Dog getDog() {
return dog;
}
@Autowired
public void setDog(Dog dog) {
this.dog = dog;
}
public Cat getCat() {
return cat;
}
}
@Autowired
@Qualifier(value="dog1")
// 一般配套使用,解决set属性名和id名映射关系不一致
@Resource
@Resource(name = "cat2")
private Cat cat;
小结:@Resource和@Autowired的区别:
- 都是用来自动装配的, 都可以放在属性字段上
- @Autowired通过byType的方式实现
- @Resource默认通过byname的方式实现,若找不到名字,则通过bytype实现
8、使用注解开发
在spring4以后,要使用煮酒开发,必须要保证aop的包导入了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h7DB8til-1621594754359)(C:\Users\MSI\AppData\Roaming\Typora\typora-user-images\image-20210517101709462.png)]
使用注解需要导入context约束,增加注解的支持
<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
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.guo.pojo"/>
<context:annotation-config />
</beans>
1.bean
@Component: 组件、放在类上,说明这个类被Spring管理了
2.属性如何注入
@Value(“prop-value”)
3.衍生的注解
@Component有几个衍生注解,在mvc架构种
dao层:@Repository
service层: @Service
control层:@Controlller
这四个注解就功能都是一样的,都是代表将某个类注册到Spring中,装配到bean
4.自动装配
@Autowired
5.作用域
@Scope("")
6.小结
xml和注解
-
xml更加万能,适用于任何场合,维护方便,但是编写复杂
-
注解编写简单,但是维护复杂
两个是互补的关系
xml和注解的最佳实践
- xml用来管理bean
- 注解只负责完成属性的注入
9、完全使用Java的方式配置Spring
JavaConfig是Spring的一个子项目
pojo类:
//这个注解表示这个类被注册到Spring容器的中的一个组件
@Component
public class User {
private String name;
private String pwd;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
public String getName() {
return name;
}
@Value("Coder Guo")
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
}
配置类:
//configuration表示这是一个配置类,就像之前的xml文件
@Configuration
public class GuoConfig {
//注册一个bean,相当于我们之前xml文件中的一个bean标签
// 方法名---bean标签中的id属性
// 方法返回值---bean标签中的class属性
//
@Bean
public User getUser() {
return new User();
}
}
测试
public class MyTest {
@Test
public void test() {
// 如果完全使用了配置类方式去做,我们就只能通过AnnotationConfig上下文来获取容器
ApplicationContext context = new AnnotationConfigApplicationContext(GuoConfig.class);
User user1 = (User) context.getBean("getUser");
User user2 = (User) context.getBean("getUser");
System.out.println(user1.getName());
System.out.println(user1 == user2);
// 使用configuration 配置的bean默认情况下也是单列模式
System.out.println(user1.hashCode());
System.out.println(user2.hashCode());
}
}
10、代理模式
为什么要学习代理模式?aop底层就是代理模式。
代理模式的分类:
- 静态代理
- 动态代理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qnhKr3yf-1621594754361)(C:\Users\MSI\AppData\Roaming\Typora\typora-user-images\image-20210519132531624.png)]
10.1 静态代理
角色分析
- 抽象角色:一般会使用接口或者抽象类实现
- 真实角色:被代理得角色
- 代理角色:代理真实角色,代理真实角色后我们一般会增加一下附属得操作
- 客户:访问代理对象的人
diamagnetic步骤:
1.接口
package com.guo.demo;
public interface Rent {
public void rent();
}
2.真实角色
package com.guo.demo;
public class LandLord implements Rent {
public void rent() {
System.out.println("房东出租房子");
}
}
3.代理角色
package com.guo.demo;
public class Proxy implements Rent{
private LandLord landLord;
public Proxy() {
}
public Proxy(LandLord landLord) {
this.landLord = landLord;
}
public void fare() {
System.out.println("中介收取保护费用");
}
public void compact() {
System.out.println("中介签合同");
}
public void rent() {
landLord.rent();
fare();
compact();
}
}
4.客户端访问
package com.guo.demo;
public class User {
public static void main(String[] args) {
LandLord landLord = new LandLord();
Proxy proxy = new Proxy(landLord);
proxy.rent();
}
}
代理模式的好处:
- 可以使真实角色的操作更加纯粹,不用关注一些公共的业务
- 公共交给了代理角色实现业务的分工
- 公共业务发生拓展的时候,方便集中管理
缺点;
- 一个真实角色会产生一个代理角色–代码量会翻倍–开发效率会更低
10.2 动态代理
- 动态代理和静态代理角色一样
- 动态代理的代理类是动态生成的,不是我们直接喜好的
- 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
- 基于接口–JDK动态代理
- 基于类:cglib
- java字节码实现
需要了解两个类Proxy,InvocationHandler
动态代理的好处:
- 一个动态大力类代理的是一个接口,一般就是对应一类业务
- 一个动态代理类可以
11.AOP
导入依赖:
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.0.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.aspectj/com.springsource.org.aspectj.weaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>com.springsource.org.aspectj.weaver</artifactId>
<version>1.6.4.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.0.0.RELEASE</version>
</dependency>
方式1:使用Spring的api接口
注意事项: Spring的动态代理的是接口
log类
package com.guo.log;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class Log implements MethodBeforeAdvice {
// method: 要执行的目标对象的方法
//args: 参数
//args: 目标对象
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName() +
"的"+method.getName() + "被执行了");
}
}
UserService接口和实现类
package com.guo.service;
public interface UserService {
public void add();
public void update();
public void delete();
public void select();
}
package com.guo.service;
public class UserServiceImpl implements UserService{
public void add() {
System.out.println("增加了一个用户");
}
public void update() {
System.out.println("修改了一个用户");
}
public void delete() {
System.out.println("删除了一个用户");
}
public void select() {
System.out.println("查询了一个用户");
}
}
applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--1.注册bean-->
<bean id="userService" class="com.guo.service.UserServiceImpl" />
<bean id="log" class="com.guo.log.Log" />
<!--方式一:使用原生Spring Api接口-->
<!-- 配置aop:需要导入aop的约束 -->
<aop:config>
<!-- 1.切入点 expression:表达式 execution(要执行的位置) -->
<aop:pointcut id="pointcut" expression="execution(* com.guo.service.UserServiceImpl.*(..))"/>
<!--执行环绕增加!-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut" />
</aop:config>
</beans>
测试:
import com.guo.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MyTest {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) context.getBean("userService");
userService.add();
}
}
方式2、自定义类实现
自定义类
package com.guo.div;
public class DivPointCut {
public void before() {
System.out.println("===================方法执行之前===================================");
}
public void after() {
System.out.println("===================方法执行之后===================================");
}
}
applicationContext.xml
<!--方式二、自定义类-->
<bean id="div" class="com.guo.div.DivPointCut" />
<aop:config>
<aop:aspect ref="div">
<!--1.定义其切入点-->
<aop:pointcut id="point" expression="execution(* com.guo.service.UserService.*(..))"/>
<!-- 2。通知 -->
<aop:before method="before" pointcut-ref="point"/>
<aop:after method="after" pointcut-ref="point"/>
</aop:aspect>
测试结果
===================方法执行之前===================================
增加了一个用户
===================方法执行之后===================================
Process finished with exit code 0
方式3、使用注解实现
AnnotationPointCut类
package com.guo.div;
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 AnnotationPointCut {
@Before("execution(* com.guo.service.UserServiceImpl.*(..))")
public void before() {
System.out.println("===================before methods===================================");
}
@After("execution(* com.guo.service.UserServiceImpl.*(..))")
public void after() {
System.out.println("===================after methods===================================");
}
//在增强环绕中,我们可以给定一个参数,代表我们要去获取切入的点
@Around("execution(* com.guo.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("==========before around===========");
Object proceed = pjp.proceed();
System.out.println("==========after around===========");
}
}
applicationContext.xml
<!-- 方式三 -->
<bean id="annotation" class="com.guo.div.AnnotationPointCut" />
<aop:aspectj-autoproxy />
测试结果
==========before around===========
===================before methods===================================
增加了一个用户
===================after methods===================================
==========after around===========
小知识点: 增强环绕前置-前置日志-方法-后置日志-增强环绕后置
12、整合Mybatis
步骤:
1.导入相关jar包
- junit
- mybatis
- mysql
- spring相关
- aop织物
- mybatis-spring【new】
2.编写配置文件
3.测试
12.1 回忆mybatis
-
编写实体类
-
编写核心配置文件
-
编写Mapper.xml
如果在src下写入mapper.xml文件时,需要在pom.xml中配置,因为idea默认不编译src下的xml文件
<build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> </resources> </build>
-
测试
12.2 Mybatis-Spring
- 编写数据源配置
- sqlSessionFactory
- sqlSessionTemplate
- 需要给接口加实现类,注入到Spring中
- 测试
13、声明式事务
13.1、回顾事务
- 要么都成功、要么都失败
- 事务在项目开发中、十分的重要设计数据一致性,不能马虎
- 确保完整性和一致性
事务ACID原则:
- 原子性
- 一致性
- 隔离性
- 多个事务可能操作同一个资源、防止数据损坏
- 事务性
- 事务一旦提交、结果不会再被影响、被持久性写入事务中 0
13.2 spring中的声明事务
- 声明式事务:AOP
- 编程式事务: 需要在代码中进行事务管理
使用声明式事务(使用时记得在xml文件头导入aop和tx的约束)
<!--结合aop实现事务的织入
配置事务通知
-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--给哪些方法配置事务-->
<!--配置事务的传播特性: new propagation=“REQUIRED”是默认选项 -->
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="update"/>
<tx:method name="delete"/>
<tx:method name="query" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!--配置事务切入-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.guo.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
为什么需要事务?
- 不配置事务,可能会存在数据提交不一致
- 配置事务只有两种办法,在spring中配置声明式事务或者使用代码配置编程式事务
``
- 测试
12.2 Mybatis-Spring
- 编写数据源配置
- sqlSessionFactory
- sqlSessionTemplate
- 需要给接口加实现类,注入到Spring中
- 测试
13、声明式事务
13.1、回顾事务
- 要么都成功、要么都失败
- 事务在项目开发中、十分的重要设计数据一致性,不能马虎
- 确保完整性和一致性
事务ACID原则:
- 原子性
- 一致性
- 隔离性
- 多个事务可能操作同一个资源、防止数据损坏
- 事务性
- 事务一旦提交、结果不会再被影响、被持久性写入事务中 0
13.2 spring中的声明事务
- 声明式事务:AOP
- 编程式事务: 需要在代码中进行事务管理
使用声明式事务(使用时记得在xml文件头导入aop和tx的约束)
<!--结合aop实现事务的织入
配置事务通知
-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--给哪些方法配置事务-->
<!--配置事务的传播特性: new propagation=“REQUIRED”是默认选项 -->
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="update"/>
<tx:method name="delete"/>
<tx:method name="query" read-only="true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<!--配置事务切入-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.guo.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
为什么需要事务?
- 不配置事务,可能会存在数据提交不一致
- 配置事务只有两种办法,在spring中配置声明式事务或者使用代码配置编程式事务