# Spring概述 1、Spring是輕量級開源JavaEE框架 2、Spring可以解決企業應用開發的複雜性 3、組成核心IOC、Aop IOC:控制反轉,把創建對象過程交給Spring進行管理 Aop:面向切麵,不修改源代碼進行功能增強 4、Spring特點 方便解耦,簡化開發 Aop編程支持 ...
# Spring概述
1、Spring是輕量級開源JavaEE框架
2、Spring可以解決企業應用開發的複雜性
3、組成核心IOC、Aop
-
IOC:控制反轉,把創建對象過程交給Spring進行管理
-
Aop:面向切麵,不修改源代碼進行功能增強
4、Spring特點
- 方便解耦,簡化開發
- Aop編程支持
- 方便程式測試
- 方便集成各種優秀框架
- 方便進行事務管理
- 降低java api的使用難度
*此文檔版本為Spring5
IOC
什麼是IOC
(1)控制反轉,把對象的創建和對象之間調用的過程,交給Spring進行管理
(2) 使用IOC的目的:為了降低耦合
(3) 做入門案例就是IOC的實現
IOC底層原理
(1) xml解析、工廠模式、反射
IOC過程 (進一步降低耦合度)
第一步 xml配置文件,配置創建的對象
<bean id="dao" class="com.atguigu.UserDao"></bean>
第二步 有service類和dao類 創建工廠類
class UserFactory{
public static UserDao getDao(){
String classValue = class屬性值;//1 xml解析
Class class = Class.forName(classValue);//2 通過反射創建對象
return (UserDao)class.newInstance();//返回對象
}
}
IOC介面
-
IOC思想基於IOC容器完成,IOC容器底層就是對象工廠
-
Spring提供IOC容器實現兩種方式:(兩個介面)
- BeanFactory :IOC容器基本實現,是Spring內部的使用介面,不提供給開發人員使用
- 載入配置文件時不會創建對象,獲取\使用對象時才會創建對象
- ApplicationContext :BeanFactory介面的子介面,提供更多更強大的功能,一般由開發人員使用
- 載入配置文件時就會把配置文件中對象創建(伺服器啟動時創建)
ApplicationContext介面有實現類
IOC操作 Bean管理
什麼是Bean管理
包含以下兩個操作
- Spring創建對象
- Spring註入屬性
Bean管理操作
1 bean創建對象
1 基於xml配置文件方式
<!--配置User對象創建-->
<bean id="user" class="com.atguigu.spring5.User"></bean>
(1)在Spring配置文件中,使用bean標簽,標簽里添加對應屬性,就可以實現對象創建
(2)在bean標簽中有很多屬性:
-
id屬性:唯一標識(不能加特殊符號)
-
class屬性:類全路徑(包類路徑)
-
name:類似id(可添加特殊符號)
(3)創建對象的時候,預設也是執行無參構造方法
2 基於註解方式
2 基於xml註入屬性
(1) DI:依賴註入,就是註入屬性(DI是IOC的一種具體實現,在創建對象的基礎之上進行)
第一種註入方式:使用set方法進行註入
第一步:創建類、創建屬性、創建對應的set方法
public class Book {
//創建屬性
private String bname;
private String bauthor;
//創建屬性對應的set方法
public void setBname(String bname) {
this.bname = bname;
}
public void getBauthor(String bauthor) {
this.bauthor = bauthor;
}
}
第二步:在Spring配置文件配置對象創建,配置屬性註入
<!--2 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 Book {
//創建屬性
private String bname;
private String bauthor;
//創建屬性對應的set方法
public void setBname(String bname) {
this.bname = bname;
}
public void setBauthor(String bauthor) {
this.bauthor = bauthor;
}
}
第二種註入方法:使用有參構造進行註入
第一步:創建類 ,定義屬性,創建屬性對應有參構造方法
/**
* 使用有參構造註入
* */
public class Orders {
private String oname;
private String address;
public Orders(String oname,String address){
this.oname = oname;
this.address = address;
}
}
第二步:在Spring的配置文件中進行配置
<!--3 使用有參構造註入屬性-->
<bean id="orders" class="com.atguigu.spring5.Orders">
<!-- constructor-arg標簽用於有參構造註入屬性-->
<constructor-arg name="oname" value="電腦"></constructor-arg>
<constructor-arg name="address" value="China"></constructor-arg>
</bean>
第三種註入方式:P名稱空間註入(底層使用的還是set方法註入)
使用p名稱空間註入,可以簡化基於xml配置方式
- 添加p名稱空間在配置文件中
-
進行屬性註入,在bean標簽裡面進行操作
<!--4 使用p名稱空間註入--> <bean id="book" class="com.atguigu.spring5.Book" p:bname="張三的一生" p:bauthor="羅翔"> </bean>
xml註入其他類型屬性
字面量:固定值
-
null值
<!--null值--> <property name="address"> <null/> </property> </bean>
-
屬性包含特殊符號
<!--註入特殊符號 1 拔尖括弧進行轉義 <> 2 把特殊符號內容寫到CDATA --> <property name="address"> <value><![CDATA[<<南京>>]]></value> </property>
註入屬性-外部bean
-
創建兩個類service類和dao類
-
在service調用dao裡面的方法
-
在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>
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(); //原始方式:創建UserDao對象 // UserDao userDao = new UserDaoImpl(); // userDao.update(); } }
註入屬性-內部bean
-
一對多關係,部門和員工
一個部門有多個員工,一個員工屬於一個部門。 部門是一 員工是多
-
在實體類之間表示一對多關係
員工表示所屬部門,使用對象類型進行表示
//部門類 public class Dept { private String dname; public void setDname(String dname) { this.dname = dname; } }
//員工類 public class Emp { private String ename; private String gender; //員工屬於某一個部門,使用對象形式表示 private Dept dept; public void setDept(Dept dept) { this.dept = dept; } public void setEname(String ename) { this.ename = ename; } public void setGender(String gender) { this.gender = gender; } }
-
在Spring配置文件中進行配置
<!--內部bean--> <bean id="emp" class="com.atguigu.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>
第二種寫法 類中需要寫get方法
//員工類
public class Emp {
private String ename;
private String gender;
//員工屬於某一個部門,使用對象形式表示
private Dept dept;
//生成dept的get方法
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
public void setEname(String ename) {
this.ename = ename;
}
public void setGender(String gender) {
this.gender = gender;
}
public void add(){
System.out.println(ename+"::"+gender+"::"+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> set; public void setSet(Set<String> set) { this.set = set; } 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; } }
在Spring配置文件進行配置
<!--1 集合類型屬性註入--> <bean id="stu" class="com.atguigu.spring5.collectiontype.Stu"> <!--數組類型屬性註入--> <property name="courses" > <array> <value>java課程</value> <value>sql課程</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="set"> <set> <value>MySQL</value> <value>Redis</value> </set> </property> </bean>
-
在集合里設置對象類型值
<!--註入list集合類型,值是對象--> <property name="courseList"> <list> <ref bean="course1"></ref> <ref bean="course2"></ref> </list> </property>
<!--創建多個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>
-
把集合註入部分提取出來
- 在Spring配置文件中引入空間名稱util
<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/beans/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" > <!--若引入對象使用ref標簽--> <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>
FactorBean
Spring里有兩種bean,一種普通的bean。另外一種是FactoryBean(Spring內置的)
普通bean
Spring配置文件中定義bean類型即為返回類型
FactoryBean
Spring配置文件中定義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; } } //測試方法 @Test public void testCollection3(){ ApplicationContext context=new ClassPathXmlApplicationContext("bean3.xml"); Course course=context.getBean("myBean",Course.class); System.out.println(course); }
配置文件
<bean id="myBean" class="com.atguigu.spring5.factorybean.MyBean"> </bean>
bean的作用域
-
在Spring里,預設設置下,bean是單實例對象
@Test public void testCollection2(){ ApplicationContext context=new ClassPathXmlApplicationContext("bean2.xml"); Book book1=context.getBean("book", Book.class); Book book2=context.getBean("book", Book.class); //book.test(); System.out.println(book1); System.out.println(book2); }
輸出顯示地址相同為單實例對象:
-
如何設置單實例或多實例
(1)在Spring配置文件bean標簽裡面有用於設置的屬性(scope)
(2)scope屬性值
- 預設值:singleton,表示單實例對象
- prototype,表示多實例對象
<!--2 提取list集合類型屬性註入使用--> <bean id="book" class="com.atguigu.spring5.collectiontype.Book" scope="prototype"> <property name="list" ref="bookList"></property> </bean>
兩對象地址不同:
-
singleton和prototype區別
- singleton表示單實例,prototype多實例
- 設置scope值是singleton時,載入Spring配置文件時就會創建單實例對象
- 設置scope值是prototype時,不是在載入Spring配置文件時創建對象,在調用getBean方法時創建多例對象
Bean生命周期
從對象的創建到生命的銷毀的過程
bean生命周期:
-
通過構造器創建bean實例(無參數構造)
-
為bean的屬性設置值和對其他bean的引用(調用set方法)
把bean實例傳給bean後置處理器的方法postProcessBeforeInitialization(bean的後置處理器BeanPostProcessor,bean共有7步)
-
調用bean初始化的方法(需要進行配置)
把bean實例傳給bean後置處理器的另外一個方法postProcessAfterInitialization(bean的後置處理器BeanPostProcessor,bean共有7步)
-
bean可以使用了(對象獲取到了)
-
當容器關閉時,調用bean的銷毀的方法(需要進行配置銷毀的方法)
<bean id="orders" class="com.atguigu.spring5.bean.Orders" init-method="initMethod" destroy-method="destoryMethod"> <property name="oname" value="手機"> </property> </bean>
public class Orders { //1 無參構造 public Orders(){ System.out.println("第一步 執行無參構造方法創建bean實例"); } private String oname; public void setOname(String oname) { this.oname = oname; System.out.println("第二步 調用set方法設置屬性值"); } //3 創建執行的初始化方法 public void initMethod(){ System.out.println("第三步 執行初始化方法"); } //5 創建執行的銷毀方法 public void destoryMethod(){ System.out.println("第五步 執行銷毀方法"); } } //測試類 @Test public void testBean3(){ // 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(); }
添加後置處理器之後:
-
創建類,實現介面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="myBeanPost" class="com.atguigu.spring5.bean.MyBeanPost"></bean>
IOC操作Bean管理(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>
</beans>
根據屬性類型自動註入
<!--實現自動裝配
bean標簽屬性autowire,配置自動裝配
autowire屬性常用的兩個值:
byName根據屬性名稱,註入值bean的id和類屬性名稱一樣
byType根據屬性類型註入
-->
<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>
</beans>
IOC操作Bean管理(外部屬性文件)
-
直接配置資料庫信息
1.配置德魯伊連接池(Druid連接池,阿裡巴巴)
2.引入德魯伊連接池依賴jar包
<!--直接配置連接池--> <bean id="database" class="com.alibaba.druid.pool.DruidDataSource"> <property name="DriverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://locahost:3306/userDb"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </bean>
-
引入外部屬性文件配置資料庫連接池
(1)創建外部屬性文件,properties格式文件,寫資料庫信息
properties文件存儲key:value格式配置
prop.driverClass=com.mysql.jdbc.Driver prop.url=jdbc:mysql://localhost:3306/userDb prop.userName=root prop.password=root
(2)把外部properties屬性文件引入到Spring配置文件中
-
引入context名稱空間
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> </beans>
-
在Spring配置文件標簽引入外部屬性文件
<!--引入外部屬性文件--> <context:property-placeholder location="classpath:jdbc.properties"/> <!--配置連接池--> <bean id="database" 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.password}"></property> </bean>
-
IOC操作Bean管理(基於註解方式)
什麼是註解?
-
註解是代碼特殊標記,格式:@註解名稱(屬性名稱=屬性值,屬性名稱=屬性值。。。)
-
使用註解,註解作用在類、方法、屬性上面
-
使用註解的目的:簡化xml配置
Spring針對Bean管理中創建對象提供註解:
-
@Component
-
@Service
-
@Controller
-
@Repository
*四個註解功能相同,都可以用來創建bean對象
基於註解方式實現對象的創建:
-
引入依賴
-
開啟組件掃描
<!--1 開啟組件掃描 1 掃描多個包時,使用逗號隔開 2 掃描包上層目錄可掃描包下多個包 --> <context:component-scan base-package="com.atguigu"></context:component-scan>
-
創建類,在類上面添加創建對象註解
//在註解裡面value屬性值可以不寫 //預設值是類名稱,首字母小寫 //UserService.class---userService @Service(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-filter,設置掃描哪些內容 --> <context:component-scan base-package="com.atguigu" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/> </context:component-scan> <!--示例2 下麵示例掃描包下所有內容 context:exclude-filter ,設置哪些不掃描 --> <context:component-scan base-package="com.atguigu"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/> </context:component-scan>
-
基於註解方式實現屬性註入
-
@AutoWired:根據屬性類型進行自動裝配
①把service和dao對象創建,在service和dao類添加創建對象註解
②在service註入dao對象,在service類添加dao類型屬性,在屬性前使用註解
@Service(value = "userService")//<bean id="userService" class="..">相同 public class UserService { //定義dao類型屬性 //不需要添加set方法(spring已經封裝了這一步) //添加註入屬性的註解 @Autowired private UserDao userDao; public void add(){ System.out.println("service add...."); userDao.add(); } }
-
@Qualifier:根據屬性名稱進行註入
此註解的使用需要與@Autowired一起使用
@Repository(value = "userDaoImpl1") public class UserDaoImpl implements UserDao{ @Override public void add() { System.out.println("dao add..."); } }
@Service(value = "userService")//<bean id="userService" class="..">相同 public class UserService { //定義dao類型屬性 //不需要添加set方法(spring已經封裝了這一步) //添加註入屬性的註解 @Autowired //根據類型進行註入 @Qualifier(value = "userDaoImpl1") private UserDao userDao; public void add(){ System.out.println("service add...."); userDao.add(); } }
-
@Resource:(javax中的註解,jdk11之後移除)可以根據類型註入,可以根據名稱註入
// @Resource//預設根據類型註入 @Resource(name = "userDaoImpl1") private UserDao userDao;
-
@Value:註入普通類型屬性
@Value(value = "abc") private String name;
-
-
完全註解開發
(1)創建配置類,替代xml配置文件
@Configuration//作為配置類,替代xml配置文件 @ComponentScan(basePackages = {"com.atguigu"}) public class SpringConfig { }
(2)編寫測試類
@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動態代理
創建子類的代理對象,增強類的方法
AOP底層原理(JDK動態代理)
使用JDK動態代理,使用Proxy類裡面的方法創建代理對象
調用newProxyInstans方法
方法有三個參數:
- 類載入器
- 增強方法所在的類,這個類實現的介面,支持多個介面
- 實現這個介面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; } }
AOP(術語)
-
連接點:類裡面可以被增強的方法
-
切入點:類裡面實際被增強的方法
-
通知(增強):實際增強的邏輯部分(新加的部分)
*假如add()方法被增強
- 前置通知:add()之前執行的增強
- 後置通知:add()之後執行的增強
- 環繞通知:add()之前之後都執行的增強
- 異常通知:add()異常時執行的增強
- 最終通知:return之後執行的增強,後置通知之後,有異常時不執行
-
切麵(動作)
把通知應用到切入點的過程
AOP操作(準備)
1、Spring框架中一般基於AspectJ實現AOP操作
AspectJ:AspectJ不是Spring組成部分,獨立AOP框架,一般把AspectJ和Spring框架一起使用,進行AOP操作
2、基於AspectJ實現AOP操作有兩種方式:
- 基於xml配置文件使用
- 基於註解方式實現(使用)
3、在項目工程里引入AOP相關依賴
4、切入點表達式
-
切入點表達式的作用:知道對哪個類裡面哪個方法進行增強
-
語法結構:
execution([許可權修飾符] [返回類型] [類全路徑] [方法名稱] ([參數列表]))
例1:對com.atguigu.dao.BookDao類裡面的add方法進行增強
execution(public void com.atguigu.dao.BookDao.add(int a,int b))
許可權修飾符可省略,預設public
返回類型可以用*號表示全類型
execution(* com.atguigu.dao.BookDao.add(int a,int b))
例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配置文件中,開啟註解的掃描
<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/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--開啟註解掃描--> <context:component-scan base-package="com.atguigu.spring5.aopanno"></context:component-scan> </beans>
-
使用註解創建User和UserProxy對象
-
在增強類上面添加註解@Aspect
//增強的類 @Component @Aspect //生產代理對象 public class UserProxy { //前置通知 public void before(){ System.out.println("before..."); } }
-
在Spring配置文件中開啟生成代理對象
<!--開啟Aspect生成代理對象--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
-
配置不同類型通知
-
在增強類的裡面,在通知方法上面添加通知類型的註解,使用切入點表達式配置
-
@Before
-
@After
-
@AfterReturning
-
@AfterThrowing
-
@Around
//增強的類 @Component @Aspect //生產代理對象 public class UserProxy { //前置通知 //@Before註解表示前置通知 @Before(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))") public void before(){ System.out.println("before..."); } @After(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))") public void after(){ System.out.println("after..."); } @AfterReturning(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))") public void afterReturning(){ System.out.println("afterReturning..."); } @AfterThrowing(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))") public void afterThrowing(){ System.out.println("afterThrowing...異常"); } @Around(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ System.out.println("around...之前"); //被增強方法 proceedingJoinPoint.proceed(); System.out.println("around...之前後"); } }
-
-
公共切入點
//相同切入點抽取 @Pointcut(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))") //切入點註解 public void pointdemo(){ } //前置通知 //@Before註解表示前置通知 @Before(value = "pointdemo()") public void before(){ System.out.println("before..."); }
-
多個增強類對同一個切入點增強時,可設置優先順序
在增強類上面加一個註釋@Order(數字類型值),數字類型值越小它的優先順序越高(值從0開始)
-
完全使用註解開發
創建配置類(config),不需要創建xml配置文件
@Configuration @ComponentScan(basePackages = "com.atguigu") //開註解掃描 @EnableAspectJAutoProxy(proxyTargetClass = true) //開啟Aspect生成代理對象 public class ConfigAop { }
-
AOP(AspectJxml配置文件)
-
創建兩個類:增強類和被增強類,創建方法
-
在Spring配置文件中創建兩個類的對象
-
在Spring配置文件中配置切入點
<!-- 創建兩個類的對象--> <bean id="book" class="com.atguigu.spring5.aopxml.Book"></bean> <bean id="bookProxy" class="com.atguigu.spring5.aopxml.BookProxy"></bean> <!-- 配置切入點--> <aop:config> <!-- 切入點--> <aop:pointcut id="buy" expression="execution(* com.atguigu.spring5.aopxml.Book.buy()) "/> <!-- 配置切麵(把增強應用到切入點的過程)--> <aop:aspect ref="bookProxy"> <!-- 增強作用在具體方法上--> <aop:before method="before" pointcut-ref="buy"></aop:before> </aop:aspect> </aop:config>
JdbcTemplate
Spring框架對JDBC進行封裝,使用JdbcTemplate方便實現對資料庫操作
JdbcTemplate使用準備工作
-
引入相關jar包
-
在Spring配置文件中配置資料庫連接池
<!--資料庫連接池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> <property name="name" 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對象
-
<!--1 開啟組件掃描--> <context:component-scan base-package="com.atguigu"></context:component-scan>
-
@Service public class BookService { //註入Dao @Autowired private BookDao bookDao; }
-
@Repository public class BookDaoImpl implements BookDao{ //註入JdbcTemplate @Autowired private JdbcTemplate jdbcTemplate; }
-
JdbcTemplate操作資料庫(添加)
-
對應資料庫創建
-
編寫service和dao
-
在dao進行資料庫添加操作
jdbcTemplate.update();
-
調用JdbcTemplate中的update()實現添加操作
有兩個參數:
1. sql語句 2. 可變參數設置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 value(?,?,?)"; //2 調用方法實現 Object[] args = {book.getUser_id(),book.getUsername(),book.getUstatus()}; int update = jdbcTemplate.update(sql,args); System.out.println(update); } }
-
測試類
public class TestBook { @Test public void testJdbcTemplate(){ ApplicationContext context= new ClassPathXmlApplicationContext("bean1.xml"); BookService bookServcie = context.getBean("bookService", BookService.class); Book book = new Book(); book.setUser_id(1); book.setUsername("張三"); book.setUstatus("a"); bookServcie.addBook(book); } }
-
JdbcTemplate操作資料庫(修改和刪除)
- 方法的實現
//修改的方法
@Override
public void updateBook(Book book) {
//1 創建sql語句
String sql="update t_book set username=?,ustatus=? where user_id=?";
//2 調用方法實現
Object[] args = {book.getUsername(),book.getUstatus(),book.getUser_id()};
int update = jdbcTemplate.update(sql,args);
System.out.println(update);
}
//刪除的方法
@Override
public void deleteBook(String id) {
//1 創建sql語句
String sql="delete from t_book where user_id=?";
//2 調用方法實現
int update = jdbcTemplate.update(sql,id);
System.out.println(update);
}
-
測試
public class TestBook { @Test public void testJdbcTemplate(){ ApplicationContext context= new ClassPathXmlApplicationContext("bean1.xml"); BookService bookServcie = context.getBean("bookService", BookService.class); // //添加 // Book book = new Book(); // book.setUser_id(1); // book.setUsername("張三"); // book.setUstatus("a"); // bookServcie.addBook(book); //修改 Book book = new Book(); book.setUser_id(1); book.setUsername("javaupup"); book.setUstatus("atguigu"); bookServcie.updateBook(book); // //刪除 // bookServcie.deleteBook("1"); }
JdbcTemplate操作資料庫(查詢)
查詢返回某個值
-
場景:查詢表裡有多少條記錄,返回是某個值
-
使用JdbcTemplate實現查詢返回某個值
jdbcTemplate.queryForObject(sql,Integer.class);
有兩個參數:
- sql語句
- 返回類型Class
//查詢表中的記錄數 @Override public int selectCount() { String sql="SELECT COUNT(*) FROM t_book"; Integer count = jdbcTemplate.queryForObject(sql, Integer.class); return count; }
查詢返回對象
-
場景:查詢圖書詳情
-
JdbcTemplate實現查詢返回對象
jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper
(Book.class), id); 三個參數:
-
sql語句
-
RowMapper,是介面,返回不同類型的數據,使用這個介面裡面的實現類完成數據封裝
-
傳遞sql語句中?(占位符)的值
@Override public Book findBookInfo(int id) { String sql="SELECT * FROM t_book where user_id=?"; //調用方法 Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), id); return book; }
-
查詢返回集合
-
場景:查詢圖書列表分頁
-
調用JdbcTemplate實現查詢返回集合
jdbcTemplate.query(sql, new BeanPropertyRowMapper
(Book.class)); 三個參數:
-
sql語句
-
RowMapper,是介面,返回不同類型的數據,使用這個介面裡面的實現類完成數據封裝
-
傳遞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操作資料庫(批量)
-
批量添加
兩個參數:
-
sql語句
-
List集合,添加多條記錄數據
//批量添加 @Override public void batchAddBook(List<Object[]> batchArgs) { String sql = "insert into t_book value(?,?,?)"; int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs); System.out.println(Arrays.toString(ints)); }
-
-
批量修改
@Override public void batchUpdateBook(List<Object[]> batchArgs) { String sql = "update t_book set username=?,ustatus=? where user_id=?"; int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs); System.out.println(Arrays.toString(ints)); }
測試
//批量修改 List<Object[]> batchArgs = new ArrayList<>(); Object[] objects1 = {"cc00","ccc00","3"}; Object[] objects2 = {"dd00","ddd00","4"}; Object[] objects3 = {"ee00","eee00","5"}; batchArgs.add(objects1); batchArgs.add(objects2); batchArgs.add(objects3); bookServcie.batchUpdate(batchArgs);
-
批量刪除
@Override public void batchDeleteBook(List<Object[]> batchArgs) { String sql="delete from t_book where user_id=?"; int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs); System.out.println(Arrays.toString(ints)); }
測試
List<Object[]> batchArgs = new ArrayList<>(); Object[] objects1 = {"3"}; Object[] objects2 = {"4"}; Object[] objects3 = {"5"}; batchArgs.add(objects1); batchArgs.add(objects2); batchArgs.add(objects3); bookServcie.batchDelete(batchArgs);
事務操作
1、事務是資料庫操作最基本單元,邏輯上一組操作,要麼都成功,如果有一個失敗則全失敗
典型場景:銀行轉賬:
- lucy轉賬100元給mary
- lucy少100,mary多100
2、事務的四個特性(ACID):
(1)原子性(atomicity):成功都成功,失敗都失敗
(2)一致性(consistency):操作之前和操作之後總量不變
(3)隔離性(isolation):多事務操作時,之間不會產生影響
(4)持久性(durability):提交之後表中數據就會發生變化
事務操作(搭建事務操作環境)
典型場景:銀行轉賬:
轉賬環境:
- 創建資料庫,添加記錄
-
創建service,搭建dao,完成對象創建和註入關係
-
service註入dao,在dao註入JdbcTemplate,在JdbcTemplate註入DateSource
-
@Service public class UserService { //註入Dao @Autowired private UserDao userDao; }
-
@Repository public class UserDaoImpl implements UserDao{ @Autowired private JdbcTemplate jdbcTemplate; }
-
<!--1 開啟組件掃描--> <context:component-scan base-package="com.atguigu"></context:component-scan> <!--資料庫連接池--> <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對象--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <!--註入DataSource--> <property name="dataSource" ref="dataSource"></property> </bean>
-
-
在dao創建兩個方:多錢和少錢,在service創建轉賬方法
-
以上代碼如果正常執行是沒有問題的,但是如果以上代碼出現異常,有問題,則應符合原子性和一致性,賬戶數據都不變
事務管理(Spring事務管理介紹)
- 事務一般添加到JavaEE三層結構里的service層(業務邏輯層)
- 在Spring進行事務管理操作有兩種方式
- 編程式事務管理
- 聲明式事務管理(常用)
- 聲明式事務管理
- 基於註解方式實現(常用)
- 基於xml配置文件方式
- 在Spring進行聲明式事務管理,底層使用AOP原理
- Spring事務管理API
- 提供了一個介面,代表事務管理器,這個介面針對不同的框架提供不同的實現類
事務操作(註解聲明式事務管理)
-
在Spring配置文件配置事務管理器
<!-- 創建事務管理器--> <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 註入數據源--> <property name="dataSource" ref="dataSource"></property> </bean>
-
在Spring配置文件中開啟事務註解
-
在Spring配置文件中引入名稱空間tx
xmlns:tx="http://www.springframework.org/schema/tx"
-
開啟事務註解
<!-- 開啟事務註解--> <tx:annotation-driven transaction-manager="dataSourceTransactionManager"></tx:annotation-driven>
-
-
在service類上面添加事務註解
@Transactional
- 如果添加到類上面,則類中所有方法都添加事務
- 如果添加到方法上面,則只給此方法添加事務
事務操作(聲明式事務管理參數配置)
-
在service類上面添加@Transactional,在這個註解里可以配置事務相關的參數
-
propagation:事務傳播行為
*多事務方法直接進行調用,這個過程中事務是如何進行管理的
-
isolation:事務隔離級別
預設值:@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
*事務有特性稱為隔離性,多事務操作之間不會產生影響,不考慮隔離性會產生很多問題
*有三個讀的問題:臟讀、不可重覆讀、虛(幻)讀
- 臟讀:一個提交事務讀取到了另一個未提交事務的數據
- 不可重覆度:一個未提交事務讀取到另一提交事務修改數據
- 虛讀:一個未提交事務讀取到另一提交事務添加****數據
解決:通過設置事務隔離級別就能解決讀問題
-
timeout:超時時間
- 事務需要在一定時間內提交,如果不提交就會進行回滾
- 預設值:-1(不超時)
- 設置以秒為單位
-
readOlay:是否只讀
- 讀:查詢操作;寫:添加修改刪除操作
- 預設值:false(表示可以讀也可以寫)
- 設置成true時只能讀不能寫
-
rollbackFor:回滾
- 設置出現哪些異常進行事務回滾
-
noRollbackFor:不回滾
- 設置出現哪些異常不進行事務回滾
-
事務操作(xml聲明式事務管理)
-
在Spring管理文件中進行配置
-
配置事務管理器
-
配置通知
-
配置切入點和切麵
<!--1 創建事務管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 註入數據源--> <property name="dataSource" ref="dataSource"></property> </bean> <!--2 配置通知--> <tx:advice id="txadvice"> <!--配置事務參數--> <tx:attributes> <!--指定在哪種規則的方法上面添加事務--> <tx:method name="accountMoney" propagation="REQUIRES_NEW" isolation="REPEATABLE_READ" read-only="false"/> <!-- <tx:method name="account*"/>--> </tx:attributes> </tx:advice> <!--3 配置切入點和切麵--> <aop:config> <!-- 切入點--> <aop:pointcut id="pt" expression="execution(* com.atguigu.spring5.service.UserService.*(..))"/> <!-- 切麵--> <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/> </aop:config>
-
事務操作(完全註解聲明式註解管理)
創建配置類,使用配置類代替xml配置文件
@Configuration //配置類
@ComponentScan(basePackages = "com.atguigu") //組件掃描
@EnableTransactionManagement //開啟事務
public class TxConfig {
//1. 創建資料庫連接池
@Bean
public DruidDataSource getDruidDataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql:///user_db");
druidDataSource.setUsername("root");
druidDataSource.setPassword("root");
return druidDataSource;
}
//2. 創建JdbcTemplate對象
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource){
//到IOC容器中根據類型找到DateSource
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//註入DateSource
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
//3. 創建事務管理器對象
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}
Spring5框架新功能
整個Spring5框架基於java8,運行時相容JDK9,許多不建議使用的類和方法在代碼庫中刪除
Spiring5框架自帶了通用的日誌框架
- Spring5已經移除了Log4jConfigListener,官方建議使用Log4j2
- Spring5框架整合Log4j2
Log4j2使用:
1. 引入jar包
-
創建Log4j2.xml配置文件
<?xml version="1.0" encoding="UTF-8"?> <!--日誌級別以及優先順序排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --> <!--Configuration後面的status用於設置log4j2自身內部的信息輸出,可以不設置,當設置成trace時,可以看到log4j2內部各種詳細輸出--> <?import org.apache.logging.log4j.core.layout.PatternLayout?> <configuration status="INFO"> <!--先定義所有的appender--> <appenders> <!--輸出日誌信息到控制台--> <console name="Console" target="SYSTEM_OUT"> <!--控制日誌輸出的格式--> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </console> </appenders> <!--然後定義logger,只有定義了logger並引入的appender,appender才會生效--> <!--root:用於指定項目的根日誌,如果沒有單獨指定Logger,則會使用root作為預設的日誌輸出--> <loggers> <root level="info"> <appender-ref ref="Console"/> </root> </loggers> </configuration>
Spring5框架核心容器支持@Nullable註解
@Nullable註解可以使用在方法上面,屬性上面,參數上面,表示方法返回可以為空,屬性值可以為空,參數值可以為空
Spring5核心容器支持函數風格GenericApplicationContext(lambda表達式)
public void testGenericApplicationContext(){
//創建GenericApplicationContext對象
GenericApplicationContext context = new GenericApplicationContext();
//調用context方法註冊對象
context.refresh();
context.registerBean("user1",User.class,()-> new User());
//獲取Spring註冊的對象
// User user = (User) context.getBean("com.atguigu.spring5.test.User");
User user = (User) context.getBean("user1");
System.out.printl