Spring使用註解對Bean進行管理
1 使用註解需配置aop相關xsd文件的約束和命名空間
xsd文件名爲:spring-aop-4.2.xsd
2 註解組件掃描配置
示例如下:base-package屬性 設置掃描指定包下的所有子孫包
<context:component-scan base-package="cn.itma.bean"></context:component-scan>
3 spring管理bean的常用註解
<1>作用在類上的 用於創建對象的 @Component組件註解 的三個衍生註解(功能一致)
* @Controller :WEB層
* @Service:業務層
* @Repository:持久層
示例如下
註解書寫:
package xxx.yyy.test;
@Component("user")
@Scope(scopeName="prototype")
public class User{
}
對應的xml配置書寫:
<bean name="user" class="xxx.yyy.test.User" scope="prototype"/>
<2> 用於屬性注入的註解 (使用註解注入的方式,可以不用提供set方法)
* @Value:用於注入普通類型
* @Autowired:自動裝配(默認按類型進行裝配)
存在的問題:
如果匹配多個類型一致的對象,將無法選擇具體注入哪一個對象,
需要加一個註解輔助,聲明它要注入哪一個Car對象
* @Qualifier:強制按照名稱進行注入。
存在的問題:
如果存在多個名稱一致的,類型不一致的對象,將無法準確匹配到。
* @Resource:相當於@Autowired和@Qualifier一起使用
示例如下
註解書寫:
public class User{
@Value("flower")
private String name;
public String getName() {return name;}
public void setName(String name) {this.name = name;}
@Resource(name="car")
private Car c;
}
對應的xml配置書寫:
<bean name="user" class="xxx.yyy.User">
<property name="name" value="flower"/>
<property name="car" ref="car"></property>
</bean>
<bean name="car" class="xx.bean.Car">
<property name="name" value="dd"></property>
<property name="color" value="black"></property>
</bean>
<3>Spring整合junit測試的 註解
* 導入的包爲:spring-test-4.2.4.RELEASE.jar
* @RunWith(SpringJUnit4Cla***unner.class) --幫我們創建容器
* @ContextConfiguration("classpath:applicationContext.xml") --指定創建容器使用哪個配置文件
<4>設置Bean作用範圍的註解
@Scope:
* singleton:單例
* prototype: 多例
<4>設置Bean生命週期的註解
@PostConstruct:相當於init-method
@PreDestroy:相當於destroy-method
示例如下
註解書寫:
public class User{
@PostConstruct
public void init(){
syso("這裏是初始化方法");
}
@PreDestroy
public void destroy(){
syso("這裏是銷燬方法")
}
}
對應的xml配置書寫:
<bean name="user" class="xxx.yyy.User" init-method="init" destroy-method="destroy">
</bean>
4 spring管理bean的xml方式與註解方式對比
xml配置 註解配置
(1)bean的創建和定義 <bean id="" class=""/> @Component(三個衍生:Controller,Service,Repository)
(2)bean名稱的指定 通過id或name指定 @Component("person")
(3)bean中參數的注入 通過<property>或p命名空間 @Autowired(按類型注入)或@QUalifier(按名稱注入)或Resource(兩者相加)
(4)bean生命週期和 通過設置Scope屬性,包括: singleton和prototype @Scope(scopeName="singleton")作用範圍的設置
總結:
xml結構清晰,註解開發方便,所以實際開發中有一種xml和註解開發(Bean有XML配置,但使用的屬性用註解注入)。
5 Spring和AOP面向切面編程
1 AOP概述
AOP(aspect oriented Programming面向切面編程),AOP是OOP(面向對象編程)的延續,
也是Spring框架的一個重要內容,spring利用AOP可以對業務邏輯各個部分進行隔離,
降低業務邏輯之間的耦合性,提供程序的重用性和開發效率。
2 Spring中AOP的主要功能
在不修改源碼的基礎上,對程序進行增強,可進行權限校驗,日誌記錄,性能監控,事務控制。
3 Spring的AOP底層實現
兩種代理機制:
* JDK的動態代理:針對實現了接口的類產生代理
示例:
//手動實現動態代理--演示(針對的是代理對象和被代理對象的實現同一接口)
public class UserServiceProxyFactory implements InvocationHandler {
private UserService us;
public UserServiceProxyFactory() {super();}
public UserServiceProxyFactory(UserService us) {super();this.us = us;}
public UserService getUserServiceProxy() {
//生成 UserService動態代理對象
UserService usProxy=(UserService) Proxy.newProxyInstance(UserServiceProxyFactory.class.getClassLoader(),
UserServiceImp.class.getInterfaces(),
this);
//返回
return usProxy;
}
//下面爲動態代理對象 對原對象的方法加強
@Override
public Object invoke(Object arg0, Method method, Object[] arg2) throws Throwable {
System.out.println("打開事務");
Object invoke = method.invoke(us,arg2);
System.out.println("提交事務");
return invoke;
}
}
* Cglib的動態代理:針對沒有實現的接口的類產生代理,應用的是底層字節碼增強技術,生成當前類的子類對象。
示例:
//通過第三方實現 代理技術--cglib技術(針對的是代理對象和被代理對象的繼承關係)
public class UserServiceProxyFactory2 implements MethodInterceptor{
private UserService us;
public UserServiceProxyFactory2(UserService us) {super();this.us = us;}
public UserServiceProxyFactory2() {super();}
public UserService getUserServiceProxy() {
Enhancer en=new Enhancer(); //幫我們生成代理的對象
en.setSuperclass(UserServiceImp.class); //設置對誰進行代理
en.setCallback(this); //指定代理對象 要增強什麼功能(代理要做什麼)
UserService us = (UserService) en.create(); //創建代理對象
return us;
}
@Override
public Object intercept(Object proxyobj, Method method, Object[] arg, MethodProxy methodProxy) throws Throwable {
//打開事務
System.out.println("打開事務");
//調用原有方法
Object returnValue = methodProxy.invokeSuper(proxyobj,arg);
//提交事務
System.out.println("提交事務");
return returnValue;
}
}
4 Spring基於AspectJ的AOP開發
(1)Spring的AOP開放相關術語:
<1>Joinpoint(連接點):所謂連接點是指那些被攔截到的點。在spring中,這些點指的是方法,因爲spring只支持方法類型的連接點.
<2>Pointcut(切入點):所謂切入點是指我們要對哪些Joinpoint進行攔截的定義
& execution()切入點表達式的基本語法:
* 基本格式:execution( [方法訪問修飾符] 方法返回值 全類名.方法名(方法參數))
* 配置方法名的不同形式:
public void cn.itheima.service.UserServiceImp.save() 簡單形式
void cn.itheima.service.UserServiceImp.save() public可省略
* cn.itheima.service.UserServiceImp.save() *代表任意的返回值類型
* cn.itheima.service.UserServiceImp.*() *()代表該目標對象下的所有方法
* cn.itheima.service.UserServiceImp.*(..) *(..)對方法參數不作任何要求(空參和實參都行)
//下面爲開發常用形式
* cn.itheima.service.*ServiceImp.*(..) *ServiceImp代表名稱包含 ServiceImp的目標對象(是開發的標準形式)
* cn.itheima.service..*ServiceImp.*(..) ..*ServiceImp 代表 在包括service下的子孫包中的 名稱包含 ServiceImp的目標對象
& 配置AOP切入點示例:
<aop:config>
id屬性:爲切入點命名
<aop:pointcut expression="execution(* cn.ith.service.*ServiceImp.*(..))" id="pc"/>
</aop:config>
<3>Advice(通知/增強):
所謂通知是指攔截到Joinpoint之後所要做的事情就是通知.
通知分爲前置通知,後置通知,異常通知,最終通知,環繞通知(切面要完成的功能)
有一種特殊的通知:Introduction(引介)
不修改類代碼的前提下, Introduction可以在運行期爲類動態地添加一些方法或Field.
通知類型詳解
前置通知(before):在目標方法執行 之前.
後置通知(afterReturning) :在目標方法執行 之後,如果出現異常則不會調用
環繞通知(around) :在目標方法執行 前和後
異常拋出通知(afterException):在目標方法執行 出現異常的時候 執行
最終通知(after) :無論目標方法是否出現異常 最終通知都會 執行 .
<4>Aspect(切面): 是切入點和通知(引介)的結合
配置切面:
<!-- 屬性ref:要關聯的 通知 名稱 -->
<!--2.配置通知對象(目標對象的增強版)-->
<bean name="myAdvice" class="cn.itheima.d_aspect.MyAdvice"></bean>
<aop:aspect ref="myAdvice">
<!-- method屬性:設置方法名 pointcut-ref:設置要關聯的 切點名稱-->
<!--指定名爲before爲前置通知-->
<aop:before method="before" pointcut-ref="pc"/>
<!--指定名爲afterReturning爲後置通知(出現異常不會調用) -->
<aop:after-returning method="afterReturning" pointcut-ref="pc"/>
<!--指定名爲around爲環繞通知 -->
<aop:around method="around" pointcut-ref="pc"/>
<!--指定名爲afterException爲異常攔截通知 -->
<aop:after-throwing method="afterException" pointcut-ref="pc"/>
<!--指定名爲after爲後置通知(是否出現異常都會調用) -->
<aop:after method="after" pointcut-ref="pc"/>
</aop:aspect>
<5>Target(目標對象):代理的目標對象
<6>Weaving(織入):
是指把增強應用到目標對象來創建新的代理對象的過程。
spring採用動態代理織入,而AspectJ採用編譯期織入和類裝在期織入.
AspectJ是一個面向切面的框架,它定義了AOP語法,擴展了Java語言。
<7>Proxy(代理):一個類被AOP織入增強後,就產生一個結果代理類
(2)Spring使用AspectJ進行AOP開發
<1>要導入相關的jar包,如下
* spring傳統aop開發包:
spring-aop-4.2.4.RELEASE.jar
com.springsource.org.aopalliance-1.0.0.jar
* aspectJ開發包:
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
spring-aspects-4.2.4.RELEASE.jar
<2>導入aop約束
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
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-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd ">
<3>配置xml方式實現AOP的代碼示例
示例需求:對一個UserServiceImpl類 進行AOP增強 其save()方法
* UserService類(目標對象)
public interface UserService {void save();}
public class UserServiceImp implements UserService {
@Override
public void save() {
System.out.println("保存");
}
}
* MyAdvice(通知對象)
public class MyAdvice {
public void before() {
System.out.println("前置通知!");
}
public void afterReturning() {
System.out.println("後置通知!(如果出現異常則不會調用)");
}
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("這是環繞通知之前的部分!");
Object proceed = pjp.proceed(); //調用目標方法
System.out.println("這是環繞通知之後的部分!");
return proceed;
}
public void afterException() {
System.out.println("出現異常了!");
}
public void after() {
System.out.println("另一個後置通知(目標方法運行之後調用,無論是否出現異常都會調用)");
}
}
* AOP相關配置
<!--1.配置目標對象 -->
<bean name="userService" class="cn.ith.service.UserServiceImp"></bean>
<!--2.配置通知對象(目標對象的增強版)-->
<bean name="myAdvice" class="cn.it_aspect.MyAdvice"></bean>
<!--3.配置將通知織入目標對象 -->
<aop:config>
配置切入點
<aop:pointcut expression="execution(* cn.itheima.service.*ServiceImp.*(..))" id="pc"/>
配置切面(切點+通知)
<aop:aspect ref="myAdvice">
<aop:before method="before" pointcut-ref="pc"/>
<aop:after-returning method="afterReturning" pointcut-ref="pc"/>
<aop:around method="around" pointcut-ref="pc"/>
<aop:after-throwing method="afterException" pointcut-ref="pc"/>
<aop:after method="after" pointcut-ref="pc"/>
</aop:aspect>
</aop:config>
* 測試類代碼
@RunWith(SpringJUnit4Cla***unner.class)
@ContextConfiguration("classpath:cn/itheima/d_aspect/applicationContext.xml")
public class Demo {
//將名爲 userService的目標對象注入到 us成員變量中
@Resource(name="userService")
private UserService us;
@Test
public void fun1() {
us.save();
}
}
* 結果對比
未使用AOP時調用UserService的save方法,結果爲 :輸出 "保存"字符串
使用了AOP後,字符串輸出結果爲:
前置通知!
這是環繞通知之前的部分!
保存
另一個後置通知(目標方法運行之後調用,無論是否出現異常都會調用)
這是環繞通知之後的部分!
後置通知!(如果出現異常則不會調用)
<4>註解方式 實現AOP的代碼示例
示例需求:對一個UserServiceImpl類 進行AOP增強 其save()方法
* UserService類(目標對象)
public interface UserService {void save();}
public class UserServiceImp implements UserService {
@Override
public void save() {
System.out.println("保存");
}
}
* MyAdvice(通知對象)
//注入切面
@Aspect
public class MyAdvice {
//注入切點
@Pointcut("execution(* cn.itheima.service.*ServiceImp.*(..))")
//空參方法名pc作爲 該切點的id
public void pc(){}
//注入前置通知,並指定切入點
//@Before("execution(* cn.itheima.service.*ServiceImp.*(..))")
@Before("MyAdvice.pc()") //簡寫
public void before() {
System.out.println("前置通知!");
}
//注入後置通知,並指定切入點
@AfterReturning("execution(* cn.itheima.service.*ServiceImp.*(..))")
public void afterReturning() {
System.out.println("後置通知!(如果出現異常則不會調用)");
}
//注入環繞通知,並指定切入點
@Around("execution(* cn.itheima.service.*ServiceImp.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("這是環繞通知之前的部分!");
Object proceed = pjp.proceed(); //調用目標方法
System.out.println("這是環繞通知之後的部分!");
return proceed;
}
//注入異常攔截通知,並指定切入點
@AfterThrowing("execution(* cn.itheima.service.*ServiceImp.*(..))")
public void afterException() {
System.out.println("出現異常了!");
}
//注入後置通知,並指定切入點
@After("execution(* cn.itheima.service.*ServiceImp.*(..))")
public void after() {
System.out.println("另一個後置通知(目標方法運行之後調用,無論是否出現異常都會調用)");
}
}
* AOP內的配置內容
<!--1.配置目標對象 -->
<bean name="userService" class="cn.itma.service.UserServiceImp"></bean>
<!--2.配置通知對象 -->
<bean name="myAdvice" class="cn.ithnotationAop.MyAdvice"></bean>
<!-- 開啓使用註解完成織入 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>