# 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" ></bean>
? 第二步 有service類和dao類 創建工廠類
class UserFactory{
public static UserDao getDao(){
String classValue = https://www.cnblogs.com/hanyk/p/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" ></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" >
<!--使用property完成屬性注入
name:類里面屬性名稱
value:向屬性注入的值
-->
<property name="bname" value="https://www.cnblogs.com/hanyk/p/張三日記"></property>
<property name="bauthor" value="https://www.cnblogs.com/hanyk/p/法外狂徒張三"></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" >
<!-- constructor-arg標簽用于有參構造注入屬性-->
<constructor-arg name="oname" value="https://www.cnblogs.com/hanyk/p/電腦"></constructor-arg>
<constructor-arg name="address" value="https://www.cnblogs.com/hanyk/p/China"></constructor-arg>
</bean>
第三種注入方式:P名稱空間注入(底層使用的還是set方法注入)
使用p名稱空間注入,可以簡化基于xml配置方式
- 添加p名稱空間在組態檔中
-
進行屬性注入,在bean標簽里面進行操作
<!--4 使用p名稱空間注入--> <bean id="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" > <!--注入userDao物件 name屬性值:類里面的屬性名稱 ref屬性:創建userDao物件bean標簽id值 --> <property name="userDao" ref="userDaoImpl"></property> </bean> <bean id="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" > <!--設定兩個普通屬性--> <property name="ename" value="https://www.cnblogs.com/hanyk/p/lucy"></property> <property name="gender" value="https://www.cnblogs.com/hanyk/p/女"></property> <!--設定物件型別屬性--> <property name="dept"> <bean id="dept" > <property name="dname" value="https://www.cnblogs.com/hanyk/p/安保部門"></property> </bean> </property> </bean>
注入屬性-級聯賦值
第一種寫法
<!--級聯賦值-->
<bean id="emp" >
<!--設定兩個普通屬性-->
<property name="ename" value="https://www.cnblogs.com/hanyk/p/lucy"></property>
<property name="gender" value="https://www.cnblogs.com/hanyk/p/女"></property>
<!--級聯賦值-->
<property name="dept" ref="dept"></property>
</bean>
<bean id="dept" >
<property name="dname" value="https://www.cnblogs.com/hanyk/p/財務部"></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" >
<!--設定兩個普通屬性-->
<property name="ename" value="https://www.cnblogs.com/hanyk/p/lucy"></property>
<property name="gender" value="https://www.cnblogs.com/hanyk/p/女"></property>
<!--級聯賦值-->
<property name="dept" ref="dept"></property>
<property name="dept.dname" value="https://www.cnblogs.com/hanyk/p/技術部" ></property>
</bean>
<bean id="dept" >
<property name="dname" value="https://www.cnblogs.com/hanyk/p/財務部"></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" > <!--陣列型別屬性注入--> <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="https://www.cnblogs.com/hanyk/p/java"></entry> <entry key="PHP" value="https://www.cnblogs.com/hanyk/p/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" > <property name="cname" value="https://www.cnblogs.com/hanyk/p/Spring5框架"></property> </bean> <bean id="course2" > <property name="cname" value="https://www.cnblogs.com/hanyk/p/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" > <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" > </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" 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" init-method="initMethod" destroy-method="destoryMethod"> <property name="oname" value="https://www.cnblogs.com/hanyk/p/手機"> </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" ></bean>
IOC操作Bean管理(xml自動裝配)
什么是自動裝配?
根據指定裝配規則(屬性名稱或屬性型別),Spring自動將匹配的屬性值進行注入
演示自動裝配程序:
根據屬性名稱自動注入
<!--實作自動裝配
bean標簽屬性autowire,配置自動裝配
autowire屬性常用的兩個值:
byName根據屬性名稱,注入值bean的id和類屬性名稱一樣
byType根據屬性型別注入
-->
<bean id="emp" autowire="byName">
<!-- <property name="dept" ref="dept"></property>-->
</bean>
<bean id="dept" ></bean>
</beans>
根據屬性型別自動注入
<!--實作自動裝配
bean標簽屬性autowire,配置自動裝配
autowire屬性常用的兩個值:
byName根據屬性名稱,注入值bean的id和類屬性名稱一樣
byType根據屬性型別注入
-->
<bean id="emp" autowire="byType">
<!-- <property name="dept" ref="dept"></property>-->
</bean>
<bean id="dept" ></bean>
</beans>
IOC操作Bean管理(外部屬性檔案)
-
直接配置資料庫資訊
? 1.配置德魯伊連接池(Druid連接池,阿里巴巴)
? 2.引入德魯伊連接池依賴jar包
<!--直接配置連接池--> <bean id="database" > <property name="DriverClassName" value="https://www.cnblogs.com/hanyk/p/com.mysql.jdbc.Driver"></property> <property name="url" value="https://www.cnblogs.com/hanyk/p/jdbc:mysql://locahost:3306/userDb"></property> <property name="username" value="https://www.cnblogs.com/hanyk/p/root"></property> <property name="password" value="https://www.cnblogs.com/hanyk/p/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" > <property name="DriverClassName" value="https://www.cnblogs.com/hanyk/p/${prop.driverClass}"></property> <property name="url" value="https://www.cnblogs.com/hanyk/p/${prop.url}"></property> <property name="username" value="https://www.cnblogs.com/hanyk/p/${prop.userName}"></property> <property name="password" value="https://www.cnblogs.com/hanyk/p/${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 = "https://www.cnblogs.com/hanyk/p/userService")//<bean id="userService" >相同 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 = "https://www.cnblogs.com/hanyk/p/userService")//<bean id="userService" >相同 public class UserService { //定義dao型別屬性 //不需要添加set方法(spring已經封裝了這一步) //添加注入屬性的注解 @Autowired private UserDao userDao; public void add(){ System.out.println("service add...."); userDao.add(); } } -
@Qualifier:根據屬性名稱進行注入
此注解的使用需要與@Autowired一起使用
@Repository(value = "https://www.cnblogs.com/hanyk/p/userDaoImpl1") public class UserDaoImpl implements UserDao{ @Override public void add() { System.out.println("dao add..."); } }@Service(value = "https://www.cnblogs.com/hanyk/p/userService")//<bean id="userService" >相同 public class UserService { //定義dao型別屬性 //不需要添加set方法(spring已經封裝了這一步) //添加注入屬性的注解 @Autowired //根據型別進行注入 @Qualifier(value = "https://www.cnblogs.com/hanyk/p/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 = "https://www.cnblogs.com/hanyk/p/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 = "https://www.cnblogs.com/hanyk/p/execution(* com.atguigu.spring5.aopanno.User.add(..))") public void before(){ System.out.println("before..."); } @After(value = "https://www.cnblogs.com/hanyk/p/execution(* com.atguigu.spring5.aopanno.User.add(..))") public void after(){ System.out.println("after..."); } @AfterReturning(value = "https://www.cnblogs.com/hanyk/p/execution(* com.atguigu.spring5.aopanno.User.add(..))") public void afterReturning(){ System.out.println("afterReturning..."); } @AfterThrowing(value = "https://www.cnblogs.com/hanyk/p/execution(* com.atguigu.spring5.aopanno.User.add(..))") public void afterThrowing(){ System.out.println("afterThrowing...例外"); } @Around(value = "https://www.cnblogs.com/hanyk/p/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 = "https://www.cnblogs.com/hanyk/p/execution(* com.atguigu.spring5.aopanno.User.add(..))") //切入點注解 public void pointdemo(){ } //前置通知 //@Before注解表示前置通知 @Before(value = "https://www.cnblogs.com/hanyk/p/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" ></bean> <bean id="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" destroy-method="close"> <property name="name" value="https://www.cnblogs.com/hanyk/p/jdbc:mysql:///user_db"/> <property name="username" value="https://www.cnblogs.com/hanyk/p/root"/> <property name="password" value="https://www.cnblogs.com/hanyk/p/root"/> <property name="driverClassName" value="https://www.cnblogs.com/hanyk/p/com.mysql.jdbc.Driver"/> </bean> -
配置JdbcTemplate物件,注入Datasource
<!--JdbcTemplate物件--> <bean id="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" destroy-method="close"> <property name="url" value="https://www.cnblogs.com/hanyk/p/jdbc:mysql:///user_db"/> <property name="username" value="https://www.cnblogs.com/hanyk/p/root"/> <property name="password" value="https://www.cnblogs.com/hanyk/p/root"/> <property name="driverClassName" value="https://www.cnblogs.com/hanyk/p/com.mysql.jdbc.Driver"/> </bean> <!--JdbcTemplate物件--> <bean id="jdbcTemplate" > <!--注入DataSource--> <property name="dataSource" ref="dataSource"></property> </bean>
-
-
在dao創建兩個方:多錢和少錢,在service創建轉賬方法
-
以上代碼如果正常執行是沒有問題的,但是如果以上代碼出現例外,有問題,則應符合原子性和一致性,賬戶資料都不變
事務管理(Spring事務管理介紹)
- 事務一般添加到JavaEE三層結構里的service層(業務邏輯層)
- 在Spring進行事務管理操作有兩種方式
- 編程式事務管理
- 宣告式事務管理(常用)
- 宣告式事務管理
- 基于注解方式實作(常用)
- 基于xml組態檔方式
- 在Spring進行宣告式事務管理,底層使用AOP原理
- Spring事務管理API
- 提供了一個介面,代表事務管理器,這個介面針對不同的框架提供不同的實作類
事務操作(注解宣告式事務管理)
-
在Spring組態檔配置事務管理器
<!-- 創建事務管理器--> <bean id="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" > <!-- 注入資料源--> <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.println(user);
}
Spring5支持整合JUnit5
-
整合JUnit4
-
引入Spring測驗相關的依賴
-
@RunWith(SpringJUnit4ClassRunner.class) //指定單元測驗框架的版本 @ContextConfiguration("classpath:bean1.xml") //加載組態檔 public class JTest4 { @Autowired private UserService userService; @Test public void test1(){ userService.accountMoney(); } }
-
-
整合JUnit5
- 引入JUnit5的jar包
-
創建測驗類,使用注解完成
package com.atguigu.spring5.test; import com.atguigu.spring5.service.UserService; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @ExtendWith(SpringExtension.class) @ContextConfiguration("classpath:bean1.xml") //加載組態檔 public class JTest5 { @Autowired private UserService userService; @Test public void test1(){ userService.accountMoney(); } }
復合注解@SpringJUnitConfig(locations = "classpath:bean1.xml")
//@ExtendWith(SpringExtension.class)
//@ContextConfiguration("classpath:bean1.xml") //加載組態檔
@SpringJUnitConfig(locations = "classpath:bean1.xml")
public class JTest5 {
@Autowired
private UserService userService;
@Test
public void test1(){
userService.accountMoney();
}
}
SpringWebFlux
webflux介紹
-
基本概念
-
Spring5添加的新的模塊,用于web開發的,功能與SpringMVC相似,webflux使用一種回應式編程出現的框架
-
使用傳統的web框架,比如SpringMVC,這些基于Servlet容器,webflux是一種異步非阻塞的框架,異步非阻塞的框架在servlet3.1之后才支持,核心是基于Reactor的相關API實作的
-
-
回應式編程
-
webflux執行流程和核心API
-
Springwebflux(基于注解編程模型)
-
Springwebflux(基于函式式編程模型)
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/466941.html
標籤:Java
