java框架之Spring(2)-註解配置IOC&AOP配置

註解配置IoC

準備

一、要使用註解方式配置 IoC,除了以前引入的基礎 jar 包,還須要引入 spring-aop 支持包,以下:html

二、在 applicationContext.xml 中引入 context 約束:java

<?xml version="1.0" encoding="utf-8" ?>
<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">
    <!--配置文件約束在 spring-framework-4.2.4.RELEASE\docs\spring-framework-reference\html\xsd-configuration.html 下能夠找到-->
</beans>

簡單使用

package com.zze.dao;

public interface UserDao {
    void save();
}
com.zze.dao.UserDao
package com.zze.dao.impl;

import com.zze.dao.UserDao;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * 實例化當前類並將其交給 Spring 管理
 * 至關於在配置文件中配置 <bean id="userDao" class="com.zze.dao.impl.UserDaoImpl"/>
 */
@Component("userDao")
public class UserDaoImpl implements UserDao {
    /*
    給屬性注入值
     */
    @Value("張三")
    private String name;

    /*
    若是提供了 set 方法,則可在 set 方法上加上註解
     */
//    @Value("張三")
//    public void setName(String name) {
//        this.name = name;
//    }

    @Override
    public void save() {
        System.out.println("from UserDaoImpl.save()" + name);
    }
}
com.zze.dao.impl.UserDaoImpl : 使用註解
<?xml version="1.0" encoding="utf-8" ?>
<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">
    <!--使用 IoC 註解開發,配置註解掃描的包(哪些包下使用了 IoC 註解)-->
    <context:component-scan base-package="com.zze.dao"/>
</beans>
applicationContext.xml : 配置註解掃描的包
@Test
public void test1(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserDao userDao = (UserDao) applicationContext.getBean("userDao");
    userDao.save();
}
test

經常使用註解

@Component:實例化註解

修飾一個類,將這個類實例交給 Spring 管理。web

這個註解有三個衍生註解(功能相同):spring

  • @Controller:修飾 web 層的類。
  • @Service:修飾 service 層的類。
  • @Repository:修飾 dao 層的類。

屬性注入註解

普通類型的屬性注入:數據庫

  • @Value:設置普通屬性的值。

對象類型的屬性注入:express

  • @Autowired:設置對象類型屬性的值。
    默認狀況下是按照類型匹配完成屬性注入,若是想按照名稱匹配完成屬性注入,能夠同時再使用一個註解 @Qualifier 來指定要注入對象的名稱。
    @Autowired
    @Qualifier("userDao")
    private UserDao userDao;
    例:
  • @Resource:根據對象名稱匹配完成屬性注入。
    @Resource(name = "userDao")
    private UserDao userDao;
    例:

生命週期相關注解

  • @PostConstruct:修飾一個方法,讓其在實例化時執行,至關於在 bean 標籤中指定 init-method 。
  • @PreDestroy:修飾一個方法,讓其在實例銷燬以前執行,至關於在 bean 標籤中指定 destroy-method。

@Scope:做用範圍註解

修飾一個類,指定其做用範圍。編程

value 有以下幾個可選值:

    singleton :默認值,Spring 會採用單例模式建立這個對象。session

    prototype :多例的。app

    request :應用在 web 項目中,Spring 建立這個類對象後,將這個對象存放到 request 範圍中。框架

    session :應用在 web 項目中,Spring 建立這個類對象後,將這個對象存放的 session 範圍中。

    globalsession :應用在 web 項目中,必須在 porlet 環境下使用。

總結

IoC的XML和註解方式開發相比較

       XML 方式:結構清晰,維護方便,適用於任意場景。

       註解方式:簡易開發,當一個類不是本身編寫而是由第三方提供時,不可使用。

XML方式和註解方式整合開發

<?xml version="1.0" encoding="utf-8" ?>
<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">
    <!--在沒有類註解掃描的狀況下,使用屬性注入註解。-->
    <context:annotation-config/>

    <bean name="userService" class="com.zze.service.impl.UserServiceImpl"/>

    <bean name="userDao" class="com.zze.dao.impl.UserDaoImpl"/>
</beans>
applicationContext.xml
package com.zze.service.impl;

import com.zze.dao.UserDao;
import com.zze.service.UserService;

import javax.annotation.Resource;

public class UserServiceImpl implements UserService {
    @Resource(name = "userDao")
    private UserDao userDao;

    @Override
    public void save() {
        System.out.println("from UserServiceImpl.save()");
        userDao.save();
    }
}
com.zze.service.impl.UserServiceImpl

AOP配置

概述

在軟件業,AOP 爲 Aspect Oriented Programming 的縮寫,意爲:面向切面編程,經過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。AOP 是 OOP 的延續,是軟件開發中的一個熱點,也是 Spring 框架中的一個重要內容,是函數式編程的一種衍生範型。利用 AOP 能夠對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度下降,提升程序的可重用性,同時提升了開發的效率。

AOP 思想最先是由 AOP 聯盟組織提出,Spring 是目前使用這種思想最好的框架。

Spring 自己有本身的 AOP 實現方式,可是很是繁瑣。AspectJ 是一個 AOP 框架,Spring 後期引入了 AspectJ 用做自身 AOP 開發。

Spring中AOP實現原理:

Spring 底層是使用動態代理技術實現 AOP。當被代理類實現了接口,此時就會使用 JDK 動態代理方式生成代理對象。當被代理類未實現接口,此時 Spring 就會使用 Cglib 動態代理方式生成代理對象。

AOP相關術語

  • 鏈接點(Joinpoint):

    程序執行的某個特定位置:如類開始初始化前、類初始化後、類某個方法調用前、調用後、方法拋出異常後。一個類或一段程序代碼擁有一些具備邊界性質的特定點,這些點中的特定點就稱爲「鏈接點」。Spring 僅支持方法的鏈接點,即僅能在方法調用前、方法調用後、方法拋出異常時以及方法調用先後這些程序執行點織入加強。鏈接點由兩個信息肯定:第一是用方法表示的程序執行點;第二是用相對點表示的方位。

    通俗講其實就是能夠被攔截到的點(方法),增刪改查方法均可以被攔截加強,這些方法就能夠稱爲是鏈接點。

  • 切入點(Pointcut):

    每一個程序類都擁有多個鏈接點,如一個擁有兩個方法的類,這兩個方法都是鏈接點,即鏈接點是程序類中客觀存在的事物。AOP經過「切點」定位特定的鏈接點。鏈接點至關於數據庫中的記錄,而切點至關於查詢條件。切點和鏈接點不是一對一的關係,一個切點能夠匹配多個鏈接點。在Spring中,切點經過org.springframework.aop.Pointcut接口進行描述,它使用類和方法做爲鏈接點的查詢條件,Spring AOP的規則解析引擎負責切點所設定的查詢條件,找到對應的鏈接點。其實確切地說,不能稱之爲查詢鏈接點,由於鏈接點是方法執行前、執行後等包括方位信息的具體程序執行點,而切點只定位到某個方法上,因此若是但願定位到具體鏈接點上,還須要提供方位信息。

    通俗講就是真正被攔截到的點(方法),若是在開發中只對 save 方法進行加強,那麼 save 就稱爲是切入點。

  • 加強、通知(Advice):

    加強是織入到目標類鏈接點上的一段程序代碼,在 Spring 中,加強除用於描述一段程序代碼外,還擁有另外一個和鏈接點相關的信息,這即是執行點的方位。結合執行點方位信息和切點信息,咱們就能夠找到特定的鏈接點。

    通俗講就是方法層面的加強,若是要使用 checkPermission 方法進行權限校驗,那麼 checkPermission 方法就稱爲是加強。

    幾種通知類型:
    • 前置通知:在目標方法執行以前進行的操做。
    • 後置通知:在目標方法執行以後進行的操做。
    • 環繞通知:在目標方法執行以前和以後進行的操做。
    • 異常拋出通知:在程序出現異常時進行的操做。
    • 最終通知:不管代碼是否有異常,老是會執行。
  • 目標對象(Target):

    加強邏輯的織入目標類。若是沒有 AOP,目標業務類須要本身實現全部邏輯,而在 AOP 的幫助下,目標業務類只實現那些非橫切邏輯的程序邏輯,而性能監視和事務管理等這些橫切邏輯則可使用 AOP 動態織入到特定的鏈接點上。

    通俗講就是指被加強的類,若是對 UserDao 類進行加強,那麼 UserDao 類就是目標對象。

  • 引介(Introduction):

    引介是一種特殊的加強,它爲類添加一些屬性和方法。這樣,即便一個業務類本來沒有實現某個接口,經過 AOP 的引介功能,咱們能夠動態地爲該業務類添加接口的實現邏輯,讓業務類成爲這個接口的實現類。

    通俗講就是類層面的加強。

  • 織入(Weaving):

    織入是將加強添加對目標類具體鏈接點上的過程。AOP 像一臺織布機,將目標類、加強或引介經過 AOP 這臺織布機完美無缺地編織到一塊兒。根據不一樣的實現技術,AOP有三種織入的方式:

    a、編譯期織入,這要求使用特殊的Java編譯器。
    b、類裝載期織入,這要求使用特殊的類裝載器。
    c、動態代理織入,在運行期爲目標類添加加強生成子類的方式。
    Spring採用動態代理織入,而AspectJ採用編譯期織入和類裝載期織入。

    通俗講就是將通知(Advice)應用到目標對象(Target)的過程。

  • 代理(Proxy):

    一個類被 AOP 織入加強後,就產出了一個結果類,它是融合了原類和加強邏輯的代理類。根據不一樣的代理方式,代理類既多是和原類具備相同接口的類,也可能就是原類的子類,因此咱們能夠採用調用原類相同的方式調用代理類。

    通俗講就是最後返回的代理對象。

  • 切面(Aspect):

    切面由切入點和加強(引介)組成,它既包括了橫切邏輯的定義,也包括了鏈接點的定義,Spring AOP 就是負責實施切面的框架,它將切面所定義的橫切邏輯織入到切面所指定的鏈接點中。

    通俗講就是切入點與通知的組合。

XML方式

一、導包,除了要引入 6 個基礎 jar 包外,還需引入以下 jar 包:

二、編寫切面類:

package com.zze.aspect;

import org.aspectj.lang.ProceedingJoinPoint;

/**
 * 切面類
 */
public class MyAspect {
    public void before() {
        System.out.println("前置通知");
    }

    public void afterReturning(Object result) {
        System.out.println("後置通知" + result);
    }

    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("環繞通知前");
        joinPoint.proceed();
        System.out.println("環繞通知後");
    }

    public void afterThrowing(Throwable ex) {
        System.out.println("異常拋出通知" + ex);
    }

    public void after(){
        System.out.println("最終通知");
    }
}
com.zze.aspect.MyAspect

三、編寫目標類:

package com.zze.dao;

public interface UserDao {
    public void save();

    public Boolean delete();

    public void list();

    public void update();
}
com.zze.dao.UserDao
package com.zze.dao.impl;

import com.zze.dao.UserDao;

public class UserDaoImpl implements UserDao {
    public void save() {
        System.out.println("保存操做 from com.zze.dao.impl.UserDaoImpl.save()");
    }

    public Boolean delete() {
        System.out.println("刪除操做 from com.zze.dao.impl.UserDaoImpl.delete()");
        return true;
    }

    public void list() {
        System.out.println("查詢操做 from com.zze.dao.impl.UserDaoImpl.list()");
        int i = 1 / 0;
    }

    public void update() {
        System.out.println("修改操做 from com.zze.dao.impl.UserDaoImpl.update()");
    }
}
com.zze.dao.impl.UserDaoImpl

四、配置 applicationContext.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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--配置目標對象-->
    <bean name="userDao" class="com.zze.dao.impl.UserDaoImpl" />
    <!--配置切面類,將切面類交給 Spring 管理-->
    <bean name="myAspect" class="com.zze.aspect.MyAspect"/>

    <!--經過 AOP 配置對目標類代理-->
    <aop:config>
        <!--
        表達式配置使用哪些方法類加強哪些類
        語法:
            [訪問修飾符] 方法返回值 包名.類名.方法名(參數)
            * 表示通配符
            參數位置使用 .. 匹配任意參數
        -->
        <aop:pointcut id="pc1" expression="execution(* com.zze.dao.UserDao.save(..))"/>
        <aop:pointcut id="pc2" expression="execution(* com.zze.dao.UserDao.delete(..))"/>
        <aop:pointcut id="pc3" expression="execution(* com.zze.dao.UserDao.update(..))"/>
        <aop:pointcut id="pc4" expression="execution(* com.zze.dao.UserDao.list(..))"/>
        <!--配置切面-->
        <aop:aspect ref="myAspect">
            <!--前置通知-->
            <aop:before pointcut-ref="pc1" method="before"/>
            <!--後置通知-->
            <aop:after-returning pointcut-ref="pc2" method="afterReturning" returning="result"/>
            <!--環繞通知-->
            <aop:around pointcut-ref="pc3" method="around"/>
            <!--異常拋出通知-->
            <aop:after-throwing pointcut-ref="pc4" method="afterThrowing" throwing="ex"/>
            <!--最終通知-->
            <aop:after pointcut-ref="pc4" method="after"/>
        </aop:aspect>
    </aop:config>
</beans>

五、測試:

package com.zze.test;

import com.zze.dao.UserDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import javax.annotation.Resource;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Test{
    @Resource(name = "userDao")
    private UserDao userDao;

    @Test
    public void test() {
        userDao.save();
        userDao.delete();
        userDao.update();
        userDao.list();
        /*
        前置通知
        保存操做 from com.zze.dao.impl.UserDaoImpl.save()
        刪除操做 from com.zze.dao.impl.UserDaoImpl.delete()
        後置通知true
        環繞通知前
        修改操做 from com.zze.dao.impl.UserDaoImpl.update()
        環繞通知後
        查詢操做 from com.zze.dao.impl.UserDaoImpl.list()
        最終通知
        異常拋出通知java.lang.ArithmeticException: / by zero
         */
    }
}

這裏使用了 Spring 整合 JUnit 測試,須要額外導入 spring-test 包。

註解方式

一、導包,和 XML 配置 AOP 時相同,以下:

二、編寫目標類:

package com.zze.dao;


public class UserDao {
    public void save() {
        System.out.println("保存操做 from com.zze.dao.impl.UserDao.save()");
    }

    public Boolean delete() {
        System.out.println("刪除操做 from com.zze.dao.impl.UserDao.delete()");
        return true;
    }

    public void list() {
        System.out.println("查詢操做 from com.zze.dao.impl.UserDao.list()");
        int i = 1 / 0;
    }

    public void update() {
        System.out.println("修改操做 from com.zze.dao.impl.UserDao.update()");
    }
}
com.zze.dao.UserDao

三、編寫切面類並使用註解配置:

package com.zze.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

/**
 * 切面類
 */
@Aspect
public class MyAspect {

    /**
     * 定義一個切入點表達式,在下方可引用
     */
    @Pointcut("execution(* com.zze.dao.UserDao.list(..))")
    public void pc1() {
    }

    /**
     * 前置通知
     */
    @Before(value = "execution(* com.zze.dao.UserDao.save(..))")
    public void before() {
        System.out.println("前置通知");
    }

    /**
     * 後置通知
     */
    @AfterReturning(value = "execution(* com.zze.dao.UserDao.delete(..))", returning = "result")
    public void afterReturning(Object result) {
        System.out.println("後置通知" + result);
    }

    /**
     * 環繞通知
     */
    @Around(value = "execution(* com.zze.dao.UserDao.update(..))")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("環繞通知前");
        joinPoint.proceed();
        System.out.println("環繞通知後");
    }

    /**
     * 異常拋出通知
     */
    @AfterThrowing(value = "pc1()", throwing = "ex")
    public void afterThrowing(Throwable ex) {
        System.out.println("異常拋出通知" + ex);
    }

    /**
     * 最終通知
     */
    @After(value = "pc1()")
    public void after() {
        System.out.println("最終通知");
    }
}
com.zze.aspect.MyAspect

四、配置 applicationContext.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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--開啓註解 AOP 開發-->
    <aop:aspectj-autoproxy/>
    <!--目標類-->
    <bean name="userDao" class="com.zze.dao.UserDao"/>
    <!--切面類-->
    <bean name="myAspect" class="com.zze.aspect.MyAspect"/>
</beans>

五、測試:

package com.zze.test;

import com.zze.dao.UserDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo2 {
    @Autowired
    private UserDao userDao;

    @Test
    public void test() {
        userDao.save();
        userDao.delete();
        userDao.update();
        userDao.list();
        /*
        前置通知
        保存操做 from com.zze.dao.impl.UserDao.save()
        刪除操做 from com.zze.dao.impl.UserDao.delete()
        後置通知true
        環繞通知前
        修改操做 from com.zze.dao.impl.UserDao.update()
        環繞通知後
        查詢操做 from com.zze.dao.impl.UserDao.list()
        最終通知
        異常拋出通知java.lang.ArithmeticException: / by zero
         */
    }
}
相關文章
相關標籤/搜索