1. 程式人生 > >Spring框架知識複習之二

Spring框架知識複習之二

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>