javascript
Spring 马士兵
1.Spring能做什么?
Spring是一個輕量級的控制反轉(IoC/di)和面向切面編程(AOP) 的對象容器框架+事務管理
Spring官網:http://spring.io/?
Spring 是一個于 2003 年興起的一個輕量級的 Java 開源開發框架
如今Spring已是Java項目的標配,它極大的簡化了開發過程,降低了開發難度。
- 方便解耦,簡化開發;
- AOP 編程的支持;
- 聲明式事務的支持;
- 方便程序的測試;
- 方便集成各種優秀框架。
**IoC**
控制反轉
Inversion of Control
**AOP**?
面向切面編程
Aspect Oriented Programming?
## Spring5新特性
- 支持JDK8
? - Spring 接口中的默認方法
? - 基于 Java8 反射增強的內部代碼改進
? - 在框架代碼中使用函數式編程 - lambda表達式 和 stream流
- 響應式編程支持Spring Web Reactive異步的、非阻塞的、事件驅動的服務
- 支持J2EE7
? - Servlet 3.1
? - JMS 2.0
? - JPA 2.1
? - JAX-RS 2.0
- Bean Validation 1.1
- Hibernate 5
- Jackson 2.6
- EhCache 2.10
- JUnit 5
- Tiles 3
- Kotlin
## IOC
**IoC是一個概念,是一種思想,其實現方式多種多樣。當前比較流行的實現方式之一是DI。**
從IOC容器中獲取bean實例
```java
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
?? ??? ?User user = (User)ctx.getBean("user");
?? ??? ?user.setName("uu");
?? ??? ?user.setAge(18);
?? ??? ?System.out.println(ToStringBuilder.reflectionToString(user));;
```
### Bean的定義與注冊
Spring的配置文件是用于指導Spring工廠進行Bean的生產、依賴關系注入及Bean實例分發的“圖紙”,它是一個或多個標準的XML文檔
<bean id=**"food"** class=**"com.msb.Food"**></bean>
一個bean只能有一個id,但是可以有多個name作為別名
### 作用域
?
spring為bean提供了6種作用域,其中4種只有在web-aware的ApplicationContext種才有用。用戶也可以創建自定義的作用域。
singleton 、prototype 、websocket(ws)、request、session、application

***\*singleton scope 單例作用域\****
單例
每一個類,在一個容器內只能產生一個實例
?***\*Request scope\****
該作用域的bean,在每個HTTP request都會新建一個實例,當一個request結束后,該實例也會被丟棄。
?***\*Session scope\****
某一個用戶在一段時間內,會使用同一個session,session有超時時間,過了超時時間則session失效。不同用戶使用不同的session。
?***\*Application\**** ***\*scope\****
該作用域的bean,每一個application會創建一個
***\*prototype scope 原型作用域\****
多例
該bean每次被注入,或者使用getBean()方法獲取時,都返回一個新的實例。
SpringBoot配置多例
```java
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
```
?***\*MVC下Spring的單例\****
想在一個singleton內多次調用短存活時間的bean(propotype、request、session等),希望調用的是不同的實例,那么就需要使用AOP proxy技術
?***\*線程安全問題\****
業務對象并沒有做線程的并發限制,因此不會出現各個線程之間的等待問題,或是死鎖問題
MVC中的實體bean不是單例的
單例類的成員變量
@Component @Service ?@RestController 注釋類中不能有狀態數據(類的成員變量),只能有方法。多個請求會共用這些單例對象,線程不安全的。
在并發訪問的時候這些成員變量將會是并發線程中的共享對象,也是影響線程安全的重要因素
**前端到后端的一次請求,后端會創建一個線程來處理,及一個請求會用一個線程處理**
```java
@Component
public class Test {
? ? public int i = 0;
? ? public int test(int t) throws InterruptedException {
? ? ? ? if (t == 1) {
? ? ? ? ? ? i = 1;
? ? ? ? ? ? TimeUnit.SECONDS.sleep(5);
? ? ? ? ? ? i =100;
? ? ? ? }
? ? ? ? if(t==2) {
? ? ? ? ? ? TimeUnit.SECONDS.sleep(10);
? ? ? ? ? ? i = i + 1;
? ? ? ? }
? ? ? ? System.out.println(i);
? ? ? ? return i;
? ? }
}
```
```java
? ? @Autowired
? ? private Test test;
? ? @GetMapping("/test/{id}")
? ? public int test(@PathVariable(name = "id") int id) throws InterruptedException {
? ? ? ? System.out.println("id1----: " + id);
? ? ? ? int i = ? test.test(id);
? ? ? ? System.out.println("test:"+test);
? ? ? ? System.out.println("id2----: " + id);
? ? ? ? return i;
? ? }
```
localhost:8380/ekafka/v1/test/1
返回:100
打印
100
test:com.chinamobile.cmss.ekafka.service.impl.Test@40e41f88
id2----: 1
localhost:8380/ekafka/v1/test/2
返回:101 ?
打印
101
test:com.chinamobile.cmss.ekafka.service.impl.Test@40e41f88
id2----: 2
再來一次localhost:8380/ekafka/v1/test/2
返回:102
打印
102
test:com.chinamobile.cmss.ekafka.service.impl.Test@40e41f88
id2----: 2
再來一次localhost:8380/ekafka/v1/test/2
返回:103
打印
id1----: 2
103
test:com.chinamobile.cmss.ekafka.service.impl.Test@40e41f88
id2----: 2
注意所有的test對象都是同一個Test@40e41f88
改成多例模式
不管執行幾次localhost:8380/ekafka/v1/test/1
返回:100
打印
100
test:com.chinamobile.cmss.ekafka.service.impl.Test@9002ded //每次的test都不一樣
id2----: 1
不管執行幾次localhost:8380/ekafka/v1/test/2
返回:1
打印
1
test:com.chinamobile.cmss.ekafka.service.impl.Test@2500323c //每次的test都不一樣
id2----: 2
**不帶屬性變量使用默認單列是線程安全的:**

接口:
```java
public interface UserDao {
?? ?public User getUserByName(String name);
}
```
兩個實現類:
```java
@Repository("daoMysql")
public class UserDaoMysqlImpl implements UserDao {
?? ?@Autowired
?? ?User user;
?? ?
?? ?public User getUserByName(String name) {
?? ??? ?System.out.println("用戶查找中。。。");
?? ??? ?return user;
?? ?}
}
```
```java
@Repository("daoSS")
public class UserDaoSqlServerImpl implements UserDao {
?? ?public User getUserByName(String name) {
?? ??? ?// TODO Auto-generated method stub
?? ??? ?System.out.println("UserDaoSqlServerImpl");
?? ??? ?return null;
?? ?}
}
```
調用:
?? ?@Autowired
?? ?@Qualifier("daoMysql") ?//使用daoMysql的實現類
```java
@Service
public class MainService {
?? ?@Autowired
?? ?@Qualifier("daoMysql")
?? ?UserDao dao;
?? ?
?? ?public User login(String loginName, String password) {
?? ??? ?System.out.println("loginName:" + loginName);
?? ??? ?System.out.println("Service 接到請求 ,開始處理");
?? ??? ?User user = dao.getUserByName(loginName);
?? ??? ?
?? ??? ?System.out.println(ToStringBuilder.reflectionToString(user));
?? ??? ?
?? ??? ?return user;
?? ?}
}
```
**帶屬性變量的類必須使用多列來保證線程安全:**
```java
@Component ?//交給spring IOC管理
@Scope("prototype") //使用多例
public class User {
?? ?@Value("zhangfg")
?? ?private String loginName;
?? ?@Value("123123")
?? ?private String password;
?? ?@Autowired
?? ?private Pet pet;
?? ?
}
```
```java
@Component
@Scope("prototype")
public class Pet {
?? ?@Value("可樂")
?? ?private String name;
?? ?
}
```
### 引用類型的成員
其中引用類型的成員變量即我們在controller中注入的service,在service中注入的dao,這里將其定義為成員變量主要是為了實例化進而調用里面的業務方法,**在這些類中一般不會有全局變量,因此只要我們的業務方法不含有獨立的全局變量即使是被多線程共享,也是線程安全的。**
Controller service dao 層中的業務類是多線程共享的,但是每個線程在處理數據的時候具體處理的數據是在每個線程中各自有一份。
?***\*controller層\****
l final類型 線程安全
l 成員變量 有狀態數據有線程安全問題
# 2.Spring Framework
## 工程模式
工廠生產不同品牌的汽車
工廠類
```java
public class CarFactory {
?? ?public CarFactory() {
?? ??? ?super();
?? ??? ?System.out.println("CarFactory");
?? ?}
?? ?
?? ?
?? ?public static Car getCar(String name) throws Exception{
?? ??? ?if (name.equals("audi")) {
?? ??? ??? ?
?? ??? ??? ?return new Audi();
?? ??? ?}else {
?? ??? ??? ?throw new Exception("暫時沒法生產這輛車");
?? ??? ?}
?? ?}
}
```
```java
public interface Car {
?? ?public String getName();
?? ?public String getPrice();
}
```
奧迪車
```java
public class Audi implements Car {
?? ?public String getName() {
?? ??? ?// TODO Auto-generated method stub
?? ??? ?return "我是奧迪A7";
?? ?}
?? ?public String getPrice() {
?? ??? ?// TODO Auto-generated method stub
?? ??? ?return "700000";
?? ?}
}
```
寶馬車
```java
public class Bmw implements Car {
?? ?public String getName() {
?? ??? ?// TODO Auto-generated method stub
?? ??? ?return "BMW 7";
?? ?}
?? ?public String getPrice() {
?? ??? ?// TODO Auto-generated method stub
?? ??? ?return "700000w";
?? ?}
}
```
通過工廠獲取汽車
```xml
?<!--
?動態工廠 每次都是new出來的
? ? ? ? <bean id="carFactory" class="com.mashibing.spring.CarFactory">
? ? ? ??
? ? ? ?? ? ?<property name="name" ?value="audi"></property>?
? ? ? ? </bean>
? ? ? ??
? ? ? ? <bean id="car" factory-bean="carFactory" factory-method="getCar">
? ? ? ? 需要set方法
? ? ? ? ?? ?
? ? ? ? </bean> -->
? ? ? ??
? ? ? ? <bean id="car" class="com.mashibing.spring.CarFactory" factory-method="getCar" >
? ? ? ? ?? ?<constructor-arg value="audi"></constructor-arg>
? ? ? ? </bean>
? ? ? ??
</beans>
```
```java
public class TestGetBean {
?? ?/**
?? ? * Spring 對對象的生產來說 一共就兩種
?? ? *?
?? ? * 1. 單例 singleton -> ws request session application -> 生命周期綁定 2. new出來的
?? ? * prototype
?? ? *?
?? ? * @param args
?? ? */
?? ?
?? ?public static void main(String[] args) {
//
//?? ??? ?try {
//?? ??? ??? ?Car car = CarFactory.getCar("audi"); ?//代碼中寫死獲取那種汽車,強耦合
//?? ??? ??? ?System.out.println(car.getName());
//?? ??? ?} catch (Exception e) {
//?? ??? ??? ?// TODO Auto-generated catch block
//?? ??? ??? ?e.printStackTrace();
//?? ??? ?}
//?? ??? ?
?? ??? ?ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); //獲取xml配置文件中的內容
?? ??? ?Car car = ctx.getBean("car",Car.class); // 通過xml動態配置的方式獲取汽車,xml中配置的獲取什么品牌的汽車
?? ??? ?System.out.println(car.getName());
?? ?}
}
```
**動態工廠**
```java
public class CarFactory {
?? ?public Car getCar(String name) throws Exception{
?? ??? ?
?? ??? ?if (name.endsWith("bmw")) {
?? ??? ??? ?return new Bmw();
?? ??? ?}else {
?? ??? ??? ??? ?throw new Exception("car not fond");
?? ??? ?}
?? ?}
}
```
**靜態工廠**
獲取類的方法是用static修飾
工廠類
```java
public class CarFactoryStatic {
?? ?public static Car getCar(String name) throws Exception{
?? ??? ?
?? ??? ?if (name.endsWith("bmw")) {
?? ??? ??? ?return new Bmw();
?? ??? ?}else {
?? ??? ??? ??? ?throw new Exception("car not fond");
?? ??? ?}
?? ?}
}
```
## 注解
**autowire自動注入:**
可選兩種類型
byName
byName方式自動注入:要求注入的bean的id必須和被注入的bean對象的屬性名一致
byType
byType方式自動注入:要求注入的bean的對象類型與被注入的bean對象類型一致,并且在配置文件中的Bean相同類型必須唯一
如果存在多個,會拋異常:
No qualifying bean of type 'com.msb.Pet' available: expected single matching bean but found 2: pet,pet2
全局自動注入
在首行Beans標簽下添加default-autowire屬性。
**@Component**
在需要注冊到容器的類上添加@Component標簽,標識這個類由Spring容器接管
約定大于配置
在一個類上添加@Component默認會使用首字母小寫的類名作為ID注冊到Spring容器。
如果需要手動指定Bean Id可以使用@Component("p")
同屬@Component的額外三個注解
@Controller @Service @Repository
這三個注意在MVC開發中會經常用到,除了注解名字和Component不一樣之外,其余功能都一樣。
Spring額外提供這三個注解的目的主要是為了區分MVC中每個類的區別。
**@Scope**
使用注解注冊Bean 默認的作用域還是singleton,可以使用@Scope("prototype")改變對象作用域
**@Value**
在使用注解給對象注入值的時候,不再需要Get/Set方法
```java
@Value("小明")
?? ?private String name;
```
## Aop概念與代理
**面向切面編程 代碼增強**?
AOP(Aspect Oriented Programming)面向切面編程。
面向切面,是與OOP(Object Oriented Programming)面向對象編程并列的編程思想。
Spring支持兩種方法,那么我們在使用spring進行動態代理時究竟使用的哪一種方法呢?spring優先支持實現接口的方式,如果沒有接口則使用cglib方式.
**代理**?
通過代理可以隱藏目標類的具體實現;在不修改目標類代碼的情況下能夠對其功能進行增強。
- 委托類和代理類有相同的接口或者共同的父類
- 代理類為委托類負責處理消息,并將消息轉發給委托類
- 委托類和代理類對象通常存在關聯關系
- 一個代理類對象與一個委托類對象關聯
- 代理類本身并不是真正的實現者!而是通過調用委托類的方法來實現功能!
靜態代理
使用硬編碼的方式增強原有方法
- 優點:可以做到不對目標對象進行修改的前提下,對目標對象進行功能的擴展和攔截。
- 缺點:因為代理對象,需要實現與目標對象一樣的接口,會導致代理類十分繁多,不易維護,同時一旦接口增加方法,則目標對象和代理類都需要維護。
# 3.SpringBoot入門
SpringBoot主要解決的是在微服務的架構下簡化配置(有快速配置)、前后端分離、快速開發
優點:
- 提供了快速啟動入門
- 開箱即用、提供默認配置
- 內嵌容器化web項目
- 沒有冗余代碼生成和xml配置要求
常見問題
Pom.xml
Springboot項目必須要繼承的parnet
?
Pom.xml第一行報錯
?
進入本地庫
for?/r?%i?in?(*.lastUpdated)?do?del?%i???
然后update
缺少或包錯誤
?
刪掉 重新update
找不到主類
?
所有錯誤都解決后
Jar方式運行 首先得有這個jar包
先clean package 生成jar文件,然后再run main方法
?
找不到jdk
?
?
把jre的路徑換成jdk的
?
?
?
總結
以上是生活随笔為你收集整理的Spring 马士兵的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: macOS下OneNote中文字体和数字
- 下一篇: 白盒测试及其基本方法