@component是用來創建對象的。默認是創建單例對象。
屬性: value , 表示<bean>對象的 id,也就是對象的名稱。
位置:在類的定義上面。 基本上所有的可以給類 使用的註解 都是寫在類的上面的。
@component(value=”所要創建的對象的名稱”) 因爲寫在了類的上面就不用再像<bean>標籤一樣需要寫類的全地址了 等價於:
<bean id=”所要創建的對象的名稱” class=”類的全地址”/>
我們使用註解來創建對象的前提是:我們需要一個掃描器來掃描我們的 這個有註解標示的類,當掃描到這個註解的時候,我們的spring容器就會根據我們的註解的中的value屬性和當前類所在的位置來 創建一個 我們所需要的對象。
和 @Component註解相同作用的爲:
@Repository:放在dao層的實現類上面,創建dao對象。
@Service:放在Service層實現類上面,創建service對象的。
@Controller:放在控制器類上面,創建控制器對象(servlet)。控制器不像我們的service和dao這兩個層,controller是沒有接口的。
package com.bjpowernode.domain;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
//在註解裏面 value就相當於我們的標籤對象的的id屬性,相當於對象的名稱或者屬性的數值。
@Component(value="MyUser")
public class User {
@Value(value="張三")
private String name ;
@Value("18")
private int age ;
@Autowired //這個默認爲byType ,按照類型進行匹配和賦值。
private School school;
//spring容器會爲我們的school對象從容器中尋找一個和他類型相同的對象: mySchool,然後給他賦值。
@Autowired //比較類型
@Qualifier(value="teacher") //比較 名字是否爲 我們設置的 對象名 teacher
//上面兩個 註解 構成了我們的 byName規則。
private Teacher teacher ;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public School getSchool() {
return school;
}
public void setSchool(School school) {
this.school = school;
}
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + ", school=" + school + ", teacher=" + teacher + "]";
}
}
我們給簡單數據類型的屬性賦值; @Value(value="張三")或者 @Value("張三")
我們給 引用數據類型的屬性賦值,給應用數據類型賦值:我們使用自動注入的方法來給我們的 引用數據類型賦值。
我們使用註解的方式來 自動注入我們的 引用數據 類型的屬性。 自動注入有分爲兩類:byName和byType;
byType 規則所對應的註解爲:
@AutoWried 在一個引用數據類型的屬性上面使用這個 註解 ,就表名這個引用數據類型的屬性 是由我們的 spring容器 按照byType的規則然後從容器中尋找 與 這個引用數據類型的屬性的 類型相同的 <bean>對象(<bean>對象我是是指的 存放在我們的spring容器的當中的對象)
byName的引用數據類型的屬性的自動注入方式:
使用的註解有: 我們需要 @Qualitier 和 @Autowried 這兩個 註解配合在一起使用纔可以。
我們的@Autowried 這個註解 告訴我們的 組件掃描器 在spring容器中進行查找的時候,應該找 相同數據類型的 對象。我們的@Qualitier(value=”對象名稱”)是告訴我們的掃描器 查找對象的時候 需要 找名字相同的,名字爲value的值。
我們的 @Autowired 註解中有一個屬性,這個屬性爲 required。
Required這個屬性是一個boolean 類型的,有兩個值:true和false;
True:設置爲true,產生的效果爲: 當引用類型賦值失敗了,程序會報錯,而且程序會中止執行。
False: 引用類型屬性如果賦值失敗了,程序不會報錯,也不會終止,正常的執行,但是我們的引用數據類型的屬性的值爲 null 。
組件掃描器 是用來掃描我們包中的類中的 註解的,然後 按照我們 註解提供的信息來創建一個 我們 想要的對象。
我們一個組件掃描器(用來掃描 當前包下和所有的子包 中 所含有的 所有的類中的 註解),我們的一個掃描器一次只能掃描一個包,掃面這個包下面的所有的子包和自身包中的類的註解。
我們掃描器 一次性不能掃描兩個 沒有父子關係的 而是兄弟關係或者是同級關係的包。
我們有三種方法來給 多個包 聲明 多個 組件掃面器:
第一種是: 每一個包 對應一個
<context:component-scan base-package="com.bjpowernode" />
第二種:我們只寫一個標籤,但是我們在base-package中同時寫多個包的全地址,地址與地址之間使用;或者,分割。
<context:component-scan base-package="com.bjpowernodo.dao;com.bjpowernode.domain;com.bjpowernode.service" />
第三種是: 我們找到 我們 需要爲那幾個包聲明組件掃描器,然後找出來這幾個包的共同的父包,然後在 base-package上面寫這個父包的 全地址。
補充知識點:
(1)使用 @Resource這個註解來給我們的引用數據類型賦值。
(2)使用這個註解來給我們的 引用數據類型的屬性賦值的時候,不需要提供set方法,直接將這個@Resource註解寫在我們引用數據類型屬性的上面就可以了。
(3)@Resource 這個註解是按照什麼標準和規則進行 對 引用數據類型屬性進行賦值的?
我們利用註解的方式來對引用數據類型賦值無非是遵守兩個規則:一個是 byName 一個是我們的 byByte。
(4)但是 @Resource 註解 先按照 byName的注入規則進行賦值,如果byName的規則不能成功,那麼@Resource這個註解就會再使用byType這種注入的規則。
使用byName規則時候,需要比較名字和類型,可能名字不相同,但是類型相同,然後byName規則賦值失敗,然後使用byType規則賦值的時候,因爲類型相同,所以byType規則賦值成功。
- 我們 如何 讓 @Resource 這個註解 只按照 byName 這種 賦值規則來 進行賦值呢?不讓它在 byName 賦值失敗之後 又使用 byType這種規則賦值,因爲這樣的話可能 在ByName賦值失敗之後 程序會不報錯的,所以說 我們要讓 @Resource這個註解只按照byName這一種賦值方式進行賦值,當byName賦值失敗之後就給我們 報錯 ,我們看到錯誤信息。
方法就是:
@Resource(name=”byName規則用來比較的對象名稱”)
給我們的 @Resource 這個註解增加一個name 屬性,然後給這個name屬性賦值,賦的值爲:需要比較的對象名,這樣我們的@Resource只會使用byName規則進行賦值。
使用 @Configuration 這個註解的類,這個類就當做了xml配置文件了。
總結一下:
- 我們使用一個實體類來模擬我們的xml配置
文件。然後 我們使用實體類中的方法類模擬我們
的xml配置文件中的<bean>標籤。
- 我們如何讓spring知道 我們類就是原先的xml配置文件呢?
在我們的類的上面 使用 @Configruation ,這個@Configruation這個註解就是用來說明我們這個類就是一個配置類。
- 我們如何讓spring知道我們的類中的方法就是原來xml文件中的<bean>標籤呢?
在我們的而每個方法上面 使用 @Bean 這個註解,這個@Bean這個註解就是用來說明我們的這個方法就相當於原來的 <bean>標籤的,使用方法模擬<bean>標籤創建出來的對象的默認名字爲 方法名。 如果我們需要自定義對象的名稱,那麼我們就給@Bean這個註解添加一個name屬性,這個屬性的值就是我們自定義對象的名稱。
一定要記住上面用到的兩個註解:分別爲: @Configruation 和 @Bean 。
回顧一下動態代理:
1)jdk的動態代理:要求目標類必須實現接口,對接口中的方法能增強功能。
使用的類和接口列表:
①:InvocationHandler接口:實現接口中的invoke()方法, 在方法中實現功能增強,例如事務處理
②:Method類: 主要作用是調用執行目標方法 method.invoke();
③:Proxy類:proxy.newProxyInstance();靜態方法,創建代理對象。
2)cglib動態代理, cglib是一個工具庫,實現動態代理的功能,修改內存中的java對象,增加功能。
cglib實現動態代理的原理:繼承, 通過繼承目標類,創建一個子類,在子類中重寫方法的內容,實現
功能的修改,增強。 這個子類就是一個代理類。
cglib使用繼承,要求目標類不能是final的,要增強功能的方法也不能是final的。
cglib在很多的框架中使用。因爲他對目標類的要求低。
cglib動態代理 實現動態代理的方法: 本質就是 繼承的原理,讓子類繼承父類中的方法,然後在子類中重寫父類中的方法,重寫的時候就可以對父類的方法進行修改增強擴展了,這一點和我們 jdk動態代理的作用就是增強和擴展 業務層方法的 功能 是一樣的。
package com.bjpowernode.ba01;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
/**
* @Aspect:表示當前類是切面類。
* 切面類作用是給目標類增強功能的。
* 位置:在類定義的上面
*
*/
@Aspect
public class MyAspect {
/**
* 前置通知:@Before
* 屬性:value ,表示切入點表達式
* 位置:在方法的上面
*
* @Before:表示切面的執行時間
* value:表示切面執行的位置。在哪裏加入切面的功能。
*
* 特點:
* 1.在目標方法之前先執行的。
* 2.不會改變目標方法的執行結果
* 3.不會影響目標方法的執行
*/
/*@Before(value="execution(* com.bjpowernode.ba01.SomeServiceImpl.doSome(..))")
public void myBefore(){
//切面的代碼,要增強的功能,寫在這裏
System.out.println("前置通知:在目標方法之前,加入日誌的功能(切面)");
}*/
/**
* 參數:
* JoinPoint:表示切入點表達式中的方法。
*/
/*@Before(value="execution(* com.bjpowernode.ba01.SomeServiceImpl.do*(..))")
public void myBefore(JoinPoint jp){
//獲取執行的方法名
String methodName = jp.getSignature().getName();
//獲取參數個數
Object args [] = jp.getArgs();//表示方法的參數集合
for(Object arg:args){
System.out.println(arg);
}
//切面的代碼,要增強的功能,寫在這裏
System.out.println("前置通知:在目標方法之前,加入日誌的功能(切面)當前正在執行的方法名:"+methodName+",方法的參數個數:"+args.length);
}*/
@Before(value="execution(* *..SomeServiceImpl.*(..))")
public void myBefore(JoinPoint jp){
//獲取執行的方法名
String methodName = jp.getSignature().getName();
//獲取參數個數
Object args [] = jp.getArgs();//表示方法的參數集合
for(Object arg:args){
System.out.println(arg);
}
//切面的代碼,要增強的功能,寫在這裏
System.out.println("前置通知:在目標方法之前,加入日誌的功能(切面)當前正在執行的方法名:"+methodName+",方法的參數個數:"+args.length);
}
}
我的理解:
package com.bjpowernode.proxy;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component(value="myProxy")
@Aspect //這個註解的意思是說明我們的這個類 是 一個 切面類,切面類是在我們的AspectJ這個框架裏面的。
public class MyProxy {//切面類中的切面就是我們類中的方法。
@Before(value="execution(* com.bjpowernode.service.UserServiceImpl.addUser(..))") //before註解的意思是 我們這個切面 切入的時間,是在 目標方法之前還是目標方法之後,我們的這個before是 表示 在目標方法執行前 切入。
//上面的意思是: 位置在com.bjpowernode.dao.UserDaoOracleImpl類中的 任意返回值類型任意參數個數的insertUser方法。
//value代表的意思是:切入點位置,這個value的值有一個 切入點表達式,一般是:
// value="execution(* com.bjpowernode.service.目標類.目標方法(..))" 其中..的意思是:任意個參數。
//如果..跟在包名的後面:說明是 包及其子包 。
// * 代表任意多個字符,..跟在包後面說明 是 本包及其子包 ..在包的前面 說明 本包和它的父包。 ..在參數裏面代表任意多個參數。
public void addAction(){//我們的這個增強目標類中目標方法的 切面 必須是 public void 的,因爲如果你 有返回值的話,到了我們的業務層目標方法中 又要處理你這個方法的返回值,這樣子我們的業務層方法又不能專心做業務了。
System.out.println("你被強化了!!!出去戰鬥吧!!!");
}
}