spring介紹
是一個輕量級的控制反轉(zhuǎn)(IOC),面向切面編程(AOP)的框架(容器),AOP的主要作用就是支持事務(wù)的處理
springboot 是一個快速開發(fā)的腳手架
-- 基于springboot可以快速開發(fā)單個微服務(wù)
-- 約定大于配置springcloud
-- springcloud是基于springboot實現(xiàn)的
現(xiàn)在多數(shù)公司在使用springboot進(jìn)行快速開發(fā)躯砰,學(xué)習(xí)springboot需要先學(xué)習(xí)spring和springMVC
spring理念:整合現(xiàn)有技術(shù)蹋偏,是個大雜燴褥蚯。弊端就是配置十分繁瑣
2. IOC:控制反轉(zhuǎn)
什么叫控制反轉(zhuǎn)酗捌,本來程序的主動權(quán)在程序員手里,客戶需要什么料仗,程序員調(diào)用什么湾盗。控制反轉(zhuǎn)就是把控制權(quán)轉(zhuǎn)交到客戶手里立轧,客戶需要什么就自己選擇格粪,不需要程序員調(diào)用
2.1 XML
xml前綴
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
2.11 spring 對bean管理
-
創(chuàng)建bean的三種方式
- 創(chuàng)建方式一:
- 使用默認(rèn)構(gòu)造函數(shù)
spring的配置文件使用bean標(biāo)簽且只有id和class屬性躏吊,沒有其他屬性和標(biāo)簽, - 如果沒有默認(rèn)構(gòu)造函數(shù)帐萎,就不能創(chuàng)建
- 使用默認(rèn)構(gòu)造函數(shù)
- 創(chuàng)建方式一:
-
<bean id="accountService" class="service.impl.AccountServiceImpl"/>
- 創(chuàng)建方法二:
- 使用jar包中的方法創(chuàng)建對象(某個類中的方法創(chuàng)建對象)比伏,并存入spring容器
- 創(chuàng)建方法二:
-
<bean id="instanceFactory" class="com.gx.InstanceFactory"/> <bean id="accountService" factory-bean="instancefactory" factory-method="saveAccount"/>
- 創(chuàng)建方法三:
- 使用工廠中的靜態(tài)方法創(chuàng)建對象(某個類中的靜態(tài)方法),并存入spring容器
static不需要實例化沒疆导,所以可以直接合并成一句話
- 使用工廠中的靜態(tài)方法創(chuàng)建對象(某個類中的靜態(tài)方法),并存入spring容器
- 創(chuàng)建方法三:
<bean id="staticFactory" class="com.gx.staticFactory" factory-method="saveAccount"></bean>
-
2.12 bean對象的作用范圍
- bean的scope屬性: 用于指定bean的作用范圍
- 取值:singleton:單例的(默認(rèn)值)
- prototype:多例的
- request:作用web請求的請求范圍
- session:作用于web應(yīng)用的會話范圍
- blobal-session;作用于全局會話(集群)范圍赁项,不是集群范圍就是session
- 取值:singleton:單例的(默認(rèn)值)
- bean的scope屬性: 用于指定bean的作用范圍
<bean id="accountService" class="service.impl.AccountServiceImpl" scope="prototype"/>
-
2.13 bean對象的生命周期
- 單例對象:單例對象的生命周期跟隨容器
- 出生:但容器創(chuàng)建時,對象就出生
- 存活:容器活著對象就活著
- 死亡:容器銷毀澈段,對象死亡
- 多例對象:
- 出生:當(dāng)我們使用對象時spring框架為我們創(chuàng)建
- 存活:單對象在使用中就存活
- 死亡:單對象長時間不用悠菜,且沒有別的對象應(yīng)用,就被被垃圾回收
- 單例對象:單例對象的生命周期跟隨容器
<bean id="accountService" class="service.impl.AccountServiceImpl" scope="prototype" init-method="init" destroy-method="destroy"/>
-
2.14 spring依賴注入
- 依賴注入DI:Dependency Injection:降低程序的耦合
- 能注入的數(shù)據(jù)由三種:
- 基本類型和string
- 其他bean類型(在配置文件中活著注解配置的bean)
- 復(fù)雜類型败富、集合類型
- 注入的方式三種:
- 構(gòu)造函數(shù)提供
- 使用set方法
- 使用注解
- 能注入的數(shù)據(jù)由三種:
- 依賴注入DI:Dependency Injection:降低程序的耦合
- 構(gòu)造函數(shù)注入
-
construct-arg :因為默認(rèn)構(gòu)造已經(jīng)不見了悔醋,所以要使用標(biāo)簽:construct-arg
- 標(biāo)簽中的屬性:
- type:指定要注入的數(shù)據(jù)類型
- index:索引位置,指定要注入的數(shù)據(jù)兽叮,從0開始
-(常用)name:給指定名稱賦值 - value:給基本類型和String類型數(shù)據(jù)
- ref:給其他類型數(shù)據(jù)芬骄,在spring容器中出現(xiàn)過的
- 標(biāo)簽中的屬性:
- 優(yōu)勢:創(chuàng)建bean對象時,必須注入數(shù)據(jù)鹦聪,否趙不能創(chuàng)建
-
劣勢:改變了bean實例化账阻,當(dāng)我們用不到某些數(shù)據(jù)也必須賦值
所以除了非用不可之外,一般使用set方法
-
construct-arg :因為默認(rèn)構(gòu)造已經(jīng)不見了悔醋,所以要使用標(biāo)簽:construct-arg
<bean id="accountService" class="service.impl.AccountServiceImpl"> <constructor-arg name="name" value="YY"></constructor-arg> <constructor-arg name="age" value="18"></constructor-arg> <constructor-arg name="birthday" ref="date"></constructor-arg> </bean> <bean id="date" class="java.util.Date" ></bean>
- set方法注入
- 標(biāo)簽: property泽本,出現(xiàn)在bean內(nèi)部
- 標(biāo)簽中的屬性:
- name:給指定名稱賦值,這邊的name不是變量名淘太,而是set的名稱
- value:給基本類型和String類型數(shù)據(jù)
- ref:給其他類型數(shù)據(jù),在spring容器中出現(xiàn)過的類里沒有默認(rèn)構(gòu)造函數(shù)不能使用
- 標(biāo)簽中的屬性:
- 優(yōu)勢规丽;創(chuàng)建對象沒有明確限制琴儿,可以直接使用默認(rèn)構(gòu)造方法
- 弊端:沒有set方法就不能注入
- 標(biāo)簽: property泽本,出現(xiàn)在bean內(nèi)部
<bean id="accountService2" class="service.impl.AccountServiceImpl2"> <property name="name" value="YY"/> <property name="age" value="18"/> <property name="birthday" ref="date"/> </bean> <bean id="date" class="java.util.Date" ></bean>
- 復(fù)雜類型、集合類型的注入
- 用于給List結(jié)構(gòu)注入的標(biāo)簽:
- list array set
- 用于Map結(jié)構(gòu)集合注入的標(biāo)簽:
- map props
- 結(jié)構(gòu)相同嘁捷,可以互換,所以只要記兩組就行了
- 用于給List結(jié)構(gòu)注入的標(biāo)簽:
<bean id="accountService3" class="service.impl.AccountServiceImpl3"> <property name="strings"> <array> <value>YY</value> <value>YZY</value> </array> </property> <property name="set"> <set> <value>YY</value> <value>YZY</value> </set> </property> <property name="map"> <map> <entry key="A" value="B"></entry> </map> </property> <property name="list"> <array> <value>YY</value> <value>YZY</value> </array> </property> <property name="properties"> <props> <prop key="A" >a</prop> <prop key="B" >b</prop> </props> </property> </bean> <bean id="person" class="entity.Person" autowire="byType"> </bean> <bean id="dog" class="entity.Dog"/> <bean id="cat" class="entity.Cat"/>
2.2 注解
注解前綴
<?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
https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
注解的第一種方式:不用@Component,直接類在xml里手動注入
<!--支持注解模式-->
<context:annotation-config></context:annotation-config>
<!--使用@Autowired注解就不用寫property標(biāo)簽屬性了显熏,也可以在bean后面加autowired屬性-->
<bean id="person" class="entity.Person"></bean>
<bean id="dog" class="entity.Dog"/>
<bean id="cat" class="entity.Cat"/>
注解的第二種方式:使用@Component雄嚣,等價于<bean id=""class=""/>
- @Controller:一般用在表現(xiàn)層
- @Service:業(yè)務(wù)層
- @Repository:持久層
<!--指定要掃描的包,包下的注解會生效喘蟆,其實這條寫了缓升,支持注解模式那條就已經(jīng)包含進(jìn)去了-->
<context:component-scan base-package="entity"></context:component-scan>
@Autowired
@Qualifier(value = "accountDao2")
//這哥們一個頂上面?zhèn)z
@Resource(name = "accountDao1")
private Dog dog;
-
2.21 注入數(shù)據(jù)的注解
相當(dāng)于配置文件中的<bean>標(biāo)簽下的<property>標(biāo)簽 使用注解注入時就可以不用set方法了
-
@Autowired:
自動按照類型注入只要容器中有唯一的bean對象類型和注入的類型匹配,就可以注入成功- 注意點 用于匹配指定類型蕴轨,但是我擁有兩個相同的類所以就會無法匹配到確定的一個類上港谊,這時候就匹配與變量名相同的bean,如果都不對就報錯橙弱,這時候
加上@Qualifier指定變量名
- 注意點 用于匹配指定類型蕴轨,但是我擁有兩個相同的類所以就會無法匹配到確定的一個類上港谊,這時候就匹配與變量名相同的bean,如果都不對就報錯橙弱,這時候
@Qualifier:
在按照類注入的基礎(chǔ)上按變量名注入歧寺,在給類成員注入時不能單獨使用燥狰,但是方法注入時可以
屬性:value
指定bean的id值-
@Resource :上面兩個的集合體
- 屬性 :name
指定bean的id值
- 屬性 :name
-
-
以上三個注入都只能注入其他bean類型的數(shù)據(jù),基本類型無法使用斜筐,集合類型的只能在xml文件實現(xiàn)
-
@Value:用于注入基本類型和string
- 屬性:value
用于指定數(shù)據(jù)的值
@Value(value="lanmao") 相當(dāng)于<property id="cat" value="lanmao"> private Cat cat;
- 屬性:value
-
@Value:用于注入基本類型和string
2.22 注解作用范圍
相當(dāng)于配置文件中的<bean>標(biāo)簽中的scope屬性
- @Scope :指定bean的作用范圍
- 屬性:value 指定范圍的取值 這個和配置文件中一樣
@Scope(value = "prototype")
2.23 注解生命周期(了解)
相當(dāng)于配置文件中的<bean>標(biāo)簽中的init-method和destroy-method屬性
-
@PostConstruct:創(chuàng)建方法
@PostConstruct: 創(chuàng)建方法 public void init(){ System.out.println("對象出生");}
-
@PreDestroy :銷毀方法
public void destroy(){ System.out.println("對象死亡"); }
小結(jié) xml和注解
- xml: 更萬能龙致,維護簡單
- 注解:不是自己的類用不了,維護復(fù)雜
最佳實踐
- xml用來管理bean
- 注解負(fù)責(zé)屬性注入
3. 代理模式
為什么要學(xué)習(xí)代理模式顷链,因為這是SpringAOP的底層
是二十三種設(shè)計模式之一目代,springAOC和springMVC必考
-
代理模式的分類
- 靜態(tài)代理
- 動態(tài)代理
3.1 靜態(tài)代理
角色分析
- 抽象角色:一般使用接口或者抽象類來解決
- 真實角色:被代理的角色
- 代理角色:代理真實角色,并做一些附屬操作
- 客戶:訪問代理角色的人
步驟:
- 接口(租房操作)
//租房接口
public interface Rent {
public void rent();
}
- 真實角色(房東)
//房東要租出去的房子
public class House implements Rent {
String housName;
public String getHousName() {
return housName;
}
public House(String housName){
this.housName = housName;
}
public void rent(){
System.out.println("房東要出租房子"+housName);
}
}
- 代理角色(中介)
//代理
public class Proxy implements Rent{
private House house;
public Proxy(){
}
//代理去幫你租這個房子
public Proxy(House house){
this.house = house;
}
// 房子被代理租出去了
@Override
public void rent(){
house.rent();
seeHouse(house.getHousName());
money(house.housName);
fire(house.getHousName());
}
public void seeHouse(String housName){
System.out.println("中介帶你看房"+ housName);
}
public void money(String housName){
System.out.println("中介收費"+ housName);
}
public void fire(String housName){
System.out.println("簽合同"+ housName);
}
}
- 客戶端訪問代理角色(租客)
public class Client {
public static void main(String[] args) {
// 看中了某個房子
House house = new House("無名公寓");
// 找中介
Proxy proxy = new Proxy(house);
// 中介的附屬操作嗤练,不用和房東接觸
proxy.rent();
}
}
靜態(tài)代理好處:
- 使真實角色(租客榛了,房東)更加操作純粹,不用關(guān)注公共業(yè)務(wù)(看房煞抬,簽合同)
- 公共業(yè)務(wù)交給了代理(中介)霜大,實現(xiàn)了業(yè)務(wù)的分工(簽合同,看房)
- 公共業(yè)務(wù)發(fā)生拓展的時候此疹,方便管理
缺點
- 一個真實角色就要一個代理僧诚,代碼量翻倍,開發(fā)效率低
3.2 靜態(tài)代理加深理解
有一個service類用于使用增刪改查的操作
public class UserServiceImpl implements UserService{
// 如果我還想再方法里加語句就很麻煩蝗碎,不方便去改源代碼湖笨,那么這個時候就要用代理類
@Override
public void add() {
System.out.println("增");
}
@Override
public void delete() {
System.out.println("刪");
}
@Override
public void update() {
System.out.println("改");
}
@Override
public void query() {
System.out.println("查");
}
}
現(xiàn)在我想加一個日志功能,能不能在service類里直接修改呢?
最好不要蹦骑,因為直接修改源代碼可能會出錯慈省,而且如果代碼量多的話十分麻煩,那么我們可以通過一個代理類來增加功能眠菇。
public class UserServiceProxy implements UserService {
private UserService userService;
// 在代理類里調(diào)用并加上日志
public void setUserService(UserService userService) {
this.userService = userService;
}
public void log(String callname){
System.out.println("調(diào)用了一次"+callname+"方法");
}
@Override
public void add() {
userService.add();
log("add");
}
@Override
public void delete() {
userService.delete();
log("delete");
}
@Override
public void update() {
userService.update();
log("update");
}
@Override
public void query() {
userService.query();
log("query");
}
}
這就是只拓展不修改边败,不在改變原有代碼的基礎(chǔ)上增加新功能
沒有加一層封裝解決不了的方法,如果有捎废,就再加一層笑窜。
不改變業(yè)務(wù)的情況下,我們要增加功能登疗,日志就要橫切進(jìn)去排截,這就是橫向開發(fā),這就是切面辐益!
3.3 動態(tài)代理
- 動態(tài)代理和靜態(tài)代理角色是一樣的
- 動態(tài)代理是動態(tài)生成的断傲,不是我們直接寫好的
- 分為兩大類
- 基于接口理:基于JDK動態(tài)代理(我們學(xué)習(xí)的)
- 基于類:cglik
需要了解兩個類:
- Proxy:代理
- 提供靜態(tài)方法方法能創(chuàng)建動態(tài)代理和實例
- invocationHandler:調(diào)用處理程序
- 每一個代理類都會有一個調(diào)用處理程序,當(dāng)代理類調(diào)用該方法智政,代理類分配到invoke方法通過反射反射
3.3.1 如果是單獨創(chuàng)建一個動態(tài)代理類然后main方法調(diào)用
import java.lang.reflect.Method;//用這個類自動生成代理類
public class ProxyInvocationhandler implements InvocationHandler {
// 被代理的接口
private Object target;
public void setRent(Object target) {
this.target = target;
}
/*newProxyInstance方法的參數(shù):
classLoader:類加載器
用于加載代理對象字節(jié)碼认罩。和被代理對象使用 相同的字節(jié)碼,固定寫法
class[]:字節(jié)碼數(shù)組
用于讓代理對象和被代理對象有相同的方法续捂。固定寫法
InovocationHandler:用于提供增強的代碼
讓我們寫如何代理垦垂。通常情況下都是匿名內(nèi)部類宦搬,但不是必須 */
//得到代理類
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
// 處理實例并返回結(jié)果
@Override
public Object invoke(Object proxy,Method method,Object[] args){
// 增強功能
log("YY");
// 返回target類的方法和方法的參數(shù)
return method.invoke(target,args);
}
public void log(String callname){
System.out.println("調(diào)用了一次"+callname+"方法");
}
}
3.32 如果是直接寫在main方法里
public class Client {
public static void main(String[] args){
final House house = new House();
/*newProxyInstance方法的參數(shù):
classLoader:類加載器
用于加載代理對象字節(jié)碼。和被代理對象使用 相同的字節(jié)碼乔外,固定寫法
class[]:字節(jié)碼數(shù)組
用于讓代理對象和被代理對象有相同的方法床三。固定寫法
InovocationHandler:用于提供增強的代碼
讓我們寫如何代理。通常情況下都是匿名內(nèi)部類杨幼,但不是必須 */
// 前兩個都是寫被代理類的類加載器
Rent12 rent12Proxy = (Rent12)Proxy.newProxyInstance(house.getClass().getClassLoader(),house.getClass().getInterfaces,
new InvocationHandler() {
@Override
/* proxy:當(dāng)前代理對象的引用
method:當(dāng)前執(zhí)行的方法
args:執(zhí)行方法需要的參數(shù)
**/
// 這里就相當(dāng)于代理類得到了真實角色的方法撇簿,可以再里面加功能,最后在main方法里調(diào)用你想要的代理類.方法
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
if("rent".equals(method.getName())){
System.out.println("房子被代理了");
}
return method(house,args);
}
});
rent12Proxy.rent();
}
}
注意點:代理類的類型為接口類型而不是被代理類的類型
通俗的講:代理類就是集成被代理類的接口差购,然后再把被代理類的具體方法方法拿過來用進(jìn)行可以拓展四瘫,
然后集合成一個調(diào)用處理程序,想用就直接調(diào)用這個程序就行了欲逃。
3.33 如果是直接寫在main方法里
上一個方法是接口代理找蜜,那么這個方法就是子類代理,需要用到一個第三方j(luò)ar包cglib
package 動態(tài)代理子類;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import 動態(tài)代理黑馬.Rent12;
import 增加日志功能.UserService;
import 增加日志功能.UserServiceImpl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author YZY
* @date 2020/3/31 - 20:33
*/
public class Client {
public static void main(final String[] args) {
final House1 house1 = new House1();
house1.setHouseName("無名公寓");
/*動態(tài)代理子類需要第三方的jar包cglib
* 使用Enhancer的create方法
create方法的參數(shù):
class:字節(jié)碼
用于指定被代理類的字節(jié)碼稳析,固定寫法
callback:用于提供增強的代碼
讓我們寫如何代理洗做。通常情況下都是匿名內(nèi)部類,但不是必須
一般寫的是該接口的子接口實現(xiàn)類:MethodInterceptor*/
// 相比于代理接口彰居,代理子類少了一個參數(shù)诚纸,getInterfaces(),因為直接代理子類陈惰,就不需要子類的接口了
// 這里就直接選擇子類類型就行了
House1 house1Proxy = (House1) Enhancer.create(house1.getClass(), new MethodInterceptor() {
/*前三個參數(shù)和代理接口的參數(shù)一樣
* methodProxy:當(dāng)前執(zhí)行方法的代理對象
* 其他內(nèi)容就和代理接口一模一樣*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
if ("rent".equals(method.getName())) {
System.out.println("動態(tài)代理接口");
}
return method.invoke(house1, args);
}
});
house1Proxy.rent();
}
}
3.33 動態(tài)代理相比于靜態(tài)代理的優(yōu)勢:
靜態(tài)代理代理的是一個業(yè)務(wù)畦徘,而動態(tài)代理繼承的是接口,可以代理一類業(yè)務(wù)抬闯,范圍范圍更廣井辆,應(yīng)用更加靈活
4 AOP
使用AOP需要導(dǎo)包
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
4.1 第一種方式:使用spring的API接口
配置復(fù)雜但是功能強大
先創(chuàng)建繼承接口的類
public class methodBeforeLog implements MethodBeforeAdvice {
@Override
/*method:方法
args:參數(shù)
target:目標(biāo)對象*/
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"的"+method.getName()+"被執(zhí)行了");
}
}
package log;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;
public class afterReturnLog implements AfterReturningAdvice {
@Override
// returnValue:返回值
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("執(zhí)行了"+target.getClass().getName()+"返回的結(jié)果是"+returnValue);
}
}
配置文件:
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置bean-->
<bean id="userService" class="service.userService1Impl"></bean>
<bean id="beforeLog" class="log.methodBeforeLog"></bean>
<bean id="afterLog" class="log.afterReturnLog"></bean>
<!--1.使用原生spring API注入-->
<!--配置AOP:需要導(dǎo)入AOP 的約束-->
<aop:config>
<!--需要一個切入點:即去哪個地方執(zhí)行(去userServiceImpl下的所有方法*,的所有位置(..))-->
<aop:pointcut id="pointcut" expression="execution(* service.userService1Impl.*(..))"/>
<!--執(zhí)行環(huán)繞-->
<!-- 把beforelog這個類連接到切入點-->
<aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
4.2 第二種方式:自定義java類實現(xiàn)AOP
配置簡單但是功能少
先創(chuàng)建一個自定義類
package diy;
public class diyPointcut {
public void MethodBeforeAdvice(){
System.out.println("方法執(zhí)行前");
}
public void AfterReturningAdvice(){
System.out.println("方法返回后");
}
}
然后寫入配置文件
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 2.自定義類-->
<!--配置bean-->
<bean id="userService" class="service.userService1Impl"></bean>
<bean id="log" class="log.diyPointcut"></bean>
<!-- aop編寫范圍-->
<aop:config>
<!-- 切點可以在切面里面也可以在切面外面溶握,在外面的話一定要在切面的前面(aop約束規(guī)定)-->
<aop:pointcut id="pointcut" expression="execution(* service.userService1Impl.*(..))"/>
<!-- 自定義切面杯缺,切面是個類,ref引用你要的類-->
<aop:aspect ref="log">
<!-- 通知-->
<!--這里就可以自定義在什么時候切入睡榆,
aop:brfore代表執(zhí)行前-->
<aop:before method="MethodBeforeAdvice" pointcut-ref="pointcut"/>
<aop:after-returning method="AfterReturningAdvice" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
</beans>
4.3 第三種方式:注解
xml文件中添加bean和AOP注解的語句夺谁,否則不會生效
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="annotation" class="log.AnnotationPointcut"/>
<bean id="userService" class="service.userService1Impl"/>
<!-- 開啟AOP注解-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
在編寫通知注解
package log;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
/**
* @author YZY
* @date 2020/4/2 - 17:37
*/
// AOP注解
@Aspect //標(biāo)志這個類成切面
public class AnnotationPointcut {
// 前置通知
@Before("execution(* service.userService1Impl.*(..))")
public void MethodBeforeAdvice(){
System.out.println("Before");
}
/*環(huán)繞通知,功能最強大肉微,可以自定義操作
它可以算是spring框架給我們提供一種手動控制通知(增強)方法的方式,你可以手動在自定義通知(增強)方式
ProceedingJoinPoint 連接點蜡塌,可以從切入點里獲取信息*/
@Around("execution(* service.userService1Impl.*(..))")
public void Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
try{
System.out.println("around before");
// 獲取簽名
Signature signature = proceedingJoinPoint.getSignature();
System.out.println("signature:"+signature);
// 執(zhí)行
Object proceed = proceedingJoinPoint.proceed();
System.out.println("around after");
}catch (Exception e){
System.out.println("around throwing");
}finally {
System.out.println("around finnal");
}
}
// 返回通知碉纳,如果拋出異常不會通知
@AfterReturning("execution(* service.userService1Impl.*(..))")
public void AfterReturningAdvice(){
System.out.println("AfterReturning");
}
// 異常通知
@AfterThrowing("execution(* service.userService1Impl.*(..))")
public void Throwing(){
System.out.println("AfterThrowing");
}
// 最終通知,結(jié)果不關(guān)心是否異常
@After("execution(* service.userService1Impl.*(..))")
public void After(){
System.out.println("After");
}
}
最后觀察輸出結(jié)果發(fā)現(xiàn)通知順序是有問題的馏艾,而上面提到環(huán)繞通知可以手動控制通知方式劳曹,所以建議使用環(huán)繞通知來處理通知事件