Spring5框架概述 Spring是輕量級的開源的JavaEE框架。 Spring可以解決企業應用開發的複雜性。 Spring有兩個核心部分:IOC和AOP IOC:控制反轉,把創建對象過程交給Spring進行管理 AOP:面向切麵,不修改源代碼進行功能增強 Spring特點 方便解耦,簡化開發( ...
Spring5框架概述
-
Spring是輕量級的開源的JavaEE框架。
-
Spring可以解決企業應用開發的複雜性。
-
Spring有兩個核心部分:
IOC
和AOP
- IOC:控制反轉,把創建對象過程交給Spring進行管理
- AOP:面向切麵,不修改源代碼進行功能增強
-
Spring特點
- 方便解耦,簡化開發(IOC可以降低耦合性)
- AOP編程支持
- 方便程式測試(Spring對Junit4支持,可以通過註解方便的測試Spring程式)
- 方便和其他框架進行整合
- 方便進行事務操作
- 降低API開發難度
-
Spring5系統結構
IOC
概念和原理
什麼是IOC
- 控制反轉,把對象創建和對象之間的調用過程,交給Spring進行管理
- 使用IOC目的:為了降低耦合度
IOC底層原理
xml解析、工廠模式、反射
-
原始方式與工廠模式的對比
-
畫圖講解IOC底層原理
IOC之BeanFactory介面
-
IOC思想基於IOC容器完成,IOC底層就是對象工廠
-
Spring提供IOC容器實現兩種方式:(兩個介面)
-
BeanFactory:IOC容器基本實現,是Spring內部的使用介面,不提供開發人員進行使用
載入配置文件時不會創建對象,在獲取對象(使用)才去創建對象
-
ApplicationContext:BeanFactory介面的子介面,提供更多更強大的功能,一般由開發人人員進行調用
載入配置文件時會把在配置文件對象進行創建
-
-
ApplicationContext
介面有實現類
-
FileSystemXmlApplicationContext
configLocation
:要寫上配置文件在系統盤(某個盤)里的路徑 -
ClassPathXmlApplicationContext
configLocation
:要寫上類路徑
-
IOC操作Bean管理
概念
-
什麼是Bean管理
Bean管理指的是兩個操作:
Spring創建對象
和Spring註入屬性
-
Bean管理操作的方式:
- 基於xml配置文件方式實現
- 基於註釋方式實現
基於xml方式
-
創建對象
<!--配置User對象創建--> <bean id="user" class="com.atguigu.spring.User"></bean>
- 在Spring配置文件中,使用
bean標簽
,標簽裡面添加對應屬性,就可以實現對象創建 bean標簽
常用的屬性id
屬性:唯一標識class
屬性:類全路徑(包類路徑)
- 創建對象時候,預設執行無參構造方法
- 在Spring配置文件中,使用
-
註入屬性
-
DI:依賴註入(Dependency Injection),就是註入屬性
DI是一種設計模式,用於管理對象之間的依賴關係,它將創建和管理對象的責任轉移給了第三方的容器或框架,從而降低了系統的耦合度。
-
第一種註入方式:使用
set方法
進行註入-
創建類,定義屬性和對應的set方法
/** * 演示set方法註入屬性 */ public class Book { //創建屬性 private String bname; private String bauthor; //創建屬性對應set方法 public void setBauthor(String bauthor) { this.bauthor = bauthor; } public void setBname(String bname) { this.bname = bname; } }
-
在
Spring配置文件
配置對象創建,配置屬性註入<!--set方法註入屬性--> <bean id="book" class="com.atguigu.spring5.Book"> <!--使用property完成屬性註入 name:類裡面屬性名稱 value:向屬性註入的值 --> <property name="bname" value="易筋經"></property> <property name="bauthor" value="達摩老祖"></property> </bean>
-
-
第二種註入方式:使用
有參構造
進行註入-
創建類,定義屬性,創建屬性對應的有參構造方法
/** *使用有參數構造註入 */ public class Orders { //屬性 private String oname; private String address; //有參構造 public Orders(String oname, String address) { this.oname = oname; this.address = address; } }
-
在spring配置文件中進行配置
<!--用有參構造註入屬性--> <bean id="orders" class="com.atguigu.spring5.Orders"> <constructor-arg name="oname" value="電腦"></constructor-arg> <constructor-arg name="address"value="China"></constructor-arg> </bean>
-
-
p名稱空間註入(瞭解)
使用p名稱空間註入,可以簡化基於xml配置方式
-
添加p名稱空間在配置文件中
<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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"></beans>
-
進行屬性註入,在bean標簽裡面進行操作
<!--set方法註入屬性--> <bean id="book" class="com.atguigu.spring5.Book" p:bname="九陽神功" p:bauthor="無名氏"> </bean>
-
-
xml註入其他類型屬性
-
字面量
-
null值
<!--null值--> <property name="address"> <null/> </property>
-
屬性值包含特殊符號
<!--屬性值包含特殊符號 1. 把<>進行轉義 <> 2. 把帶特殊符號內容寫到CDATA --> <property name="address"> <value><![CDATA[<<南京>>]]></value> </property>
-
-
註入屬性——外部bean
-
創建兩個類
service類
和dao類
package com.atguigu.spring5.dao; public interface UserDao{ public void update(); }
package com.atguigu.spring5.dao; public class UserDaoImpl implements UserDao{ @Override public void update(){ System.out.println("dao update..........."); } }
-
在
service
調用dao
裡面的方法package com.atguigu.spring5.service; public class UserService { //創建UserDao類型屬性,生成set方法 private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void add(){ System.out.println("service add................."); userDao.update(); } }
-
在
spring配置文件
中進行配置<!--1 service和dao對象的創建--> <bean id="userService" class="com.atguigu.spring5.service.UserService"> <!--註入userDao對象 name屬性值:類裡面屬性名稱 ref屬性:創建userDao對象的bean標簽id值 --> <property name="userDao" ref="userDaoImpl"></property> </bean> <bean id="userDaoImpl" class="com.atguigu.spring5.dao.UserDaoImpl"/></bean>
-
-
註入屬性——內部bean
- 一對多關係
比如:部門和員工,一個部門有多個員工,一個員工屬於一個部門。
-
在實體類之間表示一對多關係:員工使用對象類型屬性進行表示所屬部門
//部門類 public static Dept{ private String dname; public void setDname(String dname){ this.dname=dnamel } }
//員工類 public static Emp{ private String name; private String gender; private Dept dept; //用對象表示員工所屬的部門 public void setName(String name){ this.name = name; } public void setGender(String gender){ this.gender = gender; } }
-
在
Spring配置文件
<!--內部bean--> <bean id="Emp" class="com.gtguigu.spring5.bean.Emp"> <!--設置兩個普通屬性--> <property name="ename" value="lucy"></property> <property name="gender" value="女"></property> <!--設置對象類型屬性--> <property name="dept"> <bean id="dept" class="com.atguigu.spring5.bean.Dept"> <property name="dname" value="安保部"></property> </bean> </property> </bean>
-
註入屬性——級聯賦值
-
第一種寫法
<!--級聯賦值--> <bean id="emp" class="com.atguigu.spring5.bean.Emp"> <!--設置兩個普通屬性--> <property name="ename" value="lucy"></property> <property name="gender" value="女"></property> <!--級聯賦值--> <property name="dept" ref="dept"></property> </bean> <bean id="dept" class="com.atguigu.spring5.bean.Dept"> <property name="dname" value="財務部"></property> </bean>
-
第二種寫法
//使用對象形式表示員工屬於某一個部門 private Dept dept; //生成dept的get方法 public Dept getDept(){ return dept; } public void setDept(Dept dept){ this.dept = dept; }
<!--級聯賦值--> <bean id="emp" class="com.atguigu.spring5.bean.Emp"> <!--設置兩個普通屬性--> <property name="ename" value="lucy"></property> <property name="gender" value="女"></property> <!--級聯賦值--> <property name="dept" ref="dept"></property> <property name="dept.dname" value="技術部"></property> </bean> <bean id="dept" class="com.atguigu.spring5.bean.Dept"> <property name="dname" value="財務部"></property> </bean>
-
xml註入集合屬性
- 註入數組類型屬性
- 註入List集合類型屬性
- 註入Map集合類型屬性
- 註入Set集合類型屬性
第一步,創建類,定義數組
、List
、Map
、Set
類型屬性,生成對應set方法
public class Stu {
//1 數組類型的屬性
private String[] courses;
//2 List集合類型
private List<String> list;
//3 Map集合類型
private Map<String,String> maps;
//4 set集合類型
private Set<String> sets;
public void setCourses(String[] courses) {
this.courses = courses;
}
public void setList(List<String> list) {
this.list = list;
}
public void setMaps(Map<String, String> maps) {
this.maps = maps;
}
public void setSets(Set<String> sets) {
this.sets = sets;
}
}
第二步,在Spring配置文件
進行配置
<!--1 集合類型屬性的註入-->
<bean id="stu" class="com.atguigu.spring5.collectiontype.Stu">
<!--數組類型的屬性註入-->
<property name="courses">
<array>
<value>java課程</value>
<value>資料庫課程</value>
</array>
</property>
<!--list屬性註入-->
<property name="list">
<list>
<value>張三</value>
<value>李四</value>
</list>
</property>
<!--Map類型註入-->
<property name="maps">
<map>
<entry key="JAVA" value="java" ></entry>
<entry key="PHP" value="php"></entry>
</map>
</property>
<!--set集合註入-->
<property name="sets">
<set>
<value>MySQL</value>
<value>redis</value>
</set>
</property>
</bean>
-
在集合裡面設置對象類型值
<!--創建多個course對象--> <bean id="course1" class="com.atguigu.spring5.collectiontype.Course"> <property name="cname" value="Spring5框架"></property> </bean> <bean id="course2" class="com.atguigu.spring5.collectiontype.Course"> <property name="cname" value="Mybatis框架"></property> </bean> <!--註入list集合類型,值是對象--> <property name="courseList"> <list> <ref bean="course1"></ref> <ref bean="course2"></ref> </list> </property>
-
把集合註入部分提取出來
-
在
Spring配置文件
中引入名稱空間util<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/util/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> <!--把集合註入部分提取出來--> </beans>
-
使用
util標簽
完成list集合
註入提取<!--1 把提取list集合類型屬性註入--> <util:list id="bookList"> <value>易筋經</value> <value>九陽神功</value> <value>九陰真經</value> </util:list> <!--2 提取list集合類型屬性註入使用--> <bean id="book" class="com.atguigu.spring5.collectiontype.Book"> <property name="list" ref="bookList"></property> </bean>
-
FactoryBean
- Spring有兩種類型
bean
,一種普通bean
,另一種工廠bean(FactoryBean)
- 普通bean:在配置文件中定義bean類型就是返回類型
- 工廠bean:在配置文件中定義bean類型可以和返回類型不一樣
第一步,創建類,讓這個類作為工廠bean,實現介面FactoryBean
第二步,實現介面裡面的方法,在實現的方法中定義返回的bean類型
public class MyBean implements FactoryBean<Course> {
//定義返回bean
@Override
public Course getObject() throws Exception {
Course course = new Course();
course.setCname("abc");
return course;
}
@Override
public Class<?> getObjectType() {
return null;
}
@Override
public boolean isSingleton() {
return false;
}
}
第三步,配置spring配置文件
<bean id="myBean" class="com.atguigu.spring5.factorybean.MyBean"></bean>
測試類:
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Course course = context.getBean("mybean", Course.class);
System.out.println(course);
}
bean作用域
-
在Spring裡面,設置創建bean實例可以是單實例,也可以是多實例
-
在Spring裡面,預設情況下,bean是單實例對象
@Test public void testCollection2(){ ApplicationContext context=new ClassPathXmlApplicationContext("bean2.xml"); Book book =context.getBean("book", Book.class); Book book1 =context.getBean("book", Book.class); System.out.println(book); //com.atguigu.sqpring5.collectiontype.Book@5d11346a System.out.println(book1); //com.atguigu.sqpring5.collectiontype.Book@5d11346a }
以上例子的兩個輸出是相同的,說明bean預設是單實例對象。
-
如何設置單實例or多實例
- 在Spring配置文件
bean標簽
裡面有屬性scope
,用於設置單實例還是多實例 scope
屬性值singleton
:表示單實例對象(預設值)prototype
:表示多實例對象
<!--2 提取list集合類型屬性註入使用--> <bean id="myBean" class="com.frx01.spring5.factorybean.MyBean" scope="prototype"> <property name="list" ref="bookList"></property> </bean>
再次測試:
@Test public void testCollection2(){ ApplicationContext context=new ClassPathXmlApplicationContext("bean2.xml"); Book book =context.getBean("book", Book.class); Book book1 =context.getBean("book", Book.class); System.out.println(book); //com.atguigu.sqpring5.collectiontype.Book@5d11346a System.out.println(book1); //com.atguigu.sqpring5.collectiontype.Book@7a36aefa }
以上例子的兩個輸出不同,說明此時bean是多實例對象。
- 在Spring配置文件
singleton
和prototype
區別- singleton表示單實例,prototype表示多實例
- 設置scope值singleton時候,載入spring配置文件就會創建一個單實例對象
- 設置scope值是prototype時候,不是在載入spring配置文件時候創建對象,在調用getBean方法時候創建多實例對象
bean生命周期
-
生命周期:從對象創建到對象銷毀的過程
-
bean生命周期
- 通過構造器創建bean實例(無參構造)
- 為bean的屬性設置值和對其他bean的引用(調用set方法)
- 調用bean的初始化的方法(需要進行配置初始化的方法)
- bean可以使用了(對象獲取到了)
- 當容器關閉的時候,調用銷毀bean的方法(需要配置銷毀的方法)
-
演示bean生命周期
public class Orders { //無參數構造 public Orders() { System.out.println("第一步 執行無參構造創建bean實例"); } //創建set方法設置屬性的值和對其他bean的引用 private String oname; public void setOname(String oname) { this.oname = oname; System.out.println("第二步 調用set方法設置屬性的值"); } //創建執行的初始化方法 public void initMethod(){ System.out.println("第三步 執行初始化方法"); } //創建執行的銷毀方法 public void destroyMethod(){ System.out.println("第五步 執行銷毀方法"); } }
在Spring配置文件中使用
init-method
指定初始化方法,用destroy-method
指定銷毀方法<bean id="orders" class="com.atguigu.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod"> <property name="oname" value="手機"></property> </bean>
測試:
@Test public void testCollection4() { // ApplicationContext context = // new ClassPathXmlApplicationContext("bean4.xml"); ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml"); Orders orders = context.getBean("orders", Orders.class); System.out.println("第四步 獲取創建bean實例對象"); System.out.println(orders); //手動讓bean實例銷毀 context.close(); }
輸出:
第一步 執行無參構造創建bean實例
第二步 調用set方法設置屬性的值
第三步 執行初始化方法
第四步 獲取創建bean實例對象
com.atguigu.spring5.bean.Order@192d3247
第五步 執行銷毀方法
-
bean的後置處理器,bean生命周期有七步
- 通過構造器創建bean實例(無參構造)
- 為bean的屬性設置值和對其他bean的引用(調用set方法)
- 把bean實例傳遞到bean後置處理器的方法
postProcessBeforeInitialization
- 調用bean的初始化的方法(需要進行配置初始化的方法)
- 把bean實例傳遞到bean後置處理器的方法
postProcessAfterInitialization
- bean可以使用了(對象獲取到了)
- 當容器關閉的時候,調用銷毀bean的方法(需要配置銷毀的方法)
-
演示添加後置處理器效果
-
創建類,實現介面
BeanPostProcessor
,創建後置處理器public class MyBeanPost implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("在初始化之前執行的方法"); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("在初始化之後執行的方法"); return bean; } }
<bean id="orders" class="com.atguigu.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod"> <property name="oname" value="手機"></property> </bean> <!-- 配置後置處理器--> <bean id="MyBeanPost" class="com.atguigu.spring5.bean.MyBeanPost"></bean> </beans>
測試:
@Test public void testCollection4() { // ApplicationContext context = // new ClassPathXmlApplicationContext("bean4.xml"); ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml"); Orders orders = context.getBean("orders", Orders.class); System.out.println("第四步 獲取創建bean實例對象"); System.out.println(orders); //手動讓bean實例銷毀 context.close(); }
輸出:
第一步 執行無參構造創建bean實例
第二步 調用set方法設置屬性的值
在初始化之前執行的方法
第三步 執行初始化方法
在初始化之後執行的方法
第四步 獲取創建bean實例對象
com.atguigu.spring5.bean.Order@192d3247
第五步 執行銷毀方法
-
xml自動裝配
-
自動裝配:根據指定的裝配規則(屬性名稱或者屬性類型),Spring自動將匹配的屬性值註入
-
實現自動裝配:
bean標簽
屬性autowire
,配置自動裝配
autowire
屬性常用兩個值:
byName
根據屬性名稱註入,註入值bean的id值和類屬性名稱一樣
byType
根據屬性類型註入 -
演示自動裝配過程
-
根據屬性名稱自動註入
<bean id="emp" class="com.atguigu.spring5.autowire.Emp" autowire="byName"> <!--<property name="dept" ref="dept"></property>--> </bean> <bean id="dept" class="com.atguigu.spring5.autowire.Dept"></bean>
-
根據屬性類型自動註入
<bean id="emp" class="com.atguigu.spring5.autowire.Emp" autowire="byType"> <!--<property name="dept" ref="dept"></property>--> </bean> <bean id="dept" class="com.atguigu.spring5.autowire.Dept"></bean>
外部屬性文件
-
直接配置資料庫信息
-
配置德魯伊連接池
-
引入德魯伊連接池依賴jar包(druid)
<!--直接配置連接池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/userDb"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean>
-
-
引入外部屬性文件配置資料庫連接池
-
創建外部屬性文件,
properties
格式文件,寫資料庫信息 -
把外部
properties屬性文件
引入到spring配置文件
中-
引入context名稱空間
<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:util="http://www.springframework.org/schema/util" 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/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/util/spring-util.context.xsd">
-
在Spring配置文件使用標簽引入外部屬性文件
<!--引入外部屬性文件--> <context:property-placeholder location="classpath:jdbc.properties"/> <!--配置連接池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${prop.driverClass}"></property> <property name="url" value="${prop.url}"></property> <property name="username" value="${prop.userName}"></property> <property name="password" value="${prop.passwd}"></property> </bean>
-
-
-
基於註解方式
-
什麼是註解
- 註解是代碼特殊標記,格式:
@註解名稱(屬性名稱=屬性值,屬性名稱=屬性值)
- 使用註解,註解作用在類上面,方法上面,屬性上面
- 使用註解目的:簡化xml配置
- 註解是代碼特殊標記,格式:
-
Spring針對Bean管理中創建對象提供註解
@Component
@Service
@Controller
@Repository
以上四個註解功能是一樣的,都可以用來創建bean實例
-
基於註解方式實現對象創建
-
引入依賴
-
開啟組件掃描
<!--開啟組件掃描 1 如果掃描多個包 使用逗號隔開 2 掃描包上層目錄 --> <context:component-scan base-package="com.atguigu"></context:component-scan>
-
創建類,在類上面添加創建對象註解
//在註解裡面value屬性值可以省略不寫 //預設值是類名稱,首字母小寫 //UserService --> userService @Component(value = "userService") //<bean id="userService" class=".."/> public class UserService { public void add(){ System.out.println("service add......"); } }
-
開啟組件掃描細節配置
<!--示例1 use-default-filters="false 表示現在不使用預設filter,不掃描全部,自己配置filter context:include-filler,設置掃描哪些內容 --> <context:component-scan base-package="com.atguigu" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <!--示例2 下麵配置掃描包所有內容 context:exclude-filter:設置哪些內容不進行掃描 --> <context:component-scan base-package="com.atguigu"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
-
基於註解方式實現屬性註入
-
@AutoWired
:根據屬性類型自動裝配第一步,把service和dao對象創建,在service和dao類添加創建對象註解
第二步,在service註入dao對象,在service類添加dao類型屬性,在屬性上面使用註解
@Service public class UserService { //定義dao類型的屬性 //不需要添加set方法 //添加註入屬性註解 @Autowired private UserDao userDao; public void add(){ System.out.println("service add......"); userDao.add(); } }
-
@Qualifier
:根據屬性名稱註入這個@Qualifier註解的使用,和上面@Autowired一起使用
//定義dao類型的屬性 //不需要添加set方法 //添加註入屬性註解 @Autowired //根據類型進行註入 @Qualifier(value = "userDaoImpl1") //根據名稱註入 private UserDao userDao;
-
@Resource
:可以根據類型註入,可以根據名稱註入// @Resource //根據類型進行註入 @Resource(name="userDaoImpl1") //根據名稱進行註入 private UserDao userDao;
-
@Value
:註入普通類型屬性@Value(value = "abc") private String name;
-
-
完全註釋開發
-
創建配置類,代替xml配置文件
@Configuration //作為配置類,替代xml配置文件 @ComponentScan(basePackages = "com.atguigu") public class SpringConfig { }
-
編寫測試類
@Test public void testService2(){ //載入配置類 ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); UserService userService = context.getBean("userService", UserService.class); System.out.println(userService); userService.add(); }
-
-
AOP
概念
什麼是AOP?
- 面向切麵編程(方面),利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合性降低,提高程式的可重用性,同時提高了開發的效率。
- 通俗描述:不通過修改源代碼方式,在主幹功能裡面添加新功能
- 使用登錄例子說明AOP
底層原理
AOP底層使用動態代理
- 有介面代理,使用JDK動態代理
- 創建介面實現類代理對象,增強類的方法
- 沒有介面情況,使用CGLIB動態代理
- 創建子類的代理對象,增強類的方法
JDK動態代理
-
使用JDK動態代理,使用
Proxy類
裡面的方法創建代理對象Proxy類
是屬於java.lang
包中的-
調用
newProxyInstance方法
static Object newProxyInstance(ClassLoader loader, 類<?>[] interfaces, InvocationHandler h)
返回指定介面的代理類的實例,該介面將調用分派給指定的調用處理程式。
參數:
第一個參數:類載入器
第二個參數:增強方法所在類,這個類實現的介面,支持多個介面
第三個參數:實現這個介面
InvocationHandler
,創建代理對象,寫增強的部分
-
-
編寫JDK動態代理代碼
-
創建介面,定義方法
public interface UserDao { public int add(int a,int b); public String update(String id); }
-
創建介面實現類,實現方法
public class UserDaoImpl implements UserDao{ @Override public int add(int a, int b) { return a+b; } @Override public String update(String id) { return id; } }
-
使用Proxy類創建介面代理對象
public class JDKProxy { public static void main(String[] args) { //創建介面實現類代理對象 Class[] interfaces={UserDao.class}; // Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() { // @Override // public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // return null; // } // }); UserDaoImpl userDao=new UserDaoImpl(); UserDao dao=(UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao)); int result = dao.add(1, 2); System.out.println("result"+result); } } //創建代理對象代碼 class UserDaoProxy implements InvocationHandler{ //1 把創建的是誰的代理對象 把誰傳遞過來 //有參數構造器 private Object obj; public UserDaoProxy(Object obj){ this.obj=obj; } //增強的邏輯 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //方法之前 System.out.println("方法之前執行......"+method.getName()+" :傳遞的參數..."+ Arrays.toString(args)); //被增強的方法執行 Object res= method.invoke(obj,args); //方法之後 System.out.println("方法之後執行......"+obj); return res; } }
-
術語
-
連接點(Jointpoint):類裡面哪些方法可以被增強,這些方法被稱為連接點。(類中可以被增強的方法)
-
切入點(Pointcut):實際被真正增強的方法
-
增強(Advice):指的是在目標對象的方法調用前、後或拋出異常時,通過動態代理技術在目標對象的方法周圍插入攔截器的邏輯代碼,從而實現對目標對象方法的增強和定製。
-
通知(增強):實際增強的邏輯部分。通知定義了增強在何時被調用,並指定了增強的類型。
通知有多種類型:
- 前置通知
- 後置通知
- 返回增強
- 環繞通知
- 異常通知
- 最終通知
-
切麵/方面(Aspect):把通知應用到切入點的過程(動作)
AOP操作——準備工作
-
Spring框架一般基於
AspectJ
實現AOP操作AspectJ
不是Spring組成部分,獨立AOP框架,一般把AspectJ和Spring框架一起使用,進行AOP操作
-
基於AspectJ實現AOP操作
- 基於xml配置文件實現
- 基於註解方式實現(使用)
-
在項目工程裡面引入AOP相關依賴
-
切入點表達式
-
作用:知道對哪個類裡面的哪個方法進行增強
-
語法結構:
execution([許可權修飾符] [返回類型] [類全路徑] [方法名稱] [參數列表])
舉例1:
對com.atguigu.dao.BookDao類裡面的add進行增強:
execution(* com.atguigu.dao.BookDao.add(..))
舉例2:
對com.atguigu.dao.BookDao類裡面的所有方法進行增強:
execution(* com.atguigu.dao.BookDao.*(..))
舉例3:
對com.atguigu.dao包裡面所有類,類裡面的所有方法進行增強:
execution(* com.atguigu.dao*.*(..))
AOP操作——AspectJ註解
-
創建類,在類裡面定義方法
public class User { public void add(){ System.out.println("add.............."); } }
-
創建增強類(編寫增強邏輯)
在增強類裡面,創建方法,讓不同方法代表不同通知類型
public class UserProxy { public void before(){ //前置通知 System.out.println("before......."); } }
-
進行通知的配置
-
在
Spring配置文件
中,開啟註解掃描<?xml version="1.0" encoding="UTF-8"?> <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" 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/context http://www.springframework.org/schema/beans/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/beans/spring-aop.xsd"> <!-- 開啟全盤掃描--> <context:component-scan base-package="com.frx01.spring5.aopanno"></context:component-scan> </beans>
-
使用註解創建
User
和UserProxy
對象//被增強的類 @Component public class User { } //增強的類 @Component public class UserProxy { }
-
在增強類上面添加註解
@Aspect
//增強的類 @Component @Aspect //生成代理對象 public class UserProxy { }
-
在
spring配置文件
中開啟生成代理對象<!--開啟Aspect生成代理對象--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
-
-
配置不同類型的通知
在增強類裡面,在作為通知方法上面添加通知類型註解,使用切入點表達式配置
//增強的類 @Component @Aspect //生成代理對象 public class UserProxy { //前置通知 //@Before註解表示作為前置通知 @Before(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))") public void before(){ System.out.println("before......."); } //後置通知(返回通知) @AfterReturning(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))") public void afterReturning(){ System.out.println("afterReturning....."); } //最終通知 @After(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))") public void after(){ System.out.println("after....."); } //異常通知 @AfterThrowing(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))") public void afterThrowing(){ System.out.println("afterThrowing....."); } //環繞通知 @Around(value = "execution(* com.frx01.spring5.aopanno.User.add(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ System.out.println("環繞之前......."); //被增強的方法執行 proceedingJoinPoint.proceed(); System.out.println("環繞之後......."); } }
-
相同切入點抽取
//相同切入點抽取 @Pointcut(value ="execution(* com.atguigu.spring5.aopanno.User.add(..))") public void pointdemo(){ } //前置通知 //@Before註解表示作為前置通知 @Before(value = "pointdemo()") //可直接使用pointdemo作為value public void before(){ System.out.println("before......."); }
-
有多個增強類多同一個方法進行增強,設置增強類優先順序
在增強類上面添加註解
@Order(數字類型值)
,數字類型值越小優先順序越高@Component @Aspect @Order(1) public class PersonProxy { }
-
完全使用註解開發
創建配置類,不需要創建xml配置文件
@Configuration @ComponentScan(basePackages = {"com.atguigu"}) @EnableAspectJAutoProxy(proxyTargetClass = true) public class ConfigAop { }
-
AOP操作——AspectJ配置文件
-
創建兩個類,
增強類
和被增強類
,創建方法 -
在
Spring配置文件
中創建兩個類對象<!--創建對象--> <bean id="book" class="com.atguigu.spring5.aopxml.Book"></bean> <bean id="bookProxy" class="com.atguigu.spring5.aopxml.BookProxy"></bean>
-
在
Spring配置文件
中配置切入點<!--配置aop增強--> <aop:config> <!--切入點--> <aop:pointcut id="p" expression="execution(* com.frx01.spring5.aopxml.Book.buy(..))"/> <!--配置切麵--> <aop:aspect ref="bookProxy"> <!--配置增強作用在哪個方法上--> <aop:before method="before" pointcut-ref="p"/> </aop:aspect> </aop:config>
JdbcTemplate
概念和準備
-
什麼是
JdbcTemplate
Spring框架對JDBC進行封裝,使用JdbcTemplate方便實現對資料庫操作
-
準備工作
-
創建資料庫和表
CREATE DATABASE user_db CREATE TABLE t_book( userId BIGINT PRIMARY KEY, username VARCHAR(100) NOT NULL, ustatus VARCHAR(50) NOT NULL)
-
引入相關的jar包
-
在
Spring配置文件
中配置資料庫的連接池<!-- 資料庫連接池 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> <property name="url" value="jdbc:mysql:///user_db" /> <property name="username" value="root" /> <property name="password" value="root" /> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> </bean>
-
配置
JdbcTemplate對象
,註入DataSource
<!--JdbcTemplate對象--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <!--註入DataSource--> <property name="dataSource" ref="dataSource"></property> </bean>
-
創建
service類
,創建dao類
,在dao
註入jdbcTemplate對象
配置文件:
<!--開啟組件掃描--> <context:component-scan base-package="com.atguigu"></context:component-scan>
Service類:
@Service public class BookService { //註入dao @Autowired private BookDao bookDao; }
Dao類:
@Repository public class BookDaoImpl implements BookDao{ //註入JdbcTemplate @Autowired private JdbcTemplate jdbcTemplate; }
-
JdbcTemplate操作資料庫
添加
-
對應資料庫創建實體類
public class User { private String userId; private String username; private String ustatus; public String getUserId() { return userId; } public String getUsername() { return username; } public String getUstatus() { return ustatus; } public void setUserId(String userId) { this.userId = userId; } public void setUsername(String username) { this.username = username; } public void setUstatus(String ustatus) { this.ustatus = ustatus; } }
-
編寫
Service
和Dao
-
在
dao
進行資料庫添加操作 -
調用
JdbcTemplate對象
裡面update方法
實現添加操作update(String sql, Object... args)
參數:
String sql
:sql語句Object... args
:可變參數,設置sql語句值
@Repository public class BookDaoImpl implements BookDao{ //註入JdbcTemplate @Autowired private JdbcTemplate jdbcTemplate; //添加方法 @Override public void add(Book book) { //1.創建sql語句 String sql="insert into t_book values(?,?,?)"; //2.調用方法實現 Object[] args={ book.getUserId(), book.getUsername(), book.getUstatus()}; int update = jdbcTemplate.update(sql,args); System.out.println(update); } }
-
-
測試
@Test public void testJdbcTemplate(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); BookService bookService = context.getBean("bookService", BookService.class); Book book = new Book(); book.setUserId("1"); book.setUsername("java"); book.setUstatus("A"); bookService.addBook(book); }
結果:
修改和刪除
-
修改
@Override public void update(Book book) { String sql="update t_book set username=?,ustate=? where userId=?"; Object[] args={ book.getUsername(), book.getUstatus(), book.getUserId()}; int update = jdbcTemplate.update(sql,args); System.out.println(update>0?"修改成功":"修改失敗"); }
-
刪除
@Override public void delete(String id) { String sql="delete from t_book where userId=?)"; int update = jdbcTemplate.update(sql, id); System.out.println(update>0?"刪除成功":"刪除失敗"); }
查詢返回某個值
-
查詢表裡面有多少條記錄,返回是某個值
-
使用
JdbcTemplate對象
裡面的queryForOcject方法
實現查詢操作queryForObject(String sql, Class<T> requiredType)
參數:
String sql
:sql語句Class<T> requiredType
:返回類型Class
//查詢表記錄數
@Override
public int selectCount(){
String sql="select count(*) from t_book";
Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
return count;
}
查詢返回對象
-
場景:查詢圖書詳情
-
JdbcTemplate
實現查詢返回對象的方法:queryForObject
queryForObject(String sql, RowMapper<T> rowMapper, Object... args)
參數:
String sql
:sql語句RowMapper<T> rowMapper
:RowMapper
是介面,返回不同類型的數據,使用這個介面裡面實現類完成數據封裝Object... args
:sql語句值
//查詢返回對象
@Override
public Book findBookInfo(String id) {
String sql="select * from t_book where userId=?";
//調用方法
Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class),id);
return book;
}
查詢返回集合
-
場景:查詢圖書列表分頁
-
調用
JdbcTemplate
的query
方法實現查詢返回集合query(String sql, RowMapper<T> rowMapper, Object... args)
參數:
String sql
:sql語句RowMapper<T> rowMapper
:RowMapper
是介面,返回不同類型的數據,使用這個介面裡面實現類完成數據封裝Object... args
:sql語句值
//查詢返回集合
@Override
public List<Book> findAllBook() {
String sql="select * from t_book";
//調用方法
List<Book> bookList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class));
return bookList;
}
批量操作
-
批量操作:操作表裡面的多條記錄
-
JdbcTemplate
實現批量添加操作batchUpdate(String sql, List<Object[]> batchArgs)
參數:
String sql
:sql語句List<Object[]> batchArgs
:List集合,添加多條記錄數據//批量添加 @Override public void batchAddBook(List<Object[]> batchArgs) { String sql="insert into t_book values(?,?,?)"; int[] ints=jdbcTemplate.batchUpdate(sql,batchArgs); System.out.println(Arrays.toString(ints)); }
測試:
//批量添加_測試 List<Object[]> batchArgs=new ArrayList<>(); Object[] o1={"3","java","a"}; Object[] o2={"4","c++","b"}; Object[] o3={"5","MySQL","e"}; batchArgs.add(o1); batchArgs.add(o2); batchArgs.add(o3); //調用批量添加 bookService.batchAdd(batchArgs);
-
JdbcTemplate
實現批量修改操作//批量修改 @Override public void batchUpdateBook(List<Object[]> batchArgs) { String sql="update t_book set username=?,ustatus=? where userId=?"; int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs); System.out.println(Arrays.toString(ints)); }
測試:
//批量修改_測試 List<Object[]> batchArgs=new ArrayList<>(); Object[] obj1={"java111","a3","3"}; Object[] obj2={"c++1010","b4","4"}; Object[] obj3={"MySQL11","c5","5"}; batchArgs.add(obj1); batchArgs.add(obj2); batchArgs.add(obj3); //調用方法 bookService.batchUpdate(batchArgs);
-
JdbcTemplate
實現批量刪除操作//批量刪除 @Override public void batchDeleteBook(List<Object[]> batchArgs) { String sql="delete from t_book where userId=?"; int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs); System.out.println(Arrays.toString(ints)); }
測試:
//批量修改 List<Object[]> batchArgs=new ArrayList<>(); Object[] obj1={"3"}; Object[] obj2={"4"}; batchArgs.add(obj1); batchArgs.add(obj2); //調用方法實現批量刪除 bookService.batchDelete(batchArgs);
事務管理
概念
-
什麼是事務?
事務是資料庫操作最基本的單元,邏輯上一組操作,要麼都成功,如果有一個失敗所有操作都失敗
- 典型場景:銀行轉賬
- lucy轉賬100元給mary
- lucy少100,mary多100
-
事務的四個特性(ACID)
- 原子性
- 一致性
- 隔離性
- 持久性
搭建事務操作環境
-
創建資料庫表,添加數據
CREATE TABLE t_account( id VARCHAR(20), username VARCHAR(50), money VARCHAR(50)) INSERT INTO t_account VALUES('1','lucy',1000) INSERT INTO t_account VALUES('2','mary',1000)
-
創建
service
,搭建dao
,完成對象創建和註入關係service
註入dao
,在dao
註入JdbcTemplate
,在JdbcTemplate
註入DataSource
@Service public class UserService { //註入dao @Autowired private UserDao userDao; }
@Repository public class UserDaoImpl implements UserDao{ @Autowired private JdbcTemplate jdbcTemplate; }
-
在
dao
創建兩個方法,多錢和少錢的方法,在service
創建方法(轉賬的方法)@Repository public class UserDaoImpl implements UserDao{ @Autowired private JdbcTemplate jdbcTemplate; //少錢 @Override public void reduceMoney() { String sql="update t_account set money=money-? where username=?"; jdbcTemplate.update(sql,100,"lucy"); } //多錢 @Override public void addMoney() { String sql="update t_account set money=money+? where username=?"; jdbcTemplate.update(sql,100,"mary"); } }
@Service public class UserService { //註入dao @Autowired private UserDao userDao; //轉賬的方法 public void accountMoney(){ //lucy少100 userDao.reduceMoney(); //mary多100 userDao.addMoney(); } }
-
上面代碼,如果正常執行沒有問題的,但是如果代碼執行過程中出現異常,有問題
//轉賬的方法 public void accountMoney(){ //lucy少100 userDao.reduceMoney(); //模擬異常 int i =10/0; //mary多100 userDao.addMoney(); } //結果lucy少了100,而mary並沒有增加100
-
以上的問題如何解決呢?
- 使用事務進行解決
-
事務操作過程
//轉賬的方法 public void accountMoney(){ try { //第一步 開啟事務 //第二步 進行業務操作 //lucy少100 userDao.reduceMoney(); //模擬異常 int i = 10 / 0; //mary多100 userDao.addMoney(); //第三步 沒有發生異常,提交事務 }catch (Exception e){ //第四步 出現異常,事務回滾 } }
-
Spring事務管理介紹
-
事務添加到JavaEE三層結構裡面Service層(業務邏輯層)
-
在Spring進行事務管理操作有兩種方式:編程式事務管理和聲明式事務管理(使用)
-
聲明式事務管理
- 基於註解方式(使用)
- 基於xml配置文件方式
-
在Spring進行聲明式事務管理,底層使用
AOP
原理 -
Spring事務管理API:
PlatformTransactionManager
PlatformTransactionManager
介面,代表事務管理器,這個介面針對不同的框架提供不同的實現類
註解聲明式事務管理
-
在
Spring配置文件
配置事務管理器<!--創建事務管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--註入數據源--> <property name="dataSource" ref="dataSource"></property> </bean>
-
在
Spring配置文件
,開啟事務註解-
在
Spring配置文件
引入名稱空間tx
<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" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.sp
-