Spring是一個衆多的企業級應用技術還能整合開源世界衆多著名的輕量級的控制反轉(IOC)和面向切面程式設計(AOP)的架構
Spring
1.Spring簡介
Spring是分層的Java SE/EE應用fll-stack輕量級開源架構,以IoC ( Inverse Of Control :反轉控制)和AOP ( Aspect Oriented Programming :面向切面程式設計)為核心。
提供了展現層SpringMVC和持久層Spring JDBCTemplate以及業務層事務管理等衆多的企業級應用技術還能整合開源世界衆多著名的第三方架構和類庫,逐漸成為使用最多的Java EE企業應用開源架構。
總結一句話: Spring就是一個輕量級的控制反轉(IOC)和面向切面程式設計(AOP)的架構!
2.開發步驟
- 圖示:

- 詳解文字表示
- 導入Spring開發的基本包坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.5</version>
</dependency>
- 編寫Dao接口實作類
//建立接口
public interface UserDao {
public void save();
}
//實作類
public class UserDaoImpl implements UserDao {
public void save(){
System.out.println("save is running...");
}
}
- 建立spring核心配置檔案 applicationContext.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
https://www.springframework.org/schema/beans/spring-beans.xsd
">
<bean id="userDao" class="com.qd.dao.UserDaoImpl"/>
</beans>
- 測試類
public static void main(String[] args) {
//擷取spring的上下文對象
//在配置檔案加載的時候,容器中的對象就已經初始化了!:Bean
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao UserDao = (UserDao) context.getBean("userDao");
UserDao.save();
}
3.依賴注入
屬性
- property設定屬性: value:直接指派 ref:要引用spring容器建立好的對象
- 有參 直接通過參數名比對 推薦使用 :<constructor-arg name="str"value="qd123"/>
- spring配置 别名 :< alias name="hello" alias="bieming"/>
- import 将多個配置檔案導入,合并為一個 :< import resource="beans.xml"/>
- bean屬性(見圖)
說明 | |
---|---|
id | bean的唯一辨別符。也就是相當于對象名 |
class | bean對象所對應的權限定名:包名+類名 |
name | 取别名 可以取多個别名qd1,qd2 逗号或者空格分割或者分号 |
scope | singleton 單例模式 預設為單例模式 加載配置檔案時候建立 prototype:原型模式 getBean 時候建立 |
autoWire | 自動裝配 byName:會自動在容器上下文查找和自己對象set方法後面的對應的beanid bytype無需id也能跑起來 |
注入方法
- 構造輸入與set注入 (下面為set注入示例)
<!-- set方式注入 重點!!!-->
<!-- 依賴注入:set注入-->
<!-- 依賴:bean對象的建立依賴容器-->
<!-- 注入:bean對象中的所有屬性,由容器注入-->
<bean id="address" class="com.qd.pojo.Address"/>
<bean id="student" class="com.qd.pojo.Student">
<!-- 普通值注入 value-->
<property name="name" value="前度"/>
<!-- bean注入 ref-->
<property name="address" ref="address"/>
<!-- 數組注入 -->
<property name="books">
<array>
<value>紅樓夢</value>
<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="sfz" value="123456"/>
<entry key="yhk" value="654321"/>
</map>
</property>
<!-- wife null-->
<property name="wife">
<null/>
</property>
<!-- Properties-->
<property name="info">
<props>
<prop key="學号">201952050201</prop>
<prop key="性别">男</prop>
</props>
</property>
</bean>
- p命名空間注入 可以直接注入屬性的值 : < bean id="student" class= " " p:name=" "/>
4.Spring相應API
- ApplicationContext的實作類
ClassPathXmlApplicationContext | 它是從類的根路徑下加載配置檔案推薦使用這種 |
---|---|
FileSystemXmlApplicationContext | 它是從磁盤路徑.上加載配置檔案,配置檔案可以在磁盤的任意位置 |
AnnotationConfigApplicationContext | 當使用注解配置容器對象時,需要使用此類來建立spring容器。它用來讀取注解 |
getBean(String name) | 通過id或name去查找擷取bean,不能重名 |
5.配置資料源
普通配置資料源
- 導入資料源的坐标和資料庫驅動坐标
- 建立資料源對象
- 設定資料源的基本連接配接資料
- 使用資料源擷取連接配接資源和歸還連接配接資源
- 導包
Spring基礎 - 測試c3p0
Spring基礎 - 測試 druid
Spring基礎 - 資料庫位址不唯一 在resource中建立file
内容為jdbc.properties
Spring基礎 - 讀取配置檔案
用Spring配置資料源
- 導入spring坐标
- 在resource下建立applicationContext.xml
- 測試spring産生資料源對象 , 注意set方法的值
- spring加載properties檔案 引入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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
">
- 加載外部檔案
- 知識要點
6.注解開發
原始注解
Spring是輕代碼而重配置的架構,配置比較繁重,影響開發效率,是以注解開發是一種趨勢,注解代替xm配置檔案可以簡化配置,提高開發效率。
原始注解主要代替的配置
注解 | |
---|---|
@Component | 使用在類上用于執行個體化Bean |
@Controller | 使用在web層類上用于執行個體化Bean |
@Service | 使用在service層類上用于執行個體化Bean |
@Repository | 使用在dao層類上用于執行個體化Bean |
@Autowired | 使用在字段上用于根據類型依賴注入 |
@Qualifier | 結合@Autowired-起使用用于根據名稱進行依賴注入 |
@Resource | 相當于@Autowired + @Qualifier,按照名稱進行注入 |
@Value | 注入普通屬性 |
@Scope | 标注Bean的作用範圍 |
@PostConstruct | 使用在方法上标注該方法是Bean的初始化方法 |
@PreDestroy | 使用在方法上标注該方法是Bean的銷毀方法 |
- 對比 xml與注解的使用不同
- xml
注意開啟注解的支援 注解無需set方法
由于<context:component-scan base-package=”xx.xx”/> 也包含了自動注入上述Bean的功能,
是以< context:annotation-config /> 可以省略。如果兩者都進行了配置,則隻有前者有效。
< context:annotation-config > 是用于激活那些已經在spring容器裡注冊過的bean(無論是通過xml的方式還是通過package sanning的方式)上面的注解。
< context:component-scan >除了具有< context:annotation-config >的功能之外,
< context:component-scan >還可以在指定的package下掃描以及注冊javabean 。
注解代碼改進
由于前面我們不能夠立馬知道這個類在哪一層 是以更清楚的注解(看表2-4)
改示範未使用web環境 是以 @Controller 暫未示範
注解無需set方法 圖中均帶有注意注意~~~
//< property name="userDao" ref="userDao" >< /property >
@Autowired //根據資料類型從Spring容器中比對的
@Qualifier("userDao") //按照id名稱從容器比對 此處要結合Autowired一起用 按照類型注入 此行可以省略
@Resource(name = "userDao") //@Resource相當于@Autowired + @Qualifier
更多屬性注解
- Value
@Value("${jdbc.driver}")
private String driver;
- Scope
@Scope("singleton")
//@Scope("prototype")
- PostConstruct ---- PreDestroy ----- 非重點
@PostConstruct
public void init() {
System.out.println("對象的初始化方法");
}
@PreDestroy()
public void destory() {
System.out.println("對象的銷毀方法");
} //要手動關閉
Spring新注解
使用上面的注解還不能全部替代xm配置檔案,還需要使用注解替代的配置如下:
非自定義的Bean的配置: < bean >
加載properties檔案的配置: < context:property-placeholder >
元件掃描的配置: < context:component-scan >
引入其他檔案: < import >
- 新方法
詳解 | |
---|---|
@Configuration | 用于指定目前類是一個Spring配置類,當建立容器時會從該類上加載注解 |
@ComponentScan | 用于指定Spring在初始化容器時要掃描的包。 作用和在Spring的xml配置檔案中的。 <context:component-scan base package="com.itheima"/>一樣 |
@Bean | 使用在方法上,标注将該方法的傳回值存儲到Spring容器中 |
@PropertySource | 用于加載properties檔案中的配置 |
@Import | 用于導入其他配置類 |
- 專門建一個核心配置類 這裡叫 SpringConfiguration 對比一下
- 标志 :該類是spring的核心配置類
@Configuration
- 引入其他資源
//<import resource="xxx"/>
@Import({xxx.class,xxxx.class}) //參數為數組 可以傳多個class檔案
- 開啟注解的支援
//<context:component-scan base-package="com.qd"/>
@ComponentScan("com.qd")
- 加載配置檔案
//<context:property-placeholder location="classpath: jdbc.properties"/>
@PropertySource("classpath: jdbc.properties")
示範代碼
public class SpringConfiguration {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
public DataSource getDataSource() {
@Bean("dataSource") //Spring會将目前方法的傳回值以指定名稱存儲到Spring容器中
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(driver);
dataSource.setJdbcUrl(url);
dataSource.setUser(username);
dataSource.setPassword(password);
return dataSource;
}
}
測試代碼
ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
UserService userService = context.getBean(UserService.class);
userService.save();
7.內建Junit
原始Junit測試Spring的問題
在測試類中 每個方法都有以下兩行代碼,這兩行代碼的作用是擷取容器,如果不寫的話,直接會提示 空指針異常。是以又不能輕易删掉。
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = context.getBean(UserService.class);
解決方法
讓SpringJunit負責建立Spring容器,但是需要将配置檔案的名稱告訴它
将需要進行測試Bean直接在測試類中進行注入
- 導入spring內建Junit的坐标
<!-- junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--Spring-Junit-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
- 使用@Runwith注解替換原來的運作期
@RunWith(SpringJUnit4ClassRunner.class)
- 使用@ContextConfiguration指定配置檔案或配置類
@ContextConfiguration("classpath:applicationContext.xml"); //xml
@ContextConfiguration(classes = {SpringConfiguration.class}) //注解
classes的内容為要編譯的位元組碼檔案
- 使用@Autowi red注入需要測試的對象
@Autowired
private userService userService;
@Autowired
private DataSource dataSource;
- 建立測試方法進行測試
@test
public void test(){
userService.save();
System.out.println(dataSource.getConnection());
}
- 圖檔
8.內建Web環境
- ApplicationContext應用上下文擷取方式
應用上下文對象是通過new ClasspathXmlApplicationContext(spring配置檔案)方式擷取的,但是每次從容器中獲得Bean時都要編寫new ClasspathXmlApplicationContext(spring配置檔案),這樣的弊端是配置檔案加載多次,應用上下文對象建立多次。
-
在Web項目中,可以使用ServletContextListener監聽Web應用的啟動, 我們可以在Web應用啟動時,就加載Spring的配置檔案,建立應用上下文對象ApplicationContext, 在将其存儲到最大的域servletContext域中,這樣就可以在任意位置從域中獲得應用上下文ApplicationContext對象了。
- 具體實作
-
代碼優化
優化一 配置類
優化二
5.以上代碼隻做了解 在Spring中以對它做封裝
上面的分析不用手動實作,Spring提供了一 個監聽器ContextLoaderListener就是對 上述功能的封裝,該監聽器内部加載Spring配置檔案,建立應用上下文對象,并存儲到ServletContext域中, 是供了一個用戶端工具WebApplicationContextUtils供使用者獲得應用上下文對象。
是以我們需要做的隻有兩件事:
①在web.xmI中配置ContextLoaderListener監聽器 (導入spring-web坐标)
②使用WebApplicationContextUtils獲得應用 上下文對象ApplicationContext
<!-- Spring-Web-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
- web.xml加載
Spring基礎 - 使用 擷取應用上下文
9.代理模式
為什麼要學習代理模式?因為這就是SpringAOP的底層!【SpringAOP和SpringMVC】
代理模式的分類:
- 靜态代理
- 動态代理
角色分析:
- 抽象角色:一般會使用接口或者抽象類來解決
- 真實角色:被代理的角色
- 代理角色:代理真實的角色,代理真實角色後,我們一般會做附屬操作
- 客戶:通路代理的人!
代碼步驟:
- 接口
package com.qd.demo01;
//租房
public interface Rent {
public void rent();
}
- 真實角色
package com.qd.demo01;
//房東
public class Host implements Rent {
public void rent() {
System.out.println("房東要出租房子");
}
}
- 代理角色
package com.qd.demo01;
//代理
public class Proxy implements Rent {
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
public void rent() {
seeHouse();
host.rent();
fare();
}
//看房
public void seeHouse() {
System.out.println("中介帶你看房子");
}
//收費
public void fare() {
System.out.println("中介收中介費");
}
}
- 用戶端通路代理角色
package com.qd.demo01;
//租客
public class Client {
public static void main(String[] args) {
//房東要出租房子
Host host = new Host();
//代理 會有一些附屬操作
Proxy proxy = new Proxy(host);
//你不用面對房東,直接找中介即可
proxy.rent();
}
}
靜态代理的好處:
- 可以使真實角色的操作更加純粹!不用去關注一些公共的業務
- 公共的業務也交了給了代理角色!實作了業務的分工!
- 公共業務發展擴充的時候,友善集中管理!
缺點:
- 一個真實角色就會産生一個代理角色;代碼量翻倍~~ 開發效率變低~~
加深了解
示例代碼:
- 建立一個
的接口 内有增删改查的功能UserService.java
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
- 接口的實作類
UserServiceImpl.java
public class UserServiceImpl implements UserService{
public void add() {
System.out.println("增加了一個使用者");
}
public void delete() {
System.out.println("删除了一個使用者");
}
public void update() {
System.out.println("修改了一個使用者");
}
public void query() {
System.out.println("查詢了一使用者");
}
}
-
此時想法是每調用一次方法都想加個日志輸出 需要用到代理
我們編寫一個
具體内容為:UserServiceProxy.java
public class UserServiceProxy implements UserService {
private UserService userService;
public UserServiceProxy() {
}
public void add() {
log("add");
userService.add();
}
public void delete() {
log("delete");
userService.add();
}
public void update() {
log("update");
userService.add();
}
public void query() {
log("query");
userService.add();
}
public void setUserService(UserService userService) {
this.userService = userService;
}
//日志方法
public void log(String msg) {
System.out.println("[debug]使用了" + msg + "方法");
}
}
- 編寫一個測試類
Client.java
public class Client {
public static void main(String[] args) {
// UserServiceImpl userService = new UserServiceImpl();
// userService.add();
UserServiceProxy userServiceProxy = new UserServiceProxy();
userServiceProxy.add();
}
}
- 動态代理與靜态代理角色一樣!
- 動态代理的代理類是動态生成的,不是寫好的!
- 動态代理分為兩大類:基于接口 ; 基于類
- 基于接口-----JDK動态代理
- 基于類---- cglib
- Java位元組碼實作: Javaassist
需要了解兩個類 :
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(Object 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("[debug]使用了" + msg + "方法");
// }
}
- main方法
public static void main(String[] args) {
//真實角色
UserService userService = new UserServiceImpl();
//代理角色
ProxyInvocationHandler pih = new ProxyInvocationHandler();
//通過調用程式處理角色來處理我們要調用的接口對象
pih.setTarget(userService); //設定要代理的對象
Object proxy = pih.getProxy(); //動态生成代理類 此處要強轉
}
- 動态代理的好處
- 一個動态代理類代理的是一個接口,一般就是對應的一類業務!
10.AOP
什麼是AOP?
AOP (Aspect Oriented Programming)意為:面向切面程式設計,通過預編譯方式和運作期動态代理實作程式功能的統一維護的-種技術。AOP是O0P的延續,是軟體開發中的一個熱點,也是Spring架構中的一 -個重要内容,是函數式程式設計的一種衍生範型。利用AOP可以對業務邏輯的各個部分進行隔離,進而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。
使用Spring實作Aop
【重點】使用AOP織入,需要導入一個依賴包!
<dependency>
<groupId>org.assertj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
-
【方式1】使用Spring的API接口
- 日志類
配置xml檔案
測試
-
【方式2】自定義來實作AOP,主要是切面定義
自定義一個類
配置xml檔案
-
【方式3】使用注解來實作AOP
結果
11.更多
1.在maven中建立Spring項目
- maven的pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- 父工程-->
<groupId>org.example</groupId>
<artifactId>Maven</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>spring</module>
</modules>
<!-- 導入依賴-->
<dependencies>
<!-- mysql驅動-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.0.8</version>
</dependency>
<!-- mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!-- junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- Spring-Junit-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<!--spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.5</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.17</version>
</dependency>
</dependencies>
<!--在build中配置resources,來防止我們的資源導出問題-->
<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>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
- Spring的pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>Maven</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring</artifactId>
</project>
結束啦~~